Added non-generic registration interface to IKernel and IWindsor to accommodate dynam...
[castle.git] / MonoRail / Castle.MonoRail.Framework / Services / DefaultViewEngineManager.cs
blob298a937b9911f3362d2ddf82835cb9b67fbe2525
1 // Copyright 2004-2007 Castle Project - http://www.castleproject.org/
2 //
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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
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
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Collections.Specialized;
21 using System.IO;
22 using Castle.Core;
23 using Castle.MonoRail.Framework.Configuration;
24 using Castle.MonoRail.Framework.Internal;
25 using JSGeneration;
27 /// <summary>
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.
31 /// </summary>
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;
40 /// <summary>
41 /// Initializes a new instance of the <see cref="DefaultViewEngineManager"/> class.
42 /// </summary>
43 public DefaultViewEngineManager()
45 ext2ViewEngine = new HybridDictionary(true);
46 jsgFastLookup = new HybridDictionary(true);
47 viewEnginesFastLookup = new Hashtable();
50 #region IInitializable
52 /// <summary>
53 /// Implementors should perform any initialization logic.
54 /// </summary>
55 public void Initialize()
57 foreach(ViewEngineInfo info in config.ViewEngineConfig.ViewEngines)
59 IViewEngine engine;
61 try
63 engine = (IViewEngine) Activator.CreateInstance(info.Engine);
65 catch(InvalidCastException)
67 throw new MonoRailException("Type " + info.Engine.FullName + " does not implement IViewEngine");
69 catch(Exception ex)
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();
96 private void RegisterEngineForExtesionLookup(IViewEngine engine)
98 viewEnginesFastLookup.Add(engine, null);
101 #endregion
103 #region IServiceEnabledComponent
105 /// <summary>
106 /// Services the specified service provider.
107 /// </summary>
108 /// <param name="serviceProvider">The service provider.</param>
109 public void Service(IServiceProvider serviceProvider)
111 provider = serviceProvider;
113 config = (IMonoRailConfiguration) provider.GetService(typeof(IMonoRailConfiguration));
116 #endregion
118 #region IViewEngineManager
120 /// <summary>
121 /// Evaluates whether the specified template exists.
122 /// </summary>
123 /// <param name="templateName">View template name</param>
124 /// <returns><c>true</c> if it exists</returns>
125 public bool HasTemplate(String templateName)
127 IViewEngine engine = ResolveEngine(templateName, false);
129 if (engine == null) return false;
131 return engine.HasTemplate(templateName);
134 /// <summary>
135 /// Processes the view - using the templateName
136 /// to obtain the correct template
137 /// and writes the results to the System.TextWriter.
138 /// <para>
139 /// Please note that no layout is applied
140 /// </para>
141 /// </summary>
142 /// <param name="output"></param>
143 /// <param name="context"></param>
144 /// <param name="controller"></param>
145 /// <param name="controllerContext"></param>
146 /// <param name="templateName"></param>
147 public void Process(string templateName, TextWriter output, IEngineContext context, IController controller,
148 IControllerContext controllerContext)
150 IViewEngine engine = ResolveEngine(templateName);
152 ContextualizeViewEngine(engine);
154 if (engine.SupportsJSGeneration && engine.IsTemplateForJSGeneration(templateName))
156 engine.GenerateJS(templateName, output, CreateJSCodeGeneratorInfo(context, controller, controllerContext), context, controller, controllerContext);
158 else
160 engine.Process(templateName, output, context, controller, controllerContext);
164 /// <summary>
165 /// Processes the view - using the templateName
166 /// to obtain the correct template
167 /// and writes the results to the System.TextWriter.
168 /// </summary>
169 public void Process(string templateName, string layoutName, TextWriter output, IDictionary<string, object> parameters)
171 IViewEngine engine = ResolveEngine(templateName);
173 ContextualizeViewEngine(engine);
175 engine.Process(templateName, layoutName, output, parameters);
178 /// <summary>
179 /// Processes a partial view = using the partialName
180 /// to obtain the correct template and writes the
181 /// results to the System.TextWriter.
182 /// </summary>
183 /// <param name="output">The output.</param>
184 /// <param name="context">The context.</param>
185 /// <param name="controller">The controller.</param>
186 /// <param name="controllerContext">The controller context.</param>
187 /// <param name="partialName">The partial name.</param>
188 public void ProcessPartial(string partialName, TextWriter output, IEngineContext context, IController controller,
189 IControllerContext controllerContext)
191 IViewEngine engine = ResolveEngine(partialName);
193 engine.ProcessPartial(partialName, output, context, controller, controllerContext);
196 /// <summary>
197 /// Wraps the specified content in the layout using
198 /// the context to output the result.
199 /// </summary>
200 public void RenderStaticWithinLayout(String contents, IEngineContext context, IController controller,
201 IControllerContext controllerContext)
203 if (controllerContext.LayoutNames == null)
205 throw new MonoRailException("RenderStaticWithinLayout can only work with a layout");
208 String templateName = Path.Combine("layouts", controllerContext.LayoutNames[0]);
210 IViewEngine engine = ResolveEngine(templateName);
212 engine.RenderStaticWithinLayout(contents, context, controller, controllerContext);
215 /// <summary>
216 /// Creates the JS code generator info. Temporarily on IViewEngineManager
217 /// </summary>
218 /// <param name="engineContext">The engine context.</param>
219 /// <param name="controller">The controller.</param>
220 /// <param name="controllerContext">The controller context.</param>
221 /// <returns></returns>
222 public JSCodeGeneratorInfo CreateJSCodeGeneratorInfo(IEngineContext engineContext, IController controller, IControllerContext controllerContext)
224 JSGeneratorConfiguration jsConfig = config.JSGeneratorConfiguration;
226 if (jsConfig.DefaultLibrary == null)
228 throw new MonoRailException("No default JS Generator library configured. By default MonoRail configures " +
229 "itself to use the Prototype JS library. If you have configured other, make sure you set it as default.");
232 JSCodeGenerator codeGenerator =
233 new JSCodeGenerator(engineContext.Server, this,
234 engineContext, controller, controllerContext, engineContext.Services.UrlBuilder);
236 IJSGenerator jsGen = (IJSGenerator)
237 Activator.CreateInstance(jsConfig.DefaultLibrary.MainGenerator, new object[] { codeGenerator });
239 codeGenerator.JSGenerator = jsGen;
241 object[] extensions = CreateExtensions(codeGenerator, jsConfig.DefaultLibrary.MainExtensions);
242 object[] elementExtension = CreateExtensions(codeGenerator, jsConfig.DefaultLibrary.ElementExtension);
244 return new JSCodeGeneratorInfo(codeGenerator, jsGen, extensions, elementExtension);
247 #endregion
249 /// <summary>
250 /// Contextualizes the view engine.
251 /// </summary>
252 /// <param name="engine">The engine.</param>
253 private void ContextualizeViewEngine(IViewEngine engine)
255 MonoRailHttpHandlerFactory.CurrentEngineContext.AddService(typeof(IViewEngine), engine);
258 private static object[] CreateExtensions(IJSCodeGenerator generator, List<Type> extensions)
260 int index = 0;
261 object[] list = new object[extensions.Count];
263 foreach(Type extensionType in extensions)
265 object extension = Activator.CreateInstance(extensionType, generator);
267 list[index++] = extension;
269 generator.Extensions.Add(extensionType.Name, extension);
272 return list;
275 /// <summary>
276 /// The view can be informed with an extension. If so, we use it
277 /// to discover the extension. Otherwise, we ask the configured
278 /// view engines to find out which (if any) can handle the requested
279 /// view. If no suitable view engine is found, an exception would be thrown
280 /// </summary>
281 /// <param name="templateName">View name</param>
282 /// <returns>A view engine instance</returns>
283 private IViewEngine ResolveEngine(String templateName)
285 return ResolveEngine(templateName, true);
288 /// <summary>
289 /// The view can be informed with an extension. If so, we use it
290 /// to discover the extension. Otherwise, we ask the configured
291 /// view engines to find out which (if any) can handle the requested
292 /// view.
293 /// </summary>
294 /// <param name="throwIfNotFound">If true and no suitable view engine is found, an exception would be thrown</param>
295 /// <param name="templateName">View name</param>
296 /// <returns>A view engine instance</returns>
297 private IViewEngine ResolveEngine(String templateName, bool throwIfNotFound)
299 if (Path.HasExtension(templateName))
301 String extension = Path.GetExtension(templateName);
302 IViewEngine engine = ext2ViewEngine[extension] as IViewEngine;
303 return engine;
306 foreach(IViewEngine engine in viewEnginesFastLookup.Keys)
308 if (engine.HasTemplate(templateName)) return engine;
311 if (throwIfNotFound)
313 throw new MonoRailException(string.Format(
314 @"MonoRail could not resolve a view engine instance for the template '{0}'
315 There are two possible reasons: either the template does not exist, or the view engine " +
316 "that handles an specific file extension has not been configured correctly web.config (section monorail, node viewEngines).",
317 templateName));
320 return null;
323 /// <summary>
324 /// Associates extensions with the view engine instance.
325 /// </summary>
326 /// <param name="engine">The view engine instance</param>
327 private void RegisterEngineForView(IViewEngine engine)
329 if (ext2ViewEngine.Contains(engine.ViewFileExtension))
331 IViewEngine existing = (IViewEngine) ext2ViewEngine[engine.ViewFileExtension];
333 throw new MonoRailException(
334 "At least two view engines are handling the same file extension. " +
335 "This isn't going to work. Extension: " + engine.ViewFileExtension +
336 " View Engine 1: " + existing.GetType() +
337 " View Engine 2: " + engine.GetType());
340 String extension = engine.ViewFileExtension.StartsWith(".")
341 ? engine.ViewFileExtension
342 : "." + engine.ViewFileExtension;
344 ext2ViewEngine[extension] = engine;
346 if (engine.SupportsJSGeneration && ext2ViewEngine.Contains(engine.JSGeneratorFileExtension))
348 IViewEngine existing = (IViewEngine) ext2ViewEngine[engine.JSGeneratorFileExtension];
350 throw new MonoRailException(
351 "At least two view engines are handling the same file extension. " +
352 "This isn't going to work. Extension: " + engine.JSGeneratorFileExtension +
353 " View Engine 1: " + existing.GetType() +
354 " View Engine 2: " + engine.GetType());
357 if (engine.SupportsJSGeneration)
359 extension = engine.JSGeneratorFileExtension.StartsWith(".")
360 ? engine.JSGeneratorFileExtension
361 : "." + engine.JSGeneratorFileExtension;
363 ext2ViewEngine[extension] = engine;
364 jsgFastLookup[extension] = engine;