Fixing IOC-60, Custom Parameters do not play a role in resolving chains
[castle.git] / MonoRail / Castle.MonoRail.Views.Brail / BrailBase.cs
blobbbee0686a5dda79a3bb5f5d74f1d10d6c4a06ce0
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 using System.Diagnostics;
16 using System.Text;
18 namespace Castle.MonoRail.Views.Brail
20 using System;
21 using System.Collections;
22 using System.IO;
23 using Castle.MonoRail.Framework;
24 /// <summary>
25 ///Base class for all the view scripts, this is the class that is responsible for
26 /// support all the behind the scenes magic such as variable to PropertyBag trasnlation,
27 /// resources usage, etc.
28 /// </summary>
29 public abstract class BrailBase
31 private TextWriter outputStream;
33 /// <summary>
34 /// This is used by layout scripts only, for outputing the child's content
35 /// </summary>
36 protected TextWriter childOutput;
37 private Hashtable properties;
38 /// <summary>
39 /// used to hold the ComponentParams from the view, so their views/sections could access them
40 /// </summary>
41 private IList viewComponentsParameters;
43 protected IRailsEngineContext context;
44 protected Controller controller;
45 protected BooViewEngine viewEngine;
47 /// <summary>
48 /// usually used by the layout to refer to its view, or a subview to its parent
49 /// </summary>
50 protected BrailBase parent;
52 /// <summary>
53 /// Initializes a new instance of the <see cref="BrailBase"/> class.
54 /// </summary>
55 /// <param name="viewEngine">The view engine.</param>
56 /// <param name="output">The output.</param>
57 /// <param name="context">The context.</param>
58 /// <param name="controller">The controller.</param>
59 public BrailBase(BooViewEngine viewEngine, TextWriter output, IRailsEngineContext context, Controller controller)
61 this.viewEngine = viewEngine;
62 outputStream = output;
63 this.context = context;
64 this.controller = controller;
65 InitProperties(context, controller);
68 /// <summary>
69 /// Runs this instance, this is generated by the script
70 /// </summary>
71 public abstract void Run();
74 /// <summary>
75 ///The path of the script, this is filled by AddBrailBaseClassStep
76 /// and is used for sub views
77 /// </summary>
78 public virtual string ScriptDirectory
80 get { return viewEngine.ViewRootDir; }
83 /// <summary>
84 /// Gets the view engine.
85 /// </summary>
86 /// <value>The view engine.</value>
87 public BooViewEngine ViewEngine
89 get { return viewEngine; }
92 /// <summary>
93 /// Output the subview to the client, this is either a relative path "SubView" which
94 /// is relative to the current /script/ or an "absolute" path "/home/menu" which is
95 /// actually relative to ViewDirRoot
96 /// </summary>
97 /// <param name="subviewName"></param>
98 public void OutputSubView(string subviewName)
100 OutputSubView(subviewName, new Hashtable());
103 /// <summary>
104 /// Similiar to the OutputSubView(string) function, but with a bunch of parameters that are used
105 /// just for this subview. This parameters are /not/ inheritable.
106 /// </summary>
107 /// <param name="subviewName"></param>
108 /// <param name="parameters"></param>
109 public void OutputSubView(string subviewName, IDictionary parameters)
111 OutputSubView(subviewName, outputStream, parameters);
114 /// <summary>
115 /// Outputs the sub view to the writer
116 /// </summary>
117 /// <param name="subviewName">Name of the subview.</param>
118 /// <param name="writer">The writer.</param>
119 /// <param name="parameters">The parameters.</param>
120 public void OutputSubView(string subviewName, TextWriter writer, IDictionary parameters)
122 string subViewFileName = GetSubViewFilename(subviewName);
123 BrailBase subView = viewEngine.GetCompiledScriptInstance(subViewFileName, writer, context, controller);
124 subView.SetParent(this);
125 foreach (DictionaryEntry entry in parameters)
127 subView.properties[entry.Key] = entry.Value;
129 subView.Run();
132 /// <summary>
133 /// Get the sub view file name, if the subview starts with a '/'
134 /// then the filename is considered relative to ViewDirRoot
135 /// otherwise, it's relative to the current script directory
136 /// </summary>
137 /// <param name="subviewName"></param>
138 /// <returns></returns>
139 public string GetSubViewFilename(string subviewName)
141 //absolute path from Views directory
142 if (subviewName[0] == '/')
143 return subviewName.Substring(1) + viewEngine.ViewFileExtension;
144 return Path.Combine(ScriptDirectory, subviewName) + viewEngine.ViewFileExtension;
147 /// <summary>
148 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
149 /// any uknown identifier will be translate into a call for GetParameter('identifier name').
150 /// This mean that when an uknonwn identifier is in the script, it will only be found on runtime.
151 /// </summary>
152 /// <param name="name"></param>
153 /// <returns></returns>
154 public object GetParameter(string name)
156 ParameterSearch search = GetParameterInternal(name);
157 if (search.Found == false)
158 throw new RailsException("Parameter '" + name + "' was not found!");
159 return search.Value;
162 /// <summary>
163 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
164 /// any uknown identifier with the prefix of ? will be translated into a call for
165 /// TryGetParameter('identifier name without the ? prefix').
166 /// This method will return null if the value it not found.
167 /// </summary>
168 /// <param name="name"></param>
169 /// <returns></returns>
170 public object TryGetParameter(string name)
172 ParameterSearch search = GetParameterInternal(name);
173 return search.Value;
176 /// <summary>
177 /// Gets the parameter - implements the logic for searching parameters.
178 /// </summary>
179 /// <param name="name">The name.</param>
180 /// <returns></returns>
181 private ParameterSearch GetParameterInternal(string name)
183 //temporary syntax to turn @variable to varaible, imitating :symbol in ruby
184 if (name.StartsWith("@"))
185 return new ParameterSearch(name.Substring(1), true);
186 if(viewComponentsParameters!=null)
188 foreach(IDictionary viewComponentProperties in viewComponentsParameters)
190 if (viewComponentProperties.Contains(name))
191 return new ParameterSearch(viewComponentProperties[name], true);
194 if (properties.Contains(name))
195 return new ParameterSearch(properties[name], true);
196 if (parent != null)
197 return parent.GetParameterInternal(name);
198 return new ParameterSearch(null, false);
201 /// <summary>
202 /// Sets the parent.
203 /// </summary>
204 /// <param name="myParent">My parent.</param>
205 public void SetParent(BrailBase myParent)
207 parent = myParent;
210 /// <summary>
211 /// Allows to check that a parameter was defined
212 /// </summary>
213 /// <param name="name"></param>
214 /// <returns></returns>
215 public bool IsDefined(string name)
217 ParameterSearch search = GetParameterInternal(name);
218 return search.Found;
221 /// <summary>
222 /// Gets the flash.
223 /// </summary>
224 /// <value>The flash.</value>
225 public Flash Flash
227 get { return context.Flash; }
230 /// <summary>
231 /// Gets the output stream.
232 /// </summary>
233 /// <value>The output stream.</value>
234 public TextWriter OutputStream
236 get { return outputStream; }
239 /// <summary>
240 /// This is required because we may want to replace the output stream and get the correct
241 /// behavior from components call RenderText() or RenderSection()
242 /// </summary>
243 public IDisposable SetOutputStream(TextWriter newOutputStream)
245 ReturnOutputStreamToInitialWriter disposable = new ReturnOutputStreamToInitialWriter(OutputStream, this);
246 outputStream = newOutputStream;
247 return disposable;
250 /// <summary>
251 /// Gets or sets the child output.
252 /// </summary>
253 /// <value>The child output.</value>
254 public TextWriter ChildOutput
256 get { return childOutput; }
257 set { childOutput = value; }
260 /// <summary>
261 /// Gets the properties.
262 /// </summary>
263 /// <value>The properties.</value>
264 public IDictionary Properties
266 get { return properties; }
269 /// <summary>
270 /// Note that this will overwrite any existing property.
271 /// </summary>
272 public void AddProperty(string name, object item)
274 properties[name] = item;
277 /// <summary>
278 /// Adds the view component newProperties.
279 /// This will be included in the parameters searching, note that this override
280 /// the current parameters if there are clashing.
281 /// The search order is LIFO
282 /// </summary>
283 /// <param name="newProperties">The newProperties.</param>
284 public void AddViewComponentProperties(IDictionary newProperties)
286 if (viewComponentsParameters == null)
287 viewComponentsParameters = new ArrayList();
288 viewComponentsParameters.Insert(0, newProperties);
291 /// <summary>
292 /// Removes the view component properties, so they will no longer be visible to the views.
293 /// </summary>
294 /// <param name="propertiesToRemove">The properties to remove.</param>
295 public void RemoveViewComponentProperties(IDictionary propertiesToRemove)
297 if (viewComponentsParameters == null)
298 return;
299 viewComponentsParameters.Remove(propertiesToRemove);
302 /// <summary>
303 /// Initialize all the properties that a script may need
304 /// One thing to note here is that resources are wrapped in ResourceToDuck wrapper
305 /// to enable easy use by the script
306 /// </summary>
307 /// <param name="myContext"></param>
308 /// <param name="myController"></param>
309 private void InitProperties(IRailsEngineContext myContext, Controller myController)
311 properties = new Hashtable(
312 #if DOTNET2
313 StringComparer.InvariantCultureIgnoreCase
314 #else
315 CaseInsensitiveHashCodeProvider.Default,
316 CaseInsensitiveComparer.Default
317 #endif
320 properties.Add("request", myContext.Request);
321 properties.Add("response", myContext.Response);
322 properties.Add("session", myContext.Session);
324 if (myController.Resources != null)
326 foreach(object key in myController.Resources.Keys)
328 properties.Add(key, new ResourceToDuck(myController.Resources[key]));
332 foreach(DictionaryEntry entry in myController.Helpers)
334 properties.Add(entry.Key, entry.Value);
337 foreach(string key in myController.Params.AllKeys)
339 if (key == null)
340 continue;
341 properties[key] = myContext.Params[key];
344 foreach(DictionaryEntry entry in myContext.Flash)
346 properties[entry.Key] = entry.Value;
349 foreach(DictionaryEntry entry in myController.PropertyBag)
351 properties[entry.Key] = entry.Value;
354 properties["siteRoot"] = myContext.ApplicationPath;
357 private class ParameterSearch
359 private bool found;
360 private object value;
362 public bool Found
364 get { return found; }
367 public object Value
369 get { return value; }
372 public ParameterSearch(object value, bool found)
374 this.found = found;
375 this.value = value;
379 private class ReturnOutputStreamToInitialWriter : IDisposable
381 private TextWriter initialWriter;
382 private BrailBase parent;
384 public ReturnOutputStreamToInitialWriter(TextWriter initialWriter, BrailBase parent)
386 this.initialWriter = initialWriter;
387 this.parent = parent;
390 public void Dispose()
392 parent.outputStream = initialWriter;