parserImport XOCL;
parserImport Aspects;
/******************************************************************************
* *
* XMF Documentation Generation *
* ---------------------------- *
* *
* This file defines an aspect that allows standard model elements to be *
* transformed and exported to HTML. This is used to document the system. *
* Use Root.writeHTML() to construct a copy of the entire system as HTML *
* documentation. Note that the resulting documentation expects to be in a *
* file system that is relative to Root. Therefore, if you generate *
* documentation for sub-packages of XMF or your own packages then place them*
* into a file system that replicates the name-space hierarchy for all the *
* parents of the generated package (or simply just generate everything from *
* Root). *
* *
******************************************************************************/
import IO;
@Aspect ToHTML
@Class XCore::Class
@Operation toHTMLDoc(fout:OutputChannel)
self.toHTMLDoc(fout,xmf.preference("ShowLongDocumentation"))
end
@Operation toHTMLDoc(fout:OutputChannel,showOpBody:Boolean)
// Write the doc file ...
format(fout,"~%");
format(fout,"~%
~%");
// Write the parents ...
format(fout,"Parents~%");
@For p in self.allParents() do
format(fout,"~S~%",Seq{p.href(self.pathSeq()),p.name.toString()});
if not isLast then format(fout,",") end
end;
format(fout," ~%");
// Write the constructors ...
format(fout,"
~%~%");
@For p in self.allParents() do
if not p.attributes->isEmpty
then
format(fout,"From ~S: ",Seq{p.href(self.pathSeq()),p.name.toString()});
@For a in p.attributes do
format(fout,"~S",Seq{a.name});
if not isLast then format(fout,", ") else format(fout,". ") end
end
end
end;
format(fout,"~%
~%
~%");
format(fout,"
~%");
format(fout," ~%");
// Write the operations ...
format(fout,"
~%");
format(fout,"
~%");
format(fout,"
~%");
format(fout,"Operations
~%
~%");
if showOpBody then
@For o in operations->asSeq->sortNamedElements do
format(fout,"
");
if o.isKindOf(Operation)
then
format(fout,"~S~%",Seq{o.source().asHTML()})
end;
format(fout,"
~S
~%",Seq{o.doc().doc})
end
else
@For o in operations->asSeq->sortNamedElements do
format(fout,"
~%~%");
@For p in self.allParents() do
if not p.operations->isEmpty
then
format(fout,"From ~S: ",Seq{p.href(self.pathSeq()),p.name.toString()});
@For o in p.operations do
format(fout,"~S",Seq{o.name});
if not isLast then format(fout,", ") else format(fout,". ") end
end
end
end
end;
format(fout,"~%
~%
~%");
format(fout,"
~%");
if grammar <> null
then
format(fout," ~%");
// Write the grammar ...
format(fout,"
~%");
format(fout,"
~%");
format(fout,"
~%");
format(fout,"Grammar
~%
~%");
format(fout,"
~%");
grammar.pprint(fout,0);
format(fout,"
~%");
format(fout,"
~%")
end;
format(fout,"~%")
end
end
@Class XCore::DataType
@Operation toHTML(root:String)
// Ensure the directory for this datatype exists:
if (root + "/" + name.toString()).mkDir()
then
let fout = FileOutputChannel(root + "/" + name.toString() + "/index.html")
in
self.toHTMLDoc(fout);
fout.close()
end
end
end
@Operation toHTMLDoc(fout:OutputChannel)
// Write the doc file ...
format(fout,"~%");
format(fout,"~%~%~%~S~%~%~%",Seq{name});
format(fout,"~S
~%");
format(fout,"~%")
end
end
@Class Element
@Operation allHTMLEntries():Seq(NamedElement)
// Return all the entries that will occur in the
// index file.
Seq{}
end
@Operation allHTMLPackages():Seq(Package)
// Return all the entries that will occur in the
// packages index file.
Seq{}
end
@Operation toHTML(root:String)
// By default do nothing...
self
end
@Operation writeHTML()
// Write HTML documentation to the system specified
// documentation directory.
self.writeHTML(xmf.docDir())
end
@Operation writeHTML(root:String)
// If documentation is generated on a class lower down the
// package hierarchy than Root then URLs fail because URLs
// are relative to Root. The following code ensurs all HTML
// is relative to Root.
let
path = self.pathSeq().butLast();
psuedoRoot = root;
dirsCreatedOkay = true
in
dirsCreatedOkay := root.mkDir();
@For p in path do
psuedoRoot := psuedoRoot + "/" + p;
dirsCreatedOkay := psuedoRoot.mkDir()
end;
if dirsCreatedOkay
then
// Write the directory structure and entries...
self.toHTML(psuedoRoot)
else
self.error("Cannot create HTML root directory: " + root)
end
end;
// Write the index file ...
self.writeHTMLIndex(root);
// Write all the entries to a frame ...
self.writeHTMLAllEntries(root);
// Write all the packages to a frame ...
self.writeHTMLAllPackages(root);
// Write the overview file ...
self.writeHTMLOverview(root)
end
@Operation writeHTMLAllEntries(root:String)
let fout = FileOutputChannel(root + "/" + "AllEntries.html");
allEntries = self.allHTMLEntries()->sortNamedElements
in format(fout,"");
format(fout,"~%~%~%All Entries ~S~%~%",Seq{xmf.version()});
format(fout,"~%");
format(fout,"~%~%~%All Entries~% ~%");
format(fout,"
~%
~%
~%");
@For entry in allEntries do
let href = entry.pathSeq()->separateWith("/") + "/index.html";
path = entry.owner.path()
in format(fout,"~S (in ~S)~%",Seq{href,entry.name,path});
format(fout," ")
end
end;
format(fout,"~%
~%
~%
~%~%");
format(fout,"~%");
fout.close()
end
end
@Operation writeHTMLAllPackages(root:String)
let fout = FileOutputChannel(root + "/" + "AllPackages.html");
allPackages = self.allHTMLPackages()
in format(fout,"");
format(fout,"~%~%~%All Entries ~S~%~%",Seq{xmf.version()});
format(fout,"~%");
format(fout,"~%~%~%All Name Spaces~% ~%");
format(fout,"
~%
~%
~%");
@For entry in allPackages do
let href = entry.pathSeq()->separateWith("/") + "/index.html";
path = entry.owner.path()
in format(fout,"~S (in ~S)~%",Seq{href,entry.name,path});
format(fout," ")
end
end;
format(fout,"~%
~%
~%
~%~%");
format(fout,"~%");
fout.close()
end
end
@Operation writeHTMLIndex(root:String)
// Write the file that is the starting point for the documentation...
let fout = FileOutputChannel(root + "/index.html")
in format(fout,"~%");
format(fout,"~%~%~%XMF ~S~%~%~%",Seq{xmf.version()});
format(fout,"~%");
format(fout,"~%");
fout.close()
end
end
@Operation writeHTMLOverview(root:String)
let fout = FileOutputChannel(root + "/Overview.html");
title = "Documentation"
+ if self.isKindOf(NamedElement) then " for " + self.path() else "" end
in format(fout,"~%");
format(fout,"~%~%~%XMF ~S~%~%~%",Seq{xmf.version()});
format(fout,"
");
format(fout,"
~S
",Seq{title});
format(fout,"
Click on a link on the left to view details.
");
format(fout,"~%");
fout.close()
end
end
end
@Class XCore::NamedElement
@Operation href(currentPath):String
@Doc
Returns the path used in an href to the receiver. It assumes
that we are relative to a root that contains the root name space.
end
currentPath->collect( x | "..")->separateWith("/") + "/" +
self.pathSeq()->separateWith("/") +
"/index.html"
end
end
@Class XCore::NameSpace
@Operation allHTMLEntries():Seq(NamedElement)
Seq{self | self.contentsOf(NamedElement)
->excluding(self)
->asSeq
->collect(x | x.allHTMLEntries())
->flatten}
end
@Operation allHTMLPackages():Seq(NamedElement)
Seq{self | self.contentsOf(Package)
->excluding(self)
->asSeq
->collect(x | x.allHTMLPackages())
->flatten}->sortNamedElements
end
@Operation toHTML(root:String)
// Ensure the directory for this nameSpace exists:
if (root + "/" + name.toString()).mkDir()
then
// Handle each contained element:
@For element in self.contents()->excluding(self) do
element.toHTML(root + "/" + name.toString())
end;
let fout = FileOutputChannel(root + "/" + name.toString() + "/index.html")
in self.toHTMLDoc(fout);
fout.close()
end
end
end
@Operation toHTMLDoc(fout:OutputChannel)
// Write the doc file ...
format(fout,"~%");
format(fout,"~%~%~%~S~%~%~%",Seq{name});
format(fout,"~S
~%");
@For c in self.contents()->asSeq->sortNamedElements when c.isKindOf(NamedElement) do
let docString =
if c.hasSlot("documentation") and not c.isKindOf(ForeignOperation)
then
c.doc().doc
else
"No Documentation Specified"
end
in
if(c.isKindOf(Class))
then format(fout,"