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
.Specialized
;
22 using Castle
.MonoRail
.Framework
.Configuration
;
23 using Castle
.MonoRail
.Framework
.Internal
;
26 /// The view engine manager sits between MonoRail and all the registered
27 /// view engines. It is used to identify the view engine that should handle a
28 /// render request and delegates such requests properly.
30 public class DefaultViewEngineManager
: IViewEngineManager
, IServiceEnabledComponent
, IInitializable
32 private MonoRailConfiguration config
;
33 private IServiceProvider provider
;
34 private IDictionary ext2ViewEngine
;
35 private IDictionary viewEnginesFastLookup
;
36 private IDictionary jsgFastLookup
;
39 /// Initializes a new instance of the <see cref="DefaultViewEngineManager"/> class.
41 public DefaultViewEngineManager()
43 ext2ViewEngine
= new HybridDictionary(true);
44 jsgFastLookup
= new HybridDictionary(true);
45 viewEnginesFastLookup
= new Hashtable();
48 #region IInitializable
51 /// Implementors should perform any initialization logic.
53 public void Initialize()
55 foreach (ViewEngineInfo info
in config
.ViewEngineConfig
.ViewEngines
)
59 IViewEngine engine
= (IViewEngine
)Activator
.CreateInstance(info
.Engine
);
61 RegisterEngineForView(engine
);
63 RegisterEngineForExtesionLookup(engine
);
65 engine
.XHtmlRendering
= info
.XhtmlRendering
;
67 IServiceEnabledComponent serviceEnabled
= engine
as IServiceEnabledComponent
;
69 if (serviceEnabled
!= null)
71 serviceEnabled
.Service(provider
);
74 IInitializable initializable
= engine
as IInitializable
;
76 if (initializable
!= null)
78 initializable
.Initialize();
81 catch (InvalidCastException
)
83 throw new RailsException("Type " + info
.Engine
.FullName
+ " does not implement IViewEngine");
87 throw new RailsException("Could not create view engine instance: " + info
.Engine
, ex
);
94 private void RegisterEngineForExtesionLookup(IViewEngine engine
)
96 viewEnginesFastLookup
.Add(engine
, null);
101 #region IServiceEnabledComponent
104 /// Services the specified service provider.
106 /// <param name="serviceProvider">The service provider.</param>
107 public void Service(IServiceProvider serviceProvider
)
109 provider
= serviceProvider
;
111 config
= (MonoRailConfiguration
)provider
.GetService(typeof(MonoRailConfiguration
));
116 #region IViewEngineManager
119 /// Evaluates whether the specified template exists.
121 /// <param name="templateName">View template name</param>
122 /// <returns><c>true</c> if it exists</returns>
123 public bool HasTemplate(String templateName
)
125 IViewEngine engine
= ResolveEngine(templateName
, false);
128 return engine
.HasTemplate(templateName
);
132 /// Processes the view - using the templateName
133 /// to obtain the correct template,
134 /// and using the context to output the result.
136 /// <param name="context"></param>
137 /// <param name="controller"></param>
138 /// <param name="templateName"></param>
139 public void Process(IRailsEngineContext context
, IController controller
, string templateName
)
141 IViewEngine engine
= ResolveEngine(templateName
);
143 ContextualizeViewEngine(engine
);
145 if (engine
.SupportsJSGeneration
&& engine
.IsTemplateForJSGeneration(templateName
))
147 engine
.GenerateJS(context
, controller
, templateName
);
151 engine
.Process(context
, controller
, templateName
);
155 /// Processes the view - using the templateName
156 /// to obtain the correct template
157 /// and writes the results to the System.TextWriter.
159 /// Please note that no layout is applied
162 /// <param name="output"></param>
163 /// <param name="context"></param>
164 /// <param name="controller"></param>
165 /// <param name="templateName"></param>
166 public void Process(TextWriter output
, IRailsEngineContext context
, IController controller
, string templateName
)
168 IViewEngine engine
= ResolveEngine(templateName
);
170 ContextualizeViewEngine(engine
);
172 if (engine
.SupportsJSGeneration
&& engine
.IsTemplateForJSGeneration(templateName
))
174 engine
.GenerateJS(output
, context
, controller
, templateName
);
178 engine
.Process(output
, context
, controller
, templateName
);
182 /// Processes a partial view = using the partialName
183 /// to obtain the correct template and writes the
184 /// results to the System.TextWriter.
186 /// <param name="output">The output.</param>
187 /// <param name="context">The context.</param>
188 /// <param name="controller">The controller.</param>
189 /// <param name="partialName">The partial name.</param>
190 public void ProcessPartial(TextWriter output
, IRailsEngineContext context
, IController controller
, string partialName
)
193 IViewEngine engine
= ResolveEngine(partialName
);
195 engine
.ProcessPartial(output
, context
, controller
, partialName
);
199 /// Wraps the specified content in the layout using
200 /// the context to output the result.
202 public void ProcessContents(IRailsEngineContext context
, IController controller
, String contents
)
204 if (controller
.LayoutName
== null)
206 throw new RailsException("ProcessContents can only work with a layout");
209 String templateName
= Path
.Combine("layouts", controller
.LayoutName
);
211 IViewEngine engine
= ResolveEngine(templateName
);
213 engine
.ProcessContents(context
, controller
, contents
);
219 /// Contextualizes the view engine.
221 /// <param name="engine">The engine.</param>
222 private void ContextualizeViewEngine(IViewEngine engine
)
224 MonoRailHttpHandler
.CurrentContext
.AddService(typeof(IViewEngine
), engine
);
228 /// The view can be informed with an extension. If so, we use it
229 /// to discover the extension. Otherwise, we ask the configured
230 /// view engines to find out which (if any) can handle the requested
231 /// view. If no suitable view engine is found, an exception would be thrown
233 /// <param name="templateName">View name</param>
234 /// <returns>A view engine instance</returns>
235 private IViewEngine
ResolveEngine(String templateName
)
237 return ResolveEngine(templateName
, true);
241 /// The view can be informed with an extension. If so, we use it
242 /// to discover the extension. Otherwise, we ask the configured
243 /// view engines to find out which (if any) can handle the requested
246 /// <param name="throwIfNotFound">If true and no suitable view engine is found, an exception would be thrown</param>
247 /// <param name="templateName">View name</param>
248 /// <returns>A view engine instance</returns>
249 private IViewEngine
ResolveEngine(String templateName
, bool throwIfNotFound
)
251 if (Path
.HasExtension(templateName
))
253 String extension
= Path
.GetExtension(templateName
);
254 IViewEngine engine
= ext2ViewEngine
[extension
] as IViewEngine
;
258 foreach (IViewEngine engine
in viewEnginesFastLookup
.Keys
)
259 if (engine
.HasTemplate(templateName
))
263 throw new RailsException(string.Format(
264 @"MonoRail could not have resolved a view engine instance for the template '{0}'
265 There are two possible explanations: that the template does not exist, or that the relevant view engine has not been configured correctly in web.config.", templateName
));
271 /// Associates extensions with the view engine instance.
273 /// <param name="engine">The view engine instance</param>
274 private void RegisterEngineForView(IViewEngine engine
)
276 if (ext2ViewEngine
.Contains(engine
.ViewFileExtension
))
278 IViewEngine existing
= (IViewEngine
)ext2ViewEngine
[engine
.ViewFileExtension
];
280 throw new RailsException(
281 "At least two view engines are handling the same file extension. " +
282 "This isn't going to work. Extension: " + engine
.ViewFileExtension
+
283 " View Engine 1: " + existing
.GetType() +
284 " View Engine 2: " + engine
.GetType());
287 String extension
= engine
.ViewFileExtension
.StartsWith(".")
288 ? engine
.ViewFileExtension
289 : "." + engine
.ViewFileExtension
;
291 ext2ViewEngine
[extension
] = engine
;
293 if (engine
.SupportsJSGeneration
&& ext2ViewEngine
.Contains(engine
.JSGeneratorFileExtension
))
295 IViewEngine existing
= (IViewEngine
)ext2ViewEngine
[engine
.JSGeneratorFileExtension
];
297 throw new RailsException(
298 "At least two view engines are handling the same file extension. " +
299 "This isn't going to work. Extension: " + engine
.JSGeneratorFileExtension
+
300 " View Engine 1: " + existing
.GetType() +
301 " View Engine 2: " + engine
.GetType());
304 if (engine
.SupportsJSGeneration
)
306 extension
= engine
.JSGeneratorFileExtension
.StartsWith(".")
307 ? engine
.JSGeneratorFileExtension
308 : "." + engine
.JSGeneratorFileExtension
;
310 ext2ViewEngine
[extension
] = engine
;
311 jsgFastLookup
[extension
] = engine
;