/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * JavaClassRep.java * Creation date: Oct 8, 2003. * By: Edward Lam */ package org.openquark.cal.internal.javamodel; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.openquark.cal.internal.javamodel.JavaStatement.MultiLineComment; import org.openquark.cal.services.Assert; /** * A representation of a Java class. * This doesn't include bits like imports or class comments. * @author Edward Lam */ public class JavaClassRep { /** * Constants used to indicate the relation of this class to the use * of assertsions. These are used to differentiate * between the different situations of classes containing asserts. * The return value of containsAsserts() will be one of or a * combination of these values. */ public static final int ASSERTS_UNKNOWN = 0x01; public static final int ASSERTS_NONE = 0x02; public static final int ASSERTS_IN_CLASS = 0x04; public static final int ASSERTS_IN_INNER_CLASS = 0x08; /** The name of the class. */ private final JavaTypeName className; /** The name of the super class. */ private final JavaTypeName superclassName; /** The modifiers for the class, as defined by the constants in java.lang.reflect.Modifier. */ private final int modifiers; /** The names of any interfaces implemented by the class. */ private final JavaTypeName[] interfaces; /** (List of JavaFieldDeclaration) The class's field declarations. */ private final List<JavaFieldDeclaration> fieldDeclarations = new ArrayList<JavaFieldDeclaration>(); /** (List of JavaConstructor) The class's constructors. */ private final List<JavaConstructor> constructors = new ArrayList<JavaConstructor>(); /** (List of JavaMethod) The class's methods. */ private final List<JavaMethod> methods = new ArrayList<JavaMethod>(); /** (List of JavaClassRep) Inner classes. */ private final List<JavaClassRep> innerClasses = new ArrayList<JavaClassRep>(); /** Flag used to indicate the association of this class with asssert. * It will be one or a combination of the ASSERTS_... constants. */ private int assertionContainment = ASSERTS_UNKNOWN; /** The JavaDoc for this class. May be null.*/ private JavaStatement.JavaDocComment javaDocComment = null; /** A comment to go at the beginning of the class source. * Usually used for copyright notice, etc. May be null. */ private MultiLineComment comment = null; /** A List containing all the top level elements in the class * this includes constructors, methods, fields, and inner classes. */ private final List<Object> classContent = new ArrayList<Object>(); /** * Constructor for a JavaClassRep * @param className the name of the class * @param superclassName the name of the super class * @param modifiers modifiers for the class, as defined by the constants in java.lang.reflect.Modifier. * @param interfaces names of any interfaces implemented by this class. */ public JavaClassRep(JavaTypeName className, JavaTypeName superclassName, int modifiers, JavaTypeName[] interfaces) { Assert.isNotNull(className); Assert.isNotNull(superclassName); Assert.isNotNull(interfaces); this.className = className; this.superclassName = superclassName; this.modifiers = modifiers; this.interfaces = interfaces.clone(); } /** * Add a field declaration to the class representation. * @param fieldDeclaration the field declaration to add. */ public void addFieldDeclaration(JavaFieldDeclaration fieldDeclaration) { Assert.isNotNull(fieldDeclaration); fieldDeclarations.add(fieldDeclaration); classContent.add(fieldDeclaration); } /** * Add a constructor to the class representation. * @param javaConstructor the constructor to add. */ public void addConstructor(JavaConstructor javaConstructor) { Assert.isNotNull(javaConstructor); if (!isConstructorNameCompatible(javaConstructor)) { throw new IllegalArgumentException("The constructor name " + javaConstructor.getConstructorName() + " is not compatible with the class name " + getClassName()); } constructors.add(javaConstructor); classContent.add(javaConstructor); } /** * @return true if the given java constructor has a name that is compatible with this class representation. * For example, if the class name is "com.xyz.Foo", then the constructor's name must be "Foo". * If the class name is "com.xyz.Foo$Bar", then the constructor's name could be "Bar" (because this class * may be an inner class) or "Foo$Bar" (if this class is a top-level class). */ private boolean isConstructorNameCompatible(final JavaConstructor javaConstructor) { if (getClassName() instanceof JavaTypeName.Reference.Object) { final JavaTypeName.Reference.Object className = (JavaTypeName.Reference.Object)getClassName(); // base name of "com.xyz.Foo" is "Foo", of "com.xyz.Foo$Bar" is "Bar" final String baseName = className.getBaseName(); // internal unqualified name of "com.xyz.Foo" is "Foo", of "com.xyz.Foo$Bar" is "Foo$Bar" final String internalUnqualifiedName = className.getInternalUnqualifiedName(); final String constructorName = javaConstructor.getConstructorName(); if (!baseName.equals(constructorName) && !internalUnqualifiedName.equals(constructorName)) { return false; } } return true; } /** * Add a method to the class representation. * @param javaMethod the method to add. */ public void addMethod(JavaMethod javaMethod) { Assert.isNotNull(javaMethod); methods.add(javaMethod); classContent.add(javaMethod); } /** * Add an inner class to the class representation. * @param innerClassRep the inner class to add */ public void addInnerClass(JavaClassRep innerClassRep) { Assert.isNotNull(innerClassRep); innerClasses.add(innerClassRep); classContent.add(innerClassRep); } public void addComment(MultiLineComment comment) { assert (comment != null) : "Null comment in JavaClassRep.addComment"; classContent.add(comment); } /** * Get the name of the class. * @return JavaTypeName */ public JavaTypeName getClassName() { return className; } /** * Get the name of the super class. * @return JavaTypeName */ public JavaTypeName getSuperclassName() { return superclassName; } /** * Get the modifiers for the class. * @return int modifiers for the class, as defined by the constants in java.lang.reflect.Modifier. */ public int getModifiers() { return modifiers; } /** * @return int the number of implemented interfaces. */ public int getNInterfaces() { return interfaces.length; } public JavaTypeName getInterface(int n) { return interfaces[n]; } public int getNFieldDeclarations() { return fieldDeclarations.size(); } public JavaFieldDeclaration getFieldDeclaration(int n) { return fieldDeclarations.get(n); } public int getNConstructors() { return constructors.size(); } public JavaConstructor getConstructor(int n) { return constructors.get(n); } public int getNMethods() { return methods.size(); } public JavaMethod getMethod(int n) { return methods.get(n); } public int getNInnerClasses() { return innerClasses.size(); } public JavaClassRep getInnerClass(int n) { return innerClasses.get(n); } /** * @return true if this JavaClassRep contains a static field that has an initializer. */ public boolean hasInitializedStaticField() { int nFields = getNFieldDeclarations(); for (int i = 0; i < nFields; ++i) { JavaFieldDeclaration fieldDecl = getFieldDeclaration(i); JavaExpression initializer = fieldDecl.getInitializer(); if (initializer != null && Modifier.isStatic(fieldDecl.getModifiers())) { return true; } } return false; } /** * @return true if this is an inner class. */ public boolean isInnerClass () { return getClassName().getJVMInternalName().indexOf('$') >= 0; } /** * @return representation of this JavaClassRep for debug purposes only. */ @Override public String toString() { return JavaSourceGenerator.toDebugString(this); } /** * Accepts the visitation of a visitor, which implements the * JavaModelVisitor interface. This abstract method is to be overriden * by each concrete subclass so that the corrent visit method on the * visitor may be called based upon the type of the element being * visited. Each concrete subclass of JavaExpression should correspond * one-to-one with a visit method declaration in the SourceModelVisitor * interface. * <p> * * As the JavaModelVisitor follows a more general visitor pattern * where arguments can be passed into the visit methods and return * values obtained from them, this method passes through the argument * into the visit method, and returns as its return value the return * value of the visit method. * <p> * * Nonetheless, for a significant portion of the common cases, the state of the * visitation can simply be kept as member variables within the visitor itself, * thereby eliminating the need to use the argument and return value of the * visit methods. In these scenarios, the recommended approach is to use * {@link Void} as the type argument for both <code>T</code> and <code>R</code>, and * pass in null as the argument, and return null as the return value. * <p> * * @see JavaModelVisitor * * @param <T> the argument type. If the visitation argument is not used, specify {@link Void}. * @param <R> the return type. If the return value is not used, specify {@link Void}. * * @param visitor * the visitor * @param arg * the argument to be passed to the visitor's visitXXX method * @return the return value of the visitor's visitXXX method */ public <T, R> R accept(JavaModelVisitor<T, R> visitor, T arg) { return visitor.visitJavaClassRep(this, arg); } /** * Return a value describing how this class contains assertions. * @return one or a combination of the ASSERTS_... constants. */ public int getAssertionContainment() { return assertionContainment; } /** * Indicate that this class contains assertions. */ public void setContainsAssertions () { assertionContainment ^= ASSERTS_UNKNOWN; assertionContainment |= ASSERTS_IN_CLASS; } /** * Indicate that this class contains inner classes * which contain assertions. */ public void setInnerClassContainsAssertions () { assertionContainment ^= ASSERTS_UNKNOWN; assertionContainment |= ASSERTS_IN_INNER_CLASS; } public JavaStatement.JavaDocComment getJavaDoc() { return javaDocComment; } public void setJavaDoc(JavaStatement.JavaDocComment classComment) { this.javaDocComment = classComment; } public MultiLineComment getComment() { return comment; } public void setComment(MultiLineComment comment) { this.comment = comment; } public List<Object> getClassContent() { return classContent; } }