Fixing an issue with output parameters that are of type IntPtr
[castle.git] / MonoRail / Castle.MonoRail.Framework / Services / DefaultViewEngineManager.cs
blob6e4e06a32c2f3a88ff44591686db80294fb70ec9
1 // Copyright 2004-2008 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 /// <summary>
97 /// Registers the engine for extesion lookup.
98 /// </summary>
99 /// <param name="engine">The engine.</param>
100 public void RegisterEngineForExtesionLookup(IViewEngine engine)
102 viewEnginesFastLookup.Add(engine, null);
105 #endregion
107 #region IServiceEnabledComponent
109 /// <summary>
110 /// Services the specified service provider.
111 /// </summary>
112 /// <param name="serviceProvider">The service provider.</param>
113 public void Service(IServiceProvider serviceProvider)
115 provider = serviceProvider;
117 config = (IMonoRailConfiguration) provider.GetService(typeof(IMonoRailConfiguration));
120 #endregion
122 #region IViewEngineManager
124 /// <summary>
125 /// Evaluates whether the specified template exists.
126 /// </summary>
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);
138 /// <summary>
139 /// Processes the view - using the templateName
140 /// to obtain the correct template
141 /// and writes the results to the System.TextWriter.
142 /// <para>
143 /// Please note that no layout is applied
144 /// </para>
145 /// </summary>
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);
162 else
164 engine.Process(templateName, output, context, controller, controllerContext);
168 /// <summary>
169 /// Processes the view - using the templateName
170 /// to obtain the correct template
171 /// and writes the results to the System.TextWriter.
172 /// </summary>
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);
182 /// <summary>
183 /// Processes a partial view = using the partialName
184 /// to obtain the correct template and writes the
185 /// results to the System.TextWriter.
186 /// </summary>
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);
202 /// <summary>
203 /// Wraps the specified content in the layout using
204 /// the context to output the result.
205 /// </summary>
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);
223 /// <summary>
224 /// Creates the JS code generator info. Temporarily on IViewEngineManager
225 /// </summary>
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);
255 #endregion
257 /// <summary>
258 /// Contextualizes the view engine.
259 /// </summary>
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)
271 int index = 0;
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);
283 return list;
286 /// <summary>
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
291 /// </summary>
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);
299 /// <summary>
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
303 /// view.
304 /// </summary>
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;
314 return engine;
317 foreach(IViewEngine engine in viewEnginesFastLookup.Keys)
319 if (engine.HasTemplate(templateName)) return engine;
322 if (throwIfNotFound)
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).",
328 templateName));
331 return null;
334 /// <summary>
335 /// Associates extensions with the view engine instance.
336 /// </summary>
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;