uidesigner/com.nokia.sdt.component.symbian/src/com/nokia/sdt/component/symbian/sourcegen/macrohelp/MacroHelpGenerator.java
/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/
package com.nokia.sdt.component.symbian.sourcegen.macrohelp;
import com.nokia.sdt.component.symbian.sourcegen.ResolvedMacro;
import com.nokia.sdt.component.symbian.sourcegen.SourceGenMacroSupport;
import com.nokia.sdt.emf.component.DefineMacroType;
import com.nokia.sdt.emf.component.DocumentRoot;
import com.nokia.sdt.emf.component.MacroArgumentType;
import com.nokia.sdt.emf.component.loader.Loader;
import com.nokia.cpp.internal.api.utils.core.FileUtils;
import com.nokia.cpp.internal.api.utils.core.MessageLocation;
import org.eclipse.core.runtime.Path;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
/**
* Run this program with the name of macro *.inc file(s) as
* arguments and it will parse the contents and emit an
* HTML file describing each one.
*
*
*/
public class MacroHelpGenerator {
private SourceGenMacroSupport macroSupport;
private Map<String, List<ResolvedMacro>> macroMap;
private Document document;
public static void main(String[] args) {
MacroHelpGenerator generator = new MacroHelpGenerator();
// first, parse them all
for (String arg : args) {
try {
generator.parse(arg);
} catch (Exception e) {
e.printStackTrace();
}
}
// then, emit docs
for (String arg : args) {
try {
generator.generate(arg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public MacroHelpGenerator() {
macroSupport = new SourceGenMacroSupport(null);
macroMap = new HashMap<String, List<ResolvedMacro>>();
}
private void parse(String fName) throws Exception {
File incFile = new File(fName);
File tmpFile = File.createTempFile("tmpMHG", null);
tmpFile.deleteOnExit();
String tmpFName = tmpFile.getAbsolutePath();
String text= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
"<!DOCTYPE componentDefinition [\r\n" +
"<!ENTITY Macros SYSTEM \"" + incFile.getAbsolutePath() + "\">\r\n" +
"] >\r\n" +
"<componentDefinition xmlns=\"http://www.nokia.com/sdt/emf/component\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\r\n" +
"<component>\r\n"+
"<sourceGen>\r\n"+
"&Macros;\r\n"+
"</sourceGen>\r\n"+
"</component>\r\n"+
"</componentDefinition>\r\n";
FileUtils.writeFileContents(tmpFile, text.toCharArray(), null);
URI fileURI = URI.createFileURI(tmpFName);
Loader l = new Loader();
DocumentRoot root = l.load(fileURI);
if (root != null) {
MessageLocation messageLocation =
new MessageLocation(new Path(tmpFile.getAbsolutePath()));
ArrayList<ResolvedMacro> fileMacros = new ArrayList<ResolvedMacro>();
macroMap.put(fName, fileMacros);
for (TreeIterator<EObject> iter = root.eAllContents(); iter.hasNext(); ) {
EObject obj = iter.next();
if (obj instanceof DefineMacroType) {
ResolvedMacro macro = macroSupport.defineMacro(null, messageLocation, (DefineMacroType) obj);
fileMacros.add(macro);
}
}
}
else
throw new IllegalArgumentException("Cannot load " + fName);
}
private void generate(String fName) throws Exception {
document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element html = document.createElement("html");
document.appendChild(html);
html.setAttribute("xmlns", "http://www.w3.org/1999/xhtml");
Element head = document.createElement("head");
html.appendChild(head);
Element title = document.createElement("title");
head.appendChild(title);
title.setTextContent("Help for Macros in " + new File(fName).getName());
Element body = document.createElement("body");
html.appendChild(body);
Element tocParent = document.createElement("p");
body.appendChild(tocParent);
Element tocTitle = document.createElement("h1");
tocTitle.setTextContent("Table of contents");
tocParent.appendChild(tocTitle);
Element helpParent = document.createElement("p");
body.appendChild(helpParent);
for (ResolvedMacro macro : macroMap.get(fName)) {
generateMacroHelp(tocParent,
helpParent,
macro);
tocParent.appendChild(document.createTextNode("\n"));
helpParent.appendChild(document.createTextNode("\n"));
}
DOMSource domSource = new DOMSource(document);
TransformerFactory transFactory = TransformerFactory.newInstance();
String outFileName = fName + ".html";
FileOutputStream fileOutputStream = new FileOutputStream(
outFileName);
Result fileResult = new StreamResult(fileOutputStream);
Transformer transformer = transFactory.newTransformer();
//transformer.setParameter(Result.PI_DISABLE_OUTPUT_ESCAPING, "yes");
try {
//transFactory.newTransformer().transform(domSource, fileResult);
transformer.transform(domSource, fileResult);
System.out.println("Wrote " + outFileName);
} catch (Exception e) {
e.printStackTrace();
}
fileOutputStream.close();
}
private void generateMacroHelp(
Element tocParent,
Element helpParent,
ResolvedMacro macro) {
String id = macro.macro.getId();
// add entry to table of contents
Element tocEntry = document.createElement("p");
Element tocAddress = document.createElement("a");
tocAddress.setAttribute("href", "#" + id);
tocAddress.setTextContent(id);
tocEntry.appendChild(tocAddress);
tocParent.appendChild(tocEntry);
// add entry itself
Element helpAnchor = document.createElement("a");
helpAnchor.setAttribute("name", id);
helpParent.appendChild(helpAnchor);
Element helpEntry = document.createElement("h2");
helpEntry.setTextContent(id);
helpParent.appendChild(helpEntry);
generateMacroDescription(helpParent, macro);
}
private void generateMacroDescription(Element helpEntry,
ResolvedMacro macro) {
DefineMacroType dmt = macro.macro;
String help = dmt.getHelp();
if (help == null) {
Element para = document.createElement("p");
para.setTextContent("This macro is undocumented (add a 'help' attribute)");
helpEntry.appendChild(para);
}
else {
helpEntry.appendChild(encodeHTML(help));
}
Element header = document.createElement("h3");
header.setTextContent("Arguments");
helpEntry.appendChild(header);
//Element ul = document.createElement("ul");
generateMacroArgumentsHelp(helpEntry, macro);
//helpEntry.appendChild(ul);
}
private void generateMacroArgumentsHelp(Element ul,
ResolvedMacro macro) {
// organize arguments by their origin:
// these are already ordered as <imported arguments>
// then <custom arguments>. We will make a linked map that
// places custom arguments first followed by imported arguments.
Map<ResolvedMacro, List<MacroArgumentType>> originsToArguments;
originsToArguments = new LinkedHashMap<ResolvedMacro, List<MacroArgumentType>>();
// first, custom arguments
List<MacroArgumentType> args = new ArrayList<MacroArgumentType>();
originsToArguments.put(macro, args);
for (MacroArgumentType argument : macro.arguments.values()) {
ResolvedMacro fromMacro = macro.argumentOrigins.get(argument.getName());
if (fromMacro == macro) {
args.add(argument);
}
}
// now, imported argument groups
for (MacroArgumentType argument : macro.arguments.values()) {
ResolvedMacro fromMacro = macro.argumentOrigins.get(argument.getName());
args = originsToArguments.get(fromMacro);
if (args == null) {
args = new ArrayList<MacroArgumentType>();
originsToArguments.put(fromMacro, args);
}
args.add(argument);
}
// now emit the groups of arguments from outer to inner
for (Map.Entry<ResolvedMacro, List<MacroArgumentType>> entry : originsToArguments.entrySet()) {
emitArgumentGroup(document, ul,
entry.getKey() != macro,
entry.getKey(), entry.getValue());
}
}
private void emitArgumentGroup(Document document,
Element parent,
boolean isInherited,
ResolvedMacro fromMacro,
List<MacroArgumentType> arguments) {
if (isInherited) {
Element info = document.createElement("p");
info.setTextContent("Inherited from ");
Element a = document.createElement("a");
a.setAttribute("href", "#" + fromMacro.macro.getId());
a.setTextContent(fromMacro.macro.getId());
info.appendChild(a);
info.appendChild(document.createTextNode(":"));
parent.appendChild(info);
}
Element table = document.createElement("table");
table.setAttribute("cellpadding", "3");
parent.appendChild(table);
table.setAttribute("border", "1");
Element th = document.createElement("th");
th.setTextContent("Argument");
table.appendChild(th);
th = document.createElement("th");
th.setTextContent("Req'd?");
table.appendChild(th);
th = document.createElement("th");
th.setTextContent("Default");
table.appendChild(th);
th = document.createElement("th");
th.setTextContent("Description");
table.appendChild(th);
// sort arguments by optionality
Collections.sort(arguments,
new Comparator<MacroArgumentType>() {
public int compare(MacroArgumentType o1,
MacroArgumentType o2) {
if (o1.isOptional() && !o2.isOptional())
return 1;
if (!o1.isOptional() && o2.isOptional())
return -1;
return 0;
}
});
for (MacroArgumentType argument : arguments) {
generateMacroArgumentHelp(table, fromMacro, argument);
}
}
private void generateMacroArgumentHelp(Element table,
ResolvedMacro macro, MacroArgumentType argument) {
Element tr = document.createElement("tr");
table.appendChild(tr);
Element td;
td = document.createElement("td");
td.setTextContent(argument.getName());
tr.appendChild(td);
td = document.createElement("td");
td.setTextContent(argument.isOptional() ? "no" : "yes");
tr.appendChild(td);
td = document.createElement("td");
if (argument.getDefault() != null && argument.getDefault().length() > 0)
td.setTextContent(argument.getDefault());
else
td.setTextContent("\u00A0");
tr.appendChild(td);
td = document.createElement("td");
if (argument.getHelp() != null && argument.getHelp().length() > 0)
td.appendChild(encodeHTML(argument.getHelp()));
else
td.setTextContent("\u00A0");
tr.appendChild(td);
}
//static Pattern PARAGRAPH_PATTERN = Pattern.compile("(.*)(\r?\n)\\s*(\r?\n)", Pattern.MULTILINE);
private Node encodeHTML(String help) {
Node el = document.createTextNode("");
boolean hadParagraphs = false;
int idx = 0;
while (idx < help.length()) {
int para = help.indexOf("$p$", idx);
if (para < 0)
para = help.length();
if (!hadParagraphs)
el = document.createElement("p");
Element p = document.createElement("p");
hadParagraphs = true;
p.setTextContent(help.substring(idx, para));
idx = para + 3;
el.appendChild(p);
}
if (!hadParagraphs) {
el.setTextContent(help);
}
return el;
}
}