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,"~%~%~%~S~%~%~%",Seq{name}); format(fout,"~S


~%",Seq{name}); format(fout,"Overview
~%"); format(fout,"

~%~S~%",Seq{self.doc().doc}); 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,"~%"); format(fout,"~%"); format(fout,"~%~%"); @For c in constructors do format(fout,"~%",Seq{name,c.names->separateWith(","),c.doc().doc}) end; format(fout,"
~%"); format(fout,"Constructors
~S(~S)~S
~%"); format(fout,"


~%"); // Write the attributes ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); @For a in attributes->asSeq->sortNamedElements do format(fout,"~%",Seq{a.name(),a.underlyingType().href(self.pathSeq()),a.type.name().toString(),a.doc().doc}) end; format(fout,"~%~%~%"); format(fout,"
~%"); format(fout,"Attributes
~S~S<\A>~S
~%~%"); @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,"


~%"); // Write the operations ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); if showOpBody then @For o in operations->asSeq->sortNamedElements do format(fout,"~%",Seq{o.doc().doc}) end else @For o in operations->asSeq->sortNamedElements do format(fout,"~%",Seq{o.doc().doc}) end; format(fout,"~%~%~%"); format(fout,"
~%"); format(fout,"Operations
"); if o.isKindOf(Operation) then format(fout,"~S~%",Seq{o.source().asHTML()}) end; format(fout,"~S
~S(",Seq{o.name()}); @For p,t in o.paramNames(),o.paramTypes() do @TypeCase(t) Set do format(fout,"~S:~S<\A>",Seq{p,t.elementType.href(self.pathSeq()),t.name()}) end Seq do format(fout,"~S:~S<\A>",Seq{p,t.elementType.href(self.pathSeq()),t.name()}) end else format(fout,"~S:~S<\A>",Seq{p,t.href(self.pathSeq()),t.name()}) end; if not isLast then format(fout,",") end end; format(fout,"):"); @TypeCase(o.type()) Set do format(fout,"~S<\A>",Seq{o.type().elementType.href(self.pathSeq()),o.type().name()}) end Seq do format(fout,"~S<\A>",Seq{o.type().elementType.href(self.pathSeq()),o.type().name()}) end else format(fout,"~S<\A>",Seq{o.type().href(self.pathSeq()),o.type().name()}) end; format(fout,"~S
~%~%"); @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,"~%
~%"); if grammar <> null then format(fout,"


~%"); // Write the grammar ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); format(fout,"~%"); format(fout,"
~%"); format(fout,"Grammar
~%");
        grammar.pprint(fout,0);
        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


~%",Seq{name}); format(fout,"Overview
~%"); format(fout,"

~%~S~%",Seq{self.doc().doc}); format(fout,"



~%"); // Write the operations ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); /* @For o in operations->asSeq->sortNamedElements do format(fout,"~%",Seq{o.name(),o.paramNames(),o.doc().doc}) end; */ @For o in operations->asSeq->sortNamedElements do format(fout,"~%",Seq{o.doc().doc}) end; format(fout,"
~%"); format(fout,"Operations
~S(~{,~;~S~})~S
~S(",Seq{o.name()}); @For p,t in o.paramNames(),o.paramTypes() do @TypeCase(t) Set do format(fout,"~S:~S<\A>",Seq{p,t.elementType.href(self.pathSeq()),t.name()}) end Seq do format(fout,"~S:~S<\A>",Seq{p,t.elementType.href(self.pathSeq()),t.name()}) end else format(fout,"~S:~S<\A>",Seq{p,t.href(self.pathSeq()),t.name()}) end; if not isLast then format(fout,",") end end; format(fout,"):"); @TypeCase(o.type()) Set do format(fout,"~S<\A>",Seq{o.type().elementType.href(self.pathSeq()),o.type().name()}) end Seq do format(fout,"~S<\A>",Seq{o.type().elementType.href(self.pathSeq()),o.type().name()}) end else format(fout,"~S<\A>",Seq{o.type().href(self.pathSeq()),o.type().name()}) end; 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,"~%"); format(fout,"~%"); format(fout,"~%"); format(fout,"~%"); 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


~%",Seq{name}); format(fout,"Overview
~%"); format(fout,"

~%~S~%",Seq{self.doc().doc}); format(fout,"



~%"); // Write the contents ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); @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,"~%",Seq{c.href(self.pathSeq()),c.name(),docString}) else format(fout,"~%",Seq{c.name(),docString}) end end end; format(fout,"
~%"); format(fout,"Contents
~S~S
~S~S
~%"); format(fout,"~%") end end @Class XCore::Package @Operation toHTMLDoc(fout:OutputChannel) // Write the doc file ... format(fout,"~%"); format(fout,"~%~%~%~S~%~%~%",Seq{name}); format(fout,"~S


~%",Seq{name}); format(fout,"Overview
~%"); format(fout,"

~%~S~%",Seq{self.doc().doc}); format(fout,"



~%"); // Write the packages ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); @For p in packages->asSeq->sortNamedElements do // format(fout,"~%",Seq{p.href(self.pathSeq()),p.name(),p.doc().doc}) format(fout,"~%",Seq{p.href(self.pathSeq()),p.name(),p.doc().doc}) end; format(fout,"
~%"); format(fout,"Packages
~S~S
~S~S
~%"); format(fout,"


~%"); // Write the classes ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); @For c in classes->asSeq->sortNamedElements do // format(fout,"~%",Seq{c.href(self.pathSeq()),c.name(),c.doc().doc}) format(fout,"~%",Seq{c.href(self.pathSeq()),c.name(),c.doc().doc}) end; format(fout,"
~%"); format(fout,"Classes
~S~S
~S~S
~%"); format(fout,"


~%"); // Write the data types ... if self.contents()->exists(d | d <> null and d.isKindOf(DataType)) then format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); @For d in self.contents()->select(d | d <> null and d.isKindOf(DataType))->asSeq->sortNamedElements do // format(fout,"~%",Seq{d.href(self.pathSeq()),d.name(),d.doc().doc}) format(fout,"~%",Seq{d.href(self.pathSeq()),d.name(),d.doc().doc}) end; format(fout,"
~%"); format(fout,"DataTypes
~S~S
~S~S
~%"); format(fout,"


~%") end; // Write the operations ... format(fout,"~%"); format(fout,"~%"); format(fout,"~%~%"); @For o in operations->asSeq->sortNamedElements do format(fout,"~%",Seq{o.name(),o.paramNames(),o.doc().doc}) end; format(fout,"
~%"); format(fout,"Operations
~S(~{,~;~S~})~S
~%"); format(fout,"~%") end end end;