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
.Views
.Brail
18 using System
.Collections
;
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.
28 public abstract class BrailBase
30 protected Controller __controller
;
33 /// Reference to the DSL service
35 private DslProvider _dsl
;
38 /// This is used by layout scripts only, for outputing the child's content
40 protected TextWriter childOutput
;
42 protected IRailsEngineContext context
;
43 private TextWriter outputStream
;
46 /// usually used by the layout to refer to its view, or a subview to its parent
48 protected BrailBase parent
;
50 private Hashtable properties
;
53 /// used to hold the ComponentParams from the view, so their views/sections could access them
55 private IList viewComponentsParameters
;
57 protected BooViewEngine viewEngine
;
58 public string LastVariableAccessed
;
61 /// Initializes a new instance of the <see cref="BrailBase"/> class.
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
);
78 ///The path of the script, this is filled by AddBrailBaseClassStep
79 /// and is used for sub views
81 public virtual string ScriptDirectory
83 get { return viewEngine.ViewRootDir; }
87 /// Gets the view engine.
89 /// <value>The view engine.</value>
90 public BooViewEngine ViewEngine
92 get { return viewEngine; }
96 /// Gets the DSL provider
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
);
110 //while (view.parent != null)
112 // view = view.parent;
115 //if (view._dsl == null)
117 // view._dsl = new DslProvider(view);
127 /// <value>The flash.</value>
130 get { return context.Flash; }
134 /// Gets the output stream.
136 /// <value>The output stream.</value>
137 public TextWriter OutputStream
139 get { return outputStream; }
143 /// Gets or sets the child output.
145 /// <value>The child output.</value>
146 public TextWriter ChildOutput
148 get { return childOutput; }
149 set { childOutput = value; }
153 /// Gets the properties.
155 /// <value>The properties.</value>
156 public IDictionary Properties
158 get { return properties; }
162 /// Runs this instance, this is generated by the script
164 public abstract void Run();
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
171 /// <param name="subviewName"></param>
172 public string OutputSubView(string subviewName
)
174 return OutputSubView(subviewName
, new Hashtable());
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.
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
);
189 /// Outputs the sub view to the writer
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
;
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
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
;
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.
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!");
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.
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
);
251 /// Gets the parameter - implements the logic for searching parameters.
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);
272 return parent
.GetParameterInternal(name
);
273 return new ParameterSearch(null, false);
279 /// <param name="myParent">My parent.</param>
280 public void SetParent(BrailBase myParent
)
286 /// Allows to check that a parameter was defined
288 /// <param name="name"></param>
289 /// <returns></returns>
290 public bool IsDefined(string name
)
292 ParameterSearch search
= GetParameterInternal(name
);
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()
300 public IDisposable
SetOutputStream(TextWriter newOutputStream
)
302 ReturnOutputStreamToInitialWriter disposable
= new ReturnOutputStreamToInitialWriter(OutputStream
, this);
303 outputStream
= newOutputStream
;
308 /// Will output the given value as escaped HTML
310 /// <param name="toOutput"></param>
311 public void OutputEscaped(object toOutput
)
313 if (toOutput
== null)
315 string str
= toOutput
.ToString();
316 OutputStream
.Write(HttpUtility
.HtmlEncode(str
));
320 /// Note that this will overwrite any existing property.
322 public void AddProperty(string name
, object item
)
324 properties
[name
] = item
;
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
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
);
342 /// Removes the view component properties, so they will no longer be visible to the views.
344 /// <param name="propertiesToRemove">The properties to remove.</param>
345 public void RemoveViewComponentProperties(IDictionary propertiesToRemove
)
347 if (viewComponentsParameters
== null)
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
);
367 if (componentContext
.ViewToRender
!= null)
369 OutputSubView("/" + componentContext
.ViewToRender
, componentContext
.ComponentParameters
);
371 RemoveViewComponentProperties(componentContext
.ComponentParameters
);
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
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
)
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
)
456 get { return found; }
461 get { return value; }
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
;