blob: 88f14495139ab0b1cd9e62936dd60db723f592ee [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.royale.playerglobal;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* Generates .as source files to build playerglobal.swc and airglobal.swc from
* the ASDoc DITA XML files.
*
* Usage:
*
* java -jar playerglobal-source-gen.jar flex-sdk/frameworks/projects/playerglobal/bundles/en_US/docs/
* java -jar playerglobal-source-gen.jar flex-sdk/frameworks/projects/playerglobal/bundles/en_US/docs/ target/generated-sources/
*/
class PlayerglobalSourceGen {
public static void main(String[] args) {
if (args.length == 0) {
System.err.println("Must specify docs folder path");
System.exit(1);
}
if (args.length > 2) {
System.err.println("Too many arguments. Required docs folder path. Optional target folder path.");
System.exit(1);
}
File sourceFolder = new File(args[0]);
File targetFolder = (args.length == 2) ? new File(args[1])
: new File(System.getProperty("user.dir"), "target/generated-sources/");
PlayerglobalSourceGen sourceGen = new PlayerglobalSourceGen(sourceFolder, targetFolder);
try {
sourceGen.generateSources();
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(1);
}
}
private static final List<String> VECTOR_SUFFIXES = Arrays.asList("$double", "$int", "$uint", "$object");
private File sourceFolder;
private File targetFolder;
private File currentFile;
public PlayerglobalSourceGen(File sourceFolder, File targetFolder) {
this.sourceFolder = sourceFolder;
this.targetFolder = targetFolder;
}
public void generateSources() throws Exception {
preclean();
for (File sourceFile : sourceFolder.listFiles()) {
if (sourceFile.isDirectory()) {
continue;
}
String sourceFileName = sourceFile.getName();
if (!sourceFileName.endsWith(".xml")) {
continue;
}
if (sourceFileName.endsWith(".dita.xml")) {
continue;
}
parseFile(sourceFile);
}
}
private void preclean() throws Exception {
File playerglobalFolder = new File(targetFolder, "playerglobal");
FileUtils.deleteDirectory(playerglobalFolder);
File airglobalFolder = new File(targetFolder, "airglobal");
FileUtils.deleteDirectory(airglobalFolder);
}
private void writeFileForDefinition(String fullyQualifiedName, boolean airOnly, String contents)
throws IOException {
StringBuilder fileNameBuilder = new StringBuilder();
if (airOnly) {
fileNameBuilder.append("airglobal");
} else {
fileNameBuilder.append("playerglobal");
}
String[] parts = fullyQualifiedName.split("\\.");
for (String part : parts) {
fileNameBuilder.append("/");
fileNameBuilder.append(part);
}
fileNameBuilder.append(".as");
File targetFile = new File(this.targetFolder, fileNameBuilder.toString());
FileUtils.writeStringToFile(targetFile, contents);
}
private boolean isAIROnly(Element prologElement) {
if (prologElement == null) {
return false;
}
Element asMetadataElement = prologElement.element("asMetadata");
if (asMetadataElement == null) {
return false;
}
Element apiVersionElement = asMetadataElement.element("apiVersion");
if (apiVersionElement == null) {
return false;
}
List<Element> apiPlatformElements = apiVersionElement.elements("apiPlatform");
if (apiPlatformElements == null || apiPlatformElements.size() == 0) {
return false;
}
for (Element apiPlatformElement : apiPlatformElements) {
if (!"AIR".equals(apiPlatformElement.attributeValue("name"))) {
return false;
}
}
return true;
}
private void parseFile(File ditaFile) throws Exception {
currentFile = ditaFile;
String contents = null;
try {
contents = FileUtils.readFileToString(ditaFile, Charset.forName("utf8"));
} catch (Exception e) {
System.err.println("Failed to read XML file: " + ditaFile.getAbsolutePath());
return;
}
SAXReader xmlReader = new SAXReader();
Document xmlDoc = xmlReader.read(new StringReader(contents));
Element apiPackageElement = xmlDoc.getRootElement();
if (!"apiPackage".equals(apiPackageElement.getName())) {
throw new Exception("No apiPackage root element: " + ditaFile.getAbsolutePath());
}
parsePackage(apiPackageElement);
currentFile = null;
}
private void parsePackage(Element apiPackageElement) throws Exception {
List<Element> apiOperationElements = apiPackageElement.elements("apiOperation");
for (Element apiOperationElement : apiOperationElements) {
parsePackageFunction(apiOperationElement);
}
List<Element> apiValueElements = apiPackageElement.elements("apiValue");
for (Element apiValueElement : apiValueElements) {
parsePackageVariable(apiValueElement);
}
List<Element> apiClassifierElements = apiPackageElement.elements("apiClassifier");
for (Element apiClassifierElement : apiClassifierElements) {
Element apiClassifierDetailElement = apiClassifierElement.element("apiClassifierDetail");
if (apiClassifierDetailElement == null) {
String fullyQualifiedName = apiClassifierElement.attributeValue("id");
throw new Exception("Not found: " + fullyQualifiedName);
}
Element apiClassifierDefElement = apiClassifierDetailElement.element("apiClassifierDef");
if (apiClassifierDefElement == null) {
String fullyQualifiedName = apiClassifierElement.attributeValue("id");
throw new Exception("Not found: " + fullyQualifiedName);
}
Element apiInterfaceElement = apiClassifierDefElement.element("apiInterface");
if (apiInterfaceElement != null) {
parseInterface(apiClassifierElement);
continue;
}
parseClass(apiClassifierElement);
}
}
private void parseClass(Element apiClassifierElement) throws Exception {
String fullyQualifiedName = apiClassifierElement.attributeValue("id");
if (fullyQualifiedName.startsWith("globalClassifier:")) {
fullyQualifiedName = fullyQualifiedName.substring(17);
}
if (fullyQualifiedName.equals("Vector")) {
//special case in the compiler that doesn't get exposed in docs
fullyQualifiedName = "__AS3__.vec:Vector";
StringBuilder vectorBuilder = new StringBuilder();
vectorBuilder.append("// generated from: ");
vectorBuilder.append(currentFile.getName());
vectorBuilder.append("\n");
vectorBuilder.append("package __AS3__.vec {\n");
vectorBuilder.append("\tpublic final dynamic class Vector {\n");
vectorBuilder.append("\tpublic native function Vector();\n");
vectorBuilder.append("\t}\n");
vectorBuilder.append("}\n");
writeFileForDefinition("__AS3__.vec.Vector", false, vectorBuilder.toString());
for (String suffix : VECTOR_SUFFIXES) {
parseClassWithFullyQualifiedName(apiClassifierElement, fullyQualifiedName + suffix);
}
return;
}
parseClassWithFullyQualifiedName(apiClassifierElement, fullyQualifiedName);
}
private void parseClassWithFullyQualifiedName(Element apiClassifierElement, String fullyQualifiedName)
throws Exception {
String[] parts = fullyQualifiedName.split(":");
String packageName = "";
String className = fullyQualifiedName;
if (parts.length > 1) {
packageName = parts[0];
className = parts[1];
fullyQualifiedName = packageName + "." + className;
}
boolean isAIROnly = isAIROnly(apiClassifierElement.element("prolog"));
boolean isVector = className.startsWith("Vector$");
Set<String> importFullyQualifiedNames = new HashSet<String>();
collectImports(apiClassifierElement, packageName, importFullyQualifiedNames);
String baseClassFullyQualifiedName = "";
List<String> interfaceFullyQualifiedNames = new ArrayList<String>();
String access = null;
boolean isFinal = false;
boolean isDynamic = false;
Element apiClassifierDetailElement = apiClassifierElement.element("apiClassifierDetail");
if (apiClassifierDetailElement == null) {
throw new Exception("apiClassifierDetail not found for: " + className);
}
Element apiClassifierDefElement = apiClassifierDetailElement.element("apiClassifierDef");
if (apiClassifierDefElement == null) {
throw new Exception("apiClassifierDef not found for: " + className);
}
Element apiBaseClassifierElement = apiClassifierDefElement.element("apiBaseClassifier");
if (apiBaseClassifierElement != null) {
baseClassFullyQualifiedName = apiBaseClassifierElement.getTextTrim();
baseClassFullyQualifiedName = baseClassFullyQualifiedName.replace(":", ".");
}
List<Element> apiBaseInterfaceElements = apiClassifierDefElement.elements("apiBaseInterface");
for (Element apiBaseInterfaceElement : apiBaseInterfaceElements) {
String interfaceFullyQualifiedName = apiBaseInterfaceElement.getTextTrim();
interfaceFullyQualifiedName = interfaceFullyQualifiedName.replace(":", ".");
interfaceFullyQualifiedNames.add(interfaceFullyQualifiedName);
}
Element apiAccessElement = apiClassifierDefElement.element("apiAccess");
if (apiAccessElement != null) {
access = apiAccessElement.attributeValue("value");
}
if (isVector) {
access = "internal";
}
Element apiFinalElement = apiClassifierDefElement.element("apiFinal");
if (apiFinalElement != null) {
isFinal = true;
}
Element apiDynamicElement = apiClassifierDefElement.element("apiDynamic");
if (apiDynamicElement != null) {
isDynamic = true;
}
List<Element> apiConstructorElements = apiClassifierElement.elements("apiConstructor");
List<Element> apiOperationElements = apiClassifierElement.elements("apiOperation");
List<Element> apiValueElements = apiClassifierElement.elements("apiValue");
StringBuilder classBuilder = new StringBuilder();
classBuilder.append("// generated from: ");
classBuilder.append(currentFile.getName());
classBuilder.append("\n");
classBuilder.append("package");
if (packageName.length() > 0) {
classBuilder.append(" ");
classBuilder.append(packageName);
}
classBuilder.append(" ");
classBuilder.append("{");
classBuilder.append("\n");
writeImports(importFullyQualifiedNames, classBuilder);
classBuilder.append("\t");
if (access != null && access.length() > 0) {
classBuilder.append(access);
classBuilder.append(" ");
}
if (isFinal) {
classBuilder.append("final ");
}
if (isDynamic) {
classBuilder.append("dynamic ");
}
classBuilder.append("class ");
classBuilder.append(className);
if (baseClassFullyQualifiedName != null && baseClassFullyQualifiedName.length() > 0
&& !"Object".equals(baseClassFullyQualifiedName)) {
classBuilder.append(" extends ");
classBuilder.append(baseClassFullyQualifiedName);
}
for (int i = 0; i < interfaceFullyQualifiedNames.size(); i++) {
String interfaceFullyQualifiedName = interfaceFullyQualifiedNames.get(i);
if (i == 0) {
classBuilder.append(" implements ");
} else {
classBuilder.append(", ");
}
classBuilder.append(interfaceFullyQualifiedName);
}
classBuilder.append(" ");
classBuilder.append("{");
classBuilder.append("\n");
if (apiConstructorElements.size() > 0) {
parseConstructor(apiConstructorElements, className, classBuilder);
}
for (Element apiOperationElement : apiOperationElements) {
parseFunction(apiOperationElement, className, false, classBuilder);
}
for (Element apiValueElement : apiValueElements) {
parseVariable(apiValueElement, false, classBuilder);
}
classBuilder.append("\t");
classBuilder.append("}");
classBuilder.append("\n");
classBuilder.append("}");
classBuilder.append("\n");
writeFileForDefinition(fullyQualifiedName, isAIROnly, classBuilder.toString());
}
private void parseInterface(Element apiClassifierElement) throws Exception {
String fullyQualifiedName = apiClassifierElement.attributeValue("id");
if (fullyQualifiedName.startsWith("globalClassifier:")) {
fullyQualifiedName = fullyQualifiedName.substring(17);
}
String[] parts = fullyQualifiedName.split(":");
String packageName = "";
String interfaceName = fullyQualifiedName;
if (parts.length > 1) {
packageName = parts[0];
interfaceName = parts[1];
fullyQualifiedName = packageName + "." + interfaceName;
}
boolean isAIROnly = isAIROnly(apiClassifierElement.element("prolog"));
Set<String> importFullyQualifiedNames = new HashSet<String>();
collectImports(apiClassifierElement, packageName, importFullyQualifiedNames);
List<String> interfaceFullyQualifiedNames = new ArrayList<String>();
String access = null;
Element apiClassifierDetailElement = apiClassifierElement.element("apiClassifierDetail");
if (apiClassifierDetailElement == null) {
throw new Exception("apiClassifierDetail not found for: " + interfaceName);
}
Element apiClassifierDefElement = apiClassifierDetailElement.element("apiClassifierDef");
if (apiClassifierDefElement == null) {
throw new Exception("apiClassifierDef not found for: " + interfaceName);
}
List<Element> apiBaseInterfaceElements = apiClassifierDefElement.elements("apiBaseInterface");
for (Element apiBaseInterfaceElement : apiBaseInterfaceElements) {
String baseInterfaceFullyQualifiedName = apiBaseInterfaceElement.getTextTrim();
baseInterfaceFullyQualifiedName = baseInterfaceFullyQualifiedName.replace(":", ".");
interfaceFullyQualifiedNames.add(baseInterfaceFullyQualifiedName);
}
Element apiAccessElement = apiClassifierDefElement.element("apiAccess");
if (apiAccessElement != null) {
access = apiAccessElement.attributeValue("value");
}
List<Element> apiOperationElements = apiClassifierElement.elements("apiOperation");
List<Element> apiValueElements = apiClassifierElement.elements("apiValue");
StringBuilder interfaceBuilder = new StringBuilder();
interfaceBuilder.append("// generated from: ");
interfaceBuilder.append(currentFile.getName());
interfaceBuilder.append("\n");
interfaceBuilder.append("package");
if (packageName.length() > 0) {
interfaceBuilder.append(" ");
interfaceBuilder.append(packageName);
}
interfaceBuilder.append(" ");
interfaceBuilder.append("{");
interfaceBuilder.append("\n");
writeImports(importFullyQualifiedNames, interfaceBuilder);
interfaceBuilder.append("\t");
if (access != null && access.length() > 0) {
interfaceBuilder.append(access);
interfaceBuilder.append(" ");
}
interfaceBuilder.append("interface ");
interfaceBuilder.append(interfaceName);
for (int i = 0; i < interfaceFullyQualifiedNames.size(); i++) {
String interfaceFullyQualifiedName = interfaceFullyQualifiedNames.get(i);
if (i == 0) {
interfaceBuilder.append(" extends ");
} else {
interfaceBuilder.append(", ");
}
interfaceBuilder.append(interfaceFullyQualifiedName);
}
interfaceBuilder.append(" ");
interfaceBuilder.append("{");
interfaceBuilder.append("\n");
for (Element apiOperationElement : apiOperationElements) {
parseFunction(apiOperationElement, null, true, interfaceBuilder);
}
for (Element apiValueElement : apiValueElements) {
parseVariable(apiValueElement, true, interfaceBuilder);
}
interfaceBuilder.append("\t");
interfaceBuilder.append("}");
interfaceBuilder.append("\n");
interfaceBuilder.append("}");
interfaceBuilder.append("\n");
writeFileForDefinition(fullyQualifiedName, isAIROnly, interfaceBuilder.toString());
}
private void parsePackageFunction(Element apiOperationElement) throws Exception {
String fullyQualifiedName = apiOperationElement.attributeValue("id");
if (fullyQualifiedName.startsWith("globalOperation:")) {
fullyQualifiedName = fullyQualifiedName.substring(16);
}
if (fullyQualifiedName.equals("Vector")) {
//special case in the compiler that doesn't get exposed in docs
return;
}
String[] parts = fullyQualifiedName.split(":");
String packageName = "";
if (parts.length > 1) {
packageName = parts[0];
fullyQualifiedName = packageName + "." + parts[1];
}
Set<String> importFullyQualifiedNames = new HashSet<String>();
collectImports(apiOperationElement, packageName, importFullyQualifiedNames);
boolean isAIROnly = isAIROnly(apiOperationElement.element("prolog"));
StringBuilder functionBuilder = new StringBuilder();
functionBuilder.append("// generated from: ");
functionBuilder.append(currentFile.getName());
functionBuilder.append("\n");
functionBuilder.append("package");
if (packageName != null && packageName.length() > 0) {
functionBuilder.append(" ");
functionBuilder.append(packageName);
}
functionBuilder.append(" ");
functionBuilder.append("{");
functionBuilder.append("\n");
writeImports(importFullyQualifiedNames, functionBuilder);
parseFunction(apiOperationElement, null, false, functionBuilder);
functionBuilder.append("}");
functionBuilder.append("\n");
writeFileForDefinition(fullyQualifiedName, isAIROnly, functionBuilder.toString());
}
private void parsePackageVariable(Element apiValueElement) throws Exception {
String fullyQualifiedName = apiValueElement.attributeValue("id");
if (fullyQualifiedName.startsWith("globalValue:")) {
fullyQualifiedName = fullyQualifiedName.substring(12);
}
String[] parts = fullyQualifiedName.split(":");
String packageName = "";
String variableName = fullyQualifiedName;
if (parts.length > 1) {
packageName = parts[0];
variableName = parts[1];
fullyQualifiedName = packageName + "." + variableName;
}
if (variableName.startsWith("-")) {
//nothing to do here
//it's just a negative value getting documented (like -Infinity)
return;
}
Set<String> importFullyQualifiedNames = new HashSet<String>();
collectImports(apiValueElement, packageName, importFullyQualifiedNames);
boolean isAIROnly = isAIROnly(apiValueElement.element("prolog"));
StringBuilder variableBuilder = new StringBuilder();
variableBuilder.append("// generated from: ");
variableBuilder.append(currentFile.getName());
variableBuilder.append("\n");
variableBuilder.append("package");
if (packageName != null && packageName.length() > 0) {
variableBuilder.append(" ");
variableBuilder.append(packageName);
}
variableBuilder.append(" ");
variableBuilder.append("{");
variableBuilder.append("\n");
writeImports(importFullyQualifiedNames, variableBuilder);
parseVariable(apiValueElement, false, variableBuilder);
variableBuilder.append("}");
variableBuilder.append("\n");
writeFileForDefinition(fullyQualifiedName, isAIROnly, variableBuilder.toString());
}
private void parseVariable(Element apiValueElement, boolean forInterface, StringBuilder variableBuilder)
throws Exception {
String variableName = apiValueElement.element("apiName").getTextTrim();
boolean isGetter = false;
boolean isSetter = false;
boolean isConst = true;
boolean isStatic = false;
boolean isOverride = false;
String variableType = "*";
String access = null;
Element apiValueDetailElement = apiValueElement.element("apiValueDetail");
if (apiValueDetailElement == null) {
throw new Exception("apiValueDetail not found for: " + variableName);
}
Element apiValueDefElement = apiValueDetailElement.element("apiValueDef");
if (apiValueDefElement == null) {
throw new Exception("apiValueDef not found for: " + variableName);
}
Element apiValueClassifierElement = apiValueDefElement.element("apiValueClassifier");
if (apiValueClassifierElement != null) {
variableType = apiValueClassifierElement.getTextTrim();
variableType = variableType.replace(":", ".");
}
Element apiAccessElement = apiValueDefElement.element("apiAccess");
if (!forInterface && apiAccessElement != null) {
access = apiAccessElement.attributeValue("value");
}
Element apiStaticElement = apiValueDefElement.element("apiStatic");
if (!forInterface && apiStaticElement != null) {
isStatic = true;
}
Element apiDynamicElement = apiValueDefElement.element("apiDynamic");
if (apiDynamicElement != null) {
isConst = false;
}
Element apiValueAccessElement = apiValueDefElement.element("apiValueAccess");
if (apiValueAccessElement != null) {
String readwrite = apiValueAccessElement.attributeValue("value");
isGetter = "readwrite".equals(readwrite) || "read".equals(readwrite);
isSetter = "readwrite".equals(readwrite) || "write".equals(readwrite);
}
Element apiIsOverrideElement = apiValueDefElement.element("apiIsOverride");
if (apiIsOverrideElement != null) {
isOverride = true;
}
if (!forInterface && isGetter && isSetter && !isOverride
&& apiValueElement.attributeValue("id").endsWith(":set")) {
//skip the getter because it is already defined on the base class
//example: flash.text.engine.TextElement.text
isGetter = false;
}
Element apiDataElement = apiValueDefElement.element("apiData");
if (isGetter) {
variableBuilder.append("\t");
if (access != null && access.length() > 0) {
variableBuilder.append(access);
variableBuilder.append(" ");
}
if (isStatic) {
variableBuilder.append("static ");
}
if (!forInterface) {
variableBuilder.append("native ");
}
if (isOverride) {
variableBuilder.append("override ");
}
variableBuilder.append("function ");
variableBuilder.append("get ");
variableBuilder.append(variableName);
variableBuilder.append("(");
variableBuilder.append(")");
variableBuilder.append(":");
variableBuilder.append(variableType);
variableBuilder.append(";");
variableBuilder.append("\n");
}
if (isSetter) {
variableBuilder.append("\t");
if (access != null && access.length() > 0) {
variableBuilder.append(access);
variableBuilder.append(" ");
}
if (isStatic) {
variableBuilder.append("static ");
}
if (!forInterface) {
variableBuilder.append("native ");
}
if (isOverride) {
variableBuilder.append("override ");
}
variableBuilder.append("function ");
variableBuilder.append("set ");
variableBuilder.append(variableName);
variableBuilder.append("(");
variableBuilder.append("value");
variableBuilder.append(":");
variableBuilder.append(variableType);
variableBuilder.append(")");
variableBuilder.append(":");
variableBuilder.append("void");
variableBuilder.append(";");
variableBuilder.append("\n");
}
if (!isGetter && !isSetter) {
variableBuilder.append("\t");
if (access != null && access.length() > 0) {
variableBuilder.append(access);
variableBuilder.append(" ");
}
if (isStatic) {
variableBuilder.append("static ");
}
if (isConst) {
variableBuilder.append("const ");
} else {
variableBuilder.append("var ");
}
variableBuilder.append(variableName);
variableBuilder.append(":");
variableBuilder.append(variableType);
if (apiDataElement != null) {
writeVariableOrParameterValue(apiDataElement, variableType, variableBuilder);
}
variableBuilder.append(";");
variableBuilder.append("\n");
}
}
private void parseFunction(Element apiOperationElement, String contextClassName, boolean forInterface,
StringBuilder functionBuilder) throws Exception {
String functionName = apiOperationElement.element("apiName").getTextTrim();
boolean isStatic = false;
boolean isOverride = false;
String returnType = "*";
String access = null;
Element apiOperationDetailElement = apiOperationElement.element("apiOperationDetail");
if (apiOperationDetailElement == null) {
throw new Exception("apiOperationDetail not found for: " + functionName);
}
Element apiOperationDefElement = apiOperationDetailElement.element("apiOperationDef");
if (apiOperationDefElement == null) {
throw new Exception("apiOperationDef not found for: " + functionName);
}
Element apiIsOverrideElement = apiOperationDefElement.element("apiIsOverride");
if (apiIsOverrideElement != null) {
isOverride = true;
}
Element apiReturnElement = apiOperationDefElement.element("apiReturn");
if (apiReturnElement != null) {
Element apiTypeElement = apiReturnElement.element("apiType");
if (apiTypeElement != null) {
returnType = parseReturnOrParamType(apiTypeElement, contextClassName);
}
Element apiOperationClassifierElement = apiReturnElement.element("apiOperationClassifier");
if (apiOperationClassifierElement != null) {
returnType = apiOperationClassifierElement.getTextTrim();
returnType = returnType.replace(":", ".");
}
}
Element apiAccessElement = apiOperationDefElement.element("apiAccess");
if (!forInterface && apiAccessElement != null) {
access = apiAccessElement.attributeValue("value");
}
Element apiStaticElement = apiOperationDefElement.element("apiStatic");
if (!forInterface && apiStaticElement != null) {
isStatic = true;
}
List<Element> apiParamElements = apiOperationDefElement.elements("apiParam");
if ("public".equals(access) && ("toString".equals(functionName) || "toLocaleString".equals(functionName))
|| "valueOf".equals(functionName) || "hasOwnProperty".equals(functionName)
|| "propertyIsEnumerable".equals(functionName)) {
return;
}
functionBuilder.append("\t");
if (access != null && access.length() > 0) {
functionBuilder.append(access);
functionBuilder.append(" ");
}
if (isStatic) {
functionBuilder.append("static ");
}
if (!forInterface) {
functionBuilder.append("native ");
}
if (isOverride) {
functionBuilder.append("override ");
}
functionBuilder.append("function ");
functionBuilder.append(functionName);
functionBuilder.append("(");
parseParameters(apiParamElements, contextClassName, functionName, functionBuilder);
functionBuilder.append(")");
functionBuilder.append(":");
functionBuilder.append(returnType);
functionBuilder.append(";");
functionBuilder.append("\n");
}
private void parseConstructor(List<Element> apiConstructorElements, String contextClassName,
StringBuilder functionBuilder) throws Exception {
String constructorName = contextClassName;
String access = null;
List<Element> apiParamElements = null;
for (Element apiConstructorElement : apiConstructorElements) {
if (constructorName == null) {
constructorName = apiConstructorElement.element("apiName").getTextTrim();
}
Element apiConstructorDetailElement = apiConstructorElement.element("apiConstructorDetail");
if (apiConstructorDetailElement == null) {
throw new Exception("apiConstructorDetail not found for: " + constructorName);
}
Element apiConstructorDefElement = apiConstructorDetailElement.element("apiConstructorDef");
if (apiConstructorDefElement == null) {
throw new Exception("apiConstructorDef not found for: " + constructorName);
}
Element apiAccessElement = apiConstructorDefElement.element("apiAccess");
if (apiAccessElement != null) {
access = apiAccessElement.attributeValue("value");
}
List<Element> newApiParamElements = apiConstructorDefElement.elements("apiParam");
apiParamElements = mergeParameters(apiParamElements, newApiParamElements);
}
functionBuilder.append("\t");
if (access != null && access.length() > 0) {
functionBuilder.append(access);
functionBuilder.append(" ");
}
functionBuilder.append("native ");
functionBuilder.append("function ");
functionBuilder.append(constructorName);
functionBuilder.append("(");
parseParameters(apiParamElements, contextClassName, constructorName, functionBuilder);
functionBuilder.append(")");
functionBuilder.append(";");
functionBuilder.append("\n");
}
private List<Element> mergeParameters(List<Element> apiParamElements1, List<Element> apiParamElements2)
throws Exception {
if (apiParamElements1 == null) {
return apiParamElements2;
}
if (apiParamElements2 == null) {
return apiParamElements1;
}
ArrayList<Element> result = new ArrayList<Element>();
for (int i = 0, count = Math.max(apiParamElements1.size(), apiParamElements2.size()); i < count; i++) {
Element apiParamElement1 = (apiParamElements1.size() > i) ? apiParamElements1.get(i) : null;
Element apiParamElement2 = (apiParamElements2.size() > i) ? apiParamElements2.get(i) : null;
if (apiParamElement1 == null) {
Element apiDataElement2 = apiParamElement2.element("apiData");
if (apiDataElement2 == null) {
apiDataElement2 = DocumentHelper.createElement("apiData");
apiDataElement2.setText("null");
apiParamElement2.add(apiDataElement2);
}
boolean isRest = false;
Element apiTypeElement2 = apiParamElement2.element("apiType");
if (apiTypeElement2 != null) {
String apiTypeValue2 = apiTypeElement2.attributeValue("value");
if ("restParam".equals(apiTypeValue2)) {
isRest = true;
}
apiParamElement2.remove(apiTypeElement2);
}
apiTypeElement2 = DocumentHelper.createElement("apiType");
if (isRest) {
apiTypeElement2.addAttribute("value", "restParam");
} else {
apiTypeElement2.addAttribute("value", "any");
}
apiParamElement2.add(apiTypeElement2);
Element apiOperationClassifierElement2 = apiParamElement2.element("apiOperationClassifier");
if (apiOperationClassifierElement2 != null) {
apiParamElement2.remove(apiOperationClassifierElement2);
}
result.add(apiParamElement2);
if (isRest) {
//nothing after rest
break;
}
continue;
}
if (apiParamElement2 == null) {
Element apiDataElement1 = apiParamElement1.element("apiData");
if (apiDataElement1 == null) {
apiDataElement1 = DocumentHelper.createElement("apiData");
apiDataElement1.setText("null");
apiParamElement1.add(apiDataElement1);
}
boolean isRest = false;
Element apiTypeElement1 = apiParamElement1.element("apiType");
if (apiTypeElement1 != null) {
String apiTypeValue1 = apiTypeElement1.attributeValue("value");
if ("restParam".equals(apiTypeValue1)) {
isRest = true;
}
apiParamElement1.remove(apiTypeElement1);
}
apiTypeElement1 = DocumentHelper.createElement("apiType");
if (isRest) {
apiTypeElement1.addAttribute("value", "restParam");
} else {
apiTypeElement1.addAttribute("value", "any");
}
apiParamElement1.add(apiTypeElement1);
Element apiOperationClassifierElement1 = apiParamElement1.element("apiOperationClassifier");
if (apiOperationClassifierElement1 != null) {
apiParamElement1.remove(apiOperationClassifierElement1);
}
result.add(apiParamElement1);
if (isRest) {
//nothing after rest
break;
}
continue;
}
String paramName = "param" + i;
boolean isRest = false;
Element apiTypeElement1 = apiParamElement1.element("apiType");
if (apiTypeElement1 != null) {
String apiTypeValue1 = apiTypeElement1.attributeValue("value");
if ("restParam".equals(apiTypeValue1)) {
isRest = true;
Element apiItemNameElement1 = apiParamElement1.element("apiItemName");
if (apiItemNameElement1 != null) {
//keep the existing name
paramName = apiItemNameElement1.getTextTrim();
}
}
}
Element apiTypeElement2 = apiParamElement2.element("apiType");
if (apiTypeElement2 != null) {
String apiTypeValue2 = apiTypeElement2.attributeValue("value");
if ("restParam".equals(apiTypeValue2)) {
isRest = true;
Element apiItemNameElement2 = apiParamElement2.element("apiItemName");
if (apiItemNameElement2 != null) {
//keep the existing name
paramName = apiItemNameElement2.getTextTrim();
}
}
}
Element newApiParamElement = DocumentHelper.createElement("apiParam");
Element apiItemNameElement = DocumentHelper.createElement("apiItemName");
apiItemNameElement.setText(paramName);
newApiParamElement.add(apiItemNameElement);
Element newApiTypeElement = DocumentHelper.createElement("apiType");
if (isRest) {
newApiTypeElement.addAttribute("value", "restParam");
} else {
newApiTypeElement.addAttribute("value", "any");
}
newApiParamElement.add(newApiTypeElement);
if (!isRest) {
Element newApiDataElement = DocumentHelper.createElement("apiData");
newApiDataElement.setText("null");
newApiParamElement.add(newApiDataElement);
}
result.add(newApiParamElement);
if (isRest) {
//nothing after rest
break;
}
}
return result;
}
private String parseReturnOrParamType(Element apiTypeElement, String contextClassName) throws Exception {
String apiTypeValue = apiTypeElement.attributeValue("value");
if ("restParam".equals(apiTypeValue)) {
return null;
}
if ("any".equals(apiTypeValue)) {
return "*";
}
if ("T".equals(apiTypeValue)) {
return "T";
}
if ("void".equals(apiTypeValue)) {
return "void";
}
if (apiTypeValue.startsWith("Vector$")) {
String[] parts = apiTypeValue.split("\\$");
String vectorItemType = parts[1];
vectorItemType = vectorItemType.replace(":", ".");
if (contextClassName != null && contextClassName.startsWith("Vector$") && vectorItemType.equals("T")) {
return contextClassName;
}
return "Vector.<" + vectorItemType + ">";
} else {
throw new Exception("Unknown apiType value: " + apiTypeValue);
}
}
private void parseParameters(List<Element> apiParamElements, String contextClassName, String contextFunctionName,
StringBuilder functionBuilder) throws Exception {
boolean isXMLConstructor = ("XML".equals(contextClassName) && "XML".equals(contextFunctionName))
|| ("XMLList".equals(contextClassName) && "XMLList".equals(contextFunctionName));
for (int i = 0; i < apiParamElements.size(); i++) {
if (i > 0) {
functionBuilder.append(", ");
}
Element apiParamElement = apiParamElements.get(i);
Element apiTypeElement = apiParamElement.element("apiType");
String paramType = null;
if (apiTypeElement != null) {
String apiTypeValue = apiTypeElement.attributeValue("value");
if ("restParam".equals(apiTypeValue)) {
functionBuilder.append("...");
}
paramType = parseReturnOrParamType(apiTypeElement, contextClassName);
}
Element apiItemNameElement = apiParamElement.element("apiItemName");
if (apiItemNameElement == null) {
throw new Exception("apiItemName not found");
}
functionBuilder.append(apiItemNameElement.getTextTrim());
if (isXMLConstructor) {
//workaround for missing data in asdoc dita
functionBuilder.append(":* = null");
} else {
Element apiOperationClassifierElement = apiParamElement.element("apiOperationClassifier");
if (apiOperationClassifierElement != null) {
paramType = apiOperationClassifierElement.getTextTrim();
paramType = paramType.replace(":", ".");
}
if (paramType != null) {
functionBuilder.append(":");
functionBuilder.append(paramType);
}
Element apiDataElement = apiParamElement.element("apiData");
if (apiDataElement != null) {
writeVariableOrParameterValue(apiDataElement, paramType, functionBuilder);
}
}
}
}
private void writeVariableOrParameterValue(Element apiDataElement, String varType, StringBuilder builder) {
builder.append(" = ");
String paramValue = apiDataElement.getTextTrim();
if ("unknown".equals(paramValue)) {
paramValue = "null";
}
boolean isString = ("String".equals(varType) || paramValue.matches("[A-Za-z\\*]+"))
&& !"undefined".equals(paramValue) && !"null".equals(paramValue) && !"NaN".equals(paramValue)
&& !"true".equals(paramValue) && !"false".equals(paramValue);
if (isString) {
builder.append("\"");
}
builder.append(paramValue);
if (isString) {
builder.append("\"");
}
}
private void collectImport(String fullyQualifiedName, String forPackage, Set<String> result) throws Exception {
String[] parts = fullyQualifiedName.split(":");
if (parts.length == 1) {
//top-level package
return;
}
String packageName = parts[0];
if (packageName.equals(forPackage)) {
//same package
return;
}
result.add(packageName + "." + parts[1]);
}
private void collectImports(Element element, String forPackage, Set<String> result) throws Exception {
String elementName = element.getName();
if ("apiClassifier".equals(elementName)) {
String className = element.element("apiName").getTextTrim();
Element apiClassifierDetailElement = element.element("apiClassifierDetail");
if (apiClassifierDetailElement == null) {
throw new Exception("apiClassifierDetail not found for: " + className);
}
Element apiClassifierDefElement = apiClassifierDetailElement.element("apiClassifierDef");
if (apiClassifierDefElement == null) {
throw new Exception("apiClassifierDef not found for: " + className);
}
Element apiBaseClassifierElement = apiClassifierDefElement.element("apiBaseClassifier");
if (apiBaseClassifierElement != null) {
String baseClassType = apiBaseClassifierElement.getTextTrim();
collectImport(baseClassType, forPackage, result);
}
List<Element> apiBaseInterfaceElements = apiClassifierDefElement.elements("apiBaseInterface");
for (Element apiBaseInterfaceElement : apiBaseInterfaceElements) {
String interfaceType = apiBaseInterfaceElement.getTextTrim();
collectImport(interfaceType, forPackage, result);
}
Element apiConstructorElement = element.element("apiConstructor");
if (apiConstructorElement != null) {
collectImports(apiConstructorElement, forPackage, result);
}
List<Element> apiOperationElements = element.elements("apiOperation");
for (Element apiOperationElement : apiOperationElements) {
collectImports(apiOperationElement, forPackage, result);
}
List<Element> apiValueElements = element.elements("apiValue");
for (Element apiValueElement : apiValueElements) {
collectImports(apiValueElement, forPackage, result);
}
}
if ("apiOperation".equals(elementName)) {
String functionName = element.element("apiName").getTextTrim();
Element apiOperationDetailElement = element.element("apiOperationDetail");
if (apiOperationDetailElement == null) {
throw new Exception("apiOperationDetail not found for: " + functionName);
}
Element apiOperationDefElement = apiOperationDetailElement.element("apiOperationDef");
if (apiOperationDefElement == null) {
throw new Exception("apiOperationDef not found for: " + functionName);
}
Element apiReturnElement = apiOperationDefElement.element("apiReturn");
if (apiReturnElement != null) {
Element apiTypeElement = apiOperationDefElement.element("apiType");
if (apiTypeElement != null) {
String apiTypeValue = apiTypeElement.attributeValue("value");
if (apiTypeValue.startsWith("Vector$")) {
String[] parts = apiTypeValue.split("\\$");
String vectorItemType = parts[1];
collectImport(vectorItemType, forPackage, result);
}
}
Element apiOperationClassifierElement = apiReturnElement.element("apiOperationClassifier");
if (apiOperationClassifierElement != null) {
String returnType = apiOperationClassifierElement.getTextTrim();
collectImport(returnType, forPackage, result);
}
}
List<Element> apiParamElements = apiOperationDefElement.elements("apiParam");
for (Element apiParamElement : apiParamElements) {
Element apiTypeElement = apiParamElement.element("apiType");
if (apiTypeElement != null) {
String apiTypeValue = apiTypeElement.attributeValue("value");
if (apiTypeValue.startsWith("Vector$")) {
String[] parts = apiTypeValue.split("\\$");
String vectorItemType = parts[1];
collectImport(vectorItemType, forPackage, result);
}
}
Element apiOperationClassifierElement = apiParamElement.element("apiOperationClassifier");
if (apiOperationClassifierElement != null) {
String paramType = apiOperationClassifierElement.getTextTrim();
collectImport(paramType, forPackage, result);
}
}
}
if ("apiConstructor".equals(elementName)) {
String functionName = element.element("apiName").getTextTrim();
Element aapiConstructorDetailElement = element.element("apiConstructorDetail");
if (aapiConstructorDetailElement == null) {
throw new Exception("apiConstructor not found for: " + functionName);
}
Element apiConstructorDefElement = aapiConstructorDetailElement.element("apiConstructorDef");
if (apiConstructorDefElement == null) {
throw new Exception("apiConstructorDef not found for: " + functionName);
}
List<Element> apiParamElements = apiConstructorDefElement.elements("apiParam");
for (Element apiParamElement : apiParamElements) {
Element apiTypeElement = apiParamElement.element("apiType");
if (apiTypeElement != null) {
String apiTypeValue = apiTypeElement.attributeValue("value");
if (apiTypeValue.startsWith("Vector$")) {
String[] parts = apiTypeValue.split("\\$");
String vectorItemType = parts[1];
collectImport(vectorItemType, forPackage, result);
}
}
Element apiOperationClassifierElement = apiParamElement.element("apiOperationClassifier");
if (apiOperationClassifierElement != null) {
String paramType = apiOperationClassifierElement.getTextTrim();
collectImport(paramType, forPackage, result);
}
}
}
if ("apiValue".equals(elementName)) {
String variableName = element.element("apiName").getTextTrim();
Element apiValueDetailElement = element.element("apiValueDetail");
if (apiValueDetailElement == null) {
throw new Exception("apiValueDetail not found for: " + variableName);
}
Element apiValueDefElement = apiValueDetailElement.element("apiValueDef");
if (apiValueDefElement == null) {
throw new Exception("apiValueDef not found for: " + variableName);
}
Element apiValueClassifierElement = apiValueDefElement.element("apiValueClassifier");
if (apiValueClassifierElement != null) {
String variableType = apiValueClassifierElement.getTextTrim();
collectImport(variableType, forPackage, result);
}
}
}
private void writeImports(Set<String> imports, StringBuilder builder) {
for (String importName : imports) {
builder.append("\t");
builder.append("import ");
builder.append(importName);
builder.append(";");
builder.append("\n");
}
if (imports.size() > 0) {
builder.append("\n");
}
}
}