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
;
60 /// Initializes a new instance of the <see cref="BrailBase"/> class.
62 /// <param name="viewEngine">The view engine.</param>
63 /// <param name="output">The output.</param>
64 /// <param name="context">The context.</param>
65 /// <param name="__controller">The controller.</param>
66 public BrailBase(BooViewEngine viewEngine
, TextWriter output
, IRailsEngineContext context
, Controller __controller
)
68 this.viewEngine
= viewEngine
;
69 outputStream
= output
;
70 this.context
= context
;
71 this.__controller
= __controller
;
72 InitProperties(context
, __controller
);
77 ///The path of the script, this is filled by AddBrailBaseClassStep
78 /// and is used for sub views
80 public virtual string ScriptDirectory
82 get { return viewEngine.ViewRootDir; }
86 /// Gets the view engine.
88 /// <value>The view engine.</value>
89 public BooViewEngine ViewEngine
91 get { return viewEngine; }
95 /// Gets the DSL provider
97 /// <value>Reference to the current DSL Provider</value>
98 public DslProvider Dsl
102 BrailBase view
= this;
103 if (null == view
._dsl
)
105 view
._dsl
= new DslProvider(view
);
109 //while (view.parent != null)
111 // view = view.parent;
114 //if (view._dsl == null)
116 // view._dsl = new DslProvider(view);
126 /// <value>The flash.</value>
129 get { return context.Flash; }
133 /// Gets the output stream.
135 /// <value>The output stream.</value>
136 public TextWriter OutputStream
138 get { return outputStream; }
142 /// Gets or sets the child output.
144 /// <value>The child output.</value>
145 public TextWriter ChildOutput
147 get { return childOutput; }
148 set { childOutput = value; }
152 /// Gets the properties.
154 /// <value>The properties.</value>
155 public IDictionary Properties
157 get { return properties; }
161 /// Runs this instance, this is generated by the script
163 public abstract void Run();
166 /// Output the subview to the client, this is either a relative path "SubView" which
167 /// is relative to the current /script/ or an "absolute" path "/home/menu" which is
168 /// actually relative to ViewDirRoot
170 /// <param name="subviewName"></param>
171 public string OutputSubView(string subviewName
)
173 return OutputSubView(subviewName
, new Hashtable());
177 /// Similiar to the OutputSubView(string) function, but with a bunch of parameters that are used
178 /// just for this subview. This parameters are /not/ inheritable.
180 /// <returns>An empty string, just to make it possible to use inline ${OutputSubView("foo")}</returns>
181 public string OutputSubView(string subviewName
, IDictionary parameters
)
183 OutputSubView(subviewName
, outputStream
, parameters
);
188 /// Outputs the sub view to the writer
190 /// <param name="subviewName">Name of the subview.</param>
191 /// <param name="writer">The writer.</param>
192 /// <param name="parameters">The parameters.</param>
193 public void OutputSubView(string subviewName
, TextWriter writer
, IDictionary parameters
)
195 string subViewFileName
= GetSubViewFilename(subviewName
);
196 BrailBase subView
= viewEngine
.GetCompiledScriptInstance(subViewFileName
, writer
, context
, __controller
);
197 subView
.SetParent(this);
198 foreach (DictionaryEntry entry
in parameters
)
200 subView
.properties
[entry
.Key
] = entry
.Value
;
206 /// Get the sub view file name, if the subview starts with a '/'
207 /// then the filename is considered relative to ViewDirRoot
208 /// otherwise, it's relative to the current script directory
210 /// <param name="subviewName"></param>
211 /// <returns></returns>
212 public string GetSubViewFilename(string subviewName
)
214 //absolute path from Views directory
215 if (subviewName
[0] == '/')
216 return subviewName
.Substring(1) + viewEngine
.ViewFileExtension
;
217 return Path
.Combine(ScriptDirectory
, subviewName
) + viewEngine
.ViewFileExtension
;
221 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
222 /// any uknown identifier will be translate into a call for GetParameter('identifier name').
223 /// This mean that when an uknonwn identifier is in the script, it will only be found on runtime.
225 /// <param name="name"></param>
226 /// <returns></returns>
227 public object GetParameter(string name
)
229 ParameterSearch search
= GetParameterInternal(name
);
230 if (search
.Found
== false)
231 throw new RailsException("Parameter '" + name
+ "' was not found!");
236 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
237 /// any uknown identifier with the prefix of ? will be translated into a call for
238 /// TryGetParameter('identifier name without the ? prefix').
239 /// This method will return null if the value it not found.
241 /// <param name="name"></param>
242 /// <returns></returns>
243 public object TryGetParameter(string name
)
245 ParameterSearch search
= GetParameterInternal(name
);
246 return new IgnoreNull(search
.Value
);
250 /// Gets the parameter - implements the logic for searching parameters.
252 /// <param name="name">The name.</param>
253 /// <returns></returns>
254 private ParameterSearch
GetParameterInternal(string name
)
256 //temporary syntax to turn @variable to varaible, imitating :symbol in ruby
257 if (name
.StartsWith("@"))
258 return new ParameterSearch(name
.Substring(1), true);
259 if (viewComponentsParameters
!= null)
261 foreach (IDictionary viewComponentProperties
in viewComponentsParameters
)
263 if (viewComponentProperties
.Contains(name
))
264 return new ParameterSearch(viewComponentProperties
[name
], true);
267 if (properties
.Contains(name
))
268 return new ParameterSearch(properties
[name
], true);
270 return parent
.GetParameterInternal(name
);
271 return new ParameterSearch(null, false);
277 /// <param name="myParent">My parent.</param>
278 public void SetParent(BrailBase myParent
)
284 /// Allows to check that a parameter was defined
286 /// <param name="name"></param>
287 /// <returns></returns>
288 public bool IsDefined(string name
)
290 ParameterSearch search
= GetParameterInternal(name
);
295 /// This is required because we may want to replace the output stream and get the correct
296 /// behavior from components call RenderText() or RenderSection()
298 public IDisposable
SetOutputStream(TextWriter newOutputStream
)
300 ReturnOutputStreamToInitialWriter disposable
= new ReturnOutputStreamToInitialWriter(OutputStream
, this);
301 outputStream
= newOutputStream
;
306 /// Will output the given value as escaped HTML
308 /// <param name="toOutput"></param>
309 public void OutputEscaped(object toOutput
)
311 if (toOutput
== null)
313 string str
= toOutput
.ToString();
314 OutputStream
.Write(HttpUtility
.HtmlEncode(str
));
318 /// Note that this will overwrite any existing property.
320 public void AddProperty(string name
, object item
)
322 properties
[name
] = item
;
326 /// Adds the view component newProperties.
327 /// This will be included in the parameters searching, note that this override
328 /// the current parameters if there are clashing.
329 /// The search order is LIFO
331 /// <param name="newProperties">The newProperties.</param>
332 public void AddViewComponentProperties(IDictionary newProperties
)
334 if (viewComponentsParameters
== null)
335 viewComponentsParameters
= new ArrayList();
336 viewComponentsParameters
.Insert(0, newProperties
);
340 /// Removes the view component properties, so they will no longer be visible to the views.
342 /// <param name="propertiesToRemove">The properties to remove.</param>
343 public void RemoveViewComponentProperties(IDictionary propertiesToRemove
)
345 if (viewComponentsParameters
== null)
347 viewComponentsParameters
.Remove(propertiesToRemove
);
350 public void RenderComponent(string componentName
)
352 RenderComponent(componentName
, new Hashtable());
355 public void RenderComponent(string componentName
, IDictionary parameters
)
357 BrailViewComponentContext componentContext
=
358 new BrailViewComponentContext(this, null, componentName
, OutputStream
,
359 new Hashtable(StringComparer
.InvariantCultureIgnoreCase
));
360 AddViewComponentProperties(componentContext
.ComponentParameters
);
361 IViewComponentFactory componentFactory
= (IViewComponentFactory
) context
.GetService(typeof (IViewComponentFactory
));
362 ViewComponent component
= componentFactory
.Create(componentName
);
363 component
.Init(context
, componentContext
);
365 if (componentContext
.ViewToRender
!= null)
367 OutputSubView("/" + componentContext
.ViewToRender
, componentContext
.ComponentParameters
);
369 RemoveViewComponentProperties(componentContext
.ComponentParameters
);
373 /// Initialize all the properties that a script may need
374 /// One thing to note here is that resources are wrapped in ResourceToDuck wrapper
375 /// to enable easy use by the script
377 /// <param name="myContext"></param>
378 /// <param name="myController"></param>
379 private void InitProperties(IRailsEngineContext myContext
, Controller myController
)
381 properties
= new Hashtable(StringComparer
.InvariantCultureIgnoreCase
);
382 //properties.Add("dsl", new DslWrapper(this));
383 properties
.Add("Controller", myController
);
384 properties
.Add("Context", myContext
);
385 properties
.Add("request", myContext
.Request
);
386 properties
.Add("response", myContext
.Response
);
387 properties
.Add("session", myContext
.Session
);
389 if (myController
.Resources
!= null)
391 foreach (object key
in myController
.Resources
.Keys
)
393 properties
.Add(key
, new ResourceToDuck(myController
.Resources
[key
]));
397 if (myController
.Params
!= null)
399 foreach (string key
in myController
.Params
.AllKeys
)
403 properties
[key
] = myContext
.Params
[key
];
406 if (myContext
.Flash
!= null)
408 foreach (DictionaryEntry entry
in myContext
.Flash
)
410 properties
[entry
.Key
] = entry
.Value
;
414 if (myController
.PropertyBag
!= null)
416 foreach (DictionaryEntry entry
in myController
.PropertyBag
)
418 properties
[entry
.Key
] = entry
.Value
;
422 if (myController
.Helpers
!= null)
424 foreach (DictionaryEntry entry
in myController
.Helpers
)
426 properties
[entry
.Key
] = entry
.Value
;
430 properties
["siteRoot"] = myContext
.ApplicationPath
;
433 #region Nested type: ParameterSearch
435 private class ParameterSearch
437 private readonly bool found
;
438 private readonly object value;
440 public ParameterSearch(object value, bool found
)
448 get { return found; }
453 get { return value; }
459 #region Nested type: ReturnOutputStreamToInitialWriter
461 private class ReturnOutputStreamToInitialWriter
: IDisposable
463 private TextWriter initialWriter
;
464 private BrailBase parent
;
466 public ReturnOutputStreamToInitialWriter(TextWriter initialWriter
, BrailBase parent
)
468 this.initialWriter
= initialWriter
;
469 this.parent
= parent
;
472 #region IDisposable Members
474 public void Dispose()
476 parent
.outputStream
= initialWriter
;