package org.antlr.v4.codegen;
import org.antlr.v4.Tool;
import org.antlr.v4.codegen.model.ModelElement;
import org.antlr.v4.codegen.model.OutputModelObject;
import org.antlr.v4.tool.ErrorType;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.compiler.FormalArgument;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class OutputModelWalker {
Tool tool;
STGroup templates;
public OutputModelWalker(Tool tool,
STGroup templates)
{
this.tool = tool;
this.templates = templates;
}
public ST walk(OutputModelObject omo, boolean header) {
Class<? extends OutputModelObject> cl = omo.getClass();
String templateName = cl.getSimpleName();
if ( templateName == null ) {
tool.errMgr.toolError(ErrorType.NO_MODEL_TO_TEMPLATE_MAPPING, cl.getSimpleName());
return new ST("["+templateName+" invalid]");
}
if (header) templateName += "Header";
ST st = templates.getInstanceOf(templateName);
if ( st == null ) {
tool.errMgr.toolError(ErrorType.CODE_GEN_TEMPLATES_INCOMPLETE, templateName);
return new ST("["+templateName+" invalid]");
}
if ( st.impl.formalArguments == null ) {
tool.errMgr.toolError(ErrorType.CODE_TEMPLATE_ARG_ISSUE, templateName, "<none>");
return st;
}
Map<String,FormalArgument> formalArgs = st.impl.formalArguments;
Set<String> argNames = formalArgs.keySet();
Iterator<String> arg_it = argNames.iterator();
String modelArgName = arg_it.next();
st.add(modelArgName, omo);
Set<String> usedFieldNames = new HashSet<String>();
Field fields[] = cl.getFields();
for (Field fi : fields) {
ModelElement annotation = fi.getAnnotation(ModelElement.class);
if (annotation == null) {
continue;
}
String fieldName = fi.getName();
if (!usedFieldNames.add(fieldName)) {
tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "Model object " + omo.getClass().getSimpleName() + " has multiple fields named '" + fieldName + "'");
continue;
}
if ( formalArgs.get(fieldName)==null ) continue;
try {
Object o = fi.get(omo);
if ( o instanceof OutputModelObject ) {
OutputModelObject nestedOmo = (OutputModelObject)o;
ST nestedST = walk(nestedOmo, header);
st.add(fieldName, nestedST);
}
else if ( o instanceof Collection || o instanceof OutputModelObject[] ) {
if ( o instanceof OutputModelObject[] ) {
o = Arrays.asList((OutputModelObject[])o);
}
Collection<?> nestedOmos = (Collection<?>)o;
for (Object nestedOmo : nestedOmos) {
if ( nestedOmo==null ) continue;
ST nestedST = walk((OutputModelObject)nestedOmo, header);
st.add(fieldName, nestedST);
}
}
else if ( o instanceof Map ) {
Map<?, ?> nestedOmoMap = (Map<?, ?>)o;
Map<Object, ST> m = new LinkedHashMap<Object, ST>();
for (Map.Entry<?, ?> entry : nestedOmoMap.entrySet()) {
ST nestedST = walk((OutputModelObject)entry.getValue(), header);
m.put(entry.getKey(), nestedST);
}
st.add(fieldName, m);
}
else if ( o!=null ) {
tool.errMgr.toolError(ErrorType.INTERNAL_ERROR, "not recognized nested model element: "+fieldName);
}
}
catch (IllegalAccessException iae) {
tool.errMgr.toolError(ErrorType.CODE_TEMPLATE_ARG_ISSUE, templateName, fieldName);
}
}
return st;
}
}