/*
* Copyright 2006 Ralf Joachim
*
* 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.exolab.javasource;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Vector;
import org.exolab.castor.builder.SourceGenerator;
/**
* A abstract base class for representations of the Java Source code for a Java Class.
*
* @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
* @version $Revision: 6668 $ $Date: 2005-05-08 12:32:06 -0600 (Sun, 08 May 2005) $
* @since 1.1
*/
public abstract class AbstractJClass extends JStructure {
/** The source code for static initialization. */
private JSourceCode _staticInitializer;
/** The list of member variables (fields) of this JClass. */
private Map<String, JField> _fields = new LinkedHashMap<String, JField>();
/** The list of member constants of this {@link JClass}. */
private Map<String, JConstant> _constants = new LinkedHashMap<String, JConstant>();
/** The list of constructors for this JClass. */
private Vector<JConstructor> _constructors;
/** The list of methods of this JClass. */
private Vector<JMethod> _methods;
/** A collection of inner classes for this JClass. */
private Vector<JClass> _innerClasses;
private Vector<String> _sourceCodeEntries = new Vector<String>();
/**
* Returns a collection of (complete) source code fragments.
* @return A collection of (complete) source code fragments.
*/
public String[] getSourceCodeEntries() {
return _sourceCodeEntries.toArray(new String[_sourceCodeEntries.size()]);
}
/**
* Creates a new AbstractJClass with the given name.
*
* @param name The name of the AbstractJClass to create.
*/
protected AbstractJClass(final String name) {
super(name);
_staticInitializer = new JSourceCode();
_constructors = new Vector<JConstructor>();
_methods = new Vector<JMethod>();
_innerClasses = null;
//-- initialize default Java doc
getJDocComment().appendComment("Class " + getLocalName() + ".");
}
//--------------------------------------------------------------------------
/**
* Returns the JSourceCode for the static initializer of this JClass.
*
* @return The JSourceCode for the static initializer of this JClass.
*/
public final JSourceCode getStaticInitializationCode() {
return _staticInitializer;
}
/**
* {@inheritDoc}
*/
public final JField getField(final String name) {
return _fields.get(name);
}
/**
* {@inheritDoc}
*/
public final JConstant getConstant(final String name) {
return _constants.get(name);
}
/**
* {@inheritDoc}
*/
public final JField[] getFields() {
return _fields.values().toArray(new JField[_fields.size()]);
}
/**
* {@inheritDoc}
*/
public final JConstant[] getConstants() {
return _constants.values().toArray(new JConstant[_constants.size()]);
}
/**
* Returns the amount of fields.
* @return The amount of fields.
*/
public final int getFieldCount() {
return _fields.size();
}
/**
* Returns the amount of constants.
* @return The amount of constants.
*/
public final int getConstantCount() {
return _constants.size();
}
/**
* {@inheritDoc}
*/
public final void addField(final JField jField) {
if (jField == null) {
throw new IllegalArgumentException("Class members cannot be null");
}
String name = jField.getName();
if (_fields.get(name) != null) {
String nameToCompare = (name.startsWith("_")) ? name.substring(1) : name;
nameToCompare = nameToCompare.substring(0, 1).toUpperCase()
+ nameToCompare.substring(1);
if (JNaming.isReservedByCastor(nameToCompare)) {
String warn = "'" + nameToCompare + "' might conflict with a field name used"
+ " by Castor. If you get a complaint\nabout a duplicate name, you will"
+ " need to use a mapping file or change the name\nof the conflicting"
+ " schema element.";
System.out.println(warn);
}
String err = "Duplicate name found as a class member: " + name;
throw new IllegalArgumentException(err);
}
_fields.put(name, jField);
}
public final void addConstant(final JConstant jConstant) {
if (jConstant == null) {
throw new IllegalArgumentException("Class constants cannot be null");
}
String name = jConstant.getName();
if (_constants.get(name) != null) {
String nameToCompare = (name.startsWith("_")) ? name.substring(1) : name;
nameToCompare = nameToCompare.substring(0, 1).toUpperCase()
+ nameToCompare.substring(1);
if (JNaming.isReservedByCastor(nameToCompare)) {
String warn = "'" + nameToCompare + "' might conflict with a constant name used"
+ " by Castor. If you get a complaint\nabout a duplicate name, you will"
+ " need to use a mapping file or change the name\nof the conflicting"
+ " schema element.";
System.out.println(warn);
}
String err = "Duplicate name found as a class member: " + name;
throw new IllegalArgumentException(err);
}
_constants.put(name, jConstant);
}
/**
* Removes the field with the given name from this JClass.
*
* @param name The name of the field to remove.
* @return The JField if it was found and removed.
*/
public final JField removeField(final String name) {
if (name == null) { return null; }
JField field = _fields.remove(name);
//-- clean up imports
//-- NOT YET IMPLEMENTED
return field;
}
/**
* Removes the constant with the given name from this {@link JClass}.
*
* @param name The name of the constant to remove.
* @return The JConstant if it was found and removed.
*/
public final JConstant removeConstant(final String name) {
if (name == null) { return null; }
JConstant constant = _constants.remove(name);
//-- clean up imports
//-- NOT YET IMPLEMENTED
return constant;
}
/**
* Removes the given JField from this JClass.
*
* @param jField The JField to remove.
* @return true if the field was found and removed.
*/
public final boolean removeField(final JField jField) {
if (jField == null) { return false; }
Object field = _fields.get(jField.getName());
if (field == jField) {
_fields.remove(jField.getName());
return true;
}
//-- clean up imports
//-- NOT YET IMPLEMENTED
return false;
}
/**
* Removes the given {@link JConstant} from this {@link JClass}.
*
* @param jConstant The {@link JConstant} to remove.
* @return true if the constant was found and removed.
*/
public final boolean removeConstant(final JConstant jConstant) {
if (jConstant == null) { return false; }
Object constant = _constants.get(jConstant.getName());
if (constant == jConstant) {
_constants.remove(jConstant.getName());
return true;
}
//-- clean up imports
//-- NOT YET IMPLEMENTED
return false;
}
/**
* Creates a new JConstructor and adds it to this JClass.
*
* @return The newly created constructor.
*/
public final JConstructor createConstructor() {
return createConstructor(null);
}
/**
* Creates a new JConstructor and adds it to this JClass.
*
* @param params A list of parameters for this constructor.
* @return The newly created constructor.
*/
public final JConstructor createConstructor(final JParameter[] params) {
JConstructor cons = new JConstructor(this);
if (params != null) {
for (int i = 0; i < params.length; i++) {
cons.addParameter(params[i]);
}
}
addConstructor(cons);
return cons;
}
/**
* Returns the constructor at the specified index.
*
* @param index The index of the constructor to return.
* @return The JConstructor at the specified index.
*/
public final JConstructor getConstructor(final int index) {
return _constructors.elementAt(index);
}
/**
* Returns the an array of the JConstructors contained within this JClass.
*
* @return An array of JConstructor.
*/
public final JConstructor[] getConstructors() {
int size = _constructors.size();
JConstructor[] jcArray = new JConstructor[size];
for (int i = 0; i < _constructors.size(); i++) {
jcArray[i] = _constructors.elementAt(i);
}
return jcArray;
}
public final int getContructorsCount() {
return _constructors.size();
}
/**
* Adds the given Constructor to this classes list of constructors. The
* constructor must have been created with this JClass' createConstructor.
*
* @param constructor The constructor to add.
*/
public void addConstructor(final JConstructor constructor) {
if (constructor == null) {
throw new IllegalArgumentException("Constructors cannot be null");
}
if (constructor.getDeclaringClass() == this) {
/** check signatures (add later) **/
if (!_constructors.contains(constructor)) {
_constructors.addElement(constructor);
}
} else {
String err = "The given JConstructor was not created by this JClass";
throw new IllegalArgumentException(err);
}
}
/**
* Removes the given constructor from this JClass.
*
* @param constructor The JConstructor to remove.
* @return true if the constructor was removed, otherwise false.
*/
public final boolean removeConstructor(final JConstructor constructor) {
return _constructors.removeElement(constructor);
}
/**
* Returns an array of all the JMethods of this JClass.
*
* @return An array of all the JMethods of this JClass.
*/
public final JMethod[] getMethods() {
int size = _methods.size();
JMethod[] marray = new JMethod[size];
for (int i = 0; i < _methods.size(); i++) {
marray[i] = _methods.elementAt(i);
}
return marray;
}
/**
* Returns the first occurance of the method with the given name, starting
* from the specified index.
*
* @param name The name of the method to look for.
* @param startIndex The starting index to begin the search.
* @return The method if found, otherwise null.
*/
public final JMethod getMethod(final String name, final int startIndex) {
for (int i = startIndex; i < _methods.size(); i++) {
JMethod jMethod = _methods.elementAt(i);
if (jMethod.getName().equals(name)) { return jMethod; }
}
return null;
}
/**
* Returns the JMethod located at the specified index.
*
* @param index The index of the JMethod to return.
* @return The JMethod.
*/
public final JMethod getMethod(final int index) {
return _methods.elementAt(index);
}
public final int getMethodCount() {
return _methods.size();
}
/**
* Adds the given JMethod to this JClass.
*
* @param jMethod The JMethod to add.
* @param importReturnType true if we add the importReturnType to the class
* import lists. It could be useful to set it to false when all
* types are fully qualified.
*/
public final void addMethod(final JMethod jMethod, final boolean importReturnType) {
if (jMethod == null) {
throw new IllegalArgumentException("Class methods cannot be null");
}
//-- check method name and signatures *add later*
//-- keep method list sorted for esthetics when printing
//-- START SORT :-)
boolean added = false;
JModifiers modifiers = jMethod.getModifiers();
if (modifiers.isAbstract()) {
getModifiers().setAbstract(true);
}
for (int i = 0; i < _methods.size(); i++) {
JMethod tmp = _methods.elementAt(i);
//-- first compare modifiers
if (tmp.getModifiers().isPrivate()) {
if (!modifiers.isPrivate()) {
_methods.insertElementAt(jMethod, i);
added = true;
break;
}
}
//-- compare names
if (jMethod.getName().compareTo(tmp.getName()) < 0) {
_methods.insertElementAt(jMethod, i);
added = true;
break;
}
}
//-- END SORT
if (!added) { _methods.addElement(jMethod); }
}
/**
* Adds the given JMethod to this JClass.
*
* @param jMethod The JMethod to add.
*/
public final void addMethod(final JMethod jMethod) {
addMethod(jMethod, true);
}
/**
* Adds the given array of JMethods to this JClass.
*
* @param jMethods The JMethod[] to add.
*/
public final void addMethods(final JMethod[] jMethods) {
for (int i = 0; i < jMethods.length; i++) { addMethod(jMethods[i]); }
}
/**
* Removes the given method from this JClass.
*
* @param method The JMethod to remove.
* @return true if the method was removed, otherwise false.
*/
public final boolean removeMethod(final JMethod method) {
return _methods.removeElement(method);
}
/**
* Creates and returns an inner-class for this JClass.
*
* @param localname The name of the class (no package name).
* @return the new JClass.
*/
public final JClass createInnerClass(final String localname) {
if (localname == null) {
String err = "argument 'localname' must not be null.";
throw new IllegalArgumentException(err);
}
if (localname.indexOf('.') >= 0) {
String err = "The name of an inner-class must not contain a package name.";
throw new IllegalArgumentException(err);
}
String classname = getPackageName();
if (classname != null) {
classname = classname + "." + localname;
} else {
classname = localname;
}
JClass innerClass = new JInnerClass(classname);
if (_innerClasses == null) {
_innerClasses = new Vector<JClass>();
}
_innerClasses.addElement(innerClass);
return innerClass;
}
/**
* Returns an array of JClass (the inner classes) contained within this
* JClass.
*
* @return An array of JClass contained within this JClass.
*/
public final JClass[] getInnerClasses() {
if (_innerClasses != null) {
int size = _innerClasses.size();
JClass[] carray = new JClass[size];
_innerClasses.copyInto(carray);
return carray;
}
return new JClass[0];
}
public final int getInnerClassCount() {
if (_innerClasses != null) {
return _innerClasses.size();
}
return 0;
}
/**
* Removes the given inner-class (JClass) from this JClass.
*
* @param jClass The JClass (inner-class) to remove.
* @return true if the JClass was removed, otherwise false.
*/
public final boolean removeInnerClass(final JClass jClass) {
if (_innerClasses != null) {
return _innerClasses.removeElement(jClass);
}
return false;
}
//--------------------------------------------------------------------------
/**
* {@inheritDoc}
* @deprecated Please use the Velocity-template based approach instead.
* @see SourceGenerator#setJClassPrinterType(String)
*/
public final void print(final JSourceWriter jsw) {
print(jsw, false);
}
/**
* Prints the source code for this JClass to the given JSourceWriter.
*
* @param classOnly If true, the file header, package declaration, and
* imports are not printed.
* @param jsw The JSourceWriter to print to. Must not be null.
*
* @deprecated Please use the Velocity-template based approach instead.
* @see SourceGenerator#setJClassPrinterType(String)
*/
public abstract void print(final JSourceWriter jsw, final boolean classOnly);
/**
* Writes to the JSourceWriter the headers for this class file. Headers
* include the comment-header, the package declaration, and the imports.
*
* @param jsw The JSourceWriter to be used.
*/
protected final void printClassHeaders(final JSourceWriter jsw) {
printHeader(jsw);
printPackageDeclaration(jsw);
//-- get imports from inner-classes
Vector<String> removeImports = null;
if ((_innerClasses != null) && (_innerClasses.size() > 0)) {
removeImports = new Vector<String>();
for (int i = 0; i < _innerClasses.size(); i++) {
JClass iClass = _innerClasses.elementAt(i);
Enumeration<String> enumeration = iClass.getImports();
while (enumeration.hasMoreElements()) {
String classname = enumeration.nextElement();
int paramTypeIndex = classname.indexOf("<Object>");
if (paramTypeIndex != -1) {
classname = classname.substring(0, paramTypeIndex - 1);
}
if (!hasImport(classname)) {
addImport(classname);
removeImports.addElement(classname);
}
}
}
}
printImportDeclarations(jsw);
//-- remove imports from inner-classes, if necessary
if (removeImports != null) {
for (int i = 0; i < removeImports.size(); i++) {
removeImport(removeImports.elementAt(i));
}
}
}
/**
* Writes to the {@link JSourceWriter} the constant definitions of this class.
*
* @param jsw The JSourceWriter to be used.
*/
protected final void printConstantDefinitions(final JSourceWriter jsw) {
if (!_constants.isEmpty()) {
jsw.writeln();
jsw.writeln(" //--------------------------/");
jsw.writeln(" //- Class/Member constants -/");
jsw.writeln("//--------------------------/");
jsw.writeln();
}
for (JConstant constant : _constants.values()) {
printAbstractJField(jsw, constant);
}
}
/**
* Prints an {@link AbstractJField} instance to the given {@link JSourceWriter}.
*
* @param jsw The {@link JSourceWriter} to print to.
* @param field The field to print.
*/
private void printAbstractJField(final JSourceWriter jsw, final AbstractJField field) {
//-- print Java comment
JDocComment comment = field.getComment();
if (comment != null) { comment.print(jsw); }
//-- print Annotations
field.printAnnotations(jsw);
// -- print member
jsw.write(field.getModifiers().toString());
jsw.write(' ');
JType type = field.getType();
String typeName = type.toString();
//-- for esthetics use short name in some cases
if (typeName.equals(toString())) {
typeName = type.getLocalName();
}
jsw.write(typeName);
jsw.write(' ');
jsw.write(field.getName());
String init = field.getInitString();
if (init != null && !field.isDateTime()) {
jsw.write(" = ");
jsw.write(init);
}
jsw.writeln(';');
jsw.writeln();
}
/**
* Writes to the JSourceWriter the member variables of this class.
*
* @param jsw The JSourceWriter to be used.
*/
protected final void printMemberVariables(final JSourceWriter jsw) {
if (!_fields.isEmpty()) {
jsw.writeln();
jsw.writeln(" //--------------------------/");
jsw.writeln(" //- Class/Member Variables -/");
jsw.writeln("//--------------------------/");
jsw.writeln();
}
for (JField field : _fields.values()) {
printAbstractJField(jsw, field);
}
}
/**
* Writes to the JSourceWriter any static initialization used by this class.
*
* @param jsw The JSourceWriter to be used.
*/
protected final void printStaticInitializers(final JSourceWriter jsw) {
//----------------------/
//- Static Initializer -/
//----------------------/
if (!_staticInitializer.isEmpty()) {
jsw.writeln();
jsw.writeln("static {");
jsw.writeln(_staticInitializer.toString());
jsw.writeln("};");
jsw.writeln();
}
}
/**
* Writes to the JSourceWriter all constructors for this class.
*
* @param jsw The JSourceWriter to be used.
*/
protected final void printConstructors(final JSourceWriter jsw) {
if (_constructors.size() > 0) {
jsw.writeln();
jsw.writeln(" //----------------/");
jsw.writeln(" //- Constructors -/");
jsw.writeln("//----------------/");
jsw.writeln();
}
for (int i = 0; i < _constructors.size(); i++) {
JConstructor jConstructor = _constructors.elementAt(i);
jConstructor.print(jsw);
jsw.writeln();
}
}
/**
* Writes to the JSourceWriter all methods belonging to this class.
*
* @param jsw The JSourceWriter to be used.
*/
protected final void printMethods(final JSourceWriter jsw) {
if (_methods.size() > 0) {
jsw.writeln();
jsw.writeln(" //-----------/");
jsw.writeln(" //- Methods -/");
jsw.writeln("//-----------/");
jsw.writeln();
}
for (int i = 0; i < _methods.size(); i++) {
JMethod jMethod = _methods.elementAt(i);
jMethod.print(jsw);
jsw.writeln();
}
}
protected final void printSourceCodeFragments(final JSourceWriter sourceWriter) {
if (!_sourceCodeEntries.isEmpty()) {
sourceWriter.writeln();
sourceWriter.writeln(" //----------------------------------/");
sourceWriter.writeln(" //- Injected source code fragments -/");
sourceWriter.writeln("//----------------------------------/");
sourceWriter.writeln();
}
for (String sourceCode : _sourceCodeEntries) {
sourceWriter.writeln(sourceCode);
sourceWriter.writeln();
}
}
/**
* Writes to the JSourceWriter all inner classes belonging to this class.
*
* @param jsw The JSourceWriter to be used.
*/
protected final void printInnerClasses(final JSourceWriter jsw) {
if ((_innerClasses != null) && (_innerClasses.size() > 0)) {
jsw.writeln();
jsw.writeln(" //-----------------/");
jsw.writeln(" //- Inner Classes -/");
jsw.writeln("//-----------------/");
jsw.writeln();
for (int i = 0; i < _innerClasses.size(); i++) {
JClass jClass = _innerClasses.elementAt(i);
jClass.print(jsw, true);
jsw.writeln();
}
}
}
/**
* Adds a complete source code fragment (method) to this class.
* @param sourceCode The complete source code fragment to be added.
*/
public void addSourceCode(final String sourceCode) {
_sourceCodeEntries.add(sourceCode);
}
public final int getSourceCodeEntryCount() {
return _sourceCodeEntries.size();
}
}