org.modelcc.io.java.JavaModelReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ModelCC Show documentation
Show all versions of ModelCC Show documentation
ModelCC is a model-based parser generator (a.k.a. compiler compiler) that decouples language specification from language processing, avoiding some of the problems caused by grammar-driven parser generators. ModelCC receives a conceptual model as input, along with constraints that annotate it. It is then able to create a parser for the desired textual language and the generated parser fully automates the instantiation of the language conceptual model. ModelCC also includes a built-in reference resolution mechanism that results in abstract syntax graphs, rather than mere abstract syntax trees.
The newest version!
/*
* ModelCC, distributed under ModelCC Shared Software License, www.modelcc.org
*/
package org.modelcc.io.java;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import org.modelcc.IModel;
import org.modelcc.Pattern;
import org.modelcc.Priority;
import org.modelcc.io.ModelReader;
import org.modelcc.language.metamodel.MemberCollectionType;
import org.modelcc.language.metamodel.LanguageModel;
import org.modelcc.metamodel.Model;
import org.modelcc.metamodel.ModelElement;
/**
* Abstract Java model reader. Template algorithm outline:
*
*
* createMetadata()
*
* for each Java class:
* createClassElement()
* preProcessClass()
* for each field:
* createFieldElement()
* processField()
* postProcessClass()
*
* createModel()
*
*
* @author Fernando Berzal ([email protected]) & Luis Quesada ([email protected])
*
* @param ModelCC model
* @param Class model element
* @param Field model element
* @param Model reader metadata
*/
public abstract class JavaModelReader> extends ModelReader
{
/**
* Root class.
*/
protected Class root;
/**
* Constructor
* @param root the root class
*/
public JavaModelReader(Class root)
{
this.root = root;
}
/**
* Read a model from a root java class.
* @return the model
* @throws Exception
*/
@Override
public final M read() throws Exception
{
clearMessages();
if (!IModel.class.isAssignableFrom(root))
throw new ClassDoesNotExtendIModelException("Class "+root+" does not extend IModel.");
D metadata = createMetadata();
metadata.setRelevantClasses(detectRelevantClasses(root));
readClasses(metadata);
return createModel(metadata);
}
/**
* Reads a model from a given class (included for ensuring backwards compatibility)
*
* @param type the root class of the model
* @return the ModelCC language model
* @throws Exception
*/
public static final LanguageModel read (Class type)
throws Exception
{
JavaLanguageReader jmr = new JavaLanguageReader(type);
return jmr.read();
}
/**
* Create specific instance of JavaModelReaderMetadata
* @return Reader metadata
*/
public abstract D createMetadata ();
/**
* Create ModelCC model from JavaModelReader metadata
* @param metadata Metadata obtained through reflection
* @return The resulting ModelCC model
* @throws Exception
*/
public abstract M createModel (D metadata)
throws Exception;
protected abstract void preProcessClass (Class type, D metadata, C element)
throws Exception;
protected abstract void postProcessClass (Class type, D metadata, C element)
throws Exception;
/**
* Create ModelCC class model from Java metadata
*/
protected abstract C createClassElement (Class type);
/**
* Create ModelCC field model from Java metadata
*/
protected abstract F createFieldElement (Field field, Class type, D metadata);
/**
* Field processing
*/
protected abstract void processField (C element, F field);
/**
* Reads all the classes information
* @param metadata reader metadata
* @throws Exception
*/
private void readClasses(D metadata)
throws Exception
{
C el;
for (Class c: metadata.getRelevantClasses()) {
el = readClass(c,metadata);
metadata.getJavaElements().add(el);
}
for (Class cact: metadata.getRelevantClasses()) {
if (!cact.isPrimitive()) {
Class sclass = cact.getSuperclass();
if (IModel.class.isAssignableFrom(sclass)) {
metadata.addJavaSubclass(metadata.getClassElement(sclass), metadata.getClassElement(cact));
}
}
}
}
/**
* Read class information
* @param type the element class
* @param metadata reader metadata
* @throws Excception
*/
private C readClass(Class type, D metadata)
throws Exception
{
C element = createClassElement(type);
preProcessClass(type, metadata, (C) element);
// Field members
Field[] fields = type.getDeclaredFields();
for (Field field: fields) {
F c = createFieldElement(field,type,metadata);
if (c != null) {
if (!Modifier.isStatic(field.getModifiers())) {
element.addMember(c);
processField((C)element, c);
}
}
}
postProcessClass(type, metadata,(C) element);
metadata.setClassElement(type,element);
return element;
}
/**
* Detects all the relevant classes from a root one
* @param root the root class
* @return the set of relevant classes
* @throws ClassNotFoundException
*/
private Set detectRelevantClasses(Class root) throws ClassNotFoundException
{
LinkedList q = new LinkedList();
Set done = new HashSet();
Class currentClass;
// Hack to avoid processing String and Object classes, which makes no sense.
done.add(String.class);
done.add(Object.class);
if (!root.getName().contains("$"))
q.addLast(root);
while (!q.isEmpty()) {
currentClass = q.removeLast();
done.add(currentClass);
// Add precedences
if (currentClass.isAnnotationPresent(Priority.class)) {
Priority an = (Priority) currentClass.getAnnotation(Priority.class);
for (int j=0; j subclasses = Reflection.findSubclasses(currentClass);
for (Class add: subclasses) {
addClass(add, q, done);
}
}
// Add superclass
if (!currentClass.isPrimitive()) {
addClass( currentClass.getSuperclass(), q, done);
}
}
// Revert hack for avoiding the processing of String and Object classes
done.remove(Object.class);
done.remove(String.class);
return done;
}
private void addField (Class currentClass, Field field, LinkedList q, Set done)
throws ClassNotFoundException
{
MemberCollectionType collection = null;
boolean avoid = false;
if (List.class.isAssignableFrom(field.getType())) {
if (ArrayList.class.equals(field.getType()) || (List.class.equals(field.getType())))
collection = MemberCollectionType.LIST;
else
log(Level.SEVERE, "In field \"{0}\" of class \"{1}\": The class of a composite list may only be List or ArrayList.", new Object[]{field.getName(), currentClass.getCanonicalName()});
} else if (Set.class.isAssignableFrom(field.getType())) {
if (HashSet.class.equals(field.getType()) || (Set.class.equals(field.getType())))
collection = MemberCollectionType.SET;
else
log(Level.SEVERE, "In field \"{0}\" of class \"{1}\": The class of a composite set may only be Set or HashSet.", new Object[]{field.getName(), currentClass.getCanonicalName()});
} else if (Map.class.isAssignableFrom(field.getType())) {
avoid = true;
} else if (field.getType().isArray()) {
collection = MemberCollectionType.ARRAY;
}
if (!avoid) {
Class add = Reflection.getType(collection,field);
if (add != null) {
if (IModel.class.isAssignableFrom(currentClass))
addClass(add, q, done);
}
}
}
/**
* Adds a class to a queue if it hasn't been added before
* @param add the class to add
* @param q the queue
* @param done the already added classes
* @return true if it has been added now, false if it was added before
* @throws ClassNotFoundException
*/
private boolean addClass(Class add,LinkedList q,Set done)
throws ClassNotFoundException
{
if (!done.contains(add) && !q.contains(add) && IModel.class.isAssignableFrom(add) && !add.getName().contains("$")) {
q.addLast(add);
return true;
}
return false;
}
}