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.
17 namespace Castle
.MonoRail
.Framework
20 using System
.Collections
;
22 using Castle
.MonoRail
.Framework
.Configuration
;
23 using Castle
.MonoRail
.Framework
.Views
;
26 /// Default <see cref="IViewSourceLoader"/> implementation
27 /// that uses the file system and assembly source as source of view templates
29 public class FileAssemblyViewSourceLoader
: IViewSourceLoader
, IMRServiceEnabled
31 private readonly IList additionalSources
= ArrayList
.Synchronized(new ArrayList());
32 private bool enableCache
= true;
33 private string viewRootDir
;
34 private string virtualViewDir
;
35 private FileSystemWatcher viewFolderWatcher
;
38 /// Creates a new instance
40 public FileAssemblyViewSourceLoader()
45 /// Creates a new instance with the viewRootDir
47 public FileAssemblyViewSourceLoader(string viewRootDir
)
49 this.viewRootDir
= viewRootDir
;
52 #region IMRServiceEnabled implementation
55 /// Services the specified provider.
57 /// <param name="provider">The provider.</param>
58 public void Service(IMonoRailServices provider
)
60 IMonoRailConfiguration config
= (IMonoRailConfiguration
) provider
.GetService(typeof(IMonoRailConfiguration
));
64 viewRootDir
= config
.ViewEngineConfig
.ViewPathRoot
;
65 virtualViewDir
= config
.ViewEngineConfig
.VirtualPathRoot
;
67 foreach(AssemblySourceInfo sourceInfo
in config
.ViewEngineConfig
.Sources
)
69 AddAssemblySource(sourceInfo
);
77 /// Evaluates whether the specified template exists.
79 /// <param name="sourceName">The template name</param>
80 /// <returns><c>true</c> if it exists</returns>
81 public bool HasSource(String sourceName
)
83 if (HasTemplateOnFileSystem(sourceName
))
88 return HasTemplateOnAssemblies(sourceName
);
92 /// Builds and returns a representation of a view template
94 /// <param name="templateName">The template name</param>
95 /// <returns></returns>
96 public IViewSource
GetViewSource(String templateName
)
98 FileInfo fileInfo
= CreateFileInfo(templateName
);
102 return new FileViewSource(fileInfo
, enableCache
);
106 return GetStreamFromAdditionalSources(templateName
);
111 /// Gets a list of views on the specified directory
113 /// <param name="dirName">Directory name</param>
114 /// <param name="fileExtensionsToInclude">Optional fileExtensions to include in listing.</param>
115 /// <returns></returns>
116 public String
[] ListViews(String dirName
,params string[] fileExtensionsToInclude
)
118 ArrayList views
= new ArrayList();
120 CollectViewsOnFileSystem(dirName
, views
,fileExtensionsToInclude
);
121 CollectViewsOnAssemblies(dirName
, views
);
123 return (String
[]) views
.ToArray(typeof(String
));
127 /// Gets/sets the root directory of views, obtained from the configuration.
130 public string VirtualViewDir
132 get { return virtualViewDir; }
133 set { virtualViewDir = value; }
137 /// Gets/sets the root directory of views,
138 /// obtained from the configuration.
140 public string ViewRootDir
142 get { return viewRootDir; }
143 set { viewRootDir = value; }
147 /// Gets or sets whether the instance should use cache
150 public bool EnableCache
152 get { return enableCache; }
153 set { enableCache = value; }
157 /// Gets a list of assembly sources
160 public IList AssemblySources
162 get { return additionalSources; }
166 /// Adds the assembly source.
168 /// <param name="assemblySourceInfo">The assembly source info.</param>
169 public void AddAssemblySource(AssemblySourceInfo assemblySourceInfo
)
171 additionalSources
.Add(assemblySourceInfo
);
174 #region Handle File System Changes To Views
177 /// Raised when the view is changed.
179 public event FileSystemEventHandler ViewChanged
183 //avoid concurrency problems with creating/removing the watcher
184 //in two threads in parallel. Unlikely, but better to be safe.
187 //create the watcher if it doesn't exists
188 if (viewFolderWatcher
== null)
190 InitViewFolderWatch();
192 ViewChangedImpl
+= value;
197 //avoid concurrency problems with creating/removing the watcher
198 //in two threads in parallel. Unlikely, but better to be safe.
201 ViewChangedImpl
-= value;
202 if (ViewChangedImpl
== null) //no more subscribers.
204 DisposeViewFolderWatch();
210 private event FileSystemEventHandler ViewChangedImpl
= delegate { }
;
212 private void DisposeViewFolderWatch()
214 ViewChangedImpl
-= (viewFolderWatcher_Changed
);
215 if (viewFolderWatcher
!= null)
217 viewFolderWatcher
.Dispose();
221 private void InitViewFolderWatch()
223 if (Directory
.Exists(ViewRootDir
))
225 viewFolderWatcher
= new FileSystemWatcher(ViewRootDir
);
226 viewFolderWatcher
.IncludeSubdirectories
= true;
227 viewFolderWatcher
.Changed
+= (viewFolderWatcher_Changed
);
228 viewFolderWatcher
.Created
+= (viewFolderWatcher_Changed
);
229 viewFolderWatcher
.Deleted
+= (viewFolderWatcher_Changed
);
230 viewFolderWatcher
.Renamed
+= (viewFolderWatcher_Renamed
);
231 viewFolderWatcher
.EnableRaisingEvents
= true;
235 private void viewFolderWatcher_Renamed(object sender
, RenamedEventArgs e
)
237 ViewChangedImpl(this, e
);
240 private void viewFolderWatcher_Changed(object sender
, FileSystemEventArgs e
)
242 ViewChangedImpl(this, e
);
247 private bool HasTemplateOnFileSystem(string templateName
)
249 return CreateFileInfo(templateName
).Exists
;
252 private FileInfo
CreateFileInfo(string templateName
)
254 if (Path
.IsPathRooted(templateName
))
256 templateName
= templateName
.Substring(Path
.GetPathRoot(templateName
).Length
);
259 return new FileInfo(Path
.Combine(viewRootDir
, templateName
));
262 private bool HasTemplateOnAssemblies(string templateName
)
264 foreach(AssemblySourceInfo sourceInfo
in additionalSources
)
266 if (sourceInfo
.HasTemplate(templateName
))
275 private IViewSource
GetStreamFromAdditionalSources(string templateName
)
277 foreach(AssemblySourceInfo sourceInfo
in additionalSources
)
279 if (sourceInfo
.HasTemplate(templateName
))
281 return new EmbeddedResourceViewSource(templateName
, sourceInfo
);
288 private void CollectViewsOnFileSystem(string dirName
, ArrayList views
,params string[] fileExtensionsToInclude
)
290 DirectoryInfo dir
= new DirectoryInfo(Path
.Combine(ViewRootDir
, dirName
));
293 return; //early return
297 if(fileExtensionsToInclude
==null || fileExtensionsToInclude
.Length
==0)
299 fileExtensionsToInclude
= new string[] {".*"}
;
302 foreach (string ext
in fileExtensionsToInclude
)
304 foreach (FileInfo file
in dir
.GetFiles("*" + ext
))
306 views
.Add(Path
.Combine(dirName
, file
.Name
));
312 private void CollectViewsOnAssemblies(string dirName
, ArrayList views
)
314 foreach(AssemblySourceInfo sourceInfo
in additionalSources
)
316 sourceInfo
.CollectViews(dirName
, views
);