Removed untyped contructor from ComponentRegistration and add a protected setter.
[castle.git] / Experiments / IronPythonViewEngine / Castle.MonoRail.Views.IronView / IronPythonViewEngine.cs
blob145bfb6f51828e956c2831f5bea781282805bbd8
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.Views.IronView
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.IO;
21 using System.Runtime.Serialization;
22 using System.Web;
23 using Castle.Core;
24 using Castle.MonoRail.Framework;
25 using Castle.MonoRail.Views.IronView.ElementProcessor;
26 using IronPython.Hosting;
28 /// <summary>
29 /// Pendent
30 /// </summary>
31 public class IronPythonViewEngine : ViewEngineBase, IInitializable, ITemplateEngine
33 public const String TemplateExtension = ".pml";
35 private PythonEngine engine;
36 private EngineModule globalModule;
37 private IronPythonTemplateParser parser;
38 private Dictionary<string, ViewInfo> name2View;
40 /// <summary>
41 /// Initializes a new instance of the <see cref="IronPythonViewEngine"/> class.
42 /// </summary>
43 public IronPythonViewEngine()
45 name2View = new Dictionary<string, ViewInfo>();
48 #region IInitializable
50 public void Initialize()
52 EngineOptions options = new EngineOptions();
54 // options.ClrDebuggingEnabled = true;
55 // options.ExceptionDetail = true;
56 // options.ShowClrExceptions = true;
57 // options.SkipFirstLine = false;
59 engine = new PythonEngine(options);
60 engine.Import("site");
61 System.IO.FileStream fs = new System.IO.FileStream("scripting-log.txt", System.IO.FileMode.Create);
62 engine.SetStandardOutput(fs);
63 engine.SetStandardError(fs);
65 globalModule = engine.CreateModule("mr", false);
67 // TODO: processors should be configured
68 parser = new IronPythonTemplateParser(new IElementProcessor[]
70 new ContentTag(),
71 new IfTag(),
72 new UnlessTag(),
73 new ForTag(),
74 new CodeBlock(),
75 new InlineWrite(),
76 new RenderPartial(),
77 new ViewComponentTag(),
78 });
81 #endregion
83 #region ViewEngineBase overrides
85 public override bool SupportsJSGeneration
87 get { return true; }
90 public override string ViewFileExtension
92 get { return TemplateExtension; }
95 public override string JSGeneratorFileExtension
97 get { return ".py"; }
100 public override bool HasTemplate(string templateName)
102 return ViewSourceLoader.HasTemplate(ResolveTemplateName(templateName));
105 public override void Process(IRailsEngineContext context, Controller controller, string templateName)
107 AdjustContentType(context);
109 bool hasLayout = controller.LayoutName != null;
111 TextWriter writer;
113 if (hasLayout)
115 // Because we are rendering within a layout we need to catch it first
116 writer = new StringWriter();
118 else
120 // No layout so render direct to the output
121 writer = context.Response.Output;
124 Dictionary<string, object> locals = new Dictionary<string, object>();
125 PopulateLocals(writer, context, controller, locals);
127 String fileName = ResolveTemplateName(templateName);
128 ProcessViewTemplate(fileName, context, locals);
130 if (hasLayout)
132 String contents = (writer as StringWriter).GetStringBuilder().ToString();
133 ProcessLayout(contents, controller, context, locals);
137 public override void Process(TextWriter output, IRailsEngineContext context, Controller controller,
138 string templateName)
140 throw new NotImplementedException();
143 public override void ProcessPartial(TextWriter output, IRailsEngineContext context, Controller controller,
144 string partialName)
146 throw new NotImplementedException();
149 public override void GenerateJS(TextWriter output, IRailsEngineContext context, Controller controller,
150 string templateName)
152 throw new NotImplementedException();
155 /// <summary>
156 /// Need to test this
157 /// </summary>
158 /// <param name="context"></param>
159 /// <param name="controller"></param>
160 /// <param name="contents"></param>
161 public override void ProcessContents(IRailsEngineContext context, Controller controller, string contents)
163 // Dictionary<string, object> locals = new Dictionary<string, object>();
165 // PopulateLocals(context.Response.Output, context, controller, locals);
167 // String script = parser.CreateScriptBlock(
168 // new StringReader(contents), "static content", serviceProvider, this);
170 // compiledCode = engine.Compile(script);
172 // ExecuteScript(compiledCode, context, locals);
175 #endregion
177 public CompiledCode CompilePartialTemplate(IViewSource source, string partialViewName,
178 out string functionName, params String[] parameters)
180 partialViewName = partialViewName.Replace('/', '_').Replace('\\', '_').Replace('.', '_').ToLowerInvariant();
181 functionName = "render" + partialViewName;
183 ViewInfo viewInfo;
184 bool compileView;
185 CompiledCode compiledCode = null;
187 if (name2View.TryGetValue(partialViewName.ToLower(), out viewInfo))
189 compileView = viewInfo.LastModified != source.LastModified;
190 compiledCode = viewInfo.CompiledCode;
192 else
194 compileView = true;
197 Stream viewSourceStream = source.OpenViewStream();
199 if (compileView)
201 using(StreamReader reader = new StreamReader(viewSourceStream))
203 String script = parser.CreateFunctionScriptBlock(reader, partialViewName,
204 functionName, serviceProvider, this, parameters);
206 // compiledCode = engine.Compile("def something(out):\n\tout.Write('hellloo')\n");
208 compiledCode = engine.Compile(script);
210 // Evaluates the function
211 compiledCode.Execute(globalModule);
212 // compiledCode.Execute();
214 viewInfo = new ViewInfo();
215 viewInfo.LastModified = source.LastModified;
216 viewInfo.CompiledCode = compiledCode;
217 #if DEBUG
218 viewInfo.Script = script;
219 #endif
221 name2View[partialViewName.ToLower()] = viewInfo;
224 #if DEBUG
225 HttpContext.Current.Response.Output.Write("<!-- Partial script\r\n");
226 HttpContext.Current.Response.Output.Write(viewInfo.Script);
227 HttpContext.Current.Response.Output.Write("\r\n-->");
228 HttpContext.Current.Response.Output.Flush();
229 #endif
232 return compiledCode;
235 protected string ResolveTemplateName(string area, string templateName)
237 return String.Format("{0}{1}{2}", area,
238 Path.DirectorySeparatorChar, ResolveTemplateName(templateName));
241 private void ProcessLayout(string innerViewContents, Controller controller,
242 IRailsEngineContext context, Dictionary<string, object> locals)
244 String layout = ResolveTemplateName("layouts", controller.LayoutName);
246 locals["childContent"] = innerViewContents;
247 locals["output"] = context.Response.Output;
249 ProcessViewTemplate(layout, context, locals);
252 private void ProcessViewTemplate(String templateFile, IRailsEngineContext context,
253 Dictionary<string, object> locals)
255 IViewSource source = ViewSourceLoader.GetViewSource(templateFile);
257 if (source == null)
259 throw new RailsException("Could not find view template: " + templateFile);
262 CompiledCode compiledCode = CompileTemplate(source, templateFile);
264 ExecuteScript(compiledCode, context, locals);
267 private CompiledCode CompileTemplate(IViewSource source, string templateFile)
269 bool compileView;
270 ViewInfo viewInfo;
271 CompiledCode compiledCode = null;
273 if (name2View.TryGetValue(templateFile.ToLower(), out viewInfo))
275 compileView = viewInfo.LastModified != source.LastModified;
276 compiledCode = viewInfo.CompiledCode;
278 else
280 compileView = true;
283 Stream viewSourceStream = source.OpenViewStream();
285 if (compileView)
287 using(StreamReader reader = new StreamReader(viewSourceStream))
289 String script = parser.CreateScriptBlock(reader, templateFile, serviceProvider, this);
291 compiledCode = engine.Compile(script);
293 viewInfo = new ViewInfo();
294 viewInfo.LastModified = source.LastModified;
295 viewInfo.CompiledCode = compiledCode;
296 #if DEBUG
297 viewInfo.Script = script;
298 #endif
300 name2View[templateFile.ToLower()] = viewInfo;
303 #if DEBUG
304 HttpContext.Current.Response.Output.Write("<!-- Template script \r\n");
305 HttpContext.Current.Response.Output.Write(viewInfo.Script);
306 HttpContext.Current.Response.Output.Write("\r\n-->");
307 HttpContext.Current.Response.Output.Flush();
308 #endif
311 return compiledCode;
314 private void ExecuteScript(CompiledCode script,
315 IRailsEngineContext context,
316 Dictionary<string, object> locals)
318 // NullLocalDecorator decorator = new NullLocalDecorator(locals);
322 script.Execute(globalModule, locals);
324 catch(Exception ex)
326 context.Response.Write("<p>");
327 context.Response.Write(ex.Message);
328 context.Response.Write("</p>");
329 context.Response.Write(ex.InnerException);
330 context.Response.Write("<pre>");
331 context.Response.Write(ex.StackTrace);
332 context.Response.Write("</pre>");
336 private static void PopulateLocals(TextWriter output, IRailsEngineContext context,
337 Controller controller,
338 Dictionary<string, object> locals)
340 locals["controller"] = controller;
341 locals["context"] = context;
342 locals["request"] = context.Request;
343 locals["response"] = context.Response;
344 locals["session"] = context.Session;
345 locals["output"] = output;
347 if (controller.Resources != null)
349 foreach(String key in controller.Resources.Keys)
351 locals[key] = controller.Resources[key];
355 foreach(object key in controller.Helpers.Keys)
357 locals[key.ToString()] = controller.Helpers[key];
360 foreach(DictionaryEntry entry in controller.PropertyBag)
362 locals[entry.Key.ToString()] = entry.Value;
365 foreach(String key in context.Params.AllKeys)
367 if (key == null) continue; // Nasty bug?
368 object value = context.Params[key];
369 if (value == null) continue;
370 locals[key] = value;
373 locals[Flash.FlashKey] = context.Flash;
375 foreach(DictionaryEntry entry in context.Flash)
377 if (entry.Value == null) continue;
378 locals[entry.Key.ToString()] = entry.Value;
381 locals["siteroot"] = context.ApplicationPath;
385 internal class ViewInfo
387 public long LastModified;
388 public CompiledCode CompiledCode;
389 public String Script;
392 /// <summary>
393 /// This class prevents a request to a
394 /// undefined local variable.
395 /// </summary>
396 internal class NullLocalDecorator : IDictionary<string, object>
398 private readonly Dictionary<string, object> locals;
400 public NullLocalDecorator(Dictionary<string, object> locals)
402 this.locals = locals;
405 #region IDictionary<string, object>
407 public void Add(string key, object value)
409 locals.Add(key, value);
412 public void Clear()
414 locals.Clear();
417 public bool ContainsKey(string key)
419 return locals.ContainsKey(key);
422 public bool ContainsValue(object value)
424 return locals.ContainsValue(value);
427 public void GetObjectData(SerializationInfo info, StreamingContext context)
429 locals.GetObjectData(info, context);
432 public void OnDeserialization(object sender)
434 locals.OnDeserialization(sender);
437 public bool Remove(string key)
439 return locals.Remove(key);
442 public bool TryGetValue(string key, out object value)
444 if (!locals.ContainsKey(key))
446 value = null;
447 return true;
450 return locals.TryGetValue(key, out value);
453 public IEqualityComparer<string> Comparer
455 get { return locals.Comparer; }
458 public int Count
460 get { return locals.Count; }
463 public object this[string key]
465 get { return locals[key]; }
466 set { locals[key] = value; }
469 public ICollection<string> Keys
471 get { return locals.Keys; }
474 public ICollection<object> Values
476 get { return locals.Values; }
479 #endregion
481 #region ICollection
483 public void Add(KeyValuePair<string, object> item)
485 throw new NotImplementedException();
488 public bool Contains(KeyValuePair<string, object> item)
490 throw new NotImplementedException();
493 public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
495 throw new NotImplementedException();
498 public bool Remove(KeyValuePair<string, object> item)
500 throw new NotImplementedException();
503 public bool IsReadOnly
505 get { return true; }
508 IEnumerator<KeyValuePair<string, object>> IEnumerable<KeyValuePair<string, object>>.GetEnumerator()
510 return locals.GetEnumerator();
513 public IEnumerator GetEnumerator()
515 return locals.GetEnumerator();
518 #endregion