/*******************************************************************************
* Copyright 2012 Pearson Education
*
* Licensed 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.semantictools.gwt.generator;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.semantictools.frame.api.DatatypeUtil;
import org.semantictools.frame.api.TypeManager;
import org.semantictools.frame.model.BindVocabulary;
import org.semantictools.frame.model.Datatype;
import org.semantictools.frame.model.Enumeration;
import org.semantictools.frame.model.Field;
import org.semantictools.frame.model.Frame;
import org.semantictools.frame.model.NamedIndividual;
import org.semantictools.frame.model.OntologyInfo;
import org.semantictools.frame.model.RdfType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.vocabulary.OWL;
public class GwtTypeGenerator {
private static Logger logger = LoggerFactory.getLogger(GwtTypeGenerator.class);
private TypeManager typeManager;
private WriterFactory writerFactory;
private Map<String, String> uri2JavaName = new HashMap<String, String>();
private Map<String, ClassInfo> classInfoMap = new HashMap<String, ClassInfo>();
private Map<String, ModuleInfo> moduleInfoMap = new HashMap<String, ModuleInfo>();
private GwtTypeGeneratorListener listener;
private GwtTypeConfig config;
public GwtTypeGenerator(GwtTypeConfig config, TypeManager typeManager, WriterFactory writerFactory) {
this.config = config;
this.typeManager = typeManager;
this.writerFactory = writerFactory;
}
public GwtTypeGeneratorListener getListener() {
return listener;
}
public void setListener(GwtTypeGeneratorListener listener) {
this.listener = listener;
}
public void generateAll() throws IOException {
for (Frame frame : typeManager.listFrames()) {
String uri = frame.getUri();
if (!config.includeType(uri)) {
if (listener != null) {
listener.ignoreType(uri);
}
continue;
}
if (listener != null) {
listener.beginType(uri);
}
if (frame.canAsEnumeration()) {
generateEnum(frame.asEnumeration());
} else {
generateFrame(frame);
}
}
generateModules();
}
private void generateModules() throws IOException {
for (ModuleInfo module : moduleInfoMap.values()) {
generateModule(module);
}
}
private void generateModule(ModuleInfo module) throws IOException {
String path = module.getModuleName().replace(".", "/") + ".gwt.xml";
PrintWriter writer = writerFactory.getPrintWriter(path);
try {
ModuleWriter worker = new ModuleWriter(writer, module);
worker.printModule();
} finally {
writer.close();
}
}
private void generateEnum(Enumeration type) throws IOException {
logger.debug("Generate... " + type.getUri());
ClassInfo info = getClassInfo(type);
String filePath = info.getJavaName().replace('.', '/')+".java";
PrintWriter out = writerFactory.getPrintWriter(filePath);
try {
ClassWriter writer = new ClassWriter(out, info);
writer.printEnum();
} finally {
out.close();
}
}
private void generateFrame(Frame frame) throws IOException {
logger.debug("Generate... " + frame.getUri());
ClassInfo info = getClassInfo(frame);
collectImports(info);
String filePath = info.getJavaName().replace('.', '/')+".java";
PrintWriter out = writerFactory.getPrintWriter(filePath);
try {
ClassWriter writer = new ClassWriter(out, info);
writer.printClass();
} finally {
out.close();
}
}
private void collectImports(ClassInfo info) {
info.addImport("com.google.gwt.core.client.JavaScriptObject");
if (info.hasSingleSupertype()) {
RdfType superType = info.getSingleSupertype();
ClassInfo superInfo = getClassInfo(superType);
info.addImport(superInfo);
}
Frame frame = info.getRdfType().asFrame();
String frameURI = frame.getUri();
List<Field> fieldList = frame.getDeclaredFields();
for (Field field : fieldList) {
RdfType type = field.getRdfType();
String fieldName = field.getLocalName();
if (field.getMaxCardinality() != 1 && !config.excludeProperty(frameURI, fieldName)) {
info.addImport("com.google.gwt.core.client.JsArray");
}
if (type.canAsFrame()) {
String typeURI = type.getUri();
if (
!config.includeType(typeURI) ||
config.useJavaScriptObject(frameURI, fieldName)
) {
continue;
}
ClassInfo fieldInfo = getClassInfo(type);
info.addImport(fieldInfo);
}
}
}
private ClassInfo getClassInfo(RdfType frame) {
ClassInfo info = classInfoMap.get(frame.getUri());
if (info == null) {
ModuleInfo moduleInfo = getModuleInfo(frame.getNamespace());
String packageName = moduleInfo.getPackageName() + ".client";
info = new ClassInfo(moduleInfo, packageName, frame);
classInfoMap.put(frame.getUri(), info);
}
return info;
}
private ModuleInfo getModuleInfo(String namespaceURI) {
ModuleInfo info = moduleInfoMap.get(namespaceURI);
if (info == null) {
String modulePackage = toJavaName(namespaceURI);
info = new ModuleInfo(modulePackage, "DataModel");
moduleInfoMap.put(namespaceURI, info);
}
return info;
}
private String toJavaName(String uri) {
String javaName = uri2JavaName.get(uri);
if (javaName == null) {
OntModel model = typeManager.getOntModel();
Resource resource = model.getResource(uri);
if (resource != null) {
Statement statement = resource.getProperty(BindVocabulary.javaName);
if (statement != null && statement.getObject().isLiteral()) {
javaName = statement.getString();
}
}
if (javaName == null) {
javaName = defaultJavaName(uri);
}
uri2JavaName.put(uri, javaName);
}
return javaName;
}
private List<String> listParts(String uri) {
List<String> list = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(uri, "/#");
String protocol = tokenizer.nextToken();
if (!protocol.endsWith(":")) {
list.add(protocol);
}
if (tokenizer.hasMoreTokens()) {
String domain = tokenizer.nextToken();
String[] array = domain.split("\\.");
for (int i=array.length-1; i>=0; i--) {
list.add(array[i]);
}
}
while (tokenizer.hasMoreTokens()) {
list.add(tokenizer.nextToken());
}
return list;
}
String defaultJavaName(String namespace) {
StringBuilder builder = new StringBuilder();
List<String> list = listParts(namespace);
for (String part : list) {
if (builder.length()>0) {
builder.append('.');
}
builder.append(part);
}
return builder.toString();
}
class BaseWriter {
private PrintWriter out;
private int indent = 0;
public BaseWriter(PrintWriter out) {
this.out = out;
}
protected void pushIndent() {
indent++;
}
protected void popIndent() {
indent--;
}
protected BaseWriter print(String text) {
out.print(text);
return this;
}
protected void println() {
out.println();
}
protected void println(String text) {
out.println(text);
}
protected BaseWriter indent() {
for (int i=0; i<2*indent; i++) {
out.print(' ');
}
return this;
}
protected BaseWriter indent(String text) {
indent();
return print(text);
}
}
class ModuleWriter extends BaseWriter {
private ModuleInfo moduleInfo;
public ModuleWriter(PrintWriter out, ModuleInfo info) {
super(out);
this.moduleInfo = info;
}
public void printModule() {
println("<module>");
pushIndent();
printInherits();
indent().println("<source path=\"client\"/>");
popIndent();
println("</module>");
}
private void printInherits() {
List<String> list = new ArrayList<String>( moduleInfo.getInheritSet());
Collections.sort(list);
for (String name : list) {
indent("<inherits name=\"").print(name).println("\"/>");
}
}
}
class ClassWriter extends BaseWriter {
ClassInfo info;
public ClassWriter(PrintWriter out, ClassInfo info) {
super(out);
this.info = info;
}
public void printEnum() {
print("package ").print(info.getPackageName()).println(";");
Enumeration type = info.getRdfType().asEnumeration();
println();
print("public enum ").print(type.getLocalName()).println(" {");
pushIndent();
List<NamedIndividual> list = type.getIndividualList();
for (int i=0; i<list.size()-1; i++) {
indent(list.get(i).getLocalName()).println(",");
}
if (!list.isEmpty()) {
indent().println(list.get(list.size()-1).getLocalName());
}
popIndent();
println("}");
}
public void printClass() {
print("package ").print(info.getPackageName()).println(";");
println();
printImports();
beginClass();
pushIndent();
printConstructor();
printCreateMethod();
printFields();
popIndent();
endClass();
}
private void printFields() {
String frameType = info.getRdfType().asFrame().getUri();
if (!info.hasSingleSupertype()) {
printLdContextField();
printRdfTypeField();
printResourceUriField();
}
List<Field> list = !info.hasMultipleSupertypes() ?
info.getRdfType().asFrame().getDeclaredFields() :
info.getRdfType().asFrame().listAllFields();
for (Field field : list) {
if (config.excludeProperty(frameType, field.getLocalName())) {
continue;
}
printField(field);
}
}
private void printResourceUriField() {
printResourceUriGetter();
printResourceUriSetter();
}
private void printResourceUriSetter() {
println();
indent().println("public final native void setResourceUri(String uri) /*-{");
indent().println(" this[\"@id\"] = uri;");
indent().println("}-*/;");
}
private void printResourceUriGetter() {
println();
indent().println("public final native String getResourceUri() /*-{");
indent().println(" return this[\"@id\"];");
indent().println("}-*/;");
}
private void printRdfTypeField() {
printRdfTypeGetter();
printRdfTypeSetter();
}
private void printRdfTypeSetter() {
println();
indent().println("public final native void setRdfType(String type) /*-{");
indent().println(" this[\"@type\"]=type;");
indent().println("}-*/;");
}
private void printRdfTypeGetter() {
println();
indent().println("public final native String getRdfType() /*-{");
pushIndent();
indent().println("return this[\"@type\"]");
popIndent();
indent().println("}-*/;");
}
private void printLdContextField() {
printLdContextGetter();
printLdContextSetter();
}
private void printLdContextSetter() {
println();
indent().println("public final native void setLdContextUri(String uri) /*-{");
pushIndent();
indent().println("this[\"@context\"]=uri;");
popIndent();
indent().println("}-*/;");
}
/**
public final native String getLdContextUri() {
if (typeof this["@context"] == "string") {
return this["@context"];
}
return null;
}
*/
private void printLdContextGetter() {
println();
indent().println("public final native String getLdContextUri() /*-{");
pushIndent();
indent().println("if (typeof this[\"@context\"] == \"string\") {");
indent().println(" return this[\"@context\"];");
indent().println("}");
indent().println("return null;");
popIndent();
indent().println("}-*/;");
}
private void printField(Field field) {
RdfType type = field.getRdfType();
if (type.canAsEnumeration()) {
printEnumField(field);
} else if (type.canAsFrame()) {
int maxCardinality = field.getMaxCardinality();
if (maxCardinality == 1) {
printEntityField(field);
} else {
printCollectionField(field);
}
} else if (type.canAsDatatype()) {
printDatatypeField(field);
}
}
private void printEnumField(Field field) {
printEnumGetter(field);
printEnumSetter(field);
printEnumNameGetter(field);
printEnumNameSetter(field);
}
private void printEnumSetter(Field field) {
String fieldName = field.getLocalName();
String fieldType = field.getRdfType().getLocalName();
String setter = StringUtil.setter(fieldName);
String nameSetter = setter + "Name";
println();
indent("public final void ").print(setter).print("(").print(fieldType);
println(" value) {");
pushIndent();
indent(nameSetter).println("(value.name());");
popIndent();
indent().println("}");
}
private void printEnumGetter(Field field) {
String fieldName = field.getLocalName();
String fieldType = field.getRdfType().getLocalName();
String getter = StringUtil.getter(fieldName);
String nameGetter = getter + "Name";
println();
indent("public final ").print(fieldType).print(" ").print(getter).println("() {");
pushIndent();
indent("return ").print(fieldType).print(".valueOf(").print(fieldType).print(".class, ");
print(nameGetter).println("());");
popIndent();
indent().println("};");
}
private void printEnumNameSetter(Field field) {
String fieldName = field.getLocalName();
String setter = StringUtil.setter(fieldName) + "Name";
println();
indent("private final native void ").print(setter).println("(String value) /*-{");
pushIndent();
indent("this.").print(fieldName).println(" = value;");
popIndent();
indent().println("}-*/;");
}
private void printEnumNameGetter(Field field) {
String fieldName = field.getLocalName();
String getter = StringUtil.getter(fieldName) + "Name";
println();
indent("private final native String ").print(getter).println("() /*-{");
pushIndent();
indent("return this.").print(fieldName).println(";");
popIndent();
indent().println("}-*/;");
}
private void printDatatypeField(Field field) {
printDatatypeGetter(field);
printDatatypeSetter(field);
}
private void printDatatypeSetter(Field field) {
Datatype type = field.getRdfType().asDatatype();
String typeName = DatatypeUtil.toGwtType(type);
String fieldName = field.getLocalName();
String setter = StringUtil.setter(fieldName);
println();
indent("public final native void ").print(setter).print("(");
print(typeName).println(" value) /*-{");
pushIndent();
indent("this.").print(fieldName).println(" = value;");
popIndent();
indent().println("}-*/;");
}
private void printDatatypeGetter(Field field) {
Datatype type = field.getRdfType().asDatatype();
String typeName = DatatypeUtil.toGwtType(type);
String fieldName = field.getLocalName();
String getter = StringUtil.getter(fieldName);
println();
indent("public final native ").print(typeName).print(" ").print(getter);
println("() /*-{");
pushIndent();
indent("return this.").print(fieldName).println(";");
popIndent();
indent().println("}-*/;");
}
/*
public final native JsArray<Phone> getPhone() {
if (typeof this.phone == "object") {
return new Array(this.phone);
} else if (typeof this.phone == "string") {
return { "@id" : this.phone};
}
return this.phone;
}
*/
private void printCollectionField(Field field) {
String fieldType = null;
if (OWL.Thing.getURI().equals(field.getRdfType().getUri())) {
fieldType = "JavaScriptObject";
} else {
fieldType = field.getRdfType().getLocalName();
}
printCollectionGetter(field, fieldType);
printCollectionSetter(field, fieldType);
}
private void printCollectionSetter(Field field, String fieldType) {
String fieldName = field.getLocalName();
String setter = StringUtil.setter(fieldName);
println();
indent("public final native void ").print(setter).print("(JsArray<").print(fieldType);
println("> array) /*-{");
pushIndent();
indent("this.").print(fieldName).println(" = array;");
popIndent();
indent().println("}-*/;");
}
private void printCollectionGetter(Field field, String fieldType) {
String fieldName = field.getLocalName();
String getter = StringUtil.getter(fieldName);
println();
indent("public final native JsArray<");
print(fieldType);
print("> ");
print(getter);
println("() /*-{");
pushIndent();
indent("if (typeof this.").print(fieldName).println(" == \"object\") {");
indent(" return [this.").print(fieldName).println("];");
indent("} else if (typeof this.").print(fieldName).println(" == \"string\") {");
indent(" return [{ \"@id\" : this.").print(fieldName).println("}];");
indent().println("}");
indent("return this.").print(fieldName).println(";");
popIndent();
indent().println("}-*/;");
}
private void printEntityField(Field field) {
String ownerURI = field.getDeclaringFrame().getUri();
RdfType type = field.getRdfType();
String fieldType = type.getLocalName();
String fieldName = field.getLocalName();
if (
!config.includeType(type.getUri()) ||
config.useJavaScriptObject(ownerURI, fieldName)
) {
fieldType = "JavaScriptObject";
}
printEntityGetter(field, fieldType);
printEntitySetter(field, fieldType);
printEntityUriGetter(field);
printEntityUriSetter(field);
}
private void printEntityUriSetter(Field field) {
String fieldName = field.getLocalName();
String setter = StringUtil.setter(fieldName) + "Uri";
println();
indent().print("public final native void ").print(setter).println("(String uri) /*-{");
pushIndent();
indent("this.").print(fieldName).println(" = uri;");
popIndent();
indent().println("}-*/;");
}
// public final native String getPostalAddressUri() {
// if ( typeof this.postalAddress == "string") {
// return this.postalAddress;
// }
// if (typeof this.postalAddress == "object") {
// return this.postalAddress["@id"];
// }
// return null;
// }
private void printEntityUriGetter(Field field) {
String fieldName = field.getLocalName();
String getter = StringUtil.getter(fieldName) + "Uri";
println();
indent("public final native String ").print(getter).println("() /*-{");
pushIndent();
indent("if (typeof this.").print(fieldName).println(" == \"string\") {");
indent(" return this.").print(fieldName).println(";");
indent().println("}");
indent("if (typeof this.").print(fieldName).println(" == \"object\") {");
indent(" return this.").print(fieldName).println("[\"@id\"];");
indent().println("}");
indent().println("return null;");
popIndent();
indent().println("}-*/;");
}
private void printEntitySetter(Field field, String typeName) {
String fieldName = field.getLocalName();
String setter = StringUtil.setter(fieldName);
println();
indent("public final native void ").print(setter).print("(");
print(typeName).println(" value) /*-{");
pushIndent();
indent("this.").print(fieldName).println(" = value;");
popIndent();
indent().println("}-*/;");
}
private void printEntityGetter(Field field, String fieldType) {
String fieldName = field.getLocalName();
String getter = StringUtil.getter(fieldName);
println();
indent("public final native ");
print(fieldType);
print(" ");
print(getter);
println("() /*-{");
pushIndent();
indent("if (typeof this.").print(fieldName).println(" == \"string\") {");
pushIndent();
indent("return { \"@id\" : this.").print(fieldName).println("};");
popIndent();
indent().println("}");
indent("return this.");
print(fieldName);
println(";");
popIndent();
indent().println("}-*/;");
}
private void printCreateMethod() {
println();
indent("public static ");
print(info.getRdfType().getLocalName());
println(" create() {");
pushIndent();
indent().println("return JavaScriptObject.createObject().cast();");
popIndent();
indent().println("}");
}
private void printConstructor() {
println();
indent("protected ");
print(info.getRdfType().getLocalName());
println("() {}");
}
private void beginClass() {
println();
print("public class ");
print(info.getRdfType().getLocalName());
if (
info.hasSingleSupertype() &&
config.includeType(info.getSingleSupertype().getUri())
) {
print(" extends ");
String superTypeName = info.getSingleSupertype().getLocalName();
print(superTypeName);
println(" {");
} else {
println(" extends JavaScriptObject {");
}
}
private void endClass() {
println("}");
}
private void printImports() {
for (String pkg : info.listImports()) {
print("import ").print(pkg).println(";");
}
}
}
static class ModuleInfo {
private String packageName;
private String localName;
private String moduleName;
private Set<String> inheritSet = new HashSet<String>();
public ModuleInfo(String packageName, String localName) {
this.packageName = packageName;
this.localName = localName;
this.moduleName = packageName + "." + localName;
inheritSet.add("com.google.gwt.user.User");
inheritSet.add("com.google.gwt.json.JSON");
}
public String getPackageName() {
return packageName;
}
public String getLocalName() {
return localName;
}
public String getModuleName() {
return moduleName;
}
public void addInherits(ModuleInfo info) {
if (info != this) {
inheritSet.add(info.getModuleName());
}
}
public Set<String> getInheritSet() {
return inheritSet;
}
}
static class ClassInfo {
private ModuleInfo moduleInfo;
private String packageName;
private String javaName;
private RdfType rdfType;
private Set<String> imports = new HashSet<String>();
private List<Frame> supertypeList = null;
public ClassInfo(ModuleInfo moduleInfo, String packageName, RdfType rdfType) {
this.moduleInfo = moduleInfo;
this.packageName = packageName;
this.rdfType = rdfType;
javaName = packageName + "." + rdfType.getLocalName();
}
public ModuleInfo getModuleInfo() {
return moduleInfo;
}
public List<String> listImports() {
List<String> list = new ArrayList<String>(imports);
Collections.sort(list);
return list;
}
public List<Frame> listSupertypes() {
if (supertypeList == null) {
// Filter standard RDF types from the type hierarchy.
List<Frame> sourceList = rdfType.asFrame().getSupertypeList();
supertypeList = new ArrayList<Frame>();
for (Frame frame : sourceList) {
String uri = frame.getUri();
if (!"http://www.w3.org/2000/01/rdf-schema#Resource".equals(uri)) {
supertypeList.add(frame);
}
}
}
return supertypeList;
}
public boolean hasSingleSupertype() {
return listSupertypes().size()==1;
}
public boolean hasMultipleSupertypes() {
return listSupertypes().size()>1;
}
public Frame getSingleSupertype() {
List<Frame> list = listSupertypes();
return (list.size()>0) ? list.get(0) : null;
}
public String getPackageName() {
return packageName;
}
public String getJavaName() {
return javaName;
}
public RdfType getRdfType() {
return rdfType;
}
public void addImport(String className) {
if (className.startsWith(packageName)) {
if (className.indexOf('.', packageName.length()) > 0) {
return;
}
}
imports.add(className);
}
public void addImport(ClassInfo importedClass) {
if (importedClass.getModuleInfo() != moduleInfo) {
String javaName = importedClass.getJavaName();
imports.add(javaName);
moduleInfo.addInherits(importedClass.getModuleInfo());
}
}
}
}