package de.dhbwstuttgart.syntaxtree; import de.dhbwstuttgart.parser.NullToken; import de.dhbwstuttgart.parser.scope.JavaClassName; import de.dhbwstuttgart.syntaxtree.type.GenericRefType; import de.dhbwstuttgart.syntaxtree.type.RefType; import de.dhbwstuttgart.syntaxtree.type.TypePlaceholder; import de.dhbwstuttgart.syntaxtree.type.RefTypeOrTPHOrWildcardOrGeneric; import org.antlr.v4.runtime.Token; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Optional; /** * Stellt jede Art von Klasse dar. Auch abstrakte Klassen und Interfaces */ public class ClassOrInterface extends SyntaxTreeNode implements TypeScope { private Boolean methodAdded = false; // wird benoetigt bei in JavaTXCompiler.getConstraints() protected int modifiers; protected JavaClassName name; private final String fileName; private List fields = new ArrayList<>(); private Optional fieldInitializations; // PL 2018-11-24: Noetig, um Bytecode fuer initializators nur einmal zu erzeugen private Optional staticInitializer; private List methods = new ArrayList<>(); private GenericDeclarationList genericClassParameters; private RefType superClass; protected boolean isInterface; protected boolean isFunctionalInterface; private List implementedInterfaces; private List permittedSubtypes; private List constructors; public ClassOrInterface(int modifiers, JavaClassName name, List fielddecl, Optional fieldInitializations, Optional staticInitializer, List methods, List constructors, GenericDeclarationList genericClassParameters, RefType superClass, Boolean isInterface, Boolean isFunctionalInterface, List implementedInterfaces, List permittedSubtypes, Token offset, String fileName) { super(offset); if (isInterface) { modifiers |= Modifier.INTERFACE | Modifier.ABSTRACT; } this.modifiers = modifiers; this.name = name; this.fields = fielddecl; this.fieldInitializations = fieldInitializations; this.staticInitializer = staticInitializer; this.genericClassParameters = genericClassParameters; this.superClass = superClass; this.isInterface = isInterface; this.isFunctionalInterface= isFunctionalInterface; this.implementedInterfaces = implementedInterfaces; this.permittedSubtypes = permittedSubtypes; this.methods = methods; this.constructors = constructors; this.fileName = fileName; } /* * erzeugt fuer Fields, Konstruktoren und Methoden neue ArrayList-Objekte alle anderen Datenobjekte werden nur kopiert. */ public ClassOrInterface(ClassOrInterface cl) { super(cl.getOffset()); this.modifiers = cl.modifiers; this.name = cl.name; this.fields = new ArrayList<>(cl.fields); this.fieldInitializations = cl.fieldInitializations; this.staticInitializer = cl.staticInitializer; this.genericClassParameters = cl.genericClassParameters; this.superClass = cl.superClass; this.isInterface = cl.isInterface; this.isFunctionalInterface= cl.isFunctionalInterface; this.implementedInterfaces = cl.implementedInterfaces; this.methods = new ArrayList<>(cl.methods); this.constructors = new ArrayList<>(cl.constructors); this.fileName = cl.fileName; } public String getFileName() { return fileName; } public Optional getField(String name) { // TODO This should be a map return fields.stream().filter(field -> field.getName().equals(name)).findFirst(); } public Optional getStaticInitializer() { return staticInitializer; } public boolean isInterface() { return (Modifier.INTERFACE & this.getModifiers()) != 0; } public boolean isFunctionalInterface() { return this.isFunctionalInterface; } // Gets if it is added public Boolean areMethodsAdded() { return methodAdded; } // Sets that it is added public void setMethodsAdded() { methodAdded = true; } // Gets class name public JavaClassName getClassName() { return this.name; } // Get modifiers public int getModifiers() { return this.modifiers; } public List getFieldDecl() { return this.fields; } public Optional getfieldInitializations() { return this.fieldInitializations; } public List getMethods() { return this.methods; } /* * public RefType getType() { return generateTypeOfClass(this.getClassName(), this.getGenerics(), this.getOffset()); } */ // TODO: Das hier ist ein Problem. Je nach Kontext wird hier ein anderer Typ benötigt public static RefType generateTypeOfClass(JavaClassName name, GenericDeclarationList genericsOfClass, Token offset) { // Hier wird immer ein generischer Typ generiert, also mit Type placeholdern List params = new ArrayList<>(); for (GenericTypeVar genericTypeVar : genericsOfClass) { // params.add(genericTypeVar.getTypePlaceholder()); params.add(TypePlaceholder.fresh(offset)); } return new RefType(name, params, offset); } /** * * @return die aktuelle Klasse als RefType */ public RefType generateTypeOfThisClass() { List params = new ArrayList<>(); for (GenericTypeVar genericTypeVar : this.getGenerics()) { // params.add(genericTypeVar.getTypePlaceholder()); params.add(new GenericRefType(genericTypeVar.getName(), new NullToken())); } return new RefType(name, params, new NullToken()); } /** * Die Superklasse im Kontext dieser ClassOrInterface Das bedeutet, dass generische Variablen als GenericRefTypes dargestellt sind */ public RefType getSuperClass() { return superClass; } public GenericDeclarationList getGenerics() { return this.genericClassParameters; } @Override public RefTypeOrTPHOrWildcardOrGeneric getReturnType() { return null; } public List getConstructors() { return constructors; } @Override public void accept(ASTVisitor visitor) { visitor.visit(this); } public Collection getSuperInterfaces() { return implementedInterfaces; } public String toString() { return this.name.toString() + this.genericClassParameters.toString(); } }