/* * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.codemodel.internal; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.sun.codemodel.internal.writer.FileCodeWriter; import com.sun.codemodel.internal.writer.ProgressCodeWriter; /** * Root of the code DOM. * *
* Here's your typical CodeModel application. * *
* JCodeModel cm = new JCodeModel(); * * // generate source code by populating the 'cm' tree. * cm._class(...); * ... * * // write them out * cm.build(new File(".")); ** *
* Every CodeModel node is always owned by one {@link JCodeModel} object * at any given time (which can be often accesesd by the owner() method.) * * As such, when you generate Java code, most of the operation works * in a top-down fashion. For example, you create a class from {@link JCodeModel}, * which gives you a {@link JDefinedClass}. Then you invoke a method on it * to generate a new method, which gives you {@link JMethod}, and so on. * * There are a few exceptions to this, most notably building {@link JExpression}s, * but generally you work with CodeModel in a top-down fashion. * * Because of this design, most of the CodeModel classes aren't directly instanciable. * * *
* Most of the time you'd want to populate new type definitions in a {@link JCodeModel}.
* See {@link #_class(String, ClassType)}.
*/
public final class JCodeModel {
/** The packages that this JCodeWriter contains. */
private HashMap
* This method is useful when the code generation needs to include the user-specified
* class that may or may not exist, and only thing known about it is a class name.
*/
public JClass directClass(String name) {
return new JDirectClass(this,name);
}
/**
* Creates a new generated class.
*
* @exception JClassAlreadyExistsException
* When the specified class/interface was already created.
*/
public JDefinedClass _class(int mods, String fullyqualifiedName,ClassType t) throws JClassAlreadyExistsException {
int idx = fullyqualifiedName.lastIndexOf('.');
if( idx<0 ) return rootPackage()._class(fullyqualifiedName);
else
return _package(fullyqualifiedName.substring(0,idx))
._class(mods, fullyqualifiedName.substring(idx+1), t );
}
/**
* Creates a new generated class.
*
* @exception JClassAlreadyExistsException
* When the specified class/interface was already created.
*/
public JDefinedClass _class(String fullyqualifiedName,ClassType t) throws JClassAlreadyExistsException {
return _class( JMod.PUBLIC, fullyqualifiedName, t );
}
/**
* Gets a reference to the already created generated class.
*
* @return null
* If the class is not yet created.
* @see JPackage#_getClass(String)
*/
public JDefinedClass _getClass(String fullyQualifiedName) {
int idx = fullyQualifiedName.lastIndexOf('.');
if( idx<0 ) return rootPackage()._getClass(fullyQualifiedName);
else
return _package(fullyQualifiedName.substring(0,idx))
._getClass( fullyQualifiedName.substring(idx+1) );
}
/**
* Creates a new anonymous class.
*
* @deprecated
* The naming convention doesn't match the rest of the CodeModel.
* Use {@link #anonymousClass(JClass)} instead.
*/
public JDefinedClass newAnonymousClass(JClass baseType) {
return new JAnonymousClass(baseType);
}
/**
* Creates a new anonymous class.
*/
public JDefinedClass anonymousClass(JClass baseType) {
return new JAnonymousClass(baseType);
}
public JDefinedClass anonymousClass(Class> baseType) {
return anonymousClass(ref(baseType));
}
/**
* Generates Java source code.
* A convenience method for
* The parameter may not be primitive.
*
* @see #_ref(Class) for the version that handles more cases.
*/
public JClass ref(Class> clazz) {
JReferencedClass jrc = (JReferencedClass)refClasses.get(clazz);
if (jrc == null) {
if (clazz.isPrimitive())
throw new IllegalArgumentException(clazz+" is a primitive");
if (clazz.isArray()) {
return new JArrayClass(this, _ref(clazz.getComponentType()));
} else {
jrc = new JReferencedClass(clazz);
refClasses.put(clazz, jrc);
}
}
return jrc;
}
public JType _ref(Class> c) {
if(c.isPrimitive())
return JType.parse(this,c.getName());
else
return ref(c);
}
/**
* Obtains a reference to an existing class from its fully-qualified
* class name.
*
*
* First, this method attempts to load the class of the given name.
* If that fails, we assume that the class is derived straight from
* {@link Object}, and return a {@link JClass}.
*/
public JClass ref(String fullyQualifiedClassName) {
try {
// try the context class loader first
return ref(Thread.currentThread().getContextClassLoader().loadClass(fullyQualifiedClassName));
} catch (ClassNotFoundException e) {
// fall through
}
// then the default mechanism.
try {
return ref(Class.forName(fullyQualifiedClassName));
} catch (ClassNotFoundException e1) {
// fall through
}
// assume it's not visible to us.
return new JDirectClass(this,fullyQualifiedClassName);
}
/**
* Cached for {@link #wildcard()}.
*/
private JClass wildcard;
/**
* Gets a {@link JClass} representation for "?",
* which is equivalent to "? extends Object".
*/
public JClass wildcard() {
if(wildcard==null)
wildcard = ref(Object.class).wildcard();
return wildcard;
}
/**
* Obtains a type object from a type name.
*
*
* This method handles primitive types, arrays, and existing {@link Class}es.
*
* @exception ClassNotFoundException
* If the specified type is not found.
*/
public JType parseType(String name) throws ClassNotFoundException {
// array
if(name.endsWith("[]"))
return parseType(name.substring(0,name.length()-2)).array();
// try primitive type
try {
return JType.parse(this,name);
} catch (IllegalArgumentException e) {
;
}
// existing class
return new TypeNameParser(name).parseTypeName();
}
private final class TypeNameParser {
private final String s;
private int idx;
public TypeNameParser(String s) {
this.s = s;
}
/**
* Parses a type name token T (which can be potentially of the form Tr&ly;T1,T2,...>,
* or "? extends/super T".)
*
* @return the index of the character next to T.
*/
JClass parseTypeName() throws ClassNotFoundException {
int start = idx;
if(s.charAt(idx)=='?') {
// wildcard
idx++;
ws();
String head = s.substring(idx);
if(head.startsWith("extends")) {
idx+=7;
ws();
return parseTypeName().wildcard();
} else
if(head.startsWith("super")) {
throw new UnsupportedOperationException("? super T not implemented");
} else {
// not supported
throw new IllegalArgumentException("only extends/super can follow ?, but found "+s.substring(idx));
}
}
while(idx
* JReferencedClass is kept in a pool so that they are shared.
* There is one pool for each JCodeModel object.
*
*
* It is impossible to cache JReferencedClass globally only because
* there is the _package() method, which obtains the owner JPackage
* object, which is scoped to JCodeModel.
*/
private class JReferencedClass extends JClass implements JDeclaration {
private final Class> _class;
JReferencedClass(Class> _clazz) {
super(JCodeModel.this);
this._class = _clazz;
assert !_class.isArray();
}
public String name() {
return _class.getSimpleName().replace('$','.');
}
public String fullName() {
return _class.getName().replace('$','.');
}
public String binaryName() {
return _class.getName();
}
public JClass outer() {
Class> p = _class.getDeclaringClass();
if(p==null) return null;
return ref(p);
}
public JPackage _package() {
String name = fullName();
// this type is array
if (name.indexOf('[') != -1)
return JCodeModel.this._package("");
// other normal case
int idx = name.lastIndexOf('.');
if (idx < 0)
return JCodeModel.this._package("");
else
return JCodeModel.this._package(name.substring(0, idx));
}
public JClass _extends() {
Class> sp = _class.getSuperclass();
if (sp == null) {
if(isInterface())
return owner().ref(Object.class);
return null;
} else
return ref(sp);
}
public Iteratorbuild(destDir,destDir,System.out)
.
*
* @param destDir
* source files are generated into this directory.
* @param status
* if non-null, progress indication will be sent to this stream.
*/
public void build( File destDir, PrintStream status ) throws IOException {
build(destDir,destDir,status);
}
/**
* Generates Java source code.
* A convenience method that calls {@link #build(CodeWriter,CodeWriter)}.
*
* @param srcDir
* Java source files are generated into this directory.
* @param resourceDir
* Other resource files are generated into this directory.
* @param status
* if non-null, progress indication will be sent to this stream.
*/
public void build( File srcDir, File resourceDir, PrintStream status ) throws IOException {
CodeWriter src = new FileCodeWriter(srcDir);
CodeWriter res = new FileCodeWriter(resourceDir);
if(status!=null) {
src = new ProgressCodeWriter(src, status );
res = new ProgressCodeWriter(res, status );
}
build(src,res);
}
/**
* A convenience method for build(destDir,System.out)
.
*/
public void build( File destDir ) throws IOException {
build(destDir,System.out);
}
/**
* A convenience method for build(srcDir,resourceDir,System.out)
.
*/
public void build( File srcDir, File resourceDir ) throws IOException {
build(srcDir,resourceDir,System.out);
}
/**
* A convenience method for build(out,out)
.
*/
public void build( CodeWriter out ) throws IOException {
build(out,out);
}
/**
* Generates Java source code.
*/
public void build( CodeWriter source, CodeWriter resource ) throws IOException {
JPackage[] pkgs = packages.values().toArray(new JPackage[packages.size()]);
// avoid concurrent modification exception
for( JPackage pkg : pkgs )
pkg.build(source,resource);
source.close();
resource.close();
}
/**
* Returns the number of files to be generated if
* {@link #build} is invoked now.
*/
public int countArtifacts() {
int r = 0;
JPackage[] pkgs = packages.values().toArray(new JPackage[packages.size()]);
// avoid concurrent modification exception
for( JPackage pkg : pkgs )
r += pkg.countArtifacts();
return r;
}
/**
* Obtains a reference to an existing class from its Class object.
*
*