1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.MonoRail
.Framework
.Services
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
20 using System
.Collections
.Specialized
;
23 using Castle
.MonoRail
.Framework
.Configuration
;
24 using Castle
.MonoRail
.Framework
.Internal
;
28 /// The view engine manager sits between MonoRail and all the registered
29 /// view engines. It is used to identify the view engine that should handle a
30 /// render request and delegates such requests properly.
32 public class DefaultViewEngineManager
: IViewEngineManager
, IServiceEnabledComponent
, IInitializable
34 private IMonoRailConfiguration config
;
35 private IServiceProvider provider
;
36 private IDictionary ext2ViewEngine
;
37 private IDictionary viewEnginesFastLookup
;
38 private IDictionary jsgFastLookup
;
41 /// Initializes a new instance of the <see cref="DefaultViewEngineManager"/> class.
43 public DefaultViewEngineManager()
45 ext2ViewEngine
= new HybridDictionary(true);
46 jsgFastLookup
= new HybridDictionary(true);
47 viewEnginesFastLookup
= new Hashtable();
50 #region IInitializable
53 /// Implementors should perform any initialization logic.
55 public void Initialize()
57 foreach(ViewEngineInfo info
in config
.ViewEngineConfig
.ViewEngines
)
63 engine
= (IViewEngine
) Activator
.CreateInstance(info
.Engine
);
65 catch(InvalidCastException
)
67 throw new MonoRailException("Type " + info
.Engine
.FullName
+ " does not implement IViewEngine");
71 throw new MonoRailException("Could not create view engine instance: " + info
.Engine
, ex
);
74 RegisterEngineForView(engine
);
76 RegisterEngineForExtesionLookup(engine
);
78 engine
.XHtmlRendering
= info
.XhtmlRendering
;
80 IServiceEnabledComponent serviceEnabled
= engine
as IServiceEnabledComponent
;
82 if (serviceEnabled
!= null)
84 serviceEnabled
.Service(provider
);
87 IInitializable initializable
= engine
as IInitializable
;
89 if (initializable
!= null)
91 initializable
.Initialize();
97 /// Registers the engine for extesion lookup.
99 /// <param name="engine">The engine.</param>
100 public void RegisterEngineForExtesionLookup(IViewEngine engine
)
102 viewEnginesFastLookup
.Add(engine
, null);
107 #region IServiceEnabledComponent
110 /// Services the specified service provider.
112 /// <param name="serviceProvider">The service provider.</param>
113 public void Service(IServiceProvider serviceProvider
)
115 provider
= serviceProvider
;
117 config
= (IMonoRailConfiguration
) provider
.GetService(typeof(IMonoRailConfiguration
));
122 #region IViewEngineManager
125 /// Evaluates whether the specified template exists.
127 /// <param name="templateName">View template name</param>
128 /// <returns><c>true</c> if it exists</returns>
129 public bool HasTemplate(String templateName
)
131 IViewEngine engine
= ResolveEngine(templateName
, false);
133 if (engine
== null) return false;
135 return engine
.HasTemplate(templateName
);
139 /// Processes the view - using the templateName
140 /// to obtain the correct template
141 /// and writes the results to the System.TextWriter.
143 /// Please note that no layout is applied
146 /// <param name="output"></param>
147 /// <param name="context"></param>
148 /// <param name="controller"></param>
149 /// <param name="controllerContext"></param>
150 /// <param name="templateName"></param>
151 public void Process(string templateName
, TextWriter output
, IEngineContext context
, IController controller
,
152 IControllerContext controllerContext
)
154 IViewEngine engine
= ResolveEngine(templateName
);
156 ContextualizeViewEngine(engine
);
158 if (engine
.SupportsJSGeneration
&& engine
.IsTemplateForJSGeneration(templateName
))
160 engine
.GenerateJS(templateName
, output
, CreateJSCodeGeneratorInfo(context
, controller
, controllerContext
), context
, controller
, controllerContext
);
164 engine
.Process(templateName
, output
, context
, controller
, controllerContext
);
169 /// Processes the view - using the templateName
170 /// to obtain the correct template
171 /// and writes the results to the System.TextWriter.
173 public void Process(string templateName
, string layoutName
, TextWriter output
, IDictionary
<string, object> parameters
)
175 IViewEngine engine
= ResolveEngine(templateName
);
177 ContextualizeViewEngine(engine
);
179 engine
.Process(templateName
, layoutName
, output
, parameters
);
183 /// Processes a partial view = using the partialName
184 /// to obtain the correct template and writes the
185 /// results to the System.TextWriter.
187 /// <param name="output">The output.</param>
188 /// <param name="context">The context.</param>
189 /// <param name="controller">The controller.</param>
190 /// <param name="controllerContext">The controller context.</param>
191 /// <param name="partialName">The partial name.</param>
192 public void ProcessPartial(string partialName
, TextWriter output
, IEngineContext context
, IController controller
,
193 IControllerContext controllerContext
)
195 IViewEngine engine
= ResolveEngine(partialName
);
197 ContextualizeViewEngine(engine
);
199 engine
.ProcessPartial(partialName
, output
, context
, controller
, controllerContext
);
203 /// Wraps the specified content in the layout using
204 /// the context to output the result.
206 public void RenderStaticWithinLayout(String contents
, IEngineContext context
, IController controller
,
207 IControllerContext controllerContext
)
209 if (controllerContext
.LayoutNames
== null)
211 throw new MonoRailException("RenderStaticWithinLayout can only work with a layout");
214 String templateName
= Path
.Combine("layouts", controllerContext
.LayoutNames
[0]);
216 IViewEngine engine
= ResolveEngine(templateName
);
218 ContextualizeViewEngine(engine
);
220 engine
.RenderStaticWithinLayout(contents
, context
, controller
, controllerContext
);
224 /// Creates the JS code generator info. Temporarily on IViewEngineManager
226 /// <param name="engineContext">The engine context.</param>
227 /// <param name="controller">The controller.</param>
228 /// <param name="controllerContext">The controller context.</param>
229 /// <returns></returns>
230 public JSCodeGeneratorInfo
CreateJSCodeGeneratorInfo(IEngineContext engineContext
, IController controller
, IControllerContext controllerContext
)
232 JSGeneratorConfiguration jsConfig
= config
.JSGeneratorConfiguration
;
234 if (jsConfig
.DefaultLibrary
== null)
236 throw new MonoRailException("No default JS Generator library configured. By default MonoRail configures " +
237 "itself to use the Prototype JS library. If you have configured other, make sure you set it as default.");
240 JSCodeGenerator codeGenerator
=
241 new JSCodeGenerator(engineContext
.Server
, this,
242 engineContext
, controller
, controllerContext
, engineContext
.Services
.UrlBuilder
);
244 IJSGenerator jsGen
= (IJSGenerator
)
245 Activator
.CreateInstance(jsConfig
.DefaultLibrary
.MainGenerator
, new object[] { codeGenerator }
);
247 codeGenerator
.JSGenerator
= jsGen
;
249 object[] extensions
= CreateExtensions(codeGenerator
, jsConfig
.DefaultLibrary
.MainExtensions
);
250 object[] elementExtension
= CreateExtensions(codeGenerator
, jsConfig
.DefaultLibrary
.ElementExtension
);
252 return new JSCodeGeneratorInfo(codeGenerator
, jsGen
, extensions
, elementExtension
);
258 /// Contextualizes the view engine.
260 /// <param name="engine">The engine.</param>
261 private void ContextualizeViewEngine(IViewEngine engine
)
263 if (MonoRailHttpHandlerFactory
.CurrentEngineContext
!=null)//required for tests
265 MonoRailHttpHandlerFactory
.CurrentEngineContext
.AddService(typeof(IViewEngine
), engine
);
269 private static object[] CreateExtensions(IJSCodeGenerator generator
, List
<Type
> extensions
)
272 object[] list
= new object[extensions
.Count
];
274 foreach(Type extensionType
in extensions
)
276 object extension
= Activator
.CreateInstance(extensionType
, generator
);
278 list
[index
++] = extension
;
280 generator
.Extensions
.Add(extensionType
.Name
, extension
);
287 /// The view can be informed with an extension. If so, we use it
288 /// to discover the extension. Otherwise, we ask the configured
289 /// view engines to find out which (if any) can handle the requested
290 /// view. If no suitable view engine is found, an exception would be thrown
292 /// <param name="templateName">View name</param>
293 /// <returns>A view engine instance</returns>
294 private IViewEngine
ResolveEngine(String templateName
)
296 return ResolveEngine(templateName
, true);
300 /// The view can be informed with an extension. If so, we use it
301 /// to discover the extension. Otherwise, we ask the configured
302 /// view engines to find out which (if any) can handle the requested
305 /// <param name="throwIfNotFound">If true and no suitable view engine is found, an exception would be thrown</param>
306 /// <param name="templateName">View name</param>
307 /// <returns>A view engine instance</returns>
308 private IViewEngine
ResolveEngine(String templateName
, bool throwIfNotFound
)
310 if (Path
.HasExtension(templateName
))
312 String extension
= Path
.GetExtension(templateName
);
313 IViewEngine engine
= ext2ViewEngine
[extension
] as IViewEngine
;
317 foreach(IViewEngine engine
in viewEnginesFastLookup
.Keys
)
319 if (engine
.HasTemplate(templateName
)) return engine
;
324 throw new MonoRailException(string.Format(
325 @"MonoRail could not resolve a view engine instance for the template '{0}'
326 There are two possible reasons: either the template does not exist, or the view engine " +
327 "that handles an specific file extension has not been configured correctly web.config (section monorail, node viewEngines).",
335 /// Associates extensions with the view engine instance.
337 /// <param name="engine">The view engine instance</param>
338 public void RegisterEngineForView(IViewEngine engine
)
340 if (ext2ViewEngine
.Contains(engine
.ViewFileExtension
))
342 IViewEngine existing
= (IViewEngine
) ext2ViewEngine
[engine
.ViewFileExtension
];
344 throw new MonoRailException(
345 "At least two view engines are handling the same file extension. " +
346 "This isn't going to work. Extension: " + engine
.ViewFileExtension
+
347 " View Engine 1: " + existing
.GetType() +
348 " View Engine 2: " + engine
.GetType());
351 String extension
= engine
.ViewFileExtension
.StartsWith(".")
352 ? engine
.ViewFileExtension
353 : "." + engine
.ViewFileExtension
;
355 ext2ViewEngine
[extension
] = engine
;
357 if (engine
.SupportsJSGeneration
&& ext2ViewEngine
.Contains(engine
.JSGeneratorFileExtension
))
359 IViewEngine existing
= (IViewEngine
) ext2ViewEngine
[engine
.JSGeneratorFileExtension
];
361 throw new MonoRailException(
362 "At least two view engines are handling the same file extension. " +
363 "This isn't going to work. Extension: " + engine
.JSGeneratorFileExtension
+
364 " View Engine 1: " + existing
.GetType() +
365 " View Engine 2: " + engine
.GetType());
368 if (engine
.SupportsJSGeneration
)
370 extension
= engine
.JSGeneratorFileExtension
.StartsWith(".")
371 ? engine
.JSGeneratorFileExtension
372 : "." + engine
.JSGeneratorFileExtension
;
374 ext2ViewEngine
[extension
] = engine
;
375 jsgFastLookup
[extension
] = engine
;