[Laszlo-checkins] r11514 - in openlaszlo/trunk/WEB-INF/lps: schema server/src/org/openlaszlo/js2doc server/src/org/openlaszlo/utils
dda@openlaszlo.org
dda at openlaszlo.org
Wed Oct 22 17:22:37 PDT 2008
Author: dda
Date: 2008-10-22 17:22:32 -0700 (Wed, 22 Oct 2008)
New Revision: 11514
Added:
openlaszlo/trunk/WEB-INF/lps/schema/lfc-undeclared.lzx
Removed:
openlaszlo/trunk/WEB-INF/lps/schema/lfc.lzx
Modified:
openlaszlo/trunk/WEB-INF/lps/schema/build.xml
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2Doc.java
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2DocUtils.java
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/Main.java
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/PropertyReference.java
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/ReprocessComments.java
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/SchemaBuilder.java
openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/utils/FileUtils.java
Log:
Change 20081022-dda-a by dda at lester.local on 2008-10-22 13:07:21 EDT
in /Users/dda/laszlo/src/svn/openlaszlo/trunk-d
for http://svn.openlaszlo.org/openlaszlo/trunk
Summary: Improvements to XML Schemabuilder, use it as default
New Features:
Bugs Fixed: LPP-3508 (Generate LZX Schema from js2doc output), LPP-2300 (PARTIAL) (Update laszlo namespace)
Technical Reviewer: hminsky (pending)
QA Reviewer: (pending)
Doc Reviewer: (pending)
Documentation:
Release Notes:
Details:
This change set extends the XML schemabuilder in several ways described below.
The output finally is usable as a schema, and is undoubtedly more accurate, so
schema/lfc.lzx has been turned into an automatically generated file by this changeset.
There should be no visible behavioral changes made by this changeset, its purpose
is to make the LFC more maintainable, and to allow for future extensions.
Changes specific to the schemabuilder:
- Create classes in output in 'superclass order' - needed by the reader of this schema.
- Use the javadoc access in the class if it is not available from the element (attribute/method/event),
if there is no access there, use the unitid's access (that is, the javadoc at the top of each file).
- the default inheritance is from Instance, not Object.
- add class attributes
- handle default values, enum types, and 'class' allocation for attributes
- convert types (boolean->Boolean, etc.)
- Added an attribute on <library> to show a version number. This satisfies the
next step for LPP-2300. The version number is based on day/hr/min/sec of build
and looks like this:
<library version="20081022143456">
- Adds a facility to merge in pieces of the schema in order to add, modify, or remove items.
The part merged in is hand-maintained and is called lfc-undeclared.lzx (to represent
parts of the schema that are not declared in the LFC javadoc). For any
piece of the schema to be overridden or added to, a parallel fragment of the schema
is created in lfc-undeclared.lzx, the parts to be inserted appear in <insert>, parts
to be deleted in <delete> or existing items can be changed with <replace>. These
instructions are merged with (and override) information derived automatically,
so we ultimately have full control over the schema. However many entries in
lfc-undeclared represent items that are improperly declared in javadoc and
could be cleaned up. Since I was sometimes conservative in preserving parts
of lfc.lzx, some parts of this file represent out of date information (just as
the old lfc.lzx may have been out of date). Even some TODO's from the original
lfc.lzx have been preserved.
=> As a long term goal, we should seek to eliminate all or most of the items in lfc-undeclared.lzx .
Some items, like <containsElements> could only be removed by extending javadoc to contain
this information, and that would be a good thing.
Elsewhere, these changes are made:
- In utils/FileUtils.java: A fix to recognize encoding="UTF-8", which was broken.
- In js2doc, some improvements to error messages to help debugging.
Tests:
weather, lzpix (all platforms)
hello (SWF9)
smokecheck (SWF8, DHTML)
Modified: openlaszlo/trunk/WEB-INF/lps/schema/build.xml
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/schema/build.xml 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/schema/build.xml 2008-10-23 00:22:32 UTC (rev 11514)
@@ -15,13 +15,15 @@
<target name="init">
<tstamp/>
<property name="build" value="./build"/>
- <property name="build.dir" value="${LPS_HOME}/WEB-INF/lps/schema/build"/>
+ <property name="build.dir" value="${schema.dir}/build"/>
<property name="rnc" value="lzx.rnc"/>
<property name="docrng" value="lzxdoc.rng"/>
<property name="rng" value="lzx.rng"/>
<property name="xsd" value="lzx.xsd"/>
- <property name="lfc.schema.file" value="lfc-new.lzx"/>
+ <property name="lfc.schema.file" value="lfc.lzx"/>
+ <property name="lfc.undeclared.schema.file" value="lfc-undeclared.lzx"/>
<property name="html" value="lzx-reference.html"/>
+ <property name="schema.dir" value="${LPS_HOME}/WEB-INF/lps/schema"/>
<property name="server.dir" value="${LPS_HOME}/WEB-INF/lps/server" />
<property name="lfc.src.dir" value="${LPS_HOME}/WEB-INF/lps/lfc" />
<property name="js2doc.src.dir" value="${LPS_HOME}/WEB-INF/lps/server/src/org/openlaszlo/js2doc/" />
@@ -74,16 +76,17 @@
<uptodate property="lfc.schema.uptodate" targetfile="${lfc.schema.file}">
<srcfiles dir="${lfc.src.dir}" includes="**/*" />
<srcfiles dir="${js2doc.src.dir}" includes="**/*" />
+ <srcfiles file="${lfc.undeclared.schema.file}"/>
</uptodate>
</condition>
</target>
<!-- The lfc.schema target is called from lfc/build.xml -->
- <target name="lfc.schema" description="build the lfc-new.lzx schema description"
+ <target name="lfc.schema" description="build the lfc.lzx schema description"
depends="lfc.schema.uptodate" unless="uptodate">
- <property name="js2doc.args" value="--schema --out ${build.dir}/lfc-new.xml --dir ${build.dir} ${lfc.src.dir}/LaszloLibrary.lzs" />
+ <property name="js2doc.args" value="--schema --merge ${schema.dir}/${lfc.undeclared.schema.file} --out ${build.dir}/lfc.xml --dir ${build.dir} ${lfc.src.dir}/LaszloLibrary.lzs" />
<ant dir="${server.dir}" target="js2doc.schema" />
- <copy file="${build.dir}/lfc-new.xml" tofile="${lfc.schema.file}"/>
+ <copy file="${build.dir}/lfc.xml" tofile="${lfc.schema.file}"/>
</target>
Added: openlaszlo/trunk/WEB-INF/lps/schema/lfc-undeclared.lzx
Property changes on: openlaszlo/trunk/WEB-INF/lps/schema/lfc-undeclared.lzx
___________________________________________________________________
Name: svn:mime-type
+ text/plain
Name: svn:eol-style
+ native
Deleted: openlaszlo/trunk/WEB-INF/lps/schema/lfc.lzx
Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2Doc.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2Doc.java 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2Doc.java 2008-10-23 00:22:32 UTC (rev 11514)
@@ -823,6 +823,7 @@
static class XMLGenerateOptions {
boolean createSchema = false;
+ String mergeSchema = null;
}
static public Document toXML(String inputString,
@@ -884,11 +885,28 @@
Element schemaRoot = null;
if (xmlOptions.createSchema) {
- org.w3c.dom.Document schemaDoc = factory.newDocumentBuilder().newDocument();
+ javax.xml.parsers.DocumentBuilder builder = factory.newDocumentBuilder();
+ org.w3c.dom.Document schemaDoc = builder.newDocument();
org.w3c.dom.Element libraryNode = schemaDoc.createElement("library");
schemaDoc.appendChild(libraryNode);
- (new SchemaBuilder(docRoot)).build(libraryNode);
+ SchemaBuilder schemaBuilder = new SchemaBuilder(docRoot);
+ String schemaPath = xmlOptions.mergeSchema;
+ if (schemaPath != null) {
+ Element schemaElement;
+ try {
+ schemaElement = builder.parse(schemaPath).getDocumentElement();
+ }
+ catch (org.xml.sax.SAXException e) {
+ throw new RuntimeException(schemaPath + ": exception parsing schema file to merge", e);
+ }
+ catch (IOException e) {
+ throw new RuntimeException(schemaPath + ": exception parsing schema file to merge", e);
+ }
+ schemaBuilder.addSchemaToMerge(schemaPath, schemaElement);
+ }
+ schemaBuilder.build(libraryNode);
+
// Emit the schema document instead of the original js2doc doc
doc = schemaDoc;
}
Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2DocUtils.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2DocUtils.java 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/JS2DocUtils.java 2008-10-23 00:22:32 UTC (rev 11514)
@@ -43,6 +43,14 @@
public InternalError(Exception e) {
super(e);
}
+
+ /** Constructs an instance.
+ * @param message a string
+ * @param e an exception
+ */
+ public InternalError(String message, Exception e) {
+ super(message, e);
+ }
}
Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/Main.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/Main.java 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/Main.java 2008-10-23 00:22:32 UTC (rev 11514)
@@ -32,6 +32,8 @@
" Reprocess comments",
"--schema",
" Produce schema file for lfc.lzx",
+ "--merge additionalschema",
+ " Merge the schema file (used with --schema)",
"--help",
" Prints this message.",
"",
@@ -133,6 +135,12 @@
badform = true;
} else if (arg.equals("--schema")) {
xmlOptions.createSchema = true;
+ } else if (arg.equals("--merge")) {
+ if (i < args.length) {
+ i++;
+ xmlOptions.mergeSchema = args[i];
+ } else
+ badform = true;
} else if (arg.equals("--help")) {
for (int j = 0; j < USAGE.length; j++) {
System.err.println(USAGE[j]);
Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/PropertyReference.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/PropertyReference.java 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/PropertyReference.java 2008-10-23 00:22:32 UTC (rev 11514)
@@ -135,7 +135,7 @@
public String derivePropertyID() {
if (this.isValid() == false) {
logger.warning("propertyName: " + this.propertyName);
- throw new InternalError("Can't derive ID of invalid property", null);
+ throw new InternalError("Can't derive ID of invalid property", (SimpleNode)null);
}
return JS2DocUtils.derivePropertyID(this.propertyOwner, this.propertyName, this.state);
}
Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/ReprocessComments.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/ReprocessComments.java 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/ReprocessComments.java 2008-10-23 00:22:32 UTC (rev 11514)
@@ -272,7 +272,7 @@
} else {
org.w3c.dom.Element childElt = JS2DocUtils.findFirstChildElementWithAttribute(functionNode, "parameter", "name", paramName);
if (childElt == null) {
- logger.warning("Couldn't find parameter named " + paramName);
+ logger.warning("Couldn't find parameter named " + paramName + " in " + ((Element)functionNode.getParentNode()).getAttribute("id"));
} else {
if (paramType != null) {
// TODO [jgrandy 12/1/2006] check if @type already set
Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/SchemaBuilder.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/SchemaBuilder.java 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/js2doc/SchemaBuilder.java 2008-10-23 00:22:32 UTC (rev 11514)
@@ -13,46 +13,105 @@
import java.util.*;
import java.util.logging.*;
import org.openlaszlo.js2doc.JS2DocUtils.InternalError;
+import org.openlaszlo.server.LPS;
import org.w3c.dom.*;
import javax.xml.xpath.*;
import org.apache.xpath.NodeSet;
+// TODO: handle initargs, e.g. this from canvas:
+// * @initarg Boolean accessible: Specifies if this application is intended to be accessible
+// * @initarg Boolean debug: If true, the application is compiled with debugging enabled.
+//
+// should appear as:
+// <attribute name="debug" type="boolean" value="false"/>
+// <attribute name="accessible" type="boolean" value="false"/>
+//
+// Unfortunately, initargs seem 'unstructured', here are various examples:
+// @initarg secureport
+// @initarg deprecated port
+// @initarg Number thickness (swf8 only)
+// @initarg public String resource: A string denoting the library resource to use...
+//
+// We'd probably need to more sharply define the syntax of @initargs and clean up
+// all the usages
+
+
+// TODO: We are NOT looking for classname.setXXX (setters)
+// or the newer form $lzc$set_apply() from the javadocs.
+// Should we, and indicate them as attributes?
+
/**
* Builds a specialized schema for lfc,
* invoked via the --schema option.
- * This uses the parsed representation of the
- * javascript files after it is converted to XML.
+ * This uses the parsed internal XML doc tree and
+ * generates the schema in a specialized XML format.
*/
public class SchemaBuilder {
static private Logger logger = Logger.getLogger("org.openlaszlo.js2doc");
+ public static final String INITIAL_COMMENT = "\n\n" +
+ " !!!!!!!! DO NOT EDIT THIS FILE !!!!!!!!\n\n" +
+ " This file was created by js2doc.SchemaBuilder.\n" +
+ " Contents can be modified by editing lfc-undeclared.lzx.\n";
+
private Element droot;
- private TreeMap jsClasses = new TreeMap();
+ private TreeMap jsClasses = new TreeMap(); // key is name
+ private TreeMap jsClassesOrdered = new TreeMap(); // key is contrived for ordering
private XPathFactory xpathFactory=XPathFactory.newInstance();
private XPath xPath = xpathFactory.newXPath();
private XPathExpression TagNameModifiersXpr;
+ private Element mergeSchema = null;
+ private String mergeName = null;
+
+ /** A class seen in the input */
public static class JsClass {
String jsname;
+ String access;
String lzxname;
String extname;
Element classnode;
}
- public static class JsMember { // a method, event, attribute on a class
+ // values of 'kind' argument to addMember()
+ public static final String MEMBER_ATTRIBUTE = "attribute";
+ public static final String MEMBER_METHOD = "method";
+ public static final String MEMBER_EVENT = "event";
+
+ /** A member of a class seen in the input.
+ * This may be a method, event or attribute.
+ */
+ public static class JsMember {
String name;
- String type; // only used for attribute, otherwise null
+ String type = null; // only used for attribute, otherwise null
+ String defaultvalue = null; // only used for attribute (but optional), otherwise null
+ String enumvalues = null; // If attribute is enum, this is a '|' separated list
boolean isfinal;
boolean isstatic;
}
+ public class SchemaBuilderError extends RuntimeException {
+ public SchemaBuilderError(String s) {
+ super("Error building schema: " + s);
+ }
+ }
+
+ /**
+ * Initialize the SchemaBuilder using the document root.
+ */
public SchemaBuilder(Element droot) {
this.droot = droot;
}
+ /**
+ * Get the data string associated with a Text element.
+ */
private String getTextData(Element textElement) {
+ if (textElement == null) {
+ return null;
+ }
Node ch = textElement.getFirstChild();
if (ch == null || !(ch instanceof Text)) {
return null;
@@ -62,6 +121,9 @@
}
}
+ /**
+ * Create an return a new child tag beneath an existing node.
+ */
private Element createChild(Node n, String tagname) {
return (Element)n.appendChild(n.getOwnerDocument().createElement(tagname));
}
@@ -74,10 +136,252 @@
TagNameModifiersXpr = xPath.compile("tag[@name='modifiers']");
}
+ /**
+ * Create a key that guarantees that superclasses appear first.
+ * <pre>
+ * class: key:
+ * Bar "Bar"
+ * Foo extends Bar "Bar.Foo"
+ * Aha extends Foo extends Bar "Bar.Foo.Aha"
+ * </pre>
+ */
+ public String getOrderedKey(JsClass j) {
+ JsClass sup = (j.extname == null) ? null :
+ (JsClass)jsClasses.get(j.extname);
+ if (sup == null) {
+ return j.jsname;
+ }
+ else {
+ return getOrderedKey(sup) + "." + j.jsname;
+ }
+ }
+
+ public void addSchemaToMerge(String filename, Element merge) {
+ this.mergeName = filename;
+ this.mergeSchema = merge;
+ }
+
+ /**
+ * Just pull the Element children. If there are
+ * unexpected other nodes (anything other than
+ * whitespace Text nodes and Comment nodes), throw
+ * an exception.
+ */
+ public Element[] getChildElements(Element el) {
+ List list = new ArrayList();
+ NodeList children = el.getChildNodes();
+ for (int count=children.getLength(), i=0; i<count; i++) {
+ Node n = children.item(i);
+ if (n instanceof Element) {
+ list.add(n);
+ }
+ else if (n instanceof org.w3c.dom.Text) {
+ String str = ((Text)n).getData();
+ if (str.trim().length() > 0) {
+ throw new SchemaBuilderError(position(n) + "unexpected text \"" +
+ str + "\" beneath " + el);
+ }
+ }
+ else if (n instanceof org.w3c.dom.Comment) {
+ //ignored
+ }
+ else {
+ throw new SchemaBuilderError(position(n) + "unexpected node " +
+ n + "(" + n.getClass().getName() + ") child of " + el);
+ }
+ }
+ return (Element[])list.toArray(new Element[0]);
+ }
+
+ String position(Node n) {
+ String pos = mergeName + ": ";
+ // TODO: no way to get an input line number from a node?
+
+ String path = showpath(n);
+ if (path.length() > 0) {
+ path += ": ";
+ }
+ return pos + path;
+ }
+
+ Element getSingleChild(Element el) {
+ String tagname = el.getTagName();
+ Element[] children = getChildElements(el);
+ if (children.length != 1) {
+ if (children.length > 1) {
+ String got = "";
+ for (int i=0; i< children.length; i++) {
+ if (i > 0) {
+ got += ", ";
+ }
+ if (i > 2) {
+ break;
+ }
+ got += children[i];
+ }
+ throw new SchemaBuilderError(position(el) + "each <" + tagname +
+ "> may have only one child node, got: " + got);
+ } else {
+ throw new SchemaBuilderError(position(el) + "each <" + tagname +
+ "> must have a single child node");
+ }
+ }
+ return children[0];
+ }
+
+ Element getMatchingElement(Element el, Element match) {
+ String tagname = match.getTagName();
+ String name = match.getAttribute("name");
+ String xpath = tagname;
+ if (name != null) {
+ xpath += "[@name = \"" + name + "\"]";
+ }
+
+ //System.out.println("xpath=\"" + xpath + "\": matching against " + show(el));
+ try {
+ return (Element)xPath.evaluate(xpath, el,
+ XPathConstants.NODE);
+ }
+ catch (XPathExpressionException e) {
+ throw new InternalError(e);
+ }
+
+ }
+
+ String show(Element el) {
+ String name = el.getAttribute("name");
+ if (name == null) {
+ name = "";
+ }
+ else {
+ name = " name=\"" + name + "\"";
+ }
+ return "<" + el.getTagName() + name + ">";
+ }
+
+ String showpath(Node n) {
+ String path = "";
+ while (n != null) {
+ if (n instanceof Element) {
+ Element el = (Element)n;
+ String name = el.getAttribute("name");
+ if (name == null || name.length() == 0) {
+ name = "";
+ }
+ else {
+ name = " \"" + name + "\"";
+ }
+ path = "/" + el.getTagName() + name + path;
+ }
+ n = n.getParentNode();
+ }
+ return path;
+ }
+
+ /**
+ * Insert the new node under the parent.
+ * If ordering under the parent is important (e.g. for <interface> dependencies),
+ * that is handled here.
+ */
+ public void insertChild(Element parent, Element newnode) {
+ // The new node is not from the same document from the parent
+ // so it needs to be imported.
+ Element n = (Element)parent.getOwnerDocument().importNode(newnode, true);
+ if ("interface".equals(n.getTagName())) {
+ String extend = n.getAttribute("extends");
+
+ // TODO: find the best 'sorted' spot, or resort the result.
+ // right now, we put Object at the front and everything else at the end.
+ // This happens to work with what we have in lfc-undefined.lzx, but is fragile.
+ //
+ if ("Object".equals(extend)) {
+ parent.insertBefore(n, parent.getFirstChild());
+ }
+ else {
+ parent.appendChild(n);
+ }
+ }
+ else {
+ parent.appendChild(n);
+ }
+ }
+
+ public void replaceChild(Element parent, Element newnode, Element orignode) {
+ Node n = parent.getOwnerDocument().importNode(newnode, true);
+ parent.replaceChild(n, orignode);
+ }
+
+ public void merge(Element orig, Element merge) {
+ Element[] children = getChildElements(merge);
+ for (int i=0; i<children.length; i++) {
+ Element mergeel = children[i];
+ String tagname = mergeel.getTagName();
+ if (tagname.equals("insert")) {
+ Element[] subchildren = getChildElements(mergeel);
+ for (int j=0; j<subchildren.length; j++) {
+ Element sub = subchildren[j];
+ Element origel = getMatchingElement(orig, sub);
+ if (origel == null) {
+ insertChild(orig, sub);
+ }
+ else {
+ throw new SchemaBuilderError(show(sub) + " in <insert> matches an existing element: " + showpath(origel));
+ }
+ }
+ }
+ else if (tagname.equals("replace")) {
+ Element[] subchildren = getChildElements(mergeel);
+ for (int j=0; j<subchildren.length; j++) {
+ Element sub = subchildren[j];
+ Element origel = getMatchingElement(orig, sub);
+ if (origel == null) {
+ throw new SchemaBuilderError(show(sub) + " in <replace> does not match an existing element under " + showpath(orig));
+ }
+ else {
+ replaceChild(orig, sub, origel);
+ }
+ }
+ }
+ else if (tagname.equals("delete")) {
+ Element[] subchildren = getChildElements(mergeel);
+ for (int j=0; j<subchildren.length; j++) {
+ Element sub = subchildren[j];
+ Element origel = getMatchingElement(orig, sub);
+ if (origel == null) {
+ throw new SchemaBuilderError(show(sub) + " in <delete> does not match an existing element under " + showpath(orig));
+ }
+ else {
+ orig.removeChild(origel);
+ }
+ }
+ }
+ else { // merge with existing node, recursively
+ Element origel = getMatchingElement(orig, mergeel);
+ if (origel == null) {
+ throw new SchemaBuilderError(show(mergeel) + " does not match an existing element under " + showpath(orig));
+ }
+ else {
+ merge(origel, mergeel);
+ }
+ }
+ }
+ }
+
+ /**
+ * Top level method to build the XML output from the
+ * parsed XML input tree.
+ */
public void build(Element sroot) {
try {
compileXpathExpressions();
+ String versionstr = LPS.getVersion() + "|" +
+ LPS.getRelease() + "|" +
+ LPS.getBuild() + "|" +
+ LPS.getBuildDate();
+
+ sroot.setAttribute("version", versionstr);
+
// Walk the set of nodes that correspond to classes
// to collect their names and position in the input XML tree.
NodeList nodes = (NodeList)xPath.evaluate("/js2doc/property/doc/tag[@name='lzxname']/text", droot, XPathConstants.NODESET);
@@ -85,9 +389,27 @@
Element elzxname = (Element) nodes.item(i);
Element eprop = (Element)elzxname.getParentNode().getParentNode().getParentNode();
Element classNode = (Element)xPath.evaluate("class", eprop, XPathConstants.NODE);
+ // TODO: we currently say:
+ // if the class doesn't have access, get it from the unitid.
+ // If an element in the class doesn't have access, get it from the class.
+ // A slightly different approach, but one which I think matches the doc is:
+ // if the class doesn't have access, get it from the unitid.
+ // If an element in the class doesn't have access, get it from the *unitid*.
+ // TODO: test this difference, and if so, implement it.
+ //
if (classNode != null) {
JsClass j = new JsClass();
j.jsname = eprop.getAttribute("name");
+ j.access = eprop.getAttribute("access");
+ if (j.access == null) {
+ String unitid = eprop.getAttribute("unitid");
+ if (unitid != null) {
+ Element unitNode = (Element)xPath.evaluate("/js2doc/unit[id='" + unitid + "']", droot, XPathConstants.NODE);
+ if (unitNode != null) {
+ j.access = unitNode.getAttribute("access");
+ }
+ }
+ }
j.lzxname = getTextData(elzxname);
j.extname = classNode.getAttribute("extends");
j.classnode = classNode;
@@ -95,13 +417,24 @@
}
}
+ //
+ // The reader of this schema file needs the
+ // classes ordered so that no class is used as a superclass
+ // before it is defined. Use a different tree to
+ // hold that ordering. We do that after seeing
+ // all the classes so we know the complete inheritance chains.
+ for (Iterator iter = jsClasses.keySet().iterator(); iter.hasNext(); ) {
+ JsClass j = (JsClass)jsClasses.get(iter.next());
+ jsClassesOrdered.put(getOrderedKey(j), j);
+ }
+
// Walk the classes, and for each one, pull out its
// methods, events, attrs
- for (Iterator iter = jsClasses.keySet().iterator(); iter.hasNext(); ) {
- JsClass j = (JsClass)jsClasses.get(iter.next());
+ for (Iterator iter = jsClassesOrdered.keySet().iterator(); iter.hasNext(); ) {
+ JsClass j = (JsClass)jsClassesOrdered.get(iter.next());
Element iface = createChild(sroot, "interface");
iface.setAttribute("name", j.lzxname);
- String lzxExtends = "Object";
+ String lzxExtends = "Instance";
if (j.extname != null) {
JsClass extj = (JsClass)jsClasses.get(j.extname);
if (extj != null) {
@@ -118,7 +451,7 @@
addMethods(methods, j, false);
addEvents(events, j);
- // TODO: Class attributes needed.
+ addAttributes(attrs, j, true);
addAttributes(attrs, j, false);
// Put the found class members into the output XML
@@ -126,12 +459,19 @@
createMembers(events, iface, "event");
createMembers(attrs, iface, "attribute");
}
+ if (mergeSchema != null) {
+ merge(sroot, mergeSchema);
+ }
+ sroot.insertBefore(sroot.getOwnerDocument().createComment(INITIAL_COMMENT), sroot.getFirstChild());
}
catch (XPathExpressionException e) {
throw new InternalError(e);
}
}
+ /**
+ * Add any methods to the treemap.
+ */
protected void addMethods(TreeMap methods, JsClass jsclass,
boolean classMethods)
throws XPathExpressionException
@@ -142,7 +482,7 @@
NodeList nodes = (NodeList)xPath.evaluate(path, jsclass.classnode,
XPathConstants.NODESET);
for (int count=nodes.getLength(), i=0; i<count; i++) {
- JsMember member = addMember((Element) nodes.item(i).getParentNode(), classMethods);
+ JsMember member = addMember(MEMBER_METHOD, (Element) nodes.item(i).getParentNode(), jsclass, classMethods);
if (member != null) {
// ignore constructors
if (!member.name.equals(jsclass.jsname)) {
@@ -152,6 +492,9 @@
}
}
+ /**
+ * Add any events to the treemap.
+ */
protected void addEvents(TreeMap events, JsClass jsclass)
throws XPathExpressionException
{
@@ -161,60 +504,100 @@
XPathConstants.NODESET);
for (int count=nodes.getLength(), i=0; i<count; i++) {
Element eprop = (Element)nodes.item(i);
- JsMember member = addMember(eprop, false);
+ JsMember member = addMember(MEMBER_EVENT, eprop, jsclass, false);
if (member != null) {
events.put(member.name, member);
}
}
}
+ /**
+ * Add any attributes to the treemap.
+ */
protected void addAttributes(TreeMap attrs, JsClass jsclass,
boolean classAttrs)
throws XPathExpressionException
{
- // TODO: class attributes needs to be tested, this
- // guess is probably wrong.
String path = classAttrs ? "property[doc]" :
- "property[@name='__ivars__']/object/property[@access='public']";
+ "property[@name='__ivars__']/object/property";
- //showNodeTree(jsclass.classnode, " ");
NodeList nodes = (NodeList)xPath.evaluate(path, jsclass.classnode,
XPathConstants.NODESET);
for (int count=nodes.getLength(), i=0; i<count; i++) {
Element eprop = (Element)nodes.item(i);
- JsMember member = addMember(eprop, classAttrs);
+ // Our query for attributes can pick up events too.
+ String lzxtype = getTextData((Element)xPath.evaluate("doc/tag[@name='lzxtype']/text", eprop, XPathConstants.NODE));
+ String lzxdefault = getTextData((Element)xPath.evaluate("doc/tag[@name='lzxdefault']/text", eprop, XPathConstants.NODE));
+ // TODO: consider using "event".equals(lzxtype)?
+ Boolean isEvent = (Boolean)xPath.evaluate("(doc/tag[@name='lzxtype']/text) = 'event'", eprop, XPathConstants.BOOLEAN);
+ if (isEvent.booleanValue()) {
+ continue;
+ }
+ // Our query for class attributes can pick up functions too.
+ Boolean isFunction = (Boolean)xPath.evaluate("function", eprop, XPathConstants.BOOLEAN);
+ if (isFunction.booleanValue()) {
+ continue;
+ }
+ JsMember member = addMember(MEMBER_ATTRIBUTE, eprop, jsclass, classAttrs);
if (member != null) {
- member.type = eprop.getAttribute("type");
- if ("".equals(member.type)) {
+ member.type = lzxtype;
+ if (lzxdefault != null) {
+ member.defaultvalue = convertDefaultValue(lzxdefault);
+ }
+ if (member.type == null || member.type.equals("")) {
+ member.type = eprop.getAttribute("type");
+ }
+ if (member.type != null && !"".equals(member.type)) {
+ member.type = convertAttributeType(member.type);
+ if (member.type.indexOf('|') >= 0) {
+ member.enumvalues = member.type;
+ member.type = "string";
+ }
+ }
+ else {
member.type = null;
}
if (member.type == null) {
- System.err.println("Warning: attribute " + jsclass.jsname + "." + member.name + " has no type for schema");
+
+ System.err.println("Warning: attribute " + jsclass.jsname + "." + member.name + " has no @lzxtype or @type in javadoc, needed for schema");
}
attrs.put(member.name, member);
}
}
}
- protected JsMember addMember(Element eprop, boolean isstatic)
+ /**
+ * Create and initialize a JsMember (method/attribute/event),
+ * from the given property node.
+ */
+ protected JsMember addMember(String kind, Element eprop, JsClass jsclass, boolean isstatic)
throws XPathExpressionException
{
String access = eprop.getAttribute("access");
- JsMember meth = null;
- if (!"private".equals(access)) {
- meth = new JsMember();
- meth.name = eprop.getAttribute("name");
- meth.isfinal = isFinal(eprop);
- meth.isstatic = isstatic;
+ JsMember member = null;
+ if (access == null) {
+ access = jsclass.access;
}
- return meth;
+ if (access == null) {
+ System.err.println("Warning: access is unknown for " + jsclass.jsname + "." + eprop.getAttribute("name"));
+ }
+ // Note: we need to include methods that are marked private in doc,
+ // because if they are redefined, they must be marked as 'override'.
+ if (kind.equals(MEMBER_METHOD) || !"private".equals(access)) {
+ member = new JsMember();
+ member.name = eprop.getAttribute("name");
+ member.isfinal = isFinal(eprop);
+ member.isstatic = isstatic;
+ }
+ return member;
}
+ /**
+ * True if the member is marked 'final'.
+ */
protected boolean isFinal(Element eprop)
throws XPathExpressionException
{
- NodeList nodes;
-
// Modifiers can exist as attributes on the property containing
// function and also as <tag name="modifiers"> nodes under property.
String mods = eprop.getAttribute("modifiers");
@@ -228,11 +611,14 @@
mods += " " + modtext;
}
}
- //TODO: <tag name="modifiers"><text>final</text></tag>
+ //TODO: Can't final also appear as: <tag name="modifiers"><text>final</text></tag>
// TODO: how are multiple modifiers delimited?
return (mods != null && mods.contains("final"));
}
+ /**
+ * Given a map of JsMembers, create them all within the interface.
+ */
protected void createMembers(TreeMap methods, Element iface, String tag) {
for (Iterator iter = methods.keySet().iterator(); iter.hasNext(); ) {
JsMember member = (JsMember)methods.get(iter.next());
@@ -240,6 +626,9 @@
}
}
+ /**
+ * Given a JsMember, create it within the interface.
+ */
protected void createMember(Element iface, JsMember member, String tag) {
Element emember = createChild(iface, tag);
emember.setAttribute("name", member.name);
@@ -247,22 +636,70 @@
emember.setAttribute("final", "true");
}
if (member.type != null) {
- emember.setAttribute("type", convertAttributeType(member.type));
+ emember.setAttribute("type", member.type);
}
- // TODO: mark isstatic?
+ if (member.enumvalues != null) {
+ emember.setAttribute("enum", member.enumvalues);
+ }
+ if (member.defaultvalue != null) {
+ emember.setAttribute("value", member.defaultvalue);
+ }
+ if (member.isstatic) {
+ emember.setAttribute("allocation", "class");
+ }
}
+ /**
+ * A type found in doc may need to be converted to
+ * a different naming convention for the output schema format.
+ * TODO: is there a good reason for this, maybe we should
+ * generally use the type found in the doc, and teach the reader
+ * of lfc.lzx to know those types.
+ */
String convertAttributeType(String s) {
- if ("String".equals(s)) {
+ if (s.indexOf('|') >= 0) {
+ s = s.replaceAll("\"", "").replaceAll("'", "").replaceAll(" ", "");
+ // special case: booleanLiteral|'inherit' becomes inheritableBoolean
+ if ("booleanLiteral|inherit".equals(s)) {
+ s = "inheritableBoolean";
+ }
+ return s;
+ } else if ("String".equals(s)) {
return "string";
- } else if ("Number".equals(s)) {
+ } else if ("Number".equals(s) || "integer".equals(s) || "uint".equals(s)) {
return "number";
- } else {
+ } else if ("Boolean".equals(s)) {
+ return "boolean";
+ } else if ("booleanLiteral".equals(s)) {
+ return "boolean"; // TODO: not exactly true, but this is what appears in the schema...
+ } else if ("sizeExpression".equals(s)) {
+ return "size";
+ } else if ("Array".equals(s) ||
+ "LzDataNodeMixin".equals(s) ||
+ "LzDelegate".equals(s) ||
+ "LzNode".equals(s) ||
+ "LzParam".equals(s) ||
+ "LzView".equals(s) ||
+ "Object".equals(s) ||
+ "[LzView]".equals(s) ||
+ "[String]".equals(s) ||
+ "DisplayKeys".equals(s) ||
+ "Dictionary".equals(s)) {
+ // TODO: these have no good alternative!
+ return "string";
+ } else {
+ // TODO: others??
return s;
}
}
- // Used for debugging
+ String convertDefaultValue(String s) {
+ return s.replaceAll("\"", "").replaceAll("'", "");
+ }
+
+ /**
+ * For debugging, show the given tree as XML.
+ */
private void showNodeTree(Element e, String prefix)
{
System.out.print(prefix + "<" + e.getTagName());
Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/utils/FileUtils.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/utils/FileUtils.java 2008-10-22 21:05:34 UTC (rev 11513)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/utils/FileUtils.java 2008-10-23 00:22:32 UTC (rev 11514)
@@ -3,7 +3,7 @@
* ****************************************************************************/
/* J_LZ_COPYRIGHT_BEGIN *******************************************************
-* Copyright 2001-2007 Laszlo Systems, Inc. All Rights Reserved. *
+* Copyright 2001-2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* J_LZ_COPYRIGHT_END *********************************************************/
@@ -144,7 +144,7 @@
Perl5Compiler compiler = new Perl5Compiler();
Pattern tmp = null;
try {
- tmp = compiler.compile("[^<]*\\s*<[?]xml\\s+[^>]*encoding=[\"'](.*)['\"][^>]*?>", Perl5Compiler.READ_ONLY_MASK);
+ tmp = compiler.compile("[^<]*\\s*<[?]xml\\s+[^>]*encoding=[\"']([^'\"]*)['\"][^>]*?>", Perl5Compiler.READ_ONLY_MASK);
} catch( MalformedPatternException failed ) {
System.err.println( failed );
System.exit( 0 );
More information about the Laszlo-checkins
mailing list