1 // Copyright 2004-2008 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 IController __controller
;
31 protected IControllerContext __controllerContext
;
34 /// Reference to the DSL service
36 private DslProvider _dsl
;
39 /// This is used by layout scripts only, for outputing the child's content
41 protected TextWriter childOutput
;
43 protected internal IEngineContext context
;
44 public string LastVariableAccessed
;
45 private TextWriter outputStream
;
48 /// usually used by the layout to refer to its view, or a subview to its parent
50 protected BrailBase parent
;
52 private Hashtable properties
;
55 /// used to hold the ComponentParams from the view, so their views/sections could access them
57 private IList viewComponentsParameters
;
59 protected BooViewEngine viewEngine
;
62 /// Initializes a new instance of the <see cref="BrailBase"/> class.
64 /// <param name="viewEngine">The view engine.</param>
65 /// <param name="output">The output.</param>
66 /// <param name="context">The context.</param>
67 /// <param name="__controller">The controller.</param>
68 /// <param name="__controllerContext">The __controller context.</param>
69 public BrailBase(BooViewEngine viewEngine
, TextWriter output
,
70 IEngineContext context
, IController __controller
, IControllerContext __controllerContext
)
72 this.viewEngine
= viewEngine
;
73 outputStream
= output
;
74 this.context
= context
;
75 this.__controller
= __controller
;
76 this.__controllerContext
= __controllerContext
;
77 InitProperties(context
, __controller
, __controllerContext
);
81 ///The path of the script, this is filled by AddBrailBaseClassStep
82 /// and is used for sub views
84 public virtual string ScriptDirectory
86 get { return viewEngine.ViewRootDir; }
90 /// Gets the view engine.
92 /// <value>The view engine.</value>
93 public BooViewEngine ViewEngine
95 get { return viewEngine; }
99 /// Gets the DSL provider
101 /// <value>Reference to the current DSL Provider</value>
102 public DslProvider Dsl
106 BrailBase view
= this;
107 if (null == view
._dsl
)
109 view
._dsl
= new DslProvider(view
);
113 //while (view.parent != null)
115 // view = view.parent;
118 //if (view._dsl == null)
120 // view._dsl = new DslProvider(view);
130 /// <value>The flash.</value>
133 get { return context.Flash; }
137 /// Gets the output stream.
139 /// <value>The output stream.</value>
140 public TextWriter OutputStream
142 get { return outputStream; }
146 /// Gets or sets the child output.
148 /// <value>The child output.</value>
149 public TextWriter ChildOutput
151 get { return childOutput; }
152 set { childOutput = value; }
156 /// Gets the properties.
158 /// <value>The properties.</value>
159 public IDictionary Properties
161 get { return properties; }
165 /// Runs this instance, this is generated by the script
167 public abstract void Run();
170 /// Output the subview to the client, this is either a relative path "SubView" which
171 /// is relative to the current /script/ or an "absolute" path "/home/menu" which is
172 /// actually relative to ViewDirRoot
174 /// <param name="subviewName"></param>
175 public string OutputSubView(string subviewName
)
177 return OutputSubView(subviewName
, new Hashtable());
181 /// Similiar to the OutputSubView(string) function, but with a bunch of parameters that are used
182 /// just for this subview. This parameters are /not/ inheritable.
184 /// <returns>An empty string, just to make it possible to use inline ${OutputSubView("foo")}</returns>
185 public string OutputSubView(string subviewName
, IDictionary parameters
)
187 OutputSubView(subviewName
, outputStream
, parameters
);
192 /// Outputs the sub view to the writer
194 /// <param name="subviewName">Name of the subview.</param>
195 /// <param name="writer">The writer.</param>
196 /// <param name="parameters">The parameters.</param>
197 public void OutputSubView(string subviewName
, TextWriter writer
, IDictionary parameters
)
199 string subViewFileName
= GetSubViewFilename(subviewName
);
201 viewEngine
.GetCompiledScriptInstance(subViewFileName
, writer
, context
, __controller
, __controllerContext
);
202 subView
.SetParent(this);
203 foreach(DictionaryEntry entry
in parameters
)
205 subView
.properties
[entry
.Key
] = entry
.Value
;
208 foreach(DictionaryEntry entry
in subView
.Properties
)
210 if (subView
.Properties
.Contains(entry
.Key
+ ".@bubbleUp") == false)
212 properties
[entry
.Key
] = entry
.Value
;
213 properties
[entry
.Key
+ ".@bubbleUp"] = true;
218 /// Get the sub view file name, if the subview starts with a '/'
219 /// then the filename is considered relative to ViewDirRoot
220 /// otherwise, it's relative to the current script directory
222 /// <param name="subviewName"></param>
223 /// <returns></returns>
224 public string GetSubViewFilename(string subviewName
)
226 //absolute path from Views directory
227 if (subviewName
[0] == '/')
228 return subviewName
.Substring(1) + viewEngine
.ViewFileExtension
;
229 return Path
.Combine(ScriptDirectory
, subviewName
) + viewEngine
.ViewFileExtension
;
233 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
234 /// any uknown identifier will be translate into a call for GetParameter('identifier name').
235 /// This mean that when an uknonwn identifier is in the script, it will only be found on runtime.
237 /// <param name="name"></param>
238 /// <returns></returns>
239 public object GetParameter(string name
)
241 ParameterSearch search
= GetParameterInternal(name
);
242 if (search
.Found
== false)
243 throw new MonoRailException("Parameter '" + name
+ "' was not found!");
248 /// this is called by ReplaceUnknownWithParameters step to create a more dynamic experiance
249 /// any uknown identifier with the prefix of ? will be translated into a call for
250 /// TryGetParameter('identifier name without the ? prefix').
251 /// This method will return null if the value it not found.
253 /// <param name="name"></param>
254 /// <returns></returns>
255 public object TryGetParameter(string name
)
257 ParameterSearch search
= GetParameterInternal(name
);
258 return new IgnoreNull(search
.Value
);
262 /// Gets the parameter - implements the logic for searching parameters.
264 /// <param name="name">The name.</param>
265 /// <returns></returns>
266 private ParameterSearch
GetParameterInternal(string name
)
268 LastVariableAccessed
= name
;
269 //temporary syntax to turn @variable to varaible, imitating :symbol in ruby
270 if (name
.StartsWith("@"))
271 return new ParameterSearch(name
.Substring(1), true);
272 if (viewComponentsParameters
!= null)
274 foreach(IDictionary viewComponentProperties
in viewComponentsParameters
)
276 if (viewComponentProperties
.Contains(name
))
277 return new ParameterSearch(viewComponentProperties
[name
], true);
280 if (properties
.Contains(name
))
281 return new ParameterSearch(properties
[name
], true);
283 return parent
.GetParameterInternal(name
);
284 return new ParameterSearch(null, false);
290 /// <param name="myParent">My parent.</param>
291 public void SetParent(BrailBase myParent
)
297 /// Allows to check that a parameter was defined
299 /// <param name="name"></param>
300 /// <returns></returns>
301 public bool IsDefined(string name
)
303 ParameterSearch search
= GetParameterInternal(name
);
308 /// This is required because we may want to replace the output stream and get the correct
309 /// behavior from components call RenderText() or RenderSection()
311 public IDisposable
SetOutputStream(TextWriter newOutputStream
)
313 ReturnOutputStreamToInitialWriter disposable
= new ReturnOutputStreamToInitialWriter(OutputStream
, this);
314 outputStream
= newOutputStream
;
319 /// Will output the given value as escaped HTML
321 /// <param name="toOutput"></param>
322 public void OutputEscaped(object toOutput
)
324 if (toOutput
== null)
326 string str
= toOutput
.ToString();
327 OutputStream
.Write(HttpUtility
.HtmlEncode(str
));
331 /// Note that this will overwrite any existing property.
333 public void AddProperty(string name
, object item
)
335 properties
[name
] = item
;
339 /// Adds the view component newProperties.
340 /// This will be included in the parameters searching, note that this override
341 /// the current parameters if there are clashing.
342 /// The search order is LIFO
344 /// <param name="newProperties">The newProperties.</param>
345 public void AddViewComponentProperties(IDictionary newProperties
)
347 if (viewComponentsParameters
== null)
348 viewComponentsParameters
= new ArrayList();
349 viewComponentsParameters
.Insert(0, newProperties
);
353 /// Removes the view component properties, so they will no longer be visible to the views.
355 /// <param name="propertiesToRemove">The properties to remove.</param>
356 public void RemoveViewComponentProperties(IDictionary propertiesToRemove
)
358 if (viewComponentsParameters
== null)
360 viewComponentsParameters
.Remove(propertiesToRemove
);
363 public void RenderComponent(string componentName
)
365 RenderComponent(componentName
, new Hashtable());
368 public void RenderComponent(string componentName
, IDictionary parameters
)
370 BrailViewComponentContext componentContext
=
371 new BrailViewComponentContext(this, null, componentName
, OutputStream
,
372 new Hashtable(parameters
, StringComparer
.InvariantCultureIgnoreCase
));
373 AddViewComponentProperties(componentContext
.ComponentParameters
);
374 IViewComponentFactory componentFactory
= (IViewComponentFactory
) context
.GetService(typeof(IViewComponentFactory
));
375 ViewComponent component
= componentFactory
.Create(componentName
);
376 component
.Init(context
, componentContext
);
378 if (componentContext
.ViewToRender
!= null)
380 OutputSubView("/" + componentContext
.ViewToRender
, componentContext
.ComponentParameters
);
382 RemoveViewComponentProperties(componentContext
.ComponentParameters
);
386 /// Initialize all the properties that a script may need
387 /// One thing to note here is that resources are wrapped in ResourceToDuck wrapper
388 /// to enable easy use by the script
390 /// <param name="myContext">My context.</param>
391 /// <param name="myController">My controller.</param>
392 /// <param name="controllerContext">The controller context.</param>
393 private void InitProperties(IEngineContext myContext
, IController myController
, IControllerContext controllerContext
)
396 properties
= new Hashtable(StringComparer
.InvariantCultureIgnoreCase
);
397 //properties.Add("dsl", new DslWrapper(this));
398 properties
.Add("Controller", myController
);
400 if (myContext
!= null)
402 properties
.Add("request", myContext
.Request
);
403 properties
.Add("response", myContext
.Response
);
404 properties
.Add("session", myContext
.Session
);
407 if (controllerContext
.Resources
!= null)
409 foreach(string key
in controllerContext
.Resources
.Keys
)
411 properties
.Add(key
, new ResourceToDuck(controllerContext
.Resources
[key
]));
415 if (myContext
!= null && myContext
.Request
.QueryString
!= null)
417 foreach(string key
in myContext
.Request
.QueryString
.AllKeys
)
419 if (key
== null) continue;
420 properties
[key
] = myContext
.Request
.QueryString
[key
];
424 if (myContext
!= null && myContext
.Request
.Form
!= null)
426 foreach(string key
in myContext
.Request
.Form
.AllKeys
)
428 if (key
== null) continue;
429 properties
[key
] = myContext
.Request
.Form
[key
];
434 if (myContext
!= null && myContext
.Flash
!= null)
436 foreach(DictionaryEntry entry
in myContext
.Flash
)
438 properties
[entry
.Key
] = entry
.Value
;
442 if (controllerContext
.PropertyBag
!= null)
444 foreach(DictionaryEntry entry
in controllerContext
.PropertyBag
)
446 properties
[entry
.Key
] = entry
.Value
;
450 if (controllerContext
.Helpers
!= null)
452 foreach(DictionaryEntry entry
in controllerContext
.Helpers
)
454 properties
[entry
.Key
] = entry
.Value
;
458 if (myContext
!= null)
460 properties
["siteRoot"] = myContext
.ApplicationPath
;
464 #region Nested type: ParameterSearch
466 private class ParameterSearch
468 private readonly bool found
;
469 private readonly object value;
471 public ParameterSearch(object value, bool found
)
479 get { return found; }
484 get { return value; }
490 #region Nested type: ReturnOutputStreamToInitialWriter
492 private class ReturnOutputStreamToInitialWriter
: IDisposable
494 private TextWriter initialWriter
;
495 private BrailBase parent
;
497 public ReturnOutputStreamToInitialWriter(TextWriter initialWriter
, BrailBase parent
)
499 this.initialWriter
= initialWriter
;
500 this.parent
= parent
;
503 #region IDisposable Members
505 public void Dispose()
507 parent
.outputStream
= initialWriter
;