/* * Copyright (C) 2011 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jboss.errai.codegen.meta.impl.build; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.jboss.errai.codegen.BlockStatement; import org.jboss.errai.codegen.Comment; import org.jboss.errai.codegen.Context; import org.jboss.errai.codegen.DefParameters; import org.jboss.errai.codegen.InnerClass; import org.jboss.errai.codegen.Variable; import org.jboss.errai.codegen.builder.Builder; import org.jboss.errai.codegen.builder.callstack.LoadClassReference; import org.jboss.errai.codegen.builder.impl.Scope; import org.jboss.errai.codegen.literal.AnnotationLiteral; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.meta.MetaClassFactory; import org.jboss.errai.codegen.meta.MetaConstructor; import org.jboss.errai.codegen.meta.MetaField; import org.jboss.errai.codegen.meta.MetaMethod; import org.jboss.errai.codegen.meta.MetaParameterizedType; import org.jboss.errai.codegen.meta.MetaTypeVariable; import org.jboss.errai.codegen.meta.impl.AbstractMetaClass; import org.jboss.errai.codegen.util.GenUtil; import org.jboss.errai.codegen.util.PrettyPrinter; import com.google.common.collect.ObjectArrays; /** * @author Mike Brock <cbrock@redhat.com> * @author Christian Sadilek <csadilek@redhat.com> */ public class BuildMetaClass extends AbstractMetaClass<Object> implements Builder { private Context context; private final String className; private MetaClass superClass; private List<MetaClass> interfaces = new ArrayList<>(); private Scope scope; private boolean isArray; private int dimensions; private boolean isInterface; private boolean isAbstract; private boolean isFinal; private boolean isStatic; private boolean isInner; private final BlockStatement staticInitializer = new BlockStatement(); private final BlockStatement instanceInitializer = new BlockStatement(); private List<Annotation> annotations = new ArrayList<>(); private List<InnerClass> innerClasses = new ArrayList<>(); private List<BuildMetaMethod> methods = new ArrayList<>(); private List<BuildMetaField> fields = new ArrayList<>(); private List<BuildMetaConstructor> constructors = new ArrayList<>(); private List<MetaTypeVariable> typeVariables = new ArrayList<>(); private MetaClass reifiedFormOf; private String classComment; public BuildMetaClass(final Context context, final String name) { super(null); this.className = name; if (context != null) { this.context = Context.create(context); this.context.addVariable(Variable.create("this", this)); context.attachClass(this); } } private BuildMetaClass shallowCopy() { final BuildMetaClass copy = new BuildMetaClass(context, className); copy.scope = scope; copy.superClass = superClass; copy.interfaces = interfaces; copy.isArray = isArray; copy.dimensions = dimensions; copy.isInterface = isInterface; copy.isAbstract = isAbstract; copy.isFinal = isFinal; copy.isStatic = isStatic; copy.isInner = isInner; copy.innerClasses = innerClasses; copy.annotations = annotations; copy.methods = methods; copy.fields = fields; copy.constructors = constructors; copy.typeVariables = typeVariables; copy.reifiedFormOf = reifiedFormOf; return copy; } private BuildMetaClass _erasedCache; @Override public MetaClass getErased() { if (_erasedCache != null) { return _erasedCache; } else if (typeVariables == null || typeVariables.isEmpty()) { _erasedCache = this; return _erasedCache; } else { _erasedCache = shallowCopy(); _erasedCache.typeVariables = Collections.emptyList(); return _erasedCache; } } String _nameCache; @Override public String getName() { if (_nameCache != null) return _nameCache; final int idx = className.lastIndexOf('.'); if (idx != -1) { return _nameCache = className.substring(idx + 1); } else { return _nameCache = className; } } @Override public String getFullyQualifiedName() { return className; } @Override public String getCanonicalName() { return className; } @Override public String getInternalName() { final String internalName = "L" + className.replace("\\.", "/") + ";"; if (isArray) { final StringBuilder buf = new StringBuilder(""); for (int i = 0; i < dimensions; i++) { buf.append("["); } return buf.append(internalName).toString(); } else { return internalName; } } @Override public String getPackageName() { final int idx = className.lastIndexOf("."); if (idx != -1) { return className.substring(0, idx); } return ""; } private MetaMethod[] _methodsCache; @Override public MetaMethod[] getMethods() { if (_methodsCache != null) return _methodsCache; final MetaMethod[] methodArray = methods.toArray(new MetaMethod[methods.size()]); final MetaMethod[] outputMethods; if (superClass != null) { final List<MetaMethod> methodList = new ArrayList<>(); for (final MetaMethod m : superClass.getMethods()) { if (_getMethod(methodArray, m.getName(), GenUtil.fromParameters(m.getParameters())) == null) { methodList.add(m); } } methodList.addAll(Arrays.asList(methodArray)); outputMethods = methodList.toArray(new MetaMethod[methodList.size()]); } else { outputMethods = methodArray; } return _methodsCache = outputMethods; } @Override public MetaMethod[] getDeclaredMethods() { return getMethods(); } private MetaField[] _fieldsCache; @Override public MetaField[] getFields() { if (_fieldsCache == null) { _fieldsCache = fields.stream().filter(field -> field.isPublic()).toArray(size -> new MetaField[size]); if (superClass != null) { _fieldsCache = ObjectArrays.concat(_fieldsCache, superClass.getFields(), MetaField.class); } } return _fieldsCache; } private MetaField[] _declaredFieldsCache; @Override public MetaField[] getDeclaredFields() { if (_declaredFieldsCache == null) { _declaredFieldsCache = fields.toArray(new MetaField[fields.size()]); } return _declaredFieldsCache; } @Override public MetaField getField(final String name) { return fields.stream().filter(f -> f.getName().equals(name)).findFirst().orElse(null); } @Override public MetaField getDeclaredField(final String name) { return getField(name); } private MetaConstructor[] _constructorsCache; @Override public MetaConstructor[] getConstructors() { if (_constructorsCache != null) return _constructorsCache; if (constructors.isEmpty()) { // add an empty no-arg constructor final BuildMetaConstructor buildMetaConstructor = new BuildMetaConstructor(this, new BlockStatement(), DefParameters.none()); buildMetaConstructor.setScope(Scope.Public); return _constructorsCache = new MetaConstructor[]{buildMetaConstructor}; } else { return _constructorsCache = constructors.toArray(new MetaConstructor[constructors.size()]); } } @Override public MetaConstructor[] getDeclaredConstructors() { return getConstructors(); } @Override public MetaClass[] getDeclaredClasses() { final MetaClass[] declaredClass = new MetaClass[innerClasses.size()]; int i = 0; for (final InnerClass innerClass : innerClasses) { declaredClass[i++] = innerClass.getType(); } return declaredClass; } @Override public MetaClass[] getInterfaces() { return interfaces.toArray(new MetaClass[interfaces.size()]); } @Override public MetaClass getSuperClass() { return superClass; } @Override public MetaClass getComponentType() { if (isArray) { final BuildMetaClass compType = shallowCopy(); if (dimensions > 1) { compType.setDimensions(dimensions - 1); } else { compType.setArray(false); compType.setDimensions(0); } return compType; } return null; } @Override public boolean isPrimitive() { return false; } @Override public boolean isVoid() { return false; } @Override public boolean isInterface() { return isInterface; } @Override public boolean isAbstract() { return isAbstract; } @Override public boolean isArray() { return false; } @Override public boolean isEnum() { return false; } @Override public boolean isAnnotation() { return false; } @Override public boolean isPublic() { return scope == Scope.Public; } @Override public boolean isPrivate() { return scope == Scope.Private; } @Override public boolean isProtected() { return scope == Scope.Protected; } @Override public boolean isFinal() { return isFinal; } @Override public boolean isStatic() { return isStatic; } @Override public boolean isSynthetic() { return false; } @Override public boolean isAnonymousClass() { return false; } @Override public Annotation[] getAnnotations() { return annotations.toArray(new Annotation[annotations.size()]); } @Override public MetaTypeVariable[] getTypeParameters() { return typeVariables.toArray(new MetaTypeVariable[typeVariables.size()]); } public void setSuperClass(final MetaClass superClass) { this.superClass = superClass; } public void setInterfaces(final List<MetaClass> interfaces) { this.interfaces = interfaces; } public void setInterface(final boolean anInterface) { isInterface = anInterface; } public void setAbstract(final boolean anAbstract) { isAbstract = anAbstract; } public void setArray(final boolean array) { isArray = array; } public void setDimensions(final int dimensions) { this.dimensions = dimensions; } public int getDimensions() { return dimensions; } public void setFinal(final boolean aFinal) { isFinal = aFinal; } public void setStatic(final boolean aStatic) { isStatic = aStatic; } public void setInner(final boolean aInner) { isInner = aInner; } public void setScope(final Scope scope) { this.scope = scope; } public void setContext(final Context context) { this.context = context; } public Context getContext() { return context; } public void addAnnotation(final Annotation annotation) { annotations.add(annotation); } public void addInnerClass(final InnerClass innerClass) { innerClasses.add(innerClass); } public void addInterface(final MetaClass interfaceClass) { interfaces.add(interfaceClass); } public void addConstructor(final BuildMetaConstructor constructor) { _constructorsCache = null; constructors.add(constructor); } public void addMethod(final BuildMetaMethod method) { _methodsCache = null; methods.add(method); } public void addField(final BuildMetaField field) { _fieldsCache = null; if (getDeclaredField(field.getName()) != null) { throw new IllegalStateException("field '" + field.getName() + "' already exists"); } fields.add(field); } public void addTypeVariable(final MetaTypeVariable typeVariable) { typeVariables.add(typeVariable); } public void setParameterizedType(final MetaParameterizedType parameterizedType) { this.parameterizedType = parameterizedType; } public boolean isReifiedForm() { return reifiedFormOf != null; } public MetaClass getReifiedFormOf() { return reifiedFormOf; } public void setReifiedFormOf(final MetaClass reifiedFormOf) { this.reifiedFormOf = reifiedFormOf; } @Override public MetaMethod getBestMatchingMethod(final String name, final Class... parameters) { return super.getBestMatchingMethod(name, parameters); } @Override public MetaMethod getBestMatchingMethod(final String name, final MetaClass... parameters) { return super.getBestMatchingMethod(name, parameters); } @Override public MetaMethod getBestMatchingStaticMethod(final String name, final Class... parameters) { return super.getBestMatchingStaticMethod(name, parameters); } @Override public MetaMethod getBestMatchingStaticMethod(final String name, final MetaClass... parameters) { return super.getBestMatchingStaticMethod(name, parameters); } @Override public MetaConstructor getBestMatchingConstructor(final Class... parameters) { return super.getBestMatchingConstructor(parameters); } @Override public MetaConstructor getBestMatchingConstructor(final MetaClass... parameters) { return super.getBestMatchingConstructor(parameters); } @Override public MetaClass asArrayOf(final int dimensions) { final BuildMetaClass copy = shallowCopy(); copy.setArray(true); copy.setDimensions(dimensions); return copy; } public void setClassComment(final String classComment) { this.classComment = classComment; } public BlockStatement getStaticInitializer() { return staticInitializer; } public BlockStatement getInstanceInitializer() { return instanceInitializer; } String generatedCache; @Override public String toJavaString() { return toJavaString(this.context); } public String toJavaString(final Context context) { if (context == null) { throw new IllegalArgumentException("Cannot generate Java String with null Context."); } if (generatedCache != null) return generatedCache; final StringBuilder buf = new StringBuilder(512); if (classComment != null) { buf.append(new Comment(classComment).generate(null)).append("\n"); } context.addVariable(Variable.create("this", this)); MetaClass sCls = getSuperClass(); if (sCls != null) { do { for (final MetaField metaField : sCls.getDeclaredFields()) { if (!metaField.isPrivate()) { context.addVariable(Variable.create(metaField.getName(), metaField.getType())); } } } while ((sCls = sCls.getSuperClass()) != null && !sCls.getName().equals("java.lang.Object")); } for (final BuildMetaField buildMetaField : fields) { context.addVariable(Variable.create(buildMetaField.getName(), buildMetaField.getType())); } for (final Annotation a : getAnnotations()) { buf.append(new AnnotationLiteral(a).getCanonicalString(context)); buf.append(" "); } if (!annotations.isEmpty()) buf.append("\n"); buf.append("\n"); buf.append(scope.getCanonicalName()); if (isAbstract) { buf.append(" abstract"); } if (isStatic) { buf.append(" static"); } if (isInterface()) { buf.append(" interface ").append(getName()); } else { buf.append(" class ").append(getName()); } final List<MetaClass> interfacesToRender = new ArrayList<>(interfaces.size() + 1); interfacesToRender.addAll(interfaces); if (getSuperClass() != null) { if (isInterface()) { interfacesToRender.add(getSuperClass()); } else { buf.append(" extends ").append(LoadClassReference.getClassReference(getSuperClass(), context)); } } if (!interfacesToRender.isEmpty()) { if (isInterface()) { buf.append(" extends "); } else { buf.append(" implements "); } final Iterator<MetaClass> metaClassIterator = interfacesToRender.iterator(); while (metaClassIterator.hasNext()) { buf.append(LoadClassReference.getClassReference(metaClassIterator.next(), context)); if (metaClassIterator.hasNext()) buf.append(", "); } } superClass = (superClass != null) ? superClass : MetaClassFactory.get(Object.class); context.addVariable(Variable.create("super", superClass)); buf.append(" {"); if (!staticInitializer.isEmpty()) { buf.append("\nstatic {\n"); buf.append(staticInitializer.generate(context)); buf.append("\n}"); } if (!instanceInitializer.isEmpty()) { buf.append("\n{\n"); buf.append(instanceInitializer.generate(context)); buf.append("\n}\n"); } buf.append(membersToString()); final StringBuilder headerBuffer = new StringBuilder(128); if (!getPackageName().isEmpty() && !isInner) headerBuffer.append("package ").append(getPackageName()).append(";\n"); if (!context.getRequiredImports().isEmpty()) headerBuffer.append("\n"); if (!isInner) { for (final String cls : context.getRequiredImports()) { if (getFullyQualifiedName().equals(cls)) { continue; } else { final String pkg = getPackageName(); if (cls.startsWith(pkg)) { if (cls.substring(pkg.length() + 1).indexOf('.') == -1) { continue; } } } headerBuffer.append("import ").append(cls).append(";\n"); } } return generatedCache = PrettyPrinter.prettyPrintJava(headerBuffer.toString() + buf.append("}\n").toString()); } private List<Builder> diffList(final List<? extends Builder> original, final List<? extends Builder> against) { final List<Builder> copyList = new ArrayList<>(original); copyList.removeAll(against); return copyList; } public String membersToString() { if (context == null) { throw new IllegalStateException("Cannot call membersToString when no context is set."); } final StringBuilder buf = new StringBuilder(512); final Iterator<InnerClass> innerClassIterator = innerClasses.iterator(); while (innerClassIterator.hasNext()) { buf.append(innerClassIterator.next().generate(context)); if (innerClassIterator.hasNext()) buf.append("\n"); } if (!innerClasses.isEmpty()) buf.append("\n"); final Iterator<? extends Builder> constructorIterator = constructors.iterator(); while (constructorIterator.hasNext()) { buf.append(constructorIterator.next().toJavaString()); if (constructorIterator.hasNext()) buf.append("\n"); } if (!constructors.isEmpty()) buf.append("\n"); final Iterator<? extends Builder> methodsIterator = methods.iterator(); while (methodsIterator.hasNext()) { buf.append(methodsIterator.next().toJavaString()); if (methodsIterator.hasNext()) buf.append("\n"); } final StringBuilder fieldRenderBuffer = renderFieldBuffer(); if (!fields.isEmpty()) { return fieldRenderBuffer.toString() + buf.toString(); } else { return buf.toString(); } } private StringBuilder renderFieldBuffer() { List<Builder> toBuild = new ArrayList<>(fields); final List<Builder> toIterate = new ArrayList<>(toBuild); int initialFieldSize; final boolean _permissiveMode = GenUtil.isPermissiveMode(); GenUtil.setPermissiveMode(true); final StringBuilder fieldRenderBuffer = new StringBuilder(100); final int bufferStart; if (!fields.isEmpty()) { fieldRenderBuffer.append('\n'); bufferStart = 1; } else { bufferStart = 0; } do { initialFieldSize = fields.size(); if (fieldRenderBuffer.length() > 0) { fieldRenderBuffer.delete(bufferStart, fieldRenderBuffer.length()); } final Iterator<Builder> fieldIterator = toIterate.iterator(); while (fieldIterator.hasNext()) { fieldRenderBuffer.append(fieldIterator.next().toJavaString()); if (fieldIterator.hasNext()) fieldRenderBuffer.append("\n"); } toIterate.addAll(0, diffList(fields, toBuild)); toBuild = new ArrayList<>(fields); } while (initialFieldSize < fields.size()); if (!fields.isEmpty()) { fieldRenderBuffer.append('\n'); } GenUtil.setPermissiveMode(_permissiveMode); if (!_permissiveMode) { toBuild.stream().forEach(builder -> builder.toJavaString()); } return fieldRenderBuffer; } @Override public boolean equals(final Object o) { if (this == o) return true; if (!(o instanceof BuildMetaClass)) return false; if (!super.equals(o)) return false; final BuildMetaClass that = (BuildMetaClass) o; if (dimensions != that.dimensions) return false; if (isAbstract != that.isAbstract) return false; if (isArray != that.isArray) return false; if (isFinal != that.isFinal) return false; if (isInner != that.isInner) return false; if (isInterface != that.isInterface) return false; if (isStatic != that.isStatic) return false; if (!Arrays.equals(_constructorsCache, that._constructorsCache)) return false; if (!Arrays.equals(_fieldsCache, that._fieldsCache)) return false; if (!Arrays.equals(_methodsCache, that._methodsCache)) return false; if (_nameCache != null ? !_nameCache.equals(that._nameCache) : that._nameCache != null) return false; if (className != null ? !className.equals(that.className) : that.className != null) return false; if (constructors != null ? !constructors.equals(that.constructors) : that.constructors != null) return false; if (context != null ? !context.equals(that.context) : that.context != null) return false; if (fields != null ? !fields.equals(that.fields) : that.fields != null) return false; if (generatedCache != null ? !generatedCache.equals(that.generatedCache) : that.generatedCache != null) return false; if (interfaces != null ? !interfaces.equals(that.interfaces) : that.interfaces != null) return false; if (methods != null ? !methods.equals(that.methods) : that.methods != null) return false; if (reifiedFormOf != null ? !reifiedFormOf.equals(that.reifiedFormOf) : that.reifiedFormOf != null) return false; if (scope != that.scope) return false; if (superClass != null ? !superClass.equals(that.superClass) : that.superClass != null) return false; if (typeVariables != null ? !typeVariables.equals(that.typeVariables) : that.typeVariables != null) return false; return true; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (context != null ? context.hashCode() : 0); result = 31 * result + (className != null ? className.hashCode() : 0); result = 31 * result + (superClass != null ? superClass.hashCode() : 0); result = 31 * result + (interfaces != null ? interfaces.hashCode() : 0); result = 31 * result + (scope != null ? scope.hashCode() : 0); result = 31 * result + (isArray ? 1 : 0); result = 31 * result + dimensions; result = 31 * result + (isInterface ? 1 : 0); result = 31 * result + (isAbstract ? 1 : 0); result = 31 * result + (isFinal ? 1 : 0); result = 31 * result + (isStatic ? 1 : 0); result = 31 * result + (isInner ? 1 : 0); result = 31 * result + (methods != null ? methods.hashCode() : 0); result = 31 * result + (fields != null ? fields.hashCode() : 0); result = 31 * result + (constructors != null ? constructors.hashCode() : 0); result = 31 * result + (typeVariables != null ? typeVariables.hashCode() : 0); result = 31 * result + (reifiedFormOf != null ? reifiedFormOf.hashCode() : 0); result = 31 * result + (_nameCache != null ? _nameCache.hashCode() : 0); result = 31 * result + (_methodsCache != null ? Arrays.hashCode(_methodsCache) : 0); result = 31 * result + (_fieldsCache != null ? Arrays.hashCode(_fieldsCache) : 0); result = 31 * result + (_constructorsCache != null ? Arrays.hashCode(_constructorsCache) : 0); result = 31 * result + (generatedCache != null ? generatedCache.hashCode() : 0); return result; } }