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.
18 using System
.Collections
;
21 using Commons
.Collections
;
22 using Manoli
.Utils
.CSharpFormat
;
24 using NAnt
.Core
.Attributes
;
25 using NAnt
.Core
.Types
;
28 using NVelocity
.Context
;
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
;
43 private XmlDocument siteMapDoc
;
44 private XmlDocument projectDom
;
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
;
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; }
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
;
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
));
165 staticFilesToCopy
.Add(new FileToCopy(
166 fullFileName
, root
.Name
+ "/" + fileName
));
172 if (nodeName
!= String
.Empty
)
174 folders
= nodeName
.Split('\\');
175 folder
= GetFolderInstance(folders
);
182 XmlDocument doc
= new XmlDocument();
184 doc
.Load(fullFileName
);
186 DocumentNode node
= new DocumentNode(nodeName
, fileName
, doc
, CreateMeta(doc
));
187 folder
.Documents
.Add(node
);
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
))
223 File
.Delete(targetFile
);
226 File
.Copy(file2Copy
.SourceFile
, targetFile
);
231 Console
.WriteLine(ex
);
238 private void AssignNavigationDocToFolders(DocumentNode node
)
240 if (node
.NodeType
!= NodeType
.Navigation
)
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");
263 meta
.Order
= Convert
.ToInt32(order
.Value
);
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
;
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
);
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
)
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)
384 for(int i
= 0; i
< node
.ParentFolder
.Level
- 1; i
++)
388 relativePath
+= "..";
392 relativePath
+= "/..";
397 context
.Put("relativePath", relativePath
);
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
));
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
);
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
);
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
)
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
);
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
);
557 newSection
.SetAttribute("title", title
);
560 newSection
.SetAttribute("page", node
.TargetFilename
);
561 newSection
.SetAttribute("level", level
.ToString());
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
);
587 elem
.RemoveAttribute("relative");
590 if (elem
.Name
.ToLower() == "a")
592 elem
.SetAttribute("href", Relativize(level
, relative
));
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
++)
610 if (relativePath
[0] == '/')
612 return newPath
+ relativePath
.Substring(1);
615 return newPath
+ relativePath
;
618 // private void PrintStructure(Folder folder, int ident)
620 // for(int i = 0; i < ident; i++)
622 // Console.Write(' ');
625 // Console.Write(folder.Name);
626 // Console.WriteLine();
628 // foreach(Folder f in folder.Folders)
630 // PrintStructure(f, ident + 2);
634 // private void PrintStructure2(Folder folder, int ident)
636 // for(int i = 0; i < ident; i++)
638 // Console.Write(' ');
641 // Console.Write(folder.Name);
642 // Console.WriteLine();
644 // foreach(DocumentNode node in folder.Documents)
646 // for(int i = 0; i < ident + 2; i++)
648 // Console.Write(' ');
651 // Console.Write("- ");
652 // Console.Write(node.Filename);
653 // Console.WriteLine();
656 // foreach(Folder f in folder.Folders)
658 // PrintStructure2(f, ident + 2);
662 private Folder
GetFolderInstance(string[] folders
)
664 Folder current
= root
;
666 foreach(String folderName
in folders
)
668 Folder folder
= current
.Folders
[folderName
];
672 folder
= new Folder(folderName
);
674 current
.Folders
[folderName
] = folder
;