package ru.csu.stan.java.classgen.automaton; import java.math.BigInteger; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Stack; import ru.csu.stan.java.classgen.handlers.NodeAttributes; import ru.csu.stan.java.classgen.jaxb.AggregatedType; import ru.csu.stan.java.classgen.jaxb.Argument; import ru.csu.stan.java.classgen.jaxb.BaseElement; import ru.csu.stan.java.classgen.jaxb.Classes; import ru.csu.stan.java.classgen.jaxb.CommonType; import ru.csu.stan.java.classgen.jaxb.Method; import ru.csu.stan.java.classgen.jaxb.ModifierType; import ru.csu.stan.java.classgen.jaxb.ObjectFactory; import ru.csu.stan.java.classgen.jaxb.ParentClass; import ru.csu.stan.java.classgen.util.ClassIdGenerator; import ru.csu.stan.java.classgen.util.CompilationUnit; import ru.csu.stan.java.classgen.util.ImportRegistry; import ru.csu.stan.java.classgen.util.PackageRegistry; /** * Контекст анализа AST для получения классового представления. * Представляет собой конечный автомат со стековой памятью. * В новое состояние переходит явным указанием такового, * а выходит из него возвратом к предыдущему. * * @author mz * */ public class ClassContext extends ContextBase { private String currentPackage; private String currentImport; private String currentNewClass; private CompilationUnit currentUnit = new CompilationUnit(); private Stack<ru.csu.stan.java.classgen.jaxb.Class> classStack = new Stack<ru.csu.stan.java.classgen.jaxb.Class>(); private Stack<Integer> classInnersCount = new Stack<Integer>(); private ru.csu.stan.java.classgen.jaxb.Attribute currentAttribute; private Stack<Method> methodStack = new Stack<Method>(); private ParentClass currentParent; private Argument currentArgument; private List<ModifierType> currentModifier = new LinkedList<ModifierType>(); private Map<String, String> imported = new HashMap<String, String>(); private Stack<ContextState> stateStack = new Stack<ContextState>(); private CommonType currentCommonType; private AggregatedType currentAggregatedType; private boolean currentTypeAggregated = false; private int modifierLine, modifierCol; private PackageRegistry packageReg = new PackageRegistry(); public PackageRegistry getPackageReg() { return packageReg; } private ImportRegistry impReg = new ImportRegistry(); public ImportRegistry getImpReg() { return impReg; } private ClassContext() {} private ClassContext(Classes classes, ObjectFactory factory) { this.root = classes; this.factory = factory; stateStack.push(ContextState.EMPTY); } public static ClassContext getInstance(Classes classes, ObjectFactory factory){ return new ClassContext(classes, factory); } @Override public Classes getResultRoot() { return root; } public void setPackageState(){ stateStack.push(ContextState.PACKAGE); } public void setClassState(){ stateStack.push(ContextState.CLASS); } public void setFieldState(){ stateStack.push(ContextState.FIELD); } public void setMethodState(){ stateStack.push(ContextState.METHOD); } public void setArgumentState(){ stateStack.push(ContextState.ARGUMENT); } public void setParentState(){ stateStack.push(ContextState.PARENT); } public void setEmptyState(){ stateStack.push(ContextState.EMPTY); } public void setImportState(){ stateStack.push(ContextState.IMPORT); } public void setNewClassState(){ // TODO: Fix this hack! if (stateStack.peek() == ContextState.FIELD){ classStack.peek().getAttr().add(currentAttribute); currentAttribute = null; } stateStack.push(ContextState.NEW_CLASS); } public void setCompilationUnitState(){ stateStack.push(ContextState.COMPILATION_UNIT); } public void setModifierState(){ stateStack.push(ContextState.MODIFIERS); } public void setResultTypeState(){ stateStack.push(ContextState.RETURN_TYPE); } public void setStateForVar(){ ContextState state = stateStack.peek(); if (state == ContextState.CLASS) stateStack.push(ContextState.FIELD); if (state == ContextState.METHOD) stateStack.push(ContextState.ARGUMENT); } public void setVartypeState(){ ContextState state = stateStack.peek(); if (state == ContextState.FIELD) stateStack.push(ContextState.FIELD_TYPE); if (state == ContextState.ARGUMENT) stateStack.push(ContextState.ARG_TYPE); } public void setPreviousVarState(){ ContextState state = stateStack.peek(); if (state == ContextState.FIELD || state == ContextState.ARGUMENT) stateStack.pop(); } public void setPreviousVartypeState(){ ContextState state = stateStack.peek(); if (state == ContextState.FIELD_TYPE || state == ContextState.ARG_TYPE) stateStack.pop(); } public void setPreviousState(){ stateStack.pop(); } @Override public void processTag(String name, NodeAttributes attrs){ switch (this.stateStack.peek()){ case CLASS: processClassTag(name, attrs); break; case METHOD: processMethodTag(name, attrs); break; case FIELD: processFieldTag(name, attrs); break; case ARGUMENT: processArgumentTag(name, attrs); break; case PARENT: processParentTag(name, attrs); break; case PACKAGE: processPackageTag(name, attrs); break; case IMPORT: processImportTag(name, attrs); break; case NEW_CLASS: processNewClass(name, attrs); break; case MODIFIERS: processModifierTag(name, attrs); break; case FIELD_TYPE: processTypeTag(name, attrs); break; case RETURN_TYPE: processTypeTag(name, attrs); break; case ARG_TYPE: processTypeTag(name, attrs); break; case COMPILATION_UNIT: processCompilationUnitTag(name, attrs); break; case EMPTY: break; default: break; } } public void finish(){ switch (this.stateStack.peek()){ case CLASS: root.getClazz().add(classStack.pop()); classInnersCount.pop(); break; case METHOD: classStack.peek().getMethod().add(methodStack.pop()); break; case FIELD: // TODO: Impossible situation! Fix this hack! if (currentAttribute != null){ classStack.peek().getAttr().add(currentAttribute); currentAttribute = null; } break; case ARGUMENT: methodStack.peek().getArg().add(currentArgument); currentArgument = null; break; case PARENT: // if (!imported.containsKey(currentParent.getName().substring(currentParent.getName().lastIndexOf('.')+1, currentParent.getName().length()))) // currentParent.setId(ClassIdGenerator.getInstance().getClassId(currentParent.getName())); // classStack.peek().getParent().add(currentParent); // currentParent = null; break; case IMPORT: if (currentImport.indexOf('*') < 0) imported.put(currentImport.substring(currentImport.lastIndexOf('.')+1, currentImport.length()), currentImport); else imported.put(currentImport, currentImport); currentUnit.addImport(currentImport); currentImport = null; break; case NEW_CLASS: currentNewClass = null; break; case COMPILATION_UNIT: imported.clear(); currentUnit.setPackageName(currentPackage.substring(0, currentPackage.length()-1)); currentPackage = null; impReg.addCompilationUnit(currentUnit); currentUnit = new CompilationUnit(); break; case MODIFIERS: ContextState currentState = stateStack.pop(); if (stateStack.peek() == ContextState.CLASS) classStack.peek().getModifier().addAll(currentModifier); if (stateStack.peek() == ContextState.METHOD) if (!methodStack.isEmpty()) methodStack.peek().getModifier().addAll(currentModifier); if (stateStack.peek() == ContextState.FIELD) currentAttribute.getModifier().addAll(currentModifier); stateStack.push(currentState); currentModifier.clear(); break; case FIELD_TYPE: if (currentCommonType != null) currentAttribute.getCommonType().add(currentCommonType); if (currentAggregatedType != null) currentAttribute.getAggregatedType().add(currentAggregatedType); currentCommonType = null; currentAggregatedType = null; currentTypeAggregated = false; break; case RETURN_TYPE: if (currentCommonType != null) if (!methodStack.isEmpty()) methodStack.peek().getCommonType().add(currentCommonType); if (currentAggregatedType != null) if (!methodStack.isEmpty()) methodStack.peek().getAggregatedType().add(currentAggregatedType); currentCommonType = null; currentAggregatedType = null; currentTypeAggregated = false; break; case ARG_TYPE: if (currentCommonType != null) currentArgument.getCommonType().add(currentCommonType); if (currentAggregatedType != null) currentArgument.getAggregatedType().add(currentAggregatedType); currentCommonType = null; currentAggregatedType = null; currentTypeAggregated = false; break; default: break; } } public void finishVar(){ ContextState state = stateStack.peek(); if (state == ContextState.FIELD || state == ContextState.ARGUMENT) finish(); } public void finishVartype(){ ContextState state = stateStack.peek(); if (state == ContextState.FIELD_TYPE) finish(); if (state == ContextState.ARG_TYPE) finish(); } public void finishIdentifier(){ if (stateStack.peek() == ContextState.PARENT){ classStack.peek().getParent().add(currentParent); currentParent = factory.createParentClass(); } if (stateStack.peek() == ContextState.ARG_TYPE || stateStack.peek() == ContextState.RETURN_TYPE || stateStack.peek() == ContextState.FIELD_TYPE) if (currentTypeAggregated){ } } private void processPackageTag(String name, NodeAttributes attrs){ if ("package".equals(name)) currentPackage = ""; if ("member_select".equals(name) || "identifier".equals(name)) currentPackage = attrs.getNameAttribute() + '.' + currentPackage; } private void processClassTag(String name, NodeAttributes attrs){ if ("class".equals(name)) { ru.csu.stan.java.classgen.jaxb.Class newClass = factory.createClass(); String nameAttr = attrs.getNameAttribute(); if (nameAttr == null || "".equals(nameAttr)) { String upperName = classStack.get(classStack.size() - 1).getName(); int innerCount = classInnersCount.pop().intValue() + 1; newClass.setName(upperName + '$' + innerCount); classInnersCount.push(Integer.valueOf(innerCount)); } else { if (!classStack.isEmpty()) newClass.setName(classStack.peek().getName() + "." + nameAttr); else newClass.setName(currentPackage + nameAttr); } if (currentNewClass != null) { ParentClass parent = factory.createParentClass(); if (!imported.containsKey(currentNewClass)) parent.setName(currentNewClass); else parent.setName(imported.get(currentNewClass)); newClass.getParent().add(parent); } imported.put(newClass.getName().substring(currentPackage.length()), newClass.getName()); packageReg.addClassToPackage(newClass.getName().substring(currentPackage.length()), currentPackage.substring(0, currentPackage.length()-1)); newClass.setId(ClassIdGenerator.getInstance().getClassId(newClass.getName())); currentUnit.addClass(newClass.getName()); newClass.setFilename(currentUnit.getFilename()); setPosition(newClass, attrs); System.out.println("Found class '" + newClass.getName() + "'"); classStack.push(newClass); classInnersCount.push(0); } } private void processFieldTag(String name, NodeAttributes attrs){ if ("variable".equals(name)) { currentAttribute = factory.createAttribute(); setPosition(currentAttribute, attrs); currentAttribute.setName(attrs.getNameAttribute()); } } private void processMethodTag(String name, NodeAttributes attrs){ if ("method".equals(name)) { Method currentMethod = factory.createMethod(); String nameAttr = attrs.getNameAttribute(); if ("<init>".equals(nameAttr)) nameAttr = classStack.peek().getName().substring(classStack.peek().getName().lastIndexOf('.')+1); currentMethod.setName(nameAttr); setPosition(currentMethod, attrs); methodStack.push(currentMethod); } } private void processArgumentTag(String name, NodeAttributes attrs){ if ("variable".equals(name)) { currentArgument = factory.createArgument(); setPosition(currentArgument, attrs); currentArgument.setName(attrs.getNameAttribute()); } } private void processParentTag(String name, NodeAttributes attrs){ if ("extends".equals(name) || "implements".equals(name)) currentParent = factory.createParentClass(); if ("member_select".equals(name)){ currentParent.setName(attrs.getNameAttribute() + '.' + currentParent.getName()); setPosition(currentParent, attrs); } if ("identifier".equals(name)){ if (currentParent.getName() == null || "".equals(currentParent.getName())) { String nameAttr = attrs.getNameAttribute(); if (imported.containsKey(nameAttr)) currentParent.setName(imported.get(nameAttr)); else currentParent.setName(nameAttr); } else currentParent.setName(attrs.getNameAttribute() + '.' + currentParent.getName()); setPosition(currentParent, attrs); } } private void processImportTag(String name, NodeAttributes attrs){ if ("import".equals(name)) currentImport = ""; if ("member_select".equals(name) || "identifier".equals(name)) if ("".equals(currentImport)) currentImport = attrs.getNameAttribute(); else currentImport = attrs.getNameAttribute() + '.' + currentImport; } private void processNewClass(String name, NodeAttributes attrs){ if ("new_class".equals(name)) currentNewClass = ""; if ("member_select".equals(name) || "identifier".equals(name)) if ("".equals(currentNewClass)) currentNewClass = attrs.getNameAttribute(); else currentNewClass = attrs.getNameAttribute() + '.' + currentNewClass; } private void processModifierTag(String name, NodeAttributes attrs){ if ("modifier".equals(name)){ ModifierType mod = factory.createModifierType(); mod.setName(attrs.getNameAttribute()); mod.setFromlineno(BigInteger.valueOf(modifierLine)); mod.setColOffset(BigInteger.valueOf(modifierCol)); if (currentModifier == null) currentModifier = new LinkedList<ModifierType>(); currentModifier.add(mod); } if ("modifiers".equals(name)){ modifierLine = attrs.getIntAttribute(NodeAttributes.LINE_ATTRIBUTE); modifierCol = attrs.getIntAttribute(NodeAttributes.COL_ATTRIBUTE); } } private void processTypeTag(String name, NodeAttributes attrs){ if ("member_select".equals(name) || "identifier".equals(name)){ if (currentAggregatedType == null){ if (currentCommonType == null) { currentCommonType = factory.createCommonType(); currentCommonType.setName(""); } if (currentCommonType.getName().isEmpty()) currentCommonType.setName(attrs.getNameAttribute()); else currentCommonType.setName(attrs.getNameAttribute() + '.' + currentCommonType.getName()); setPosition(currentCommonType, attrs); } else{ if (currentTypeAggregated){ if (currentAggregatedType.getElementType().isEmpty()) currentAggregatedType.setElementType(attrs.getNameAttribute()); else currentAggregatedType.setElementType(attrs.getNameAttribute() + '.' + currentAggregatedType.getElementType()); } else{ if (currentAggregatedType.getName().isEmpty()) currentAggregatedType.setName(attrs.getNameAttribute()); else currentAggregatedType.setName(attrs.getNameAttribute() + '.' + currentAggregatedType.getName()); } } } if ("parameterized_type".equals(name) && currentCommonType == null){ if (currentAggregatedType == null){ currentAggregatedType = factory.createAggregatedType(); currentAggregatedType.setName(""); currentAggregatedType.setElementType(""); setPosition(currentAggregatedType, attrs); } } if ("arguments".equals(name)){ currentTypeAggregated = true; } } private void processCompilationUnitTag(String name, NodeAttributes attrs){ if ("compilation_unit".equals(name)){ currentUnit.setFilename(attrs.getStringAttribute(NodeAttributes.FILENAME_ATTRIBUTE)); } } @Override public IContext<Classes> getNextState(IContext<Classes> context, String eventName) { if (eventName.equals("package")){ this.setPackageState(); } if (eventName.equals("class")){ this.setClassState(); } if (eventName.equals("method")){ this.setMethodState(); } if (eventName.equals("variable")){ this.setStateForVar(); } if (eventName.equals("extends") || eventName.equals("implements")){ this.setParentState(); } if (eventName.equals("import")){ this.setImportState(); } if (eventName.equals("block")){ this.setEmptyState(); } if (eventName.equals("new_class")){ this.setNewClassState(); } // if (eventName.equals("arguments")){ // context.setEmptyState(); // } if (eventName.equals("compilation_unit")){ this.setCompilationUnitState(); } if (eventName.equals("modifiers")){ this.setModifierState(); } if (eventName.equals("vartype")){ this.setVartypeState(); } if (eventName.equals("resulttype")){ this.setResultTypeState(); } return this; } @Override public IContext<Classes> getPreviousState(String eventName) { if (eventName.equals("package") || eventName.equals("class") || eventName.equals("method") || eventName.equals("extends") || eventName.equals("implements") || eventName.equals("import") || eventName.equals("block") || eventName.equals("new_class") || // event.getName().toString().equals("arguments") || eventName.equals("modifiers") || eventName.equals("resulttype") || eventName.equals("compilation_unit") ){ this.setPreviousState(); } if (eventName.equals("variable")) { this.setPreviousVarState(); } if (eventName.equals("vartype")) { this.setPreviousVartypeState(); } return this; } @Override public void finish(String eventName) { if (eventName.equals("package") || eventName.equals("class") || eventName.equals("method") || eventName.equals("extends") || eventName.equals("implements") || eventName.equals("import") || eventName.equals("block") || eventName.equals("new_class") || // event.getName().toString().equals("arguments") || eventName.equals("modifiers") || eventName.equals("resulttype") || eventName.equals("compilation_unit") ){ this.finish(); } if (eventName.equals("variable")) { this.finishVar(); } if (eventName.equals("identifier")) this.finishIdentifier(); if (eventName.equals("vartype")) { this.finishVartype(); } } private void setPosition(BaseElement element, NodeAttributes attrs){ if (element.getFromlineno() == null || element.getFromlineno().intValue() <= 0) element.setFromlineno(BigInteger.valueOf(attrs.getIntAttribute(NodeAttributes.LINE_ATTRIBUTE))); if (element.getColOffset() == null || element.getColOffset().intValue() <= 0) element.setColOffset(BigInteger.valueOf(attrs.getIntAttribute(NodeAttributes.COL_ATTRIBUTE))); } }