Skip to content

Commit

Permalink
Prepare to generate the constructor parameter list
Browse files Browse the repository at this point in the history
  • Loading branch information
matozoid committed Jan 27, 2017
1 parent 023f2fd commit 0f67970
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 279 deletions.
Expand Up @@ -13,21 +13,16 @@
import com.github.javaparser.generator.utils.SourceRoot;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import static com.github.javaparser.JavaParser.*;
import static com.github.javaparser.ast.Modifier.FINAL;
import static com.github.javaparser.ast.Modifier.PUBLIC;
import static com.github.javaparser.generator.utils.GeneratorUtils.*;
import static com.github.javaparser.generator.utils.GeneratorUtils.getJavaParserBasePath;

public class MetaModelGenerator {
private static final String NODE_META_MODEL = "BaseNodeMetaModel";
static final String NODE_META_MODEL = "BaseNodeMetaModel";
private static List<Class<? extends Node>> ALL_MODEL_CLASSES = new ArrayList<Class<? extends Node>>() {{
/* Base classes go first, so we don't have to do any sorting to make sure
generated classes can refer to their base generated classes without
Expand Down Expand Up @@ -137,7 +132,7 @@ public class MetaModelGenerator {
add(WildcardType.class);
}};

private static String METAMODEL_PACKAGE = "com.github.javaparser.metamodel";
static String METAMODEL_PACKAGE = "com.github.javaparser.metamodel";

public static void main(String[] args) throws IOException, NoSuchMethodException {
new MetaModelGenerator().run();
Expand All @@ -152,118 +147,34 @@ private void run() throws IOException, NoSuchMethodException {

CompilationUnit javaParserMetaModel = sourceRoot.parse(METAMODEL_PACKAGE, "JavaParserMetaModel.java", javaParser).get();

generateClassMetaModels(javaParserMetaModel, sourceRoot);
generateNodeMetaModels(javaParserMetaModel, sourceRoot);

sourceRoot.saveAll();
}

private void generateClassMetaModels(CompilationUnit javaParserMetaModelCu, SourceRoot sourceRoot) throws NoSuchMethodException {
private void generateNodeMetaModels(CompilationUnit javaParserMetaModelCu, SourceRoot sourceRoot) throws NoSuchMethodException {
ClassOrInterfaceDeclaration mmClass = javaParserMetaModelCu.getClassByName("JavaParserMetaModel").get();
NodeList<Statement> initializeNodeMetaModelsStatements = mmClass.getMethodsByName("initializeNodeMetaModels").get(0).getBody().get().getStatements();
NodeList<Statement> initializeFieldMetaModelsStatements = mmClass.getMethodsByName("initializeFieldMetaModels").get(0).getBody().get().getStatements();
NodeList<Statement> initializePropertyMetaModelsStatements = mmClass.getMethodsByName("initializePropertyMetaModels").get(0).getBody().get().getStatements();
NodeList<Statement> initializeConstructorParametersStatements = mmClass.getMethodsByName("initializeConstructorParameters").get(0).getBody().get().getStatements();
initializeNodeMetaModelsStatements.clear();
initializeFieldMetaModelsStatements.clear();
initializePropertyMetaModelsStatements.clear();
initializeConstructorParametersStatements.clear();

for (Class<?> c : ALL_MODEL_CLASSES) {
String className = metaModelName(c);
String fieldName = decapitalize(className);
mmClass.getFieldByName(fieldName).ifPresent(Node::remove);
FieldDeclaration f = mmClass.addField(className, fieldName, PUBLIC, FINAL);

Class<?> superclass = c.getSuperclass();
final String superClassMetaModel = optionalOf(decapitalize(metaModelName(superclass)), isNode(superclass));

f.getVariable(0).setInitializer(parseExpression(f("new %s(this, %s)", className, superClassMetaModel)));
initializeNodeMetaModelsStatements.add(parseStatement(f("nodeMetaModels.add(%s);", fieldName)));

CompilationUnit classMetaModelJavaFile = new CompilationUnit(METAMODEL_PACKAGE);
classMetaModelJavaFile.addImport("java.util.Optional");
sourceRoot.add(METAMODEL_PACKAGE, className + ".java", classMetaModelJavaFile);
ClassOrInterfaceDeclaration classMetaModelClass = classMetaModelJavaFile.addClass(className, PUBLIC);
classMetaModelClass.addExtendedType(new ClassOrInterfaceType(NODE_META_MODEL));

AstTypeAnalysis typeAnalysis = new AstTypeAnalysis(c);

ConstructorDeclaration classMMConstructor = classMetaModelClass
.addConstructor()
.addParameter("JavaParserMetaModel", "parent")
.addParameter("Optional<" + NODE_META_MODEL + ">", "super" + NODE_META_MODEL);
classMMConstructor
.getBody()
.addStatement(parseExplicitConstructorInvocationStmt(f("super(super%s, parent, %s.class, \"%s\", \"%s\", %s, %s);",
NODE_META_MODEL,
c.getName(),
c.getSimpleName(),
c.getPackage().getName(),
typeAnalysis.isAbstract,
typeAnalysis.isSelfType)));

generateFieldMetaModels(c, classMetaModelClass, fieldName, initializeFieldMetaModelsStatements);
final NodeMetaModelGenerator nodeMetaModelGenerator = new NodeMetaModelGenerator();
for (Class<? extends Node> c : ALL_MODEL_CLASSES) {
nodeMetaModelGenerator.generate(c, mmClass, initializeNodeMetaModelsStatements, initializePropertyMetaModelsStatements, initializeConstructorParametersStatements, sourceRoot);
}

initializeNodeMetaModelsStatements.sort(Comparator.comparing(Node::toString));
}

private boolean isNode(Class<?> c) {
static boolean isNode(Class<?> c) {
return Node.class.isAssignableFrom(c);
}

private void generateFieldMetaModels(Class<?> c, ClassOrInterfaceDeclaration classMetaModelClass, String classMetaModelFieldName, NodeList<Statement> initializeFieldMetaModelsStatements) throws NoSuchMethodException {
List<Field> fields = new ArrayList<>(Arrays.asList(c.getDeclaredFields()));
fields.sort(Comparator.comparing(Field::getName));
for (Field field : fields) {
if (fieldShouldBeIgnored(field)) {
continue;
}

AstTypeAnalysis fieldAnalysis = new AstTypeAnalysis(c.getMethod(getter(field)).getGenericReturnType());

Class<?> fieldType = fieldAnalysis.innerType;
String typeName = fieldType.getTypeName().replace('$', '.');
String propertyMetaModelFieldName = field.getName() + "PropertyMetaModel";
classMetaModelClass.addField("PropertyMetaModel", propertyMetaModelFieldName, PUBLIC);
String propertyInitializer = f("new PropertyMetaModel(%s, \"%s\", %s.class, %s, %s, %s, %s, %s)",
classMetaModelFieldName,
field.getName(),
typeName,
optionalOf(decapitalize(metaModelName(fieldType)), isNode(fieldType)),
fieldAnalysis.isOptional,
fieldAnalysis.isNodeList,
fieldAnalysis.isEnumSet,
fieldAnalysis.isSelfType);
String fieldSetting = f("%s.%s=%s;", classMetaModelFieldName, propertyMetaModelFieldName, propertyInitializer);
String fieldAddition = f("%s.getPropertyMetaModels().add(%s.%s);", classMetaModelFieldName, classMetaModelFieldName, propertyMetaModelFieldName);

initializeFieldMetaModelsStatements.add(parseStatement(fieldSetting));
initializeFieldMetaModelsStatements.add(parseStatement(fieldAddition));
}
}

private String getter(Field field) {
return getterName(field.getType(), field.getName());
}

private static String metaModelName(Class<?> c) {
static String metaModelName(Class<?> c) {
return c.getSimpleName() + "MetaModel";
}

private boolean fieldShouldBeIgnored(Field reflectionField) {
if (java.lang.reflect.Modifier.isStatic(reflectionField.getModifiers())) {
return true;
}
String name = reflectionField.getName();
switch (name) {
case "parentNode":
case "observers":
case "innerList":
case "data":
case "range":
case "childNodes":
case "commentedNode":
case "orphanComments":
return true;
}
return false;
}

}
@@ -0,0 +1,92 @@
package com.github.javaparser.generator.metamodel;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.generator.utils.SourceRoot;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

import static com.github.javaparser.JavaParser.*;
import static com.github.javaparser.ast.Modifier.FINAL;
import static com.github.javaparser.ast.Modifier.PUBLIC;
import static com.github.javaparser.generator.metamodel.MetaModelGenerator.*;
import static com.github.javaparser.generator.utils.GeneratorUtils.*;

public class NodeMetaModelGenerator {
private final PropertyMetaModelGenerator propertyMetaModelGenerator = new PropertyMetaModelGenerator();

public void generate(Class<? extends Node> c, ClassOrInterfaceDeclaration mmClass, NodeList<Statement> initializeNodeMetaModelsStatements, NodeList<Statement> initializePropertyMetaModelsStatements, NodeList<Statement> initializeConstructorParametersStatements, SourceRoot sourceRoot) throws NoSuchMethodException {
String className = metaModelName(c);
String fieldName = decapitalize(className);
mmClass.getFieldByName(fieldName).ifPresent(Node::remove);
FieldDeclaration f = mmClass.addField(className, fieldName, PUBLIC, FINAL);

Class<?> superclass = c.getSuperclass();
final String superClassMetaModel = optionalOf(decapitalize(metaModelName(superclass)), isNode(superclass));

f.getVariable(0).setInitializer(parseExpression(f("new %s(this, %s)", className, superClassMetaModel)));
initializeNodeMetaModelsStatements.add(parseStatement(f("nodeMetaModels.add(%s);", fieldName)));

CompilationUnit classMetaModelJavaFile = new CompilationUnit(METAMODEL_PACKAGE);
classMetaModelJavaFile.addImport("java.util.Optional");
sourceRoot.add(METAMODEL_PACKAGE, className + ".java", classMetaModelJavaFile);
ClassOrInterfaceDeclaration classMetaModelClass = classMetaModelJavaFile.addClass(className, PUBLIC);
classMetaModelClass.addExtendedType(new ClassOrInterfaceType(NODE_META_MODEL));

AstTypeAnalysis typeAnalysis = new AstTypeAnalysis(c);

ConstructorDeclaration classMMConstructor = classMetaModelClass
.addConstructor()
.addParameter("JavaParserMetaModel", "parent")
.addParameter("Optional<" + NODE_META_MODEL + ">", "super" + NODE_META_MODEL);
classMMConstructor
.getBody()
.addStatement(parseExplicitConstructorInvocationStmt(f("super(super%s, parent, %s.class, \"%s\", \"%s\", %s, %s);",
NODE_META_MODEL,
c.getName(),
c.getSimpleName(),
c.getPackage().getName(),
typeAnalysis.isAbstract,
typeAnalysis.isSelfType)));

List<Field> fields = new ArrayList<>(Arrays.asList(c.getDeclaredFields()));
fields.sort(Comparator.comparing(Field::getName));
for (Field field : fields) {
if (fieldShouldBeIgnored(field)) {
continue;
}

propertyMetaModelGenerator.generate(c, field, classMetaModelClass, fieldName, initializePropertyMetaModelsStatements, initializeConstructorParametersStatements);
}
}

private boolean fieldShouldBeIgnored(Field reflectionField) {
if (java.lang.reflect.Modifier.isStatic(reflectionField.getModifiers())) {
return true;
}
String name = reflectionField.getName();
switch (name) {
case "parentNode":
case "observers":
case "innerList":
case "data":
case "range":
case "childNodes":
case "commentedNode":
case "orphanComments":
return true;
}
return false;
}

}
@@ -0,0 +1,44 @@
package com.github.javaparser.generator.metamodel;

import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.stmt.Statement;

import java.lang.reflect.Field;

import static com.github.javaparser.JavaParser.parseStatement;
import static com.github.javaparser.ast.Modifier.PUBLIC;
import static com.github.javaparser.generator.metamodel.MetaModelGenerator.isNode;
import static com.github.javaparser.generator.metamodel.MetaModelGenerator.metaModelName;
import static com.github.javaparser.generator.utils.GeneratorUtils.*;

public class PropertyMetaModelGenerator {
public void generate(Class<?> c, Field field, ClassOrInterfaceDeclaration nodeMetaModelClass, String classMetaModelFieldName, NodeList<Statement> initializeFieldMetaModelsStatements, NodeList<Statement> initializeConstructorParametersStatements) throws NoSuchMethodException {

AstTypeAnalysis fieldAnalysis = new AstTypeAnalysis(c.getMethod(getter(field)).getGenericReturnType());

Class<?> fieldType = fieldAnalysis.innerType;
String typeName = fieldType.getTypeName().replace('$', '.');
String propertyMetaModelFieldName = field.getName() + "PropertyMetaModel";
nodeMetaModelClass.addField("PropertyMetaModel", propertyMetaModelFieldName, PUBLIC);
String propertyInitializer = f("new PropertyMetaModel(%s, \"%s\", %s.class, %s, %s, %s, %s, %s)",
classMetaModelFieldName,
field.getName(),
typeName,
optionalOf(decapitalize(metaModelName(fieldType)), isNode(fieldType)),
fieldAnalysis.isOptional,
fieldAnalysis.isNodeList,
fieldAnalysis.isEnumSet,
fieldAnalysis.isSelfType);
String fieldSetting = f("%s.%s=%s;", classMetaModelFieldName, propertyMetaModelFieldName, propertyInitializer);
String fieldAddition = f("%s.getDeclaredPropertyMetaModels().add(%s.%s);", classMetaModelFieldName, classMetaModelFieldName, propertyMetaModelFieldName);

initializeFieldMetaModelsStatements.add(parseStatement(fieldSetting));
initializeFieldMetaModelsStatements.add(parseStatement(fieldAddition));
}

private String getter(Field field) {
return getterName(field.getType(), field.getName());
}

}
Expand Up @@ -13,7 +13,8 @@
public abstract class BaseNodeMetaModel {
private final Optional<BaseNodeMetaModel> superNodeMetaModel;
private final JavaParserMetaModel javaParserMetaModel;
private final List<PropertyMetaModel> propertyMetaModels = new ArrayList<>();
private final List<PropertyMetaModel> declaredPropertyMetaModels = new ArrayList<>();
private final List<PropertyMetaModel> constructorParameters = new ArrayList<>();
private final Class<? extends Node> type;
private final String name;
private final String packageName;
Expand Down Expand Up @@ -60,10 +61,19 @@ public JavaParserMetaModel getJavaParserMetaModel() {
}

/**
* @return a list of all properties. These are also available as fields.
* @return a list of all properties declared directly in this node (not its parent nodes.) These are also available
* as fields.
*/
public List<PropertyMetaModel> getPropertyMetaModels() {
return propertyMetaModels;
public List<PropertyMetaModel> getDeclaredPropertyMetaModels() {
return declaredPropertyMetaModels;
}

/**
* @return a list of all properties that describe the parameters to the all-fields (but not "range" and "comment")
* constructor, in the order of appearance in the constructor parameter list.
*/
public List<PropertyMetaModel> getConstructorParameters() {
return constructorParameters;
}

/**
Expand Down

0 comments on commit 0f67970

Please sign in to comment.