Fix the build.
[castle.git] / MonoRail / Castle.MonoRail.Views.Brail / BrailBase.cs
blobb6c5f735f872ee0a21b73cd2bf18ebf557ccbc70
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.Views.Brail
17 using System;
18 using System.Collections;
19 using System.IO;
20 using System.Web;
21 using Framework;
23 /// <summary>
24 ///Base class for all the view scripts, this is the class that is responsible for
25 /// support all the behind the scenes magic such as variable to PropertyBag trasnlation,
26 /// resources usage, etc.
27 /// </summary>
28 public abstract class BrailBase
30 protected Controller __controller;
32 /// <summary>
33 /// Reference to the DSL service
34 /// </summary>
35 private DslProvider _dsl;
37 /// <summary>
38 /// This is used by layout scripts only, for outputing the child's content
39 /// </summary>
40 protected TextWriter childOutput;
42 protected IRailsEngineContext context;
43 private TextWriter outputStream;
45 /// <summary>
46 /// usually used by the layout to refer to its view, or a subview to its parent
47 /// </summary>
48 protected BrailBase parent;
50 private Hashtable properties;
52 /// <summary>
53 /// used to hold the ComponentParams from the view, so their views/sections could access them
54 /// </summary>
55 private IList viewComponentsParameters;
57 protected BooViewEngine viewEngine;
58 public string LastVariableAccessed;
60 /// <summary>
61 /// Initializes a new instance of the <see cref="BrailBase"/> class.
62 /// </summary>
63 /// <param name="viewEngine">The view engine.</param>
64 /// <param name="output">The output.</param>
65 /// <param name="context">The context.</param>
66 /// <param name="__controller">The controller.</param>
67 public BrailBase(BooViewEngine viewEngine, TextWriter output, IRailsEngineContext context, Controller __controller)
69 this.viewEngine = viewEngine;
70 outputStream = output;
71 this.context = context;
72 this.__controller = __controller;
73 InitProperties(context, __controller);
77 /// <summary>
78 ///The path of the script, this is filled by AddBrailBaseClassStep
79 /// and is used for sub views
80 /// </summary>
81 public virtual string ScriptDirectory
83 get { return viewEngine.ViewRootDir; }
86 /// <summary>
87 /// Gets the view engine.
88 /// </summary>
89 /// <value>The view engine.</value>
90 public BooViewEngine ViewEngine
92 get { return viewEngine; }
95 /// <summary>
96 /// Gets the DSL provider
97 /// </summary>
98 /// <value>Reference to the current DSL Provider</value>
99 public DslProvider Dsl
103 BrailBase view = this;
104 if (null == view._dsl)
106 view._dsl = new DslProvider(view);
109 return view._dsl;
110 //while (view.parent != null)
112 // view = view.parent;
115 //if (view._dsl == null)
117 // view._dsl = new DslProvider(view);
120 //return view._dsl;
124 /// <summary>
125 /// Gets the flash.
126 /// </summary>
127 /// <value>The flash.</value>
128 public Flash Flash
130 get { return context.Flash; }
133 /// <summary>
134 /// Gets the output stream.
135 /// </summary>
136 /// <value>The output stream.</value>
137 public TextWriter OutputStream
139 get { return outputStream; }
142 /// <summary>
143 /// Gets or sets the child output.
144 /// </summary>
145 /// <value>The child output.</value>
146 public TextWriter ChildOutput
148 get { return childOutput; }
149 set { childOutput = value; }
152 /// <summary>
153 /// Gets the properties.
154 /// </summary>
155 /// <value>The properties.</value>
156 public IDictionary Properties
158 get { return properties; }
161 /// <summary>
162 /// Runs this instance, this is generated by the script
163 /// </summary>
164 public abstract void Run();
166 /// <summary>
167 /// Output the subview to the client, this is either a relative path "SubView" which
168 /// is relative to the current /script/ or an "absolute" path "/home/menu" which is
169 /// actually relative to ViewDirRoot
170 /// </summary>
171 /// <param name="subviewName"></param>
172 public string OutputSubView(string subviewName)
174 return OutputSubView(subviewName, new Hashtable());
177 /// <summary>
178 /// Similiar to the OutputSubView(string) function, but with a bunch of parameters that are used
179 /// just for this subview. This parameters are /not/ inheritable.
180 /// </summary>
181 /// <returns>An empty string, just to make it possible to use inline ${OutputSubView("foo")}</returns>
182 public string OutputSubView(string subviewName, IDictionary parameters)
184 OutputSubView(subviewName, outputStream, parameters);
185 return string.Empty;
188 /// <summary>
189 /// Outputs the sub view to the writer
190 /// </summary>
191 /// <param name="subviewName">Name of the subview.</param>
192 /// <param name="writer">The writer.</param>
193 /// <param name="parameters">The parameters.</param>
194 public void OutputSubView(string subviewName, TextWriter writer, IDictionary parameters)
196 string subViewFileName = GetSubViewFilename(subviewName);
197 BrailBase subView = viewEngine.GetCompiledScriptInstance(subViewFileName, writer, context, __controller);
198 subView.SetParent(this);
199 foreach (DictionaryEntry entry in parameters)
201 subView.properties[entry.Key] = entry.Value;
203 subView.Run();
206 /// <summary>
207 /// Get the sub view file name, if the subview starts with a '/'
208 /// then the filename is considered relative to ViewDirRoot
209 /// otherwise, it's relative to the current script directory
210 /// </summary>
211 /// <param name="subviewName"></param>
212 /// <returns></returns>
213 public string GetSubViewFilename(string subviewName)
215 //absolute path from Views directory
216 if (subviewName[0] == '/')
217 return subviewName.Substring(1) + viewEngine.ViewFileExtension;
218 return Path.Combine(ScriptDirectory, subviewName) + viewEngine.ViewFileExtension;
221 /// <summary>
222 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
223 /// any uknown identifier will be translate into a call for GetParameter('identifier name').
224 /// This mean that when an uknonwn identifier is in the script, it will only be found on runtime.
225 /// </summary>
226 /// <param name="name"></param>
227 /// <returns></returns>
228 public object GetParameter(string name)
230 ParameterSearch search = GetParameterInternal(name);
231 if (search.Found == false)
232 throw new RailsException("Parameter '" + name + "' was not found!");
233 return search.Value;
236 /// <summary>
237 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
238 /// any uknown identifier with the prefix of ? will be translated into a call for
239 /// TryGetParameter('identifier name without the ? prefix').
240 /// This method will return null if the value it not found.
241 /// </summary>
242 /// <param name="name"></param>
243 /// <returns></returns>
244 public object TryGetParameter(string name)
246 ParameterSearch search = GetParameterInternal(name);
247 return new IgnoreNull(search.Value);
250 /// <summary>
251 /// Gets the parameter - implements the logic for searching parameters.
252 /// </summary>
253 /// <param name="name">The name.</param>
254 /// <returns></returns>
255 private ParameterSearch GetParameterInternal(string name)
257 LastVariableAccessed = name;
258 //temporary syntax to turn @variable to varaible, imitating :symbol in ruby
259 if (name.StartsWith("@"))
260 return new ParameterSearch(name.Substring(1), true);
261 if (viewComponentsParameters != null)
263 foreach (IDictionary viewComponentProperties in viewComponentsParameters)
265 if (viewComponentProperties.Contains(name))
266 return new ParameterSearch(viewComponentProperties[name], true);
269 if (properties.Contains(name))
270 return new ParameterSearch(properties[name], true);
271 if (parent != null)
272 return parent.GetParameterInternal(name);
273 return new ParameterSearch(null, false);
276 /// <summary>
277 /// Sets the parent.
278 /// </summary>
279 /// <param name="myParent">My parent.</param>
280 public void SetParent(BrailBase myParent)
282 parent = myParent;
285 /// <summary>
286 /// Allows to check that a parameter was defined
287 /// </summary>
288 /// <param name="name"></param>
289 /// <returns></returns>
290 public bool IsDefined(string name)
292 ParameterSearch search = GetParameterInternal(name);
293 return search.Found;
296 /// <summary>
297 /// This is required because we may want to replace the output stream and get the correct
298 /// behavior from components call RenderText() or RenderSection()
299 /// </summary>
300 public IDisposable SetOutputStream(TextWriter newOutputStream)
302 ReturnOutputStreamToInitialWriter disposable = new ReturnOutputStreamToInitialWriter(OutputStream, this);
303 outputStream = newOutputStream;
304 return disposable;
307 /// <summary>
308 /// Will output the given value as escaped HTML
309 /// </summary>
310 /// <param name="toOutput"></param>
311 public void OutputEscaped(object toOutput)
313 if (toOutput == null)
314 return;
315 string str = toOutput.ToString();
316 OutputStream.Write(HttpUtility.HtmlEncode(str));
319 /// <summary>
320 /// Note that this will overwrite any existing property.
321 /// </summary>
322 public void AddProperty(string name, object item)
324 properties[name] = item;
327 /// <summary>
328 /// Adds the view component newProperties.
329 /// This will be included in the parameters searching, note that this override
330 /// the current parameters if there are clashing.
331 /// The search order is LIFO
332 /// </summary>
333 /// <param name="newProperties">The newProperties.</param>
334 public void AddViewComponentProperties(IDictionary newProperties)
336 if (viewComponentsParameters == null)
337 viewComponentsParameters = new ArrayList();
338 viewComponentsParameters.Insert(0, newProperties);
341 /// <summary>
342 /// Removes the view component properties, so they will no longer be visible to the views.
343 /// </summary>
344 /// <param name="propertiesToRemove">The properties to remove.</param>
345 public void RemoveViewComponentProperties(IDictionary propertiesToRemove)
347 if (viewComponentsParameters == null)
348 return;
349 viewComponentsParameters.Remove(propertiesToRemove);
352 public void RenderComponent(string componentName)
354 RenderComponent(componentName, new Hashtable());
357 public void RenderComponent(string componentName, IDictionary parameters)
359 BrailViewComponentContext componentContext =
360 new BrailViewComponentContext(this, null, componentName, OutputStream,
361 new Hashtable(StringComparer.InvariantCultureIgnoreCase));
362 AddViewComponentProperties(componentContext.ComponentParameters);
363 IViewComponentFactory componentFactory = (IViewComponentFactory) context.GetService(typeof (IViewComponentFactory));
364 ViewComponent component = componentFactory.Create(componentName);
365 component.Init(context, componentContext);
366 component.Render();
367 if (componentContext.ViewToRender != null)
369 OutputSubView("/" + componentContext.ViewToRender, componentContext.ComponentParameters);
371 RemoveViewComponentProperties(componentContext.ComponentParameters);
374 /// <summary>
375 /// Initialize all the properties that a script may need
376 /// One thing to note here is that resources are wrapped in ResourceToDuck wrapper
377 /// to enable easy use by the script
378 /// </summary>
379 /// <param name="myContext"></param>
380 /// <param name="myController"></param>
381 private void InitProperties(IRailsEngineContext myContext, IController myController)
383 properties = new Hashtable(StringComparer.InvariantCultureIgnoreCase);
384 //properties.Add("dsl", new DslWrapper(this));
385 properties.Add("Controller", myController);
386 properties.Add("Context", myContext);
387 if (myContext != null)
389 properties.Add("request", myContext.Request);
390 properties.Add("response", myContext.Response);
391 properties.Add("session", myContext.Session);
394 if (myController.Resources != null)
396 foreach (object key in myController.Resources.Keys)
398 properties.Add(key, new ResourceToDuck(myController.Resources[key]));
402 if (myContext != null && myController.Params != null)
404 foreach (string key in myController.Params.AllKeys)
406 if (key == null)
407 continue;
408 properties[key] = myContext.Params[key];
411 if (myContext != null && myContext.Flash != null)
413 foreach (DictionaryEntry entry in myContext.Flash)
415 properties[entry.Key] = entry.Value;
419 if (myController.PropertyBag != null)
421 foreach (DictionaryEntry entry in myController.PropertyBag)
423 properties[entry.Key] = entry.Value;
427 if (myController.Helpers != null)
429 foreach (DictionaryEntry entry in myController.Helpers)
431 properties[entry.Key] = entry.Value;
435 if(myContext != null )
437 properties["siteRoot"] = myContext.ApplicationPath;
441 #region Nested type: ParameterSearch
443 private class ParameterSearch
445 private readonly bool found;
446 private readonly object value;
448 public ParameterSearch(object value, bool found)
450 this.found = found;
451 this.value = value;
454 public bool Found
456 get { return found; }
459 public object Value
461 get { return value; }
465 #endregion
467 #region Nested type: ReturnOutputStreamToInitialWriter
469 private class ReturnOutputStreamToInitialWriter : IDisposable
471 private TextWriter initialWriter;
472 private BrailBase parent;
474 public ReturnOutputStreamToInitialWriter(TextWriter initialWriter, BrailBase parent)
476 this.initialWriter = initialWriter;
477 this.parent = parent;
480 #region IDisposable Members
482 public void Dispose()
484 parent.outputStream = initialWriter;
487 #endregion
490 #endregion