Refactored the Kernel registration fluent interface to be more readable, better suppo...
[castle.git] / Experiments / AnakiaNet / Anakia / AnakiaTask.cs
blob3823cd2df3c66e8e7011952668d61706ca67a65e
1 // Copyright 2004-2008 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 namespace Anakia
17 using System;
18 using System.Collections;
19 using System.IO;
20 using System.Xml;
21 using Commons.Collections;
22 using Manoli.Utils.CSharpFormat;
23 using NAnt.Core;
24 using NAnt.Core.Attributes;
25 using NAnt.Core.Types;
26 using NVelocity;
27 using NVelocity.App;
28 using NVelocity.Context;
30 [TaskName("anakia")]
31 public class AnakiaTask : Task
33 private DirectoryInfo targetDir;
34 private FileSet sourceFileSet;
35 private String navigationFile;
36 private String projectFile;
37 private String templateFile;
38 private String baseGenFolder;
40 private VelocityEngine velocity;
41 private Template template;
42 private Folder root;
43 private XmlDocument siteMapDoc;
44 private XmlDocument projectDom;
46 // Formatters
48 private CSharpFormat csharpFormatter = new CSharpFormat();
49 private HtmlFormat htmlFormatter = new HtmlFormat();
50 private JavaScriptFormat jsFormatter = new JavaScriptFormat();
51 private TsqlFormat tsqlFormatter = new TsqlFormat();
52 private VisualBasicFormat vbFormatter = new VisualBasicFormat();
53 private int orderCount = int.MinValue;
55 public AnakiaTask()
59 #region Properties
61 [TaskAttribute("basefolder")]
62 public string BaseGenFolder
64 get { return baseGenFolder; }
65 set { baseGenFolder = value; }
68 [TaskAttribute("navigationfile")]
69 public String NavigationFile
71 get { return navigationFile; }
72 set { navigationFile = value.ToLower(); }
75 [TaskAttribute("templatefile")]
76 public String TemplateFile
78 get { return templateFile; }
79 set { templateFile = value; }
82 [TaskAttribute("projectfile")]
83 public String ProjectFile
85 get { return projectFile; }
86 set { projectFile = value; }
89 [TaskAttribute("targetdir")]
90 public DirectoryInfo TargetDir
92 get { return targetDir; }
93 set { targetDir = value; }
96 [BuildElement("source")]
97 public FileSet SourceFileSet
99 get { return sourceFileSet; }
100 set { sourceFileSet = value; }
103 #endregion
105 #region overrides
107 protected override void InitializeTask(XmlNode taskNode)
109 base.InitializeTask(taskNode);
111 // Initializes NVelocity
113 velocity = new VelocityEngine();
115 ExtendedProperties props = new ExtendedProperties();
116 velocity.Init(props);
118 template = velocity.GetTemplate(templateFile);
120 // TODO: validate all arguments are present
123 protected override void ExecuteTask()
125 projectDom = new XmlDocument();
126 projectDom.Load(projectFile);
128 DirectoryInfo basedir = sourceFileSet.BaseDirectory;
130 root = new Folder("castle");
134 ArrayList staticFilesToCopy = new ArrayList();
136 foreach(String fullFileName in sourceFileSet.FileNames)
138 string lastProcessedFile = null;
142 lastProcessedFile = fullFileName;
144 String dir = Path.GetDirectoryName(fullFileName);
145 String fileName = Path.GetFileName(fullFileName);
146 String nodeName = String.Empty;
148 Folder folder;
149 String[] folders;
151 if (basedir.FullName.ToLower() != dir.ToLower())
153 nodeName = dir.Substring(basedir.FullName.Length + 1);
156 if (IsStaticFile(fullFileName))
158 if (nodeName != String.Empty)
160 staticFilesToCopy.Add(new FileToCopy(
161 fullFileName, root.Name + "/" + nodeName + "/" + fileName));
163 else
165 staticFilesToCopy.Add(new FileToCopy(
166 fullFileName, root.Name + "/" + fileName));
169 continue;
172 if (nodeName != String.Empty)
174 folders = nodeName.Split('\\');
175 folder = GetFolderInstance(folders);
177 else
179 folder = root;
182 XmlDocument doc = new XmlDocument();
184 doc.Load(fullFileName);
186 DocumentNode node = new DocumentNode(nodeName, fileName, doc, CreateMeta(doc));
187 folder.Documents.Add(node);
189 catch(Exception ex)
191 Console.WriteLine("File: {0} \r\n", lastProcessedFile);
192 Console.WriteLine(ex);
193 Console.WriteLine("\r\n --------------------------------------------------------");
197 siteMapDoc = CreateSiteMap();
199 siteMapDoc.Save(Path.Combine(TargetDir.FullName, "generatedsitemap.xml"));
201 ITreeWalker walker = new BreadthFirstWalker();
203 walker.Walk(root, new Act(AssignNavigationDocToFolders));
204 walker.Walk(root, new Act(CreateBreadCrumb));
205 walker.Walk(root, new Act(FixRelativePaths));
206 walker.Walk(root, new Act(CreateHtml));
208 foreach(FileToCopy file2Copy in staticFilesToCopy)
210 String dir = Path.GetDirectoryName(file2Copy.TargetFile);
212 EnsureDirExists(dir);
214 String targetFile = targetDir.FullName + "/" + file2Copy.TargetFile;
216 if (File.Exists(targetFile))
218 if (File.GetLastWriteTime(targetFile) >= File.GetLastWriteTime(file2Copy.SourceFile))
220 continue;
223 File.Delete(targetFile);
226 File.Copy(file2Copy.SourceFile, targetFile);
229 catch(Exception ex)
231 Console.WriteLine(ex);
232 Console.Read();
236 #endregion
238 private void AssignNavigationDocToFolders(DocumentNode node)
240 if (node.NodeType != NodeType.Navigation)
242 return;
245 node.ParentFolder.NavigationNode = node;
248 private bool IsStaticFile(string fileName)
250 String ext = Path.GetExtension(fileName);
252 return ext != ".xml";
255 private DocumentMeta CreateMeta(XmlDocument xmlDocument)
257 DocumentMeta meta = new DocumentMeta();
259 XmlNode order = xmlDocument.SelectSingleNode("document/@order");
261 if (order != null)
263 meta.Order = Convert.ToInt32(order.Value);
265 else
267 meta.Order = orderCount++;
270 XmlNode properties = xmlDocument.SelectSingleNode("document/properties");
272 if (properties != null)
274 foreach(XmlNode child in properties.ChildNodes)
276 if (child.NodeType != XmlNodeType.Element) continue;
278 if (child.Name == "title")
280 meta.Title = child.ChildNodes[0].Value;
282 else if (child.Name == "categories")
284 ArrayList categories = new ArrayList();
286 foreach(XmlNode cat in child.SelectNodes("category"))
288 categories.Add(cat.ChildNodes[0].Value);
291 meta.Categories = (String[]) categories.ToArray(typeof(String));
293 else if (child.Name == "audience")
295 meta.Audience = child.ChildNodes[0].Value;
300 return meta;
303 private void CreateHtml(DocumentNode node)
307 EnsureDirExists(node.ParentFolder.Path);
309 if (node.NodeType == NodeType.Navigation) return;
311 DirectoryInfo targetFolder = new DirectoryInfo(
312 targetDir.FullName + "/" + node.ParentFolder.Path);
314 FileInfo htmlFileInTarget = new FileInfo(
315 Path.Combine(targetFolder.FullName, node.TargetFilename));
317 if (htmlFileInTarget.Exists)
319 htmlFileInTarget.Delete();
322 IContext ctx = CreateContext(node);
324 using(StreamWriter writer = new StreamWriter(htmlFileInTarget.FullName, false))
326 template.Merge(ctx, writer);
329 catch(Exception ex)
331 throw new Exception("Error generating html for " + node.TargetFilename +
332 " at " + node.ParentFolder.Path, ex);
336 private void EnsureDirExists(String path)
338 DirectoryInfo finalDir = new DirectoryInfo(targetDir.FullName + "/" + path);
340 if (!finalDir.Exists)
342 finalDir.Create();
346 private IContext CreateContext(DocumentNode node)
348 VelocityContext context = new VelocityContext();
350 context.Put("cs", csharpFormatter);
351 context.Put("html", htmlFormatter);
352 context.Put("js", jsFormatter);
353 context.Put("tsql", tsqlFormatter);
354 context.Put("vb", vbFormatter);
356 context.Put("basefolder", BaseGenFolder);
357 context.Put("breadcrumbs", node.BreadCrumbs);
358 context.Put("meta", node.Meta);
359 context.Put("doc", node.XmlDoc);
360 context.Put("root", node.XmlDoc.DocumentElement);
361 context.Put("node", node);
362 context.Put("sitemap", siteMapDoc.DocumentElement);
363 context.Put("project", projectDom.DocumentElement);
364 context.Put("folder", node.ParentFolder);
365 context.Put("converter", new SimpleConverter());
366 context.Put("helper", new SimpleHelper());
367 context.Put("generationdate", DateTime.Now);
369 if (node.ParentFolder.NavigationNode != null)
371 context.Put("navigation", node.ParentFolder.NavigationNode.XmlDoc);
374 context.Put("navlevel", node.ParentFolder.NavigationLevel);
376 String relativePath = String.Empty;
378 if (node.ParentFolder.Level == 1)
380 relativePath = ".";
382 else
384 for(int i = 0; i < node.ParentFolder.Level - 1; i++)
386 if (i == 0)
388 relativePath += "..";
390 else
392 relativePath += "/..";
397 context.Put("relativePath", relativePath);
399 return context;
402 #region BreadCrumb related operations
404 private void CreateBreadCrumb(DocumentNode node)
406 if (node.NodeType == NodeType.Navigation) return;
408 EnsureFolderHasBreadCrumb(node.ParentFolder);
410 node.BreadCrumbs.AddRange(node.ParentFolder.BreadCrumbs);
412 if (node.NodeType != NodeType.Index)
414 node.BreadCrumbs.Add(new BreadCrumb("", node.Meta.Title));
418 private void EnsureFolderHasBreadCrumb(Folder folder)
420 if (folder.BreadCrumbs.Count != 0) return;
422 if (folder.Parent != null)
424 folder.BreadCrumbs.AddRange(folder.Parent.BreadCrumbs);
427 String title = folder.Name;
429 if (folder.Documents.IndexNode != null)
431 title = folder.Documents.IndexNode.Meta.Title;
434 folder.BreadCrumbs.Add(new BreadCrumb(folder.Path + "/index.html", title));
437 #endregion
439 private XmlDocument CreateSiteMap()
441 ITreeWalker walker = new DepthFirstWalker();
442 walker.Walk(root, new Act(CreateNodeSiteMapFragments));
444 XmlDocument siteMapDoc = new XmlDocument();
445 XmlElement sitemapElem = siteMapDoc.CreateElement("sitemap");
446 siteMapDoc.AppendChild(sitemapElem);
448 AppendFragments(root, sitemapElem);
450 return siteMapDoc;
453 private void AppendFragments(Folder folder, XmlElement elem)
455 XmlElement el = elem.OwnerDocument.CreateElement(folder.Name);
457 elem.AppendChild(el);
459 el.SetAttribute("level", folder.Level.ToString());
461 if (folder.Documents.IndexNode != null)
463 el.SetAttribute("page", folder.Documents.IndexNode.TargetFilename);
464 el.SetAttribute("title", folder.Documents.IndexNode.Meta.Title);
466 else
468 Console.WriteLine("No index.xml found for folder " + folder.Path);
471 el.SetAttribute("path", folder.Path);
473 foreach(XmlDocumentFragment fragment in folder.SectionFragments)
475 foreach(XmlNode node in fragment.ChildNodes)
477 el.AppendChild(el.OwnerDocument.ImportNode(node, true));
481 foreach(Folder f in folder.Folders)
483 AppendFragments(f, el);
487 private void CreateNodeSiteMapFragments(DocumentNode node)
489 if (node.NodeType == NodeType.Navigation) return;
493 XmlDocumentFragment fragment = node.XmlDoc.CreateDocumentFragment();
495 XmlNode parent = fragment;
497 int level = node.ParentFolder.Level;
499 if (node.NodeType == NodeType.Ordinary)
501 level++;
503 parent = node.XmlDoc.CreateElement(Path.GetFileNameWithoutExtension(node.Filename).Replace(' ', '_'));
505 ((XmlElement) parent).SetAttribute("level", node.ParentFolder.Level.ToString());
506 ((XmlElement) parent).SetAttribute("page", node.TargetFilename);
507 ((XmlElement) parent).SetAttribute("title", node.Meta.Title);
508 ((XmlElement) parent).SetAttribute("issubpage", "true");
509 ((XmlElement) parent).SetAttribute("path", node.ParentFolder.Path);
511 fragment.AppendChild(parent);
514 foreach(XmlElement section in node.XmlDoc.SelectNodes("document/body/section"))
516 XmlElement newSection = CreateSectionXmlElement(level, node, section);
518 parent.AppendChild(newSection);
520 foreach(XmlElement secSectionLevel in section.SelectNodes("section"))
522 XmlElement newSectionSecLevel = CreateSectionXmlElement(level + 1, node, secSectionLevel);
524 newSection.AppendChild(newSectionSecLevel);
526 foreach(XmlElement thirdSectionLevel in secSectionLevel.SelectNodes("section"))
528 XmlElement newSectionThrdLevel = CreateSectionXmlElement(level + 2, node, thirdSectionLevel);
530 newSectionSecLevel.AppendChild(newSectionThrdLevel);
535 node.ParentFolder.SectionFragments.Add(fragment);
537 catch(Exception ex)
539 throw new Exception("Error creating site map fragments for " + node.Path + "\\" + node.Filename, ex);
543 private static XmlElement CreateSectionXmlElement(int level, DocumentNode node, XmlElement section)
545 String sectionId = section.GetAttribute("id");
546 XmlNode titleElem = section.SelectSingleNode("title");
547 String title = titleElem.FirstChild.Value;
549 XmlElement newSection = node.XmlDoc.CreateElement("section");
551 if (sectionId != String.Empty)
553 newSection.SetAttribute("id", sectionId);
555 if (title != null)
557 newSection.SetAttribute("title", title);
560 newSection.SetAttribute("page", node.TargetFilename);
561 newSection.SetAttribute("level", level.ToString());
563 return newSection;
566 private void FixRelativePaths(DocumentNode node)
568 if (node.XmlDoc == null) return;
570 int level = node.ParentFolder.Level;
572 XmlNodeList nodes = node.XmlDoc.SelectNodes("//@relative");
574 foreach(XmlAttribute xmlNode in nodes)
576 XmlElement elem = xmlNode.OwnerElement;
578 String relative = elem.GetAttribute("relative");
580 if (relative.StartsWith("!"))
582 relative = relative.Substring(1);
583 elem.SetAttribute("relative", relative);
585 else
587 elem.RemoveAttribute("relative");
590 if (elem.Name.ToLower() == "a")
592 elem.SetAttribute("href", Relativize(level, relative));
594 else
596 elem.SetAttribute("src", Relativize(level, relative));
601 private string Relativize(int level, string relativePath)
603 String newPath = "./";
605 for(int i = 1; i < level; i++)
607 newPath += "../";
610 if (relativePath[0] == '/')
612 return newPath + relativePath.Substring(1);
615 return newPath + relativePath;
618 // private void PrintStructure(Folder folder, int ident)
619 // {
620 // for(int i = 0; i < ident; i++)
621 // {
622 // Console.Write(' ');
623 // }
625 // Console.Write(folder.Name);
626 // Console.WriteLine();
628 // foreach(Folder f in folder.Folders)
629 // {
630 // PrintStructure(f, ident + 2);
631 // }
632 // }
634 // private void PrintStructure2(Folder folder, int ident)
635 // {
636 // for(int i = 0; i < ident; i++)
637 // {
638 // Console.Write(' ');
639 // }
641 // Console.Write(folder.Name);
642 // Console.WriteLine();
644 // foreach(DocumentNode node in folder.Documents)
645 // {
646 // for(int i = 0; i < ident + 2; i++)
647 // {
648 // Console.Write(' ');
649 // }
651 // Console.Write("- ");
652 // Console.Write(node.Filename);
653 // Console.WriteLine();
654 // }
656 // foreach(Folder f in folder.Folders)
657 // {
658 // PrintStructure2(f, ident + 2);
659 // }
660 // }
662 private Folder GetFolderInstance(string[] folders)
664 Folder current = root;
666 foreach(String folderName in folders)
668 Folder folder = current.Folders[folderName];
670 if (folder == null)
672 folder = new Folder(folderName);
674 current.Folders[folderName] = folder;
677 current = folder;
680 return current;