9191 * text/html} request {@code Accept} header has the same result. 
9292 * 
9393 * @author Arjen Poutsma 
94-  * @author Rostislav Hristov 
94+  * @author Juergen Hoeller 
95+  * @since 3.0 
9596 * @see ViewResolver 
9697 * @see InternalResourceViewResolver 
9798 * @see BeanNameViewResolver 
98-  * @since 3.0 
9999 */ 
100100public  class  ContentNegotiatingViewResolver  extends  WebApplicationObjectSupport  implements  ViewResolver , Ordered  {
101101
102+ 	private  static  final  String  ACCEPT_HEADER  = "Accept" ;
103+ 
102104	private  static  final  boolean  jafPresent  =
103105			ClassUtils .isPresent ("javax.activation.FileTypeMap" , ContentNegotiatingViewResolver .class .getClassLoader ());
104106
105- 	private  static  final  String  ACCEPT_HEADER  = "Accept" ;
107+ 	private  static  final  UrlPathHelper  urlPathHelper  = new  UrlPathHelper ();
108+ 
106109
107- 	private  UrlPathHelper   urlPathHelper  = new   UrlPathHelper () ;
110+ 	private  int   order  = Ordered . HIGHEST_PRECEDENCE ;
108111
109112	private  boolean  favorPathExtension  = true ;
110113
111114	private  boolean  favorParameter  = false ;
112115
113- 	private  boolean  ignoreAcceptHeader  = false ;
114- 
115116	private  String  parameterName  = "format" ;
116117
117- 	private  int   order  = Ordered . HIGHEST_PRECEDENCE ;
118+ 	private  boolean   ignoreAcceptHeader  = false ;
118119
119120	private  ConcurrentMap <String , MediaType > mediaTypes  = new  ConcurrentHashMap <String , MediaType >();
120121
121122	private  List <View > defaultViews ;
122123
124+ 	private  MediaType  defaultContentType ;
125+ 
123126	private  List <ViewResolver > viewResolvers ;
124127
125- 	private  MediaType  defaultContentType ;
126128
127129	public  void  setOrder (int  order ) {
128130		this .order  = order ;
129131	}
130132
131133	public  int  getOrder () {
132- 		return  order ;
134+ 		return  this . order ;
133135	}
134136
135137	/** 
136- 	 * Indicates whether the extension of the request path should be used to determine the requested media type, in favor 
137- 	 * of looking at the {@code Accept} header. The default value is {@code true}. 
138- 	 * 
139- 	 * <p>For instance, when this flag is <code>true</code> (the default), a request for {@code /hotels.pdf} will result in 
140- 	 * an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined {@code 
141- 	 * text/html,application/xhtml+xml}. 
138+ 	 * Indicates whether the extension of the request path should be used to determine the requested media type, 
139+ 	 * in favor of looking at the {@code Accept} header. The default value is {@code true}. 
140+ 	 * <p>For instance, when this flag is <code>true</code> (the default), a request for {@code /hotels.pdf} 
141+ 	 * will result in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the 
142+ 	 * browser-defined {@code text/html,application/xhtml+xml}. 
142143	 */ 
143144	public  void  setFavorPathExtension (boolean  favorPathExtension ) {
144145		this .favorPathExtension  = favorPathExtension ;
145146	}
146147
147148	/** 
148- 	 * Indicates whether a request parameter should be used to determine the requested media type, in favor of looking at 
149- 	 * the {@code Accept} header. The default value is {@code false}. 
150- 	 * 
151- 	 * <p>For instance, when this flag is <code>true</code>, a request for {@code /hotels?format=pdf} will result in an 
152- 	 * {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined {@code 
153- 	 * text/html,application/xhtml+xml}. 
149+ 	 * Indicates whether a request parameter should be used to determine the requested media type, 
150+ 	 * in favor of looking at the {@code Accept} header. The default value is {@code false}. 
151+ 	 * <p>For instance, when this flag is <code>true</code>, a request for {@code /hotels?format=pdf} will result 
152+ 	 * in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined 
153+ 	 * {@code text/html,application/xhtml+xml}. 
154154	 */ 
155155	public  void  setFavorParameter (boolean  favorParameter ) {
156156		this .favorParameter  = favorParameter ;
@@ -166,20 +166,18 @@ public void setParameterName(String parameterName) {
166166
167167	/** 
168168	 * Indicates whether the HTTP {@code Accept} header should be ignored. Default is {@code false}. 
169- 	 * 
170- 	 * If set to {@code true}, this view resolver will only refer to the file extension and/or paramter, as indicated by 
171- 	 * the {@link #setFavorPathExtension(boolean) favorPathExtension} and {@link #setFavorParameter(boolean) 
172- 	 * favorParameter} properties. 
169+ 	 * If set to {@code true}, this view resolver will only refer to the file extension and/or paramter, 
170+ 	 * as indicated by the {@link #setFavorPathExtension(boolean) favorPathExtension} and 
171+ 	 * {@link #setFavorParameter(boolean) favorParameter} properties. 
173172	 */ 
174173	public  void  setIgnoreAcceptHeader (boolean  ignoreAcceptHeader ) {
175174		this .ignoreAcceptHeader  = ignoreAcceptHeader ;
176175	}
177176
178177	/** 
179178	 * Sets the mapping from file extensions to media types. 
180- 	 * 
181- 	 * <p>When this mapping is not set or when an extension is not present, this view resolver will fall back to using a 
182- 	 * {@link FileTypeMap} when the Java Action Framework is available. 
179+ 	 * <p>When this mapping is not set or when an extension is not present, this view resolver 
180+ 	 * will fall back to using a {@link FileTypeMap} when the Java Action Framework is available. 
183181	 */ 
184182	public  void  setMediaTypes (Map <String , String > mediaTypes ) {
185183		Assert .notNull (mediaTypes , "'mediaTypes' must not be null" );
@@ -190,13 +188,17 @@ public void setMediaTypes(Map<String, String> mediaTypes) {
190188		}
191189	}
192190
193- 	/** Sets the default views to use when a more specific view can not be obtained from the {@link ViewResolver} chain. */ 
191+ 	/** 
192+ 	 * Sets the default views to use when a more specific view can not be obtained 
193+ 	 * from the {@link ViewResolver} chain. 
194+ 	 */ 
194195	public  void  setDefaultViews (List <View > defaultViews ) {
195196		this .defaultViews  = defaultViews ;
196197	}
197198
198199	/** 
199- 	 * Sets the default content type. This content type will be used when file extension, parameter, nor {@code Accept} 
200+ 	 * Sets the default content type. 
201+ 	 * <p>This content type will be used when file extension, parameter, nor {@code Accept} 
200202	 * header define a content-type, either through being disabled or empty. 
201203	 */ 
202204	public  void  setDefaultContentType (MediaType  defaultContentType ) {
@@ -205,7 +207,6 @@ public void setDefaultContentType(MediaType defaultContentType) {
205207
206208	/** 
207209	 * Sets the view resolvers to be wrapped by this view resolver. 
208- 	 * 
209210	 * <p>If this property is not set, view resolvers will be detected automatically. 
210211	 */ 
211212	public  void  setViewResolvers (List <ViewResolver > viewResolvers ) {
@@ -214,9 +215,9 @@ public void setViewResolvers(List<ViewResolver> viewResolvers) {
214215
215216	@ Override 
216217	protected  void  initServletContext (ServletContext  servletContext ) {
217- 		if  (viewResolvers  == null ) {
218- 			Map <String , ViewResolver > matchingBeans  =  BeanFactoryUtils 
219- 					.beansOfTypeIncludingAncestors (getApplicationContext (), ViewResolver .class ,  true ,  false );
218+ 		if  (this . viewResolvers  == null ) {
219+ 			Map <String , ViewResolver > matchingBeans  =
220+ 					BeanFactoryUtils .beansOfTypeIncludingAncestors (getApplicationContext (), ViewResolver .class );
220221			this .viewResolvers  = new  ArrayList <ViewResolver >(matchingBeans .size ());
221222			for  (ViewResolver  viewResolver  : matchingBeans .values ()) {
222223				if  (this  != viewResolver ) {
@@ -233,19 +234,16 @@ protected void initServletContext(ServletContext servletContext) {
233234
234235	/** 
235236	 * Determines the list of {@link MediaType} for the given {@link HttpServletRequest}. 
236- 	 * 
237237	 * <p>The default implementation invokes {@link #getMediaTypeFromFilename(String)} if {@linkplain 
238238	 * #setFavorPathExtension(boolean) favorPathExtension} property is <code>true</code>. If the property is 
239- 	 * <code>false</code>, or when a media type cannot be determined from the request path, this method will inspect the 
240- 	 * {@code Accept} header of the request. 
241- 	 * 
239+ 	 * <code>false</code>, or when a media type cannot be determined from the request path, this method will 
240+ 	 * inspect the {@code Accept} header of the request. 
242241	 * <p>This method can be overriden to provide a different algorithm. 
243- 	 * 
244242	 * @param request the current servlet request 
245243	 * @return the list of media types requested, if any 
246244	 */ 
247245	protected  List <MediaType > getMediaTypes (HttpServletRequest  request ) {
248- 		if  (favorPathExtension ) {
246+ 		if  (this . favorPathExtension ) {
249247			String  requestUri  = urlPathHelper .getRequestUri (request );
250248			String  filename  = WebUtils .extractFullFilenameFromUrlPath (requestUri );
251249			MediaType  mediaType  = getMediaTypeFromFilename (filename );
@@ -258,23 +256,22 @@ protected List<MediaType> getMediaTypes(HttpServletRequest request) {
258256				return  mediaTypes ;
259257			}
260258		}
261- 		if  (favorParameter ) {
262- 			if  (request .getParameter (parameterName ) != null ) {
263- 				String  parameterValue  = request .getParameter (parameterName );
259+ 		if  (this . favorParameter ) {
260+ 			if  (request .getParameter (this . parameterName ) != null ) {
261+ 				String  parameterValue  = request .getParameter (this . parameterName );
264262				MediaType  mediaType  = getMediaTypeFromParameter (parameterValue );
265263				if  (mediaType  != null ) {
266264					if  (logger .isDebugEnabled ()) {
267- 						logger .debug (
268- 								"Requested media type is '"  + mediaType  + "' (based on parameter '"  + parameterName  +
269- 										"'='"  + parameterValue  + "')" );
265+ 						logger .debug ("Requested media type is '"  + mediaType  + "' (based on parameter '"  +
266+ 								this .parameterName  + "'='"  + parameterValue  + "')" );
270267					}
271268					List <MediaType > mediaTypes  = new  ArrayList <MediaType >();
272269					mediaTypes .add (mediaType );
273270					return  mediaTypes ;
274271				}
275272			}
276273		}
277- 		if  (!ignoreAcceptHeader ) {
274+ 		if  (!this . ignoreAcceptHeader ) {
278275			String  acceptHeader  = request .getHeader (ACCEPT_HEADER );
279276			if  (StringUtils .hasText (acceptHeader )) {
280277				List <MediaType > mediaTypes  = MediaType .parseMediaTypes (acceptHeader );
@@ -284,8 +281,8 @@ protected List<MediaType> getMediaTypes(HttpServletRequest request) {
284281				return  mediaTypes ;
285282			}
286283		}
287- 		if  (defaultContentType  != null ) {
288- 			return  Collections .singletonList (defaultContentType );
284+ 		if  (this . defaultContentType  != null ) {
285+ 			return  Collections .singletonList (this . defaultContentType );
289286		}
290287		else  {
291288			return  Collections .emptyList ();
@@ -294,13 +291,10 @@ protected List<MediaType> getMediaTypes(HttpServletRequest request) {
294291
295292	/** 
296293	 * Determines the {@link MediaType} for the given filename. 
297- 	 * 
298294	 * <p>The default implementation will check the {@linkplain #setMediaTypes(Map) media types} property first for a 
299295	 * defined mapping. If not present, and if the Java Activation Framework can be found on the class path, it will call 
300296	 * {@link FileTypeMap#getContentType(String)} 
301- 	 * 
302297	 * <p>This method can be overriden to provide a different algorithm. 
303- 	 * 
304298	 * @param filename the current request file name (i.e. {@code hotels.html}) 
305299	 * @return the media type, if any 
306300	 */ 
@@ -310,50 +304,51 @@ protected MediaType getMediaTypeFromFilename(String filename) {
310304			return  null ;
311305		}
312306		extension  = extension .toLowerCase (Locale .ENGLISH );
313- 		MediaType  mediaType  = mediaTypes .get (extension );
307+ 		MediaType  mediaType  = this . mediaTypes .get (extension );
314308		if  (mediaType  == null  && jafPresent ) {
315309			mediaType  = ActivationMediaTypeFactory .getMediaType (filename );
316310			if  (mediaType  != null ) {
317- 				mediaTypes .putIfAbsent (extension , mediaType );
311+ 				this . mediaTypes .putIfAbsent (extension , mediaType );
318312			}
319313		}
320314		return  mediaType ;
321315	}
322316
323317	/** 
324318	 * Determines the {@link MediaType} for the given parameter value. 
325- 	 * 
326- 	 * <p>The default implementation will check the {@linkplain #setMediaTypes(Map) media types} property for a defined 
327- 	 * mapping. 
328- 	 * 
319+ 	 * <p>The default implementation will check the {@linkplain #setMediaTypes(Map) media types} 
320+ 	 * property for a defined mapping. 
329321	 * <p>This method can be overriden to provide a different algorithm. 
330- 	 * 
331322	 * @param parameterValue the parameter value (i.e. {@code pdf}). 
332323	 * @return the media type, if any 
333324	 */ 
334325	protected  MediaType  getMediaTypeFromParameter (String  parameterValue ) {
335- 		parameterValue  = parameterValue .toLowerCase (Locale .ENGLISH );
336- 		return  mediaTypes .get (parameterValue );
326+ 		return  this .mediaTypes .get (parameterValue .toLowerCase (Locale .ENGLISH ));
337327	}
338328
339329	public  View  resolveViewName (String  viewName , Locale  locale ) throws  Exception  {
340330		RequestAttributes  attrs  = RequestContextHolder .getRequestAttributes ();
341331		Assert .isInstanceOf (ServletRequestAttributes .class , attrs );
342332		ServletRequestAttributes  servletAttrs  = (ServletRequestAttributes ) attrs ;
333+ 
343334		List <MediaType > requestedMediaTypes  = getMediaTypes (servletAttrs .getRequest ());
344- 		Collections .sort (requestedMediaTypes );
335+ 		if  (requestedMediaTypes .size () > 1 ) {
336+ 			// avoid sorting attempt for empty list and singleton list 
337+ 			Collections .sort (requestedMediaTypes );
338+ 		}
345339
346340		SortedMap <MediaType , View > views  = new  TreeMap <MediaType , View >();
347341		List <View > candidateViews  = new  ArrayList <View >();
348- 		for  (ViewResolver  viewResolver  : viewResolvers ) {
342+ 		for  (ViewResolver  viewResolver  : this . viewResolvers ) {
349343			View  view  = viewResolver .resolveViewName (viewName , locale );
350344			if  (view  != null ) {
351345				candidateViews .add (view );
352346			}
353347		}
354- 		if  (!CollectionUtils .isEmpty (defaultViews )) {
355- 			candidateViews .addAll (defaultViews );
348+ 		if  (!CollectionUtils .isEmpty (this . defaultViews )) {
349+ 			candidateViews .addAll (this . defaultViews );
356350		}
351+ 
357352		for  (View  candidateView  : candidateViews ) {
358353			MediaType  viewMediaType  = MediaType .parseMediaType (candidateView .getContentType ());
359354			for  (MediaType  requestedMediaType  : requestedMediaTypes ) {
@@ -365,6 +360,7 @@ public View resolveViewName(String viewName, Locale locale) throws Exception {
365360				}
366361			}
367362		}
363+ 
368364		if  (!views .isEmpty ()) {
369365			MediaType  mediaType  = views .firstKey ();
370366			View  view  = views .get (mediaType );
@@ -378,7 +374,10 @@ public View resolveViewName(String viewName, Locale locale) throws Exception {
378374		}
379375	}
380376
381- 	/** Inner class to avoid hard-coded JAF dependency. */ 
377+ 
378+ 	/** 
379+ 	 * Inner class to avoid hard-coded JAF dependency. 
380+ 	 */ 
382381	private  static  class  ActivationMediaTypeFactory  {
383382
384383		private  static  final  FileTypeMap  fileTypeMap ;
0 commit comments