-
Johannes Mey authoredJohannes Mey authored
Backend.jadd 62.25 KiB
aspect BackendAbstractGrammar {
public static String ASTNode.listClass = "ArrayList";
public static String ASTNode.jastAddListType = "List";
public static boolean ASTNode.resolverHelper = false;
public static boolean ASTNode.serializer = false;
public static boolean ASTNode.useJastAddNames = false;
public String Program.generateAbstractGrammar() {
StringBuilder sb = new StringBuilder();
generateAbstractGrammar(sb);
return sb.toString();
}
public void Program.generateAbstractGrammar(StringBuilder sb) {
for (TypeDecl td: getTypeDecls()) {
td.generateAbstractGrammar(sb);
}
}
public void TypeDecl.generateUnresolvedClass(StringBuilder sb) {
if (getAbstract()) {
sb.append(ind(1) + "abstract ");
} else {
sb.append(ind(1));
}
sb.append("class " + "Unresolved$" + getID() + " extends " + getID() + " implements Unresolved$Node {\n");
sb.append(ind(2) + "private String unresolved$Token;\n");
sb.append(ind(2) + "public String getUnresolved$Token() {\n");
sb.append(ind(3) + "return unresolved$Token;\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "void setUnresolved$Token(String token) {\n");
sb.append(ind(3) + "this.unresolved$Token = token;\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "private boolean unresolved$ResolveOpposite;\n");
sb.append(ind(2) + "public boolean getUnresolved$ResolveOpposite() {\n");
sb.append(ind(3) + "return unresolved$ResolveOpposite;\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "void setUnresolved$ResolveOpposite(boolean resolveOpposite) {\n");
sb.append(ind(3) + "this.unresolved$ResolveOpposite = resolveOpposite;\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "Unresolved$Node " + getID() + ".as$Unresolved() {\n");
sb.append(ind(2) + "return null;\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "Unresolved$Node Unresolved$" + getID() + ".as$Unresolved() {\n");
sb.append(ind(2) + "return this;\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "boolean " + getID() + ".is$Unresolved() {\n");
sb.append(ind(2) + "return false;\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "boolean Unresolved$" + getID() + ".is$Unresolved() {\n");
sb.append(ind(2) + "return true;\n");
sb.append(ind(1) + "}\n");
}
public void TypeDecl.generateAbstractGrammar(StringBuilder sb) {
if (getAbstract()) {
sb.append("abstract ");
}
sb.append(getID());
if (hasSuper()) {
sb.append(" : " + getSuper());
}
if (getNumComponent() > 0 || relationComponents().size() > 0) {
sb.append(" ::=");
}
for (Component c: getComponents()) {
sb.append(" ");
sb.append(c.generateAbstractGrammar());
}
for (RelationComponent c: relationComponents()) {
sb.append(" ");
sb.append(c.generateAbstractGrammar());
}
sb.append(";\n");
}
public String Component.generateAbstractGrammar() {
if (getID().equals(getTypeUse().toString())) {
return getTypeUse().toString();
} else {
return getID() + ":" + getTypeUse();
}
}
public String ListComponent.generateAbstractGrammar() {
return super.generateAbstractGrammar() + "*";
}
public String OptComponent.generateAbstractGrammar() {
return "[" + super.generateAbstractGrammar() + "]";
}
public String NTAComponent.generateAbstractGrammar() {
return "/" + super.generateAbstractGrammar() + "/";
}
public String TokenComponent.generateAbstractGrammar() {
return "<" + getID() + ":" + getTypeUse() + ">";
}
public String RelationComponent.generateAbstractGrammar() {
return "<" + getImplAttributeName() + ":" + ofTypeDecl() + ">";
}
public String ManyRelationComponent.generateAbstractGrammar() {
return "<" + getImplAttributeName() + ":" + ASTNode.listClass + "<" + ofTypeDecl() + ">>";
}
public String RelationComponent.getImplAttributeName() {
return "_impl_" + getID();
}
public String RelationComponent.getImplAttributeField() {
// tt.bind("TypeInSignature", ASTNode.convTypeNameToSignature(type()));
return "token" + ofTypeDecl() + "__impl_" + getID();
}
public String ManyRelationComponent.getImplAttributeField() {
// tt.bind("TypeInSignature", ASTNode.convTypeNameToSignature(type()));
return "token" + listClass + "_" + ofTypeDecl() + "___impl_" + getID();
}
}
aspect BackendAspect {
public String Program.generateAspect() {
StringBuilder sb = new StringBuilder();
generateAspect(sb);
return sb.toString();
}
public void Program.generateAspect(StringBuilder sb) {
sb.append("import java.util.ArrayList;\n");
sb.append("import java.util.Collections;\n");
sb.append("import java.time.Instant;\n");
sb.append("import java.time.Period;\n");
sb.append("aspect RelAstAPI {\n");
for (TypeDecl td: getTypeDecls()) {
if (td.needsConstructor()) {
td.generateConstructor(sb);
}
}
for (Relation r: getRelations()) {
r.generateAPI(sb);
}
generateLowerBoundCheck(sb);
sb.append(ind(1) + "public static void ASTNode.assertNotNull(Object obj) {\n");
sb.append(ind(2) + "if (obj == null) {\n");
sb.append(ind(3) + "throw new NullPointerException();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
sb.append("}\n");
}
public void TypeDecl.generateConstructor(StringBuilder sb) {
sb.append(ind(1) + "public " + getID() + "." + getID() + "(");
int i = 0;
for (Component c: componentsTransitive()) {
sb.append(c.constructorParameter());
if (++i < componentsTransitive().size()) {
sb.append(", ");
}
}
sb.append(") {\n");
for (Component c: componentsTransitive()) {
sb.append(ind(2) + c.constructorSetMethod() + "(" + c.getID() + ");\n");
}
sb.append(ind(1) + "}\n");
}
public String Component.constructorParameter() {
return getTypeUse() + " " + getID();
}
public String ListComponent.constructorParameter() {
return ASTNode.jastAddListType + "<" + getTypeUse() + "> " + getID();
}
public String OptComponent.constructorParameter() {
return "Opt<" + getTypeUse() + "> " + getID();
}
public String Component.constructorSetMethod() {
return "set" + getID();
}
public String ListComponent.constructorSetMethod() {
return "set" + getID() + "List";
}
public String OptComponent.constructorSetMethod() {
return "set" + getID() + "Opt";
}
}
aspect BackendAPI {
public void Relation.generateAPI(StringBuilder sb) {
sb.append(ind(1) + "// " + prettyPrint() + "\n");
getDirection().generateAPI(sb);
sb.append("\n");
}
public abstract void Direction.generateAPI(StringBuilder sb);
inh Relation Direction.relation();
eq Relation.getChild().relation() = this;
eq Program.getChild().relation() = null;
public String RelationComponent.nameCapitalized() {
return name().substring(0,1).toUpperCase() + name().substring(1);
}
}
aspect BackendDirectedAPI {
public void RightDirection.generateAPI(StringBuilder sb) {
relation().getLeft().generateDirectedAPI(sb);
}
public abstract void RelationComponent.generateDirectedAPI(StringBuilder sb);
public void OneRelationComponent.generateDirectedAPI(StringBuilder sb) {
generateDirectedZeroOneAPI(sb, false);
}
public void OptionalRelationComponent.generateDirectedAPI(StringBuilder sb) {
generateDirectedZeroOneAPI(sb, true);
generateExtraOptAPI(sb);
}
public void RelationComponent.generateDirectedZeroOneAPI(StringBuilder sb, boolean optional) {
// Get
generateGetOne(sb);
// Set
sb.append(ind(1) + "public " + toTypeDecl() + " " + toTypeDecl());
sb.append(".set" + nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
if (!optional) {
sb.append(ind(2) + "assertNotNull(o);\n");
}
sb.append(ind(2) + "set" + getImplAttributeName() + "(o);\n");
sb.append(ind(2) + "return this;\n");
sb.append(ind(1) + "}\n");
}
public void ManyRelationComponent.generateDirectedAPI(StringBuilder sb) {
// Get
sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl() + ".");
if (useJastAddNames) {
// getXs
sb.append("get" + nameCapitalized() + "s() {\n");
sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
sb.append(ind(1) + "}\n");
// getXList
sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
sb.append(".get" + nameCapitalized() + "List() {\n");
} else {
sb.append(name() + "() {\n");
}
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> l = get" + getImplAttributeName() + "();\n");
// resolve the entire list
if (resolverHelper | serializer) {
sb.append(ind(2) + "if (l != null) {\n");
sb.append(ind(3) + "boolean changed = false;\n");
sb.append(ind(3) + "for (int i = 0; i < l.size(); i++) {\n");
sb.append(ind(4) + ofTypeDecl() + " element = l.get(i);\n");
sb.append(ind(4) + "if (element.is$Unresolved()) {\n");
sb.append(ind(5) + "changed = true;\n");
sb.append(ind(5) + ofTypeDecl() + " resolvedElement = resolve" + nameCapitalized() + "ByToken(element.as$Unresolved().getUnresolved$Token(), i);\n");
sb.append(ind(5) + "l.set(i, resolvedElement);\n");
sb.append(ind(4) + "}\n");
sb.append(ind(3) + "}\n");
sb.append(ind(3) + "if (changed) {\n");
sb.append(ind(4) + "set" + getImplAttributeName() + "(l);\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
}
sb.append(ind(2) + "return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();\n");
sb.append(ind(1) + "}\n");
// Add
sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
if (!useJastAddNames) {
sb.append("To");
}
sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list == null) {\n");
sb.append(ind(3) + "list = new " + ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "list.add(o);\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(1) + "}\n");
// Insert / add at specific position
sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
if (!useJastAddNames) {
sb.append("To");
}
sb.append(nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list == null) {\n");
sb.append(ind(3) + "list = new " + ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "list.add(index, o);\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(1) + "}\n");
// Remove
sb.append(ind(1) + "public void " + toTypeDecl() + ".remove");
if (!useJastAddNames) {
sb.append("From");
}
sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list != null && list.remove(o)) {\n");
sb.append(ind(3) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
}
public void RelationComponent.generateGetOne(StringBuilder sb) {
sb.append(ind(1) + "public " + ofTypeDecl() + " " + toTypeDecl() + ".");
if (useJastAddNames) {
sb.append("get" + nameCapitalized());
} else {
sb.append(name());
}
sb.append("() {\n");
if (resolverHelper | serializer) {
sb.append(ind(2) + "if (" + getImplAttributeField() + " != null && " + getImplAttributeField() + ".is$Unresolved()) {\n");
sb.append(ind(3) + "if (" + getImplAttributeField() + ".as$Unresolved().getUnresolved$ResolveOpposite()) {\n");
sb.append(ind(4) + "set" + nameCapitalized() + "(resolve" + nameCapitalized() + "ByToken(" + getImplAttributeField() + ".as$Unresolved().getUnresolved$Token()));\n");
sb.append(ind(3) + "} else {\n");
sb.append(ind(4) + "set" + getImplAttributeName() + "(resolve" + nameCapitalized() + "ByToken(" + getImplAttributeField() + ".as$Unresolved().getUnresolved$Token()));\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
}
sb.append(ind(2) + "return get" + getImplAttributeName() + "();\n");
sb.append(ind(1) + "}\n");
}
public void RelationComponent.generateExtraOptAPI(StringBuilder sb) {
// has
sb.append(ind(1) + "public boolean " + toTypeDecl());
sb.append(".has" + nameCapitalized() + "() {\n");
sb.append(ind(2) + "return ");
if (useJastAddNames) {
sb.append("get" + nameCapitalized());
} else {
sb.append(name());
}
sb.append("() != null;\n");
sb.append(ind(1) + "}\n");
// clear
sb.append(ind(1) + "public void " + toTypeDecl());
sb.append(".clear" + nameCapitalized() + "() {\n");
sb.append(ind(2) + "set" + nameCapitalized() + "(null);\n");
sb.append(ind(1) + "}\n");
}
}
aspect BackendBidirectionalAPI {
public void Bidirectional.generateAPI(StringBuilder sb) {
RelationComponent l = relation().getLeft();
RelationComponent r = relation().getRight();
if (l.multiplicityOne()) {
if (r.multiplicityOne()) {
l.generateBiOneOne(sb, false);
r.generateBiOneOne(sb, false);
} else if (r.multiplicityOpt()) {
l.generateBiOneOne(sb, false);
r.generateBiOneOne(sb, true);
} else if (r.multiplicityMany()) {
l.generateBiOneMany(sb, false);
r.generateBiManyOne(sb, l);
}
} else if (l.multiplicityOpt()) {
if (r.multiplicityOne()) {
l.generateBiOneOne(sb, true);
r.generateBiOneOne(sb, false);
} else if (r.multiplicityOpt()) {
l.generateBiOneOne(sb, true);
r.generateBiOneOne(sb, true);
} else if (r.multiplicityMany()) {
l.generateBiOneMany(sb, true);
r.generateBiManyOne(sb, l);
}
} else if (l.multiplicityMany()) {
if (r.multiplicityOne()) {
l.generateBiManyOne(sb, r);
r.generateBiOneMany(sb, false);
} else if (r.multiplicityOpt()) {
l.generateBiManyOne(sb, r);
r.generateBiOneMany(sb, true);
} else if (r.multiplicityMany()) {
l.generateBiManyMany(sb, r);
r.generateBiManyMany(sb, l);
}
}
}
public void RelationComponent.generateBiOneOne(StringBuilder sb, boolean isOpt) {
// Get
generateGetOne(sb);
// Set
sb.append(ind(1) + "public " + toTypeDecl() + " " + toTypeDecl());
sb.append(".set" + nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
if (!isOpt) {
sb.append(ind(2) + "assertNotNull(o);\n");
}
// unset the old opposite
sb.append(ind(2) + "if (" + getImplAttributeField() + " != null) {\n");
sb.append(ind(3) + "" + getImplAttributeField() + ".set" + otherSide().getImplAttributeName() + "(null);\n");
sb.append(ind(2) + "}\n");
if (resolverHelper | serializer) {
sb.append(ind(2) + "if (o != null && !o.is$Unresolved() && o." + otherSide().getImplAttributeField() + " != null) {\n");
} else {
sb.append(ind(2) + "if (o != null && o." + otherSide().getImplAttributeField() + " != null) {\n");
}
sb.append(ind(3) + "o." + otherSide().getImplAttributeField() + ".set" + getImplAttributeName() + "(null);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(o);\n");
if (resolverHelper | serializer) {
sb.append(ind(2) + "if (o == null || !o.is$Unresolved()) {\n");
if (isOpt) {
sb.append(ind(3) + "if (o != null) {\n");
sb.append(ind(4) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
sb.append(ind(3) + "}\n");
} else {
sb.append(ind(3) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
}
sb.append(ind(2) + "}\n");
} else {
if (isOpt) {
sb.append(ind(2) + "if (o != null) {\n");
sb.append(ind(3) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
sb.append(ind(2) + "}\n");
} else {
sb.append(ind(2) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
}
}
sb.append(ind(2) + "return this;\n");
sb.append(ind(1) + "}\n");
if (isOpt) {
generateExtraOptAPI(sb);
}
}
public void RelationComponent.generateBiManyMany(StringBuilder sb, RelationComponent opposite) {
// Get
sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl() + ".");
if (useJastAddNames) {
// getXs
sb.append("get" + nameCapitalized() + "s() {\n");
sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
sb.append(ind(1) + "}\n");
// getXList
sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
sb.append(".get" + nameCapitalized() + "List() {\n");
} else {
sb.append(name() + "() {\n");
}
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> l = get" + getImplAttributeName() + "();\n");
// resolve the entire list
if (resolverHelper | serializer) {
sb.append(ind(2) + "if (l != null) {\n");
sb.append(ind(3) + "boolean changed = false;\n");
sb.append(ind(3) + "for (int i = 0; i < l.size(); i++) {\n");
sb.append(ind(4) + ofTypeDecl() + " element = l.get(i);\n");
sb.append(ind(4) + "if (element.is$Unresolved()) {\n");
sb.append(ind(5) + "changed = true;\n");
sb.append(ind(5) + ofTypeDecl() + " resolvedElement = resolve" + nameCapitalized() + "ByToken(element.as$Unresolved().getUnresolved$Token(), i);\n");
sb.append(ind(5) + "if (resolvedElement != null && element.as$Unresolved().getUnresolved$ResolveOpposite()) {\n");
sb.append(ind(6) + ASTNode.listClass + "<" + toTypeDecl() + "> otherList = resolvedElement." + opposite.getImplAttributeField() + ";\n");
sb.append(ind(6) + "if (otherList == null) {\n");
sb.append(ind(7) + "otherList = new " + listClass + "<>();\n");
sb.append(ind(6) + "}\n");
sb.append(ind(6) + "otherList.add(this);\n");
sb.append(ind(6) + "resolvedElement.set" + opposite.getImplAttributeName() + "(otherList);\n");
sb.append(ind(5) + "}\n");
sb.append(ind(5) + "l.set(i, resolvedElement);\n");
sb.append(ind(4) + "}\n");
sb.append(ind(3) + "}\n");
sb.append(ind(3) + "if (changed) {\n");
sb.append(ind(4) + "set" + getImplAttributeName() + "(l);\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
}
sb.append(ind(2) + "return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();\n");
sb.append(ind(1) + "}\n");
// Add
sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
if (!useJastAddNames) {
sb.append("To");
}
sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list == null) {\n");
sb.append(ind(3) + "list = new " + ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + ASTNode.listClass + "<" + otherSide().ofTypeDecl() + "> list2 = o." + otherSide().getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list2 == null) {\n");
sb.append(ind(3) + "list2 = new "+ ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "list.add(o);\n");
sb.append(ind(2) + "list2.add(this);\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(2) + "o.set" + otherSide().getImplAttributeName() + "(list2);\n");
sb.append(ind(1) + "}\n");
// Insert / add at specific position
sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
if (!useJastAddNames) {
sb.append("To");
}
sb.append(nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list == null) {\n");
sb.append(ind(3) + "list = new " + ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + ASTNode.listClass + "<" + otherSide().ofTypeDecl() + "> list2 = o."
+ otherSide().getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list2 == null) {\n");
sb.append(ind(3) + "list2 = new "+ ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "list.add(index, o);\n");
sb.append(ind(2) + "list2.add(this);\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(2) + "o.set" + otherSide().getImplAttributeName() + "(list2);\n");
sb.append(ind(1) + "}\n");
// Remove
sb.append(ind(1) + "public void " + toTypeDecl() + ".remove");
if (!useJastAddNames) {
sb.append("From");
}
sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list != null && list.remove(o)) {\n");
sb.append(ind(3) + ASTNode.listClass + "<" + otherSide().ofTypeDecl() + "> list2 = o."
+ otherSide().getImplAttributeField() + ";\n");
sb.append(ind(3) + "if (list2 != null) list2.remove(this);\n");
sb.append(ind(3) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(3) + "o.set" + otherSide().getImplAttributeName() + "(list2);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
}
public void RelationComponent.generateBiManyOne(StringBuilder sb, RelationComponent opposite) {
// Get
sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl() + ".");
if (useJastAddNames) {
// getXs
sb.append("get" + nameCapitalized() + "s() {\n");
sb.append(ind(2) + "return get" + nameCapitalized() + "List();\n");
sb.append(ind(1) + "}\n");
// getXList
sb.append(ind(1) + "public java.util.List<" + ofTypeDecl() + "> " + toTypeDecl());
sb.append(".get" + nameCapitalized() + "List() {\n");
} else {
sb.append(name() + "() {\n");
}
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> l = get" + getImplAttributeName() + "();\n");
// resolve the entire list
if (resolverHelper | serializer) {
sb.append(ind(2) + "if (l != null) {\n");
sb.append(ind(3) + "boolean changed = false;\n");
sb.append(ind(3) + "for (int i = 0; i < l.size(); i++) {\n");
sb.append(ind(4) + ofTypeDecl() + " element = l.get(i);\n");
sb.append(ind(4) + "if (element.is$Unresolved()) {\n");
sb.append(ind(5) + "changed = true;\n");
sb.append(ind(5) + ofTypeDecl() + " resolvedElement = resolve" + nameCapitalized() + "ByToken(element.as$Unresolved().getUnresolved$Token(), i);\n");
sb.append(ind(5) + "if (element.as$Unresolved().getUnresolved$ResolveOpposite()) {\n");
sb.append(ind(6) + toTypeDecl() + " oldTarget = resolvedElement." + opposite.getImplAttributeField() + ";\n");
sb.append(ind(6) + "if (oldTarget != null && oldTarget != this) {\n");
sb.append(ind(7) + "oldTarget." + getImplAttributeField() + ".remove(resolvedElement);\n");
sb.append(ind(6) + "}\n");
sb.append(ind(6) + "if (oldTarget == this) {\n");
sb.append(ind(7) + "l.remove(i);\n");
sb.append(ind(7) + "i--;\n");
sb.append(ind(6) + "} else {\n");
sb.append(ind(7) + "resolvedElement.set" + opposite.getImplAttributeName() + "(this);\n");
sb.append(ind(7) + "l.set(i, resolvedElement);\n");
sb.append(ind(6) + "}\n");
sb.append(ind(5) + "} else {\n");
sb.append(ind(6) + "l.set(i, resolvedElement);\n");
sb.append(ind(5) + "}\n");
sb.append(ind(4) + "}\n");
sb.append(ind(3) + "}\n");
sb.append(ind(3) + "if (changed) {\n");
sb.append(ind(4) + "set" + getImplAttributeName() + "(l);\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
}
sb.append(ind(2) + "return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();\n");
sb.append(ind(1) + "}\n");
// Add
sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
if (!useJastAddNames) {
sb.append("To");
}
sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + "if (o != null && o." + otherSide().getImplAttributeField() + " != null) {\n");
sb.append(ind(3) + ASTNode.listClass + "<" + ofTypeDecl() + "> list2 = o."
+ otherSide().getImplAttributeField() + "." + getImplAttributeField() + ";\n");
sb.append(ind(3) + "if (list2.remove(o))\n");
sb.append(ind(4) + "o." + otherSide().getImplAttributeField()
+ ".set" + getImplAttributeName() + "(list2);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list == null) {\n");
sb.append(ind(3) + "list = new " + ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "list.add(o);\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(2) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
sb.append(ind(1) + "}\n");
// Insert / add at specific position
sb.append(ind(1) + "public void " + toTypeDecl() + ".add");
if (!useJastAddNames) {
sb.append("To");
}
sb.append(nameCapitalized() + "(int index, " + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + "if (o != null && o." + otherSide().getImplAttributeField() + " != null) {\n");
sb.append(ind(3) + ASTNode.listClass + "<" + ofTypeDecl() + "> list2 = o." + otherSide().getImplAttributeField() + "." + getImplAttributeField() + ";\n");
sb.append(ind(3) + "if (list2.remove(o))\n");
sb.append(ind(4) + "o." + otherSide().getImplAttributeField() + ".set" + getImplAttributeName() + "(list2);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list == null) {\n");
sb.append(ind(3) + "list = new " + ASTNode.listClass + "<>();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "list.add(index, o);\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(2) + "o.set" + otherSide().getImplAttributeName() + "(this);\n");
sb.append(ind(1) + "}\n");
// Remove
sb.append(ind(1) + "public void " + toTypeDecl() + ".remove");
if (!useJastAddNames) {
sb.append("From");
}
sb.append(nameCapitalized() + "(" + ofTypeDecl() + " o) {\n");
sb.append(ind(2) + "assertNotNull(o);\n");
sb.append(ind(2) + ASTNode.listClass + "<" + ofTypeDecl() + "> list = " + getImplAttributeField() + ";\n");
sb.append(ind(2) + "if (list != null && list.remove(o)) {\n");
sb.append(ind(3) + "set" + getImplAttributeName() + "(list);\n");
sb.append(ind(3) + "if (o." + otherSide().getImplAttributeField() + " == this) {\n");
sb.append(ind(4) + "o.set" + otherSide().getImplAttributeName() + "(null);\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
}
public void RelationComponent.generateBiOneMany(StringBuilder sb, boolean isOpt) {
// Get
generateGetOne(sb);
// Set
sb.append(ind(1) + "public " + toTypeDecl() + " " + toTypeDecl() + ".set" + nameCapitalized()
+ "(" + ofTypeDecl() + " o) {\n");
if (!isOpt) {
sb.append(ind(2) + "assertNotNull(o);\n");
}
sb.append(ind(2) + "if (" + getImplAttributeField() + " != null) {\n");
sb.append(ind(3) + ASTNode.listClass + "<" + toTypeDecl() + "> list2 = " + getImplAttributeField()
+ "." + otherSide().getImplAttributeField() + ";\n");
sb.append(ind(3) + "list2.remove(this);\n");
sb.append(ind(3) + getImplAttributeField() + "." + "set"
+ otherSide().getImplAttributeName() + "(list2);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "set" + getImplAttributeName() + "(o);\n");
int ind = isOpt ? 3 : 2;
if (isOpt) {
sb.append(ind(2) + "if (o != null) {\n");
}
sb.append(ind(ind) + ASTNode.listClass + "<" + toTypeDecl() + "> list = o."
+ otherSide().getImplAttributeField() + ";\n");
sb.append(ind(ind) + "if (list == null) {\n");
sb.append(ind(ind+1) + "list = new " + ASTNode.listClass + "<>();\n");
sb.append(ind(ind) + "}\n");
sb.append(ind(ind) + "list.add(this);\n");
sb.append(ind(ind) + "o.set" + otherSide().getImplAttributeName() + "(list);\n");
if (isOpt) {
sb.append(ind(2) + "}\n");
}
sb.append(ind(2) + "return this;\n");
sb.append(ind(1) + "}\n");
if (isOpt) {
generateExtraOptAPI(sb);
}
}
}
aspect LowerBoundCheck {
public void Program.generateLowerBoundCheck(StringBuilder sb) {
sb.append(ind(1) + "public boolean ASTNode.violatesLowerBounds() {\n");
sb.append(ind(2) + "return !getLowerBoundsViolations().isEmpty();\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public java.util.List<Pair<ASTNode, String>> "
+ "ASTNode.getLowerBoundsViolations() {\n");
sb.append(ind(2) + "ArrayList<Pair<ASTNode, String>> list = new ArrayList<>();\n");
sb.append(ind(2) + "computeLowerBoundsViolations(list);\n");
sb.append(ind(2) + "return list;\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public void ASTNode.computeLowerBoundsViolations("
+ "java.util.List<Pair<ASTNode, String>> list) {\n");
sb.append(ind(2) + "for (int i = 0; i < getNumChildNoTransform(); i++) {\n");
sb.append(ind(3) + "getChildNoTransform(i).computeLowerBoundsViolations(list);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
for (TypeDecl td: getTypeDecls()) {
td.generateLowerBoundCheck(sb);
}
generatePairClass(sb);
}
public void TypeDecl.generateLowerBoundCheck(StringBuilder sb) {
if (!oneRelationComponents().isEmpty()) {
sb.append(ind(1) + "public void " + getID() + ".computeLowerBoundsViolations(" +
"java.util.List<Pair<ASTNode, String>> list) {\n");
for (OneRelationComponent o: oneRelationComponents()) {
o.generateLowerBoundCheck(sb);
}
sb.append(ind(2) + "super.computeLowerBoundsViolations(list);\n");
sb.append(ind(1) + "}\n");
}
}
public void OneRelationComponent.generateLowerBoundCheck(StringBuilder sb) {
sb.append(ind(2) + "if (");
if (useJastAddNames) {
sb.append("get" + nameCapitalized());
} else {
sb.append(name());
}
sb.append("() == null) {\n");
sb.append(ind(3) + "list.add(new Pair<>(this, \"" + name() + "\"));\n");
sb.append(ind(2) + "}\n");
}
public void Program.generatePairClass(StringBuilder sb) {
sb.append(ind(1) + "public class Pair<T1, T2> {\n");
sb.append(ind(2) + "public final T1 _1;\n");
sb.append(ind(2) + "public final T2 _2;\n");
// Constructor
sb.append(ind(2) + "public Pair(T1 _1, T2 _2) {\n");
sb.append(ind(3) + "ASTNode.assertNotNull(_1);\n");
sb.append(ind(3) + "ASTNode.assertNotNull(_2);\n");
sb.append(ind(3) + "this._1 = _1;\n");
sb.append(ind(3) + "this._2 = _2;\n");
sb.append(ind(2) + "}\n");
// equals
sb.append(ind(2) + "public boolean equals(Object other) {\n");
sb.append(ind(3) + "if (other instanceof Pair) {\n");
sb.append(ind(4) + "Pair<?,?> p = (Pair<?,?>) other;\n");
sb.append(ind(4) + "return _1.equals(p._1) && _2.equals(p._2);\n");
sb.append(ind(3) + "} else {\n");
sb.append(ind(4) + "return false;\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
// hashCode
sb.append(ind(2) + "public int hashCode() {\n");
sb.append(ind(3) + "return 31*_1.hashCode() + _2.hashCode();\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
}
}
aspect NameResolutionHelper {
public String Program.generateRewriteToSuperTypeStub() {
StringBuilder sb = new StringBuilder();
generateRewriteToSuperTypeStub(sb);
return sb.toString();
}
public String Program.generateResolverStubs() {
StringBuilder sb = new StringBuilder();
generateResolverStubs(sb);
return sb.toString();
}
public void Program.generateResolverStubs(StringBuilder sb) {
sb.append("aspect RefResolverStubs {\n\n");
for (Relation r: getRelations()) {
r.generateContextDependentNameResolution(sb);
}
if (resolverHelper) {
for (TypeDecl decl : getTypeDeclList()) {
decl.generateContextIndependentNameResolution(sb);
sb.append("\n");
}
}
sb.append("}\n\n");
}
public void Program.generateRewriteToSuperTypeStub(StringBuilder sb) {
sb.append("aspect ReferenceCreation {\n\n");
for (TypeDecl decl : getTypeDeclList()) {
decl.createReferenceCreator(sb);
}
sb.append("}\n\n");
sb.append("aspect ResolverTrigger {\n\n");
resolveAll(sb);
for (TypeDecl decl : getTypeDeclList()) {
decl.resolveAll(sb);
}
sb.append("}\n\n");
sb.append("aspect RefResolverHelpers {\n\n");
sb.append(ind(1) + "interface Unresolved$Node {\n");
sb.append(ind(2) + "String getUnresolved$Token();\n");
sb.append(ind(2) + "boolean getUnresolved$ResolveOpposite();\n");
sb.append(ind(1) + "}\n\n");
for (TypeDecl td: getTypeDecls()) {
if (td.needUnresolvedClass()) {
td.generateUnresolvedClass(sb);
}
}
sb.append("\n}\n");
}
public void TypeDecl.createReferenceCreator(StringBuilder sb) {
TypeDecl instantiableSubType = instantiableSubType();
if (instantiableSubType == null) {
throw new RuntimeException("unable to find instantiable subtype for " + getID());
}
sb.append(ind(1) + "public static " + getID() + " " + getID() + ".createRef(String ref) {\n");
sb.append(ind(2) + "Unresolved$" + instantiableSubType.getID() + " unresolvedNode = new Unresolved$" + instantiableSubType.getID() + "();\n");
sb.append(ind(2) + "unresolvedNode.setUnresolved$Token(ref);\n");
sb.append(ind(2) + "unresolvedNode.setUnresolved$ResolveOpposite(true);\n");
sb.append(ind(2) + "return unresolvedNode;\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public static " + getID() + " " + getID() + ".createRefDirection(String ref) {\n");
sb.append(ind(2) + "Unresolved$" + instantiableSubType.getID() + " unresolvedNode = new Unresolved$" + instantiableSubType.getID() + "();\n");
sb.append(ind(2) + "unresolvedNode.setUnresolved$Token(ref);\n");
sb.append(ind(2) + "unresolvedNode.setUnresolved$ResolveOpposite(false);\n");
sb.append(ind(2) + "return unresolvedNode;\n");
sb.append(ind(1) + "}\n");
}
public void TypeDecl.generateContextIndependentNameResolution(StringBuilder sb) {
sb.append(ind(1) + "// context-independent name resolution\n");
sb.append(ind(1) + "uncache ASTNode.globallyResolve" + getID() + "ByToken(String id);\n");
sb.append(ind(1) + "syn " + getID() + " ASTNode.globallyResolve" + getID() + "ByToken(String id) {\n");
if (serializer) {
sb.append(ind(2) + "return (" + getID() + ") globallyResolveASTNodeByUID(id);\n");
} else {
sb.append(ind(2) + "// perform context independent name resolution here using the id\n");
sb.append(ind(2) + "throw new RuntimeException(\"Context-independent name resolution for " + getID() + " not implemented.\");\n");
}
sb.append(ind(1) + "}\n");
}
public void Relation.generateContextDependentNameResolution(StringBuilder sb) {
sb.append(ind(1) + "// " + prettyPrint() + "\n");
getDirection().generateContextDependentNameResolution(sb);
sb.append("\n");
}
public abstract void Direction.generateContextDependentNameResolution(StringBuilder sb);
public void RightDirection.generateContextDependentNameResolution(StringBuilder sb) {
relation().getLeft().generateContextDependentNameResolution(sb);
}
public void Bidirectional.generateContextDependentNameResolution(StringBuilder sb) {
relation().getLeft().generateContextDependentNameResolution(sb);
relation().getRight().generateContextDependentNameResolution(sb);
}
public abstract void RelationComponent.generateContextDependentNameResolution(StringBuilder sb);
public void OneRelationComponent.generateContextDependentNameResolution(StringBuilder sb) {
generateDirectedContextDependentNameResolution(sb);
}
public void OptionalRelationComponent.generateContextDependentNameResolution(StringBuilder sb) {
// optional relations are resolved in the same way as mandatory relations
// TODO maybe, there should be a check if the id to be solved is empty or null
generateDirectedContextDependentNameResolution(sb);
}
public void ManyRelationComponent.generateContextDependentNameResolution(StringBuilder sb) {
if (serializer && !resolverHelper) {
sb.append(ind(1) + ofTypeDecl() + " " + toTypeDecl() + ".resolve" + nameCapitalized() + "ByToken(String id, int position) {\n");
sb.append(ind(2) + "return (" + ofTypeDecl() + ") globallyResolveASTNodeByUID(id);\n");
sb.append(ind(1) + "}\n");
} else {
sb.append(ind(1) + "// context-dependent name resolution\n");
sb.append(ind(1) + "uncache " + toTypeDecl() + ".resolve" + nameCapitalized() + "ByToken(String id, int position);\n");
sb.append(ind(1) + "syn " + ofTypeDecl() + " " + toTypeDecl() + ".resolve" + nameCapitalized() + "ByToken(String id, int position) {\n");
sb.append(ind(2) + "// default to context-independent name resolution\n");
sb.append(ind(2) + "return globallyResolve" + ofTypeDecl() + "ByToken(id);\n");
sb.append(ind(1) + "}\n");
}
}
public void RelationComponent.generateDirectedContextDependentNameResolution(StringBuilder sb) {
if (serializer && !resolverHelper) {
sb.append(ind(1) + ofTypeDecl() + " " + toTypeDecl() + ".resolve" + nameCapitalized() + "ByToken(String id) {\n");
sb.append(ind(2) + "return (" + ofTypeDecl() + ") globallyResolveASTNodeByUID(id);\n");
sb.append(ind(1) + "}\n");
} else {
sb.append(ind(1) + "// context-dependent name resolution\n");
sb.append(ind(1) + "uncache " + toTypeDecl() + ".resolve" + nameCapitalized() + "ByToken(String id);\n");
sb.append(ind(1) + "syn " + ofTypeDecl() + " " + toTypeDecl() + ".resolve" + nameCapitalized() + "ByToken(String id) {\n");
sb.append(ind(2) + "// default to context-independent name resolution\n");
sb.append(ind(2) + "return globallyResolve" + ofTypeDecl() + "ByToken(id);\n");
sb.append(ind(1) + "}\n");
}
}
public void Program.resolveAll(StringBuilder sb) {
sb.append(ind(1) + "// enforce resolving of all non-containment relations of the current non-terminal\n");
sb.append(ind(1) + "public void ASTNode.resolveAll() {\n");
sb.append(ind(1) + "}\n\n");
sb.append(ind(1) + "// enforce resolving in the entire subtree\n");
sb.append(ind(1) + "public void ASTNode.treeResolveAll() {\n");
sb.append(ind(2) + "if (children != null) {\n");
sb.append(ind(3) + "for (int i = 0; i < numChildren; ++i) {\n");
sb.append(ind(4) + "ASTNode child = children[i];\n");
sb.append(ind(4) + "if (child != null) {\n");
sb.append(ind(5) + "child.treeResolveAll();\n");
sb.append(ind(4) + "}\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "resolveAll();\n");
sb.append(ind(1) + "}\n");
}
public void TypeDecl.resolveAll(StringBuilder sb) {
sb.append(ind(1) + "// enforce resolving of all non-containment relations of the current non-terminal\n");
sb.append(ind(1) + "public void " + getID() + ".resolveAll() {\n");
for (RelationComponent relationComponent : relationComponents()) {
sb.append(ind(2));
if (useJastAddNames) {
sb.append("get" + relationComponent.nameCapitalized());
} else {
sb.append(relationComponent.name());
}
sb.append(relationComponent.isMany() && useJastAddNames ? "List" : "").append("();\n");
}
sb.append(ind(2) + "super.resolveAll();\n");
sb.append(ind(1) + "}\n");
}
}
aspect Serializer {
protected static String ASTNode.jsonTypeKey = "type";
protected static String ASTNode.jsonNodeType = "com.fasterxml.jackson.databind.JsonNode";
protected static String ASTNode.jsonNodeTypeAccessor = ".get(\"" + jsonTypeKey + "\").asText()";
public String Program.generateSerializer() {
StringBuilder sb = new StringBuilder();
generateFromJson(sb);
generateToJson(sb);
writeUID(sb);
return sb.toString();
}
public void Program.generateFromJson(StringBuilder sb) {
sb.append("aspect JsonToModel {\n");
sb.append(ind(1) + "public class DeserializationException extends Exception {\n");
sb.append(ind(2) + "public DeserializationException(String message) {\n");
sb.append(ind(3) + "super(message);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "public DeserializationException(String message, Exception cause) {\n");
sb.append(ind(3) + "super(message, cause);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public void ASTNode.serialize(com.fasterxml.jackson.core.JsonGenerator g) throws SerializationException {\n");
sb.append(ind(2) + "serialize(g, null);\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public void ASTNode.serialize(com.fasterxml.jackson.core.JsonGenerator g, String field) throws SerializationException {\n");
sb.append(ind(2) + "throw new SerializationException(\"unable to serialize class \" + this.getClass().getSimpleName());\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public void ASTNode.serialize(java.io.File file) throws SerializationException {\n");
sb.append(ind(2) + "serialize(file, false);\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public void ASTNode.serialize(java.io.File file, boolean humanReadable) throws SerializationException {\n");
sb.append(ind(2) + "try {\n");
sb.append(ind(3) + "com.fasterxml.jackson.core.JsonFactory factory = new com.fasterxml.jackson.core.JsonFactory();\n");
sb.append(ind(3) + "com.fasterxml.jackson.core.JsonGenerator generator = factory.createGenerator(file, com.fasterxml.jackson.core.JsonEncoding.UTF8);\n");
sb.append(ind(3) + "if (humanReadable) {\n");
sb.append(ind(4) + "generator.setPrettyPrinter(new com.fasterxml.jackson.core.util.DefaultPrettyPrinter());\n");
sb.append(ind(3) + "}\n");
sb.append(ind(3) + "serialize(generator);\n");
sb.append(ind(3) + "generator.close();\n");
sb.append(ind(2) + "} catch (java.io.IOException e) {\n");
sb.append(ind(3) + "throw new SerializationException(\"Unable to serialize file \" + file.getAbsolutePath(), e);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
for (TypeDecl decl : getTypeDeclList()) {
decl.deserialize(sb);
}
sb.append("}\n");
}
public void Program.generateToJson(StringBuilder sb) {
sb.append("aspect ModelToJson {\n");
sb.append(ind(1) + "public class SerializationException extends Exception {\n");
sb.append(ind(2) + "public SerializationException(String message) {\n");
sb.append(ind(3) + "super(message);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "public SerializationException(String message, Exception cause) {\n");
sb.append(ind(3) + "super(message, cause);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
for (TypeDecl decl : getTypeDeclList()) {
decl.serialize(sb);
}
sb.append("}\n");
}
public void TypeDecl.serialize(StringBuilder sb) {
sb.append(ind(1) + "public void " + getID() + ".serialize(com.fasterxml.jackson.core.JsonGenerator g, String fieldName) throws SerializationException {\n");
sb.append(ind(2) + "try {\n");
sb.append(ind(3) + "if (fieldName == null) {\n");
sb.append(ind(4) + "g.writeStartObject();\n");
sb.append(ind(3) + "} else {\n");
sb.append(ind(4) + "g.writeObjectFieldStart(fieldName);\n");
sb.append(ind(3) + "}\n");
sb.append(ind(3) + "g.writeStringField(\"" + jsonTypeKey + "\", \"" + getID() + "\");\n");
sb.append(ind(3) + "if (unique$Id() == null) throw new SerializationException(\"The unique identifier of " + getID() + " is missing.\");\n");
sb.append(ind(3) + "g.writeStringField(\"id\", unique$Id());\n");
if (componentsTransitive().size() > 0) {
sb.append(ind(3) + "g.writeObjectFieldStart(\"children\");\n");
for (Component child : componentsTransitive()) {
if (child.isNullable()) {
String componentAccessor = child.getID();
if (child.isList()) {
componentAccessor += "List";
} else if (child.isOpt()) {
componentAccessor += "Opt";
}
sb.append(ind(3) + "if (get" + componentAccessor + "() == null) throw new SerializationException(\"The component " + child.getID() + " of " + getID() + " is missing.\");\n");
}
child.serialize(sb, 3);
}
sb.append(ind(3) + "g.writeEndObject(); // children\n");
}
if (relationComponentsTransitive().size() > 0) {
sb.append(ind(3) + "g.writeObjectFieldStart(\"relations\");\n");
for (RelationComponent relation : relationComponentsTransitive()) {
relation.serialize(sb, 3);
}
sb.append(ind(3) + "g.writeEndObject(); // relations\n");
}
sb.append(ind(3) + "g.writeEndObject();\n");
sb.append(ind(2) + "} catch (java.io.IOException e) {\n");
sb.append(ind(3) + "throw new SerializationException(\"unable to serialize " + getID() + "\", e);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
}
public abstract void Component.serialize(StringBuilder sb, int indent);
public void NormalComponent.serialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "get" + getID() + "().serialize(g, \"" + getID() + "\");\n");
}
public void OptComponent.serialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "if (has" + getID() + "()) {\n");
sb.append(ind(indent + 1) + "get" + getID() + "().serialize(g, \"" + getID() + "\");\n");
sb.append(ind(indent) + "}\n");
}
public void TokenComponent.serialize(StringBuilder sb, int indent) {
String type = getTypeUse().getID();
switch (type) {
case "float":
case "Float":
case "double":
case "Double":
case "int":
case "Integer":
case "short":
case "Short":
case "long":
case "Long":
case "byte":
case "Byte":
sb.append(ind(indent) + "g.writeNumberField(\"" + getID() + "\", get" + getID() + "());\n");
break;
case "boolean":
case "Boolean":
sb.append(ind(indent) + "g.writeBooleanField(\"" + getID() + "\", get" + getID() + "());\n");
break;
case "char":
case "Character":
sb.append(ind(indent) + "g.writeStringField(\"" + getID() + "\", Character.toString(get"+ getID() + "()));\n");
break;
case "String":
sb.append(ind(indent) + "g.writeStringField(\"" + getID() + "\", get" + getID() + "());\n");
break;
case "Instant":
sb.append(ind(indent) + "g.writeStringField(\"" + getID() + "\", get" + getID() + "().toString());\n");
break;
case "Period":
sb.append(ind(indent) + "g.writeStringField(\"" + getID() + "\", get" + getID() + "().toString());\n");
break;
default:
// assume that the type is an enum. there is no way of checking this here
sb.append(ind(indent) + "g.writeStringField(\"" + getID() + "\", get" + getID() + "().name());\n");
// sb.append("throw new DeserializationException(\"Unable to deserialize child node of type \"" + getTypeUse() + "\"\")")
}
}
public void NTAComponent.serialize(StringBuilder sb, int indent) {
// do not serialize NTA
}
public void ListComponent.serialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "if (getNum" + getID() + "() > 0) {\n");
sb.append(ind(indent + 1) + "g.writeArrayFieldStart(\"" + getID() + "\");\n");
sb.append(ind(indent + 1) + "for (" + getTypeUse().decl().getID() + " child : get" + getID() + "List()) {\n");
sb.append(ind(indent + 2) + "child.serialize(g);\n");
sb.append(ind(indent + 1) + "}\n");
sb.append(ind(indent + 1) + "g.writeEndArray();\n");
sb.append(ind(indent) + "}\n");
}
public void OneRelationComponent.serialize(StringBuilder sb, int indent) {
if (useJastAddNames){
sb.append(ind(indent) + "g.writeStringField(\""+getID()+"\", get" + getID() + "().unique$Id());\n");
} else {
sb.append(ind(indent) + "g.writeStringField(\""+getID()+"\", " + getID() + "().unique$Id());\n");
}
}
public void OptionalRelationComponent.serialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "if (has" + nameCapitalized() + "()) {\n");
if (useJastAddNames){
sb.append(ind(indent + 1) + "g.writeStringField(\"" + getID() + "\", get" + getID() + "().unique$Id());\n");
} else {
sb.append(ind(indent + 1) + "g.writeStringField(\"" + getID() + "\", " + getID() + "().unique$Id());\n");
}
sb.append(ind(indent) + "}\n");
}
public void ManyRelationComponent.serialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "g.writeArrayFieldStart(\"" + getID() + "\");\n");
if (useJastAddNames) {
sb.append(ind(indent) + "for (" + ofTypeDecl().getID() + " child : get" + getID() + "List()) {\n");
} else {
sb.append(ind(indent) + "for (" + ofTypeDecl().getID() + " child : " + getID() + "()) {\n");
}
sb.append(ind(indent + 1) + "g.writeString(child.unique$Id());\n");
sb.append(ind(indent) + "}\n");
sb.append(ind(indent) + "g.writeEndArray();\n");
}
public void TypeDecl.deserialize(StringBuilder sb) {
sb.append(ind(1) + "public static " + getID() + " " + getID() + ".deserialize(java.io.File file) throws DeserializationException {\n");
sb.append(ind(2) + "try {\n");
sb.append(ind(3) + "com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();\n");
sb.append(ind(3) + "com.fasterxml.jackson.core.JsonFactory factory = mapper.getFactory();\n");
sb.append(ind(3) + "com.fasterxml.jackson.core.JsonParser parser = factory.createParser(file);\n");
sb.append(ind(3) + getID() + " result = deserialize((com.fasterxml.jackson.databind.JsonNode)mapper.readTree(parser));\n");
sb.append(ind(3) + "parser.close();\n");
sb.append(ind(3) + "return result;\n");
sb.append(ind(2) + "} catch (java.io.IOException e) {\n");
sb.append(ind(3) + "throw new DeserializationException(\"unable to deserialize \" + file.getAbsolutePath(), e);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "public static " + getID() + " " + getID() + ".deserialize(" + jsonNodeType + " node) throws DeserializationException {\n");
sb.append(ind(2) + getID() + " element;\n");
if (getAbstract()) {
// switch case between all implementations of the abstract class
sb.append(ind(2) + "switch (node" + jsonNodeTypeAccessor + ") {\n");
for (TypeDecl subType : subTypeDecls()) {
sb.append(ind(3) + "case \"" + subType.getID() + "\":\n");
sb.append(ind(4) + "element = " + subType.getID() + ".deserialize(node);\n");
sb.append(ind(4) + "break;\n");
}
sb.append(ind(3) + "default:\n");
sb.append(ind(4) + "throw new DeserializationException(\"Unable to deserialize child of unexpected type \" + node" + jsonNodeTypeAccessor + " + \"(" + getID() + " expected)\");\n");
sb.append(ind(2) + "}\n");
} else {
sb.append(ind(2) + "element = new " + getID() + "();\n");
}
// deserialize id
sb.append(ind(2) + "if (node.has(\"id\")) {\n");
sb.append(ind(3) + "element.unique$Id = node.get(\"id\").asText();\n");
sb.append(ind(2) + "}\n");
// deserialize containment children
if (componentsTransitive().size() > 0) {
sb.append(ind(2) + "if (node.has(\"children\")) {\n");
sb.append(ind(3) + jsonNodeType + " children = node.get(\"children\");\n");
for (Component component : componentsTransitive()) {
sb.append(ind(3) + "if (children.has(\"" + component.getID() + "\")) {\n");
component.deserialize(sb, 4);
sb.append(ind(3) + "}\n");
}
sb.append(ind(2) + "}\n");
}
// deserialize non-containment children
Set<RelationComponent> relationComponents = relationComponents();
if (relationComponents.size() > 0) {
sb.append(ind(2) + "if (node.has(\"relations\")) {\n");
sb.append(ind(3) + jsonNodeType + " relations = node.get(\"relations\");\n");
for (RelationComponent component : relationComponents) {
sb.append(ind(3) + "if (relations.has(\"" + component.getID() + "\")) {\n");
component.deserialize(sb, 4);
sb.append(ind(3) + "}\n");
}
sb.append(ind(2) + "}\n");
}
sb.append(ind(2) + "return element;\n");
sb.append(ind(1) + "}\n");
}
public abstract void Component.deserialize(StringBuilder sb, int indent);
public void NormalComponent.deserialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "element.set" + getID() + "(" + getTypeUse().decl().getID() + ".deserialize(children.get(\"" + getID() + "\")));\n");
sb.append(ind(indent - 1) + "} else {\n");
sb.append(ind(indent) + "throw new DeserializationException(\"deserializer of missing mandatory child " + getID() + "\");\n");
}
public void OptComponent.deserialize(StringBuilder sb, int indent) {
sb.append(ind(indent + 1) + "element.set" + getID() + "(" + getTypeUse().decl().getID() + ".deserialize(children.get(\"" + getID() + "\")));\n");
}
public void TokenComponent.deserialize(StringBuilder sb, int indent) {
String type = getTypeUse().getID();
switch (type) {
case "float":
case "Float":
sb.append(ind(indent) + "element.set" + getID() + "(Float.valueOf(children.get(\"" + getID() + "\").asText()));\n");
break;
case "double":
case "Double":
sb.append(ind(indent) + "element.set" + getID() + "(children.get(\"" + getID() + "\").asDouble());\n");
break;
case "int":
case "Integer":
sb.append(ind(indent) + "element.set" + getID() + "(children.get(\"" + getID() + "\").asInt());\n");
break;
case "short":
case "Short":
sb.append(ind(indent) + "element.set" + getID() + "(Short.valueOf(children.get(\"" + getID() + "\").asText()));\n");
break;
case "long":
case "Long":
sb.append(ind(indent) + "element.set" + getID() + "(children.get(\"" + getID() + "\").asLong());\n");
break;
case "byte":
case "Byte":
sb.append(ind(indent) + "element.set" + getID() + "(Byte.valueOf(children.get(\"" + getID() + "\").asText()));\n");
break;
case "boolean":
case "Boolean":
sb.append(ind(indent) + "element.set" + getID() + "(children.get(\"" + getID() + "\").asBoolean());\n");
break;
case "char":
case "Character":
sb.append(ind(indent) + "String chars = children.get(\"" + getID() + "\").asText();\n");
sb.append(ind(indent) + "if (chars.length() == 1) {\n");
sb.append(ind(indent + 2) + "element.set" + getID() + "(chars.charAt(0));\n");
sb.append(ind(indent) + "} else {\n");
sb.append(ind(indent + 2) + "throw new DeserializationException(\"unable to deserialize char '\" + chars + \"'\");\n");
sb.append(ind(indent) + "}\n");
break;
case "String":
sb.append(ind(indent) + "element.set" + getID() + "(children.get(\"" + getID() + "\").asText());\n");
break;
case "Instant":
sb.append(ind(indent) + "element.set" + getID() + "(Instant.parse(children.get(\"" + getID() + "\").asText()));\n");
break;
case "Period":
sb.append(ind(indent) + "element.set" + getID() + "(Period.parse(children.get(\"" + getID() + "\").asText()));\n");
break;
default:
// assume that the type is an enum. there is no way of checking this here
sb.append(ind(indent) + "element.set" + getID() + "(Enum.valueOf(" + type + ".class, children.get(\"" + getID() + "\").asText()));\n");
// sb.append("throw new DeserializationException(\"Unable to deserialize child node of type \"" + getTypeUse() + "\"\")")
}
}
public void Program.writeUID(StringBuilder sb) {
sb.append("aspect UID {\n");
sb.append(ind(1) + "class UIDProvider {\n");
sb.append(ind(2) + "private static long nextUID = 0;\n");
sb.append(ind(2) + "public static String getUID() {\n");
sb.append(ind(3) + "return String.valueOf(nextUID++);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
sb.append("\n");
sb.append(ind(1) + "protected String ASTNode.unique$Id = null;\n");
sb.append("\n");
sb.append(ind(1) + "protected String ASTNode.unique$Id() {\n");
sb.append(ind(2) + "String customUID = customID();\n");
sb.append(ind(2) + "if (customUID == null) {\n");
sb.append(ind(3) + "if (unique$Id == null) {\n");
sb.append(ind(4) + "unique$Id = UIDProvider.getUID();\n");
sb.append(ind(3) + "}\n");
sb.append(ind(3) + "return unique$Id;\n");
sb.append(ind(2) + "} else {\n");
sb.append(ind(3) + "return customUID;\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
sb.append("\n");
sb.append(ind(1) + "protected String ASTNode.customID() {\n");
sb.append(ind(1) + " return null;\n");
sb.append(ind(1) + "}\n");
sb.append("\n");
sb.append(ind(1) + "ASTNode ASTNode.globallyResolveASTNodeByUID(String uid) {\n");
sb.append(ind(2) + "if (getParent() == null) {\n");
sb.append(ind(3) + "return uid$Map().get(uid).get();\n");
sb.append(ind(2) + "} else {\n");
sb.append(ind(3) + "return getParent().globallyResolveASTNodeByUID(uid);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(1) + "}\n");
sb.append("\n");
sb.append(ind(1) + "java.util.Map<String, java.lang.ref.WeakReference<ASTNode>> ASTNode.uid$Map;\n");
sb.append("\n");
sb.append(ind(1) + "java.util.Map<String, java.lang.ref.WeakReference<ASTNode>> ASTNode.uid$Map() {\n");
sb.append(ind(2) + "if (uid$Map == null) {\n");
sb.append(ind(3) + "uid$Map = uid$Map(new java.util.HashMap());\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "return uid$Map;\n");
sb.append(ind(1) + "}\n");
sb.append(ind(1) + "protected java.util.Map<String, java.lang.ref.WeakReference<ASTNode>> ASTNode.uid$Map(java.util.Map<String, java.lang.ref.WeakReference<ASTNode>> map) {\n");
sb.append(ind(2) + "if (!(this instanceof " + jastAddListType + " || this instanceof Opt)) {\n");
sb.append(ind(3) + "if (map.keySet().contains(unique$Id())) {\n");
sb.append(ind(4) + "throw new RuntimeException(new SerializationException(\"UID \" + this.unique$Id() + \" is assigned to both \" + this.getClass().getSimpleName() + \":\" + this.hashCode() + \" and \" + map.get(unique$Id()).getClass().getSimpleName() + \":\" + map.get(unique$Id()).hashCode()));\n");
sb.append(ind(3) + "} else {\n");
sb.append(ind(4) + "map.put(this.unique$Id, new java.lang.ref.WeakReference(this));\n");
sb.append(ind(3) + "}\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "for (ASTNode child : astChildren()) {\n");
sb.append(ind(3) + "child.uid$Map(map);\n");
sb.append(ind(2) + "}\n");
sb.append(ind(2) + "return map;\n");
sb.append(ind(1) + "}\n");
sb.append("}\n");
}
public void NTAComponent.deserialize(StringBuilder sb, int indent) {
// do not serialize NTA
}
public void ListComponent.deserialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "for (" + jsonNodeType + " child : children.get(\"" + getID() + "\")) {\n");
sb.append(ind(indent + 1) + "element.add" + getID() + "(" + getTypeUse().decl().getID() + ".deserialize(child));\n");
sb.append(ind(indent) + "}\n");
}
public void OneRelationComponent.deserialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "element.set" + nameCapitalized() + "(" + ofTypeDecl().getID() + ".createRefDirection(relations.get(\"" + getID() + "\").asText()));\n");
sb.append(ind(indent - 1) + "} else {\n");
sb.append(ind(indent) + "throw new DeserializationException(\"deserializer of missing mandatory relation child " + getID() + "\");\n");
}
public void OptionalRelationComponent.deserialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "element.set" + nameCapitalized() + "(" + ofTypeDecl().getID() + ".createRefDirection(relations.get(\"" + getID() + "\").asText()));\n");
}
public void ManyRelationComponent.deserialize(StringBuilder sb, int indent) {
sb.append(ind(indent) + "for (" + jsonNodeType + " child : relations.get(\"" + getID() + "\")) {\n");
sb.append(ind(indent + 1) + "element.add" + (useJastAddNames?"":"To") + nameCapitalized() + "(" + ofTypeDecl().getID() + ".createRefDirection(child.asText()));\n");
sb.append(ind(indent) + "}\n");
}
}
aspect PrettyPrint {
public String Relation.prettyPrint() {
return "rel "
+ getLeft().prettyPrint() + " "
+ getDirection().prettyPrint() + " "
+ getRight().prettyPrint();
}
public String RelationComponent.prettyPrint() {
if (getID().isEmpty()) {
return getTypeUse().toString();
} else {
return getTypeUse() + "." + getID();
}
}
public String OptionalRelationComponent.prettyPrint() {
return super.prettyPrint() + "?";
}
public String ManyRelationComponent.prettyPrint() {
return super.prettyPrint() + "*";
}
abstract public String Direction.prettyPrint();
public String RightDirection.prettyPrint() {
return "->";
}
public String Bidirectional.prettyPrint() {
return "<->";
}
}
aspect Utils {
public String ASTNode.ind(int n) {
String s = "";
for (int i = 0; i < n; i++) {
s += " ";
}
return s;
}
}