1 // Copyright 2004-2007 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();
96 private void RegisterEngineForExtesionLookup(IViewEngine engine
)
98 viewEnginesFastLookup
.Add(engine
, null);
103 #region IServiceEnabledComponent
106 /// Services the specified service provider.
108 /// <param name="serviceProvider">The service provider.</param>
109 public void Service(IServiceProvider serviceProvider
)
111 provider
= serviceProvider
;
113 config
= (IMonoRailConfiguration
) provider
.GetService(typeof(IMonoRailConfiguration
));
118 #region IViewEngineManager
121 /// Evaluates whether the specified template exists.
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
);
135 /// Processes the view - using the templateName
136 /// to obtain the correct template
137 /// and writes the results to the System.TextWriter.
139 /// Please note that no layout is applied
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
);
160 engine
.Process(templateName
, output
, context
, controller
, controllerContext
);
165 /// Processes the view - using the templateName
166 /// to obtain the correct template
167 /// and writes the results to the System.TextWriter.
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
);
179 /// Processes a partial view = using the partialName
180 /// to obtain the correct template and writes the
181 /// results to the System.TextWriter.
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
);
197 /// Wraps the specified content in the layout using
198 /// the context to output the result.
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
);
216 /// Creates the JS code generator info. Temporarily on IViewEngineManager
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
);
250 /// Contextualizes the view engine.
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
)
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
);
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
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);
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
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
;
306 foreach(IViewEngine engine
in viewEnginesFastLookup
.Keys
)
308 if (engine
.HasTemplate(templateName
)) return engine
;
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).",
324 /// Associates extensions with the view engine instance.
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
;