325N/A/*
325N/A * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.codemodel.internal;
325N/A
325N/Aimport java.io.File;
325N/Aimport java.io.IOException;
325N/Aimport java.io.PrintStream;
325N/Aimport java.lang.reflect.Modifier;
325N/Aimport java.util.ArrayList;
325N/Aimport java.util.Collections;
325N/Aimport java.util.HashMap;
325N/Aimport java.util.Iterator;
325N/Aimport java.util.List;
325N/Aimport java.util.Map;
325N/A
325N/Aimport com.sun.codemodel.internal.writer.FileCodeWriter;
325N/Aimport com.sun.codemodel.internal.writer.ProgressCodeWriter;
325N/A
325N/A
325N/A/**
325N/A * Root of the code DOM.
325N/A *
325N/A * <p>
325N/A * Here's your typical CodeModel application.
325N/A *
325N/A * <pre>
325N/A * JCodeModel cm = new JCodeModel();
325N/A *
325N/A * // generate source code by populating the 'cm' tree.
325N/A * cm._class(...);
325N/A * ...
325N/A *
325N/A * // write them out
325N/A * cm.build(new File("."));
325N/A * </pre>
325N/A *
325N/A * <p>
325N/A * Every CodeModel node is always owned by one {@link JCodeModel} object
325N/A * at any given time (which can be often accesesd by the <tt>owner()</tt> method.)
325N/A *
325N/A * As such, when you generate Java code, most of the operation works
325N/A * in a top-down fashion. For example, you create a class from {@link JCodeModel},
325N/A * which gives you a {@link JDefinedClass}. Then you invoke a method on it
325N/A * to generate a new method, which gives you {@link JMethod}, and so on.
325N/A *
325N/A * There are a few exceptions to this, most notably building {@link JExpression}s,
325N/A * but generally you work with CodeModel in a top-down fashion.
325N/A *
325N/A * Because of this design, most of the CodeModel classes aren't directly instanciable.
325N/A *
325N/A *
325N/A * <h2>Where to go from here?</h2>
325N/A * <p>
325N/A * Most of the time you'd want to populate new type definitions in a {@link JCodeModel}.
325N/A * See {@link #_class(String, ClassType)}.
325N/A */
325N/Apublic final class JCodeModel {
325N/A
325N/A /** The packages that this JCodeWriter contains. */
325N/A private HashMap<String,JPackage> packages = new HashMap<String,JPackage>();
325N/A
325N/A /** All JReferencedClasses are pooled here. */
325N/A private final HashMap<Class<?>,JReferencedClass> refClasses = new HashMap<Class<?>,JReferencedClass>();
325N/A
325N/A
325N/A /** Obtains a reference to the special "null" type. */
325N/A public final JNullType NULL = new JNullType(this);
325N/A // primitive types
325N/A public final JPrimitiveType VOID = new JPrimitiveType(this,"void", Void.class);
325N/A public final JPrimitiveType BOOLEAN = new JPrimitiveType(this,"boolean",Boolean.class);
325N/A public final JPrimitiveType BYTE = new JPrimitiveType(this,"byte", Byte.class);
325N/A public final JPrimitiveType SHORT = new JPrimitiveType(this,"short", Short.class);
325N/A public final JPrimitiveType CHAR = new JPrimitiveType(this,"char", Character.class);
325N/A public final JPrimitiveType INT = new JPrimitiveType(this,"int", Integer.class);
325N/A public final JPrimitiveType FLOAT = new JPrimitiveType(this,"float", Float.class);
325N/A public final JPrimitiveType LONG = new JPrimitiveType(this,"long", Long.class);
325N/A public final JPrimitiveType DOUBLE = new JPrimitiveType(this,"double", Double.class);
325N/A
325N/A /**
325N/A * If the flag is true, we will consider two classes "Foo" and "foo"
325N/A * as a collision.
325N/A */
325N/A protected static final boolean isCaseSensitiveFileSystem = getFileSystemCaseSensitivity();
325N/A
325N/A private static boolean getFileSystemCaseSensitivity() {
325N/A try {
325N/A // let the system property override, in case the user really
325N/A // wants to override.
325N/A if( System.getProperty("com.sun.codemodel.internal.FileSystemCaseSensitive")!=null )
325N/A return true;
325N/A } catch( Exception e ) {}
325N/A
325N/A // on Unix, it's case sensitive.
325N/A return (File.separatorChar == '/');
325N/A }
325N/A
325N/A
325N/A public JCodeModel() {}
325N/A
325N/A /**
325N/A * Add a package to the list of packages to be generated
325N/A *
325N/A * @param name
325N/A * Name of the package. Use "" to indicate the root package.
325N/A *
325N/A * @return Newly generated package
325N/A */
325N/A public JPackage _package(String name) {
325N/A JPackage p = packages.get(name);
325N/A if (p == null) {
325N/A p = new JPackage(name, this);
325N/A packages.put(name, p);
325N/A }
325N/A return p;
325N/A }
325N/A
325N/A public final JPackage rootPackage() {
325N/A return _package("");
325N/A }
325N/A
325N/A /**
325N/A * Returns an iterator that walks the packages defined using this code
325N/A * writer.
325N/A */
325N/A public Iterator<JPackage> packages() {
325N/A return packages.values().iterator();
325N/A }
325N/A
325N/A /**
325N/A * Creates a new generated class.
325N/A *
325N/A * @exception JClassAlreadyExistsException
325N/A * When the specified class/interface was already created.
325N/A */
325N/A public JDefinedClass _class(String fullyqualifiedName) throws JClassAlreadyExistsException {
325N/A return _class(fullyqualifiedName,ClassType.CLASS);
325N/A }
325N/A
325N/A /**
325N/A * Creates a dummy, unknown {@link JClass} that represents a given name.
325N/A *
325N/A * <p>
325N/A * This method is useful when the code generation needs to include the user-specified
325N/A * class that may or may not exist, and only thing known about it is a class name.
325N/A */
325N/A public JClass directClass(String name) {
325N/A return new JDirectClass(this,name);
325N/A }
325N/A
325N/A /**
325N/A * Creates a new generated class.
325N/A *
325N/A * @exception JClassAlreadyExistsException
325N/A * When the specified class/interface was already created.
325N/A */
325N/A public JDefinedClass _class(int mods, String fullyqualifiedName,ClassType t) throws JClassAlreadyExistsException {
325N/A int idx = fullyqualifiedName.lastIndexOf('.');
325N/A if( idx<0 ) return rootPackage()._class(fullyqualifiedName);
325N/A else
325N/A return _package(fullyqualifiedName.substring(0,idx))
325N/A ._class(mods, fullyqualifiedName.substring(idx+1), t );
325N/A }
325N/A
325N/A /**
325N/A * Creates a new generated class.
325N/A *
325N/A * @exception JClassAlreadyExistsException
325N/A * When the specified class/interface was already created.
325N/A */
325N/A public JDefinedClass _class(String fullyqualifiedName,ClassType t) throws JClassAlreadyExistsException {
325N/A return _class( JMod.PUBLIC, fullyqualifiedName, t );
325N/A }
325N/A
325N/A /**
325N/A * Gets a reference to the already created generated class.
325N/A *
325N/A * @return null
325N/A * If the class is not yet created.
325N/A * @see JPackage#_getClass(String)
325N/A */
325N/A public JDefinedClass _getClass(String fullyQualifiedName) {
325N/A int idx = fullyQualifiedName.lastIndexOf('.');
325N/A if( idx<0 ) return rootPackage()._getClass(fullyQualifiedName);
325N/A else
325N/A return _package(fullyQualifiedName.substring(0,idx))
325N/A ._getClass( fullyQualifiedName.substring(idx+1) );
325N/A }
325N/A
325N/A /**
325N/A * Creates a new anonymous class.
325N/A *
325N/A * @deprecated
325N/A * The naming convention doesn't match the rest of the CodeModel.
325N/A * Use {@link #anonymousClass(JClass)} instead.
325N/A */
325N/A public JDefinedClass newAnonymousClass(JClass baseType) {
325N/A return new JAnonymousClass(baseType);
325N/A }
325N/A
325N/A /**
325N/A * Creates a new anonymous class.
325N/A */
325N/A public JDefinedClass anonymousClass(JClass baseType) {
325N/A return new JAnonymousClass(baseType);
325N/A }
325N/A
325N/A public JDefinedClass anonymousClass(Class<?> baseType) {
325N/A return anonymousClass(ref(baseType));
325N/A }
325N/A
325N/A /**
325N/A * Generates Java source code.
325N/A * A convenience method for <code>build(destDir,destDir,System.out)</code>.
325N/A *
325N/A * @param destDir
325N/A * source files are generated into this directory.
325N/A * @param status
325N/A * if non-null, progress indication will be sent to this stream.
325N/A */
325N/A public void build( File destDir, PrintStream status ) throws IOException {
325N/A build(destDir,destDir,status);
325N/A }
325N/A
325N/A /**
325N/A * Generates Java source code.
325N/A * A convenience method that calls {@link #build(CodeWriter,CodeWriter)}.
325N/A *
325N/A * @param srcDir
325N/A * Java source files are generated into this directory.
325N/A * @param resourceDir
325N/A * Other resource files are generated into this directory.
325N/A * @param status
325N/A * if non-null, progress indication will be sent to this stream.
325N/A */
325N/A public void build( File srcDir, File resourceDir, PrintStream status ) throws IOException {
325N/A CodeWriter src = new FileCodeWriter(srcDir);
325N/A CodeWriter res = new FileCodeWriter(resourceDir);
325N/A if(status!=null) {
325N/A src = new ProgressCodeWriter(src, status );
325N/A res = new ProgressCodeWriter(res, status );
325N/A }
325N/A build(src,res);
325N/A }
325N/A
325N/A /**
325N/A * A convenience method for <code>build(destDir,System.out)</code>.
325N/A */
325N/A public void build( File destDir ) throws IOException {
325N/A build(destDir,System.out);
325N/A }
325N/A
325N/A /**
325N/A * A convenience method for <code>build(srcDir,resourceDir,System.out)</code>.
325N/A */
325N/A public void build( File srcDir, File resourceDir ) throws IOException {
325N/A build(srcDir,resourceDir,System.out);
325N/A }
325N/A
325N/A /**
325N/A * A convenience method for <code>build(out,out)</code>.
325N/A */
325N/A public void build( CodeWriter out ) throws IOException {
325N/A build(out,out);
325N/A }
325N/A
325N/A /**
325N/A * Generates Java source code.
325N/A */
325N/A public void build( CodeWriter source, CodeWriter resource ) throws IOException {
325N/A JPackage[] pkgs = packages.values().toArray(new JPackage[packages.size()]);
325N/A // avoid concurrent modification exception
325N/A for( JPackage pkg : pkgs )
325N/A pkg.build(source,resource);
325N/A source.close();
325N/A resource.close();
325N/A }
325N/A
325N/A /**
325N/A * Returns the number of files to be generated if
325N/A * {@link #build} is invoked now.
325N/A */
325N/A public int countArtifacts() {
325N/A int r = 0;
325N/A JPackage[] pkgs = packages.values().toArray(new JPackage[packages.size()]);
325N/A // avoid concurrent modification exception
325N/A for( JPackage pkg : pkgs )
325N/A r += pkg.countArtifacts();
325N/A return r;
325N/A }
325N/A
325N/A
325N/A /**
325N/A * Obtains a reference to an existing class from its Class object.
325N/A *
325N/A * <p>
325N/A * The parameter may not be primitive.
325N/A *
325N/A * @see #_ref(Class) for the version that handles more cases.
325N/A */
325N/A public JClass ref(Class<?> clazz) {
325N/A JReferencedClass jrc = (JReferencedClass)refClasses.get(clazz);
325N/A if (jrc == null) {
325N/A if (clazz.isPrimitive())
325N/A throw new IllegalArgumentException(clazz+" is a primitive");
325N/A if (clazz.isArray()) {
325N/A return new JArrayClass(this, _ref(clazz.getComponentType()));
325N/A } else {
325N/A jrc = new JReferencedClass(clazz);
325N/A refClasses.put(clazz, jrc);
325N/A }
325N/A }
325N/A return jrc;
325N/A }
325N/A
325N/A public JType _ref(Class<?> c) {
325N/A if(c.isPrimitive())
325N/A return JType.parse(this,c.getName());
325N/A else
325N/A return ref(c);
325N/A }
325N/A
325N/A /**
325N/A * Obtains a reference to an existing class from its fully-qualified
325N/A * class name.
325N/A *
325N/A * <p>
325N/A * First, this method attempts to load the class of the given name.
325N/A * If that fails, we assume that the class is derived straight from
325N/A * {@link Object}, and return a {@link JClass}.
325N/A */
325N/A public JClass ref(String fullyQualifiedClassName) {
325N/A try {
325N/A // try the context class loader first
325N/A return ref(Thread.currentThread().getContextClassLoader().loadClass(fullyQualifiedClassName));
325N/A } catch (ClassNotFoundException e) {
325N/A // fall through
325N/A }
325N/A // then the default mechanism.
325N/A try {
325N/A return ref(Class.forName(fullyQualifiedClassName));
325N/A } catch (ClassNotFoundException e1) {
325N/A // fall through
325N/A }
325N/A
325N/A // assume it's not visible to us.
325N/A return new JDirectClass(this,fullyQualifiedClassName);
325N/A }
325N/A
325N/A /**
325N/A * Cached for {@link #wildcard()}.
325N/A */
325N/A private JClass wildcard;
325N/A
325N/A /**
325N/A * Gets a {@link JClass} representation for "?",
325N/A * which is equivalent to "? extends Object".
325N/A */
325N/A public JClass wildcard() {
325N/A if(wildcard==null)
325N/A wildcard = ref(Object.class).wildcard();
325N/A return wildcard;
325N/A }
325N/A
325N/A /**
325N/A * Obtains a type object from a type name.
325N/A *
325N/A * <p>
325N/A * This method handles primitive types, arrays, and existing {@link Class}es.
325N/A *
325N/A * @exception ClassNotFoundException
325N/A * If the specified type is not found.
325N/A */
325N/A public JType parseType(String name) throws ClassNotFoundException {
325N/A // array
325N/A if(name.endsWith("[]"))
325N/A return parseType(name.substring(0,name.length()-2)).array();
325N/A
325N/A // try primitive type
325N/A try {
325N/A return JType.parse(this,name);
325N/A } catch (IllegalArgumentException e) {
325N/A ;
325N/A }
325N/A
325N/A // existing class
325N/A return new TypeNameParser(name).parseTypeName();
325N/A }
325N/A
325N/A private final class TypeNameParser {
325N/A private final String s;
325N/A private int idx;
325N/A
325N/A public TypeNameParser(String s) {
325N/A this.s = s;
325N/A }
325N/A
325N/A /**
325N/A * Parses a type name token T (which can be potentially of the form Tr&ly;T1,T2,...>,
325N/A * or "? extends/super T".)
325N/A *
325N/A * @return the index of the character next to T.
325N/A */
325N/A JClass parseTypeName() throws ClassNotFoundException {
325N/A int start = idx;
325N/A
325N/A if(s.charAt(idx)=='?') {
325N/A // wildcard
325N/A idx++;
325N/A ws();
325N/A String head = s.substring(idx);
325N/A if(head.startsWith("extends")) {
325N/A idx+=7;
325N/A ws();
325N/A return parseTypeName().wildcard();
325N/A } else
325N/A if(head.startsWith("super")) {
325N/A throw new UnsupportedOperationException("? super T not implemented");
325N/A } else {
325N/A // not supported
325N/A throw new IllegalArgumentException("only extends/super can follow ?, but found "+s.substring(idx));
325N/A }
325N/A }
325N/A
325N/A while(idx<s.length()) {
325N/A char ch = s.charAt(idx);
325N/A if(Character.isJavaIdentifierStart(ch)
325N/A || Character.isJavaIdentifierPart(ch)
325N/A || ch=='.')
325N/A idx++;
325N/A else
325N/A break;
325N/A }
325N/A
325N/A JClass clazz = ref(s.substring(start,idx));
325N/A
325N/A return parseSuffix(clazz);
325N/A }
325N/A
325N/A /**
325N/A * Parses additional left-associative suffixes, like type arguments
325N/A * and array specifiers.
325N/A */
325N/A private JClass parseSuffix(JClass clazz) throws ClassNotFoundException {
325N/A if(idx==s.length())
325N/A return clazz; // hit EOL
325N/A
325N/A char ch = s.charAt(idx);
325N/A
325N/A if(ch=='<')
325N/A return parseSuffix(parseArguments(clazz));
325N/A
325N/A if(ch=='[') {
325N/A if(s.charAt(idx+1)==']') {
325N/A idx+=2;
325N/A return parseSuffix(clazz.array());
325N/A }
325N/A throw new IllegalArgumentException("Expected ']' but found "+s.substring(idx+1));
325N/A }
325N/A
325N/A return clazz;
325N/A }
325N/A
325N/A /**
325N/A * Skips whitespaces
325N/A */
325N/A private void ws() {
325N/A while(Character.isWhitespace(s.charAt(idx)) && idx<s.length())
325N/A idx++;
325N/A }
325N/A
325N/A /**
325N/A * Parses '&lt;T1,T2,...,Tn>'
325N/A *
325N/A * @return the index of the character next to '>'
325N/A */
325N/A private JClass parseArguments(JClass rawType) throws ClassNotFoundException {
325N/A if(s.charAt(idx)!='<')
325N/A throw new IllegalArgumentException();
325N/A idx++;
325N/A
325N/A List<JClass> args = new ArrayList<JClass>();
325N/A
325N/A while(true) {
325N/A args.add(parseTypeName());
325N/A if(idx==s.length())
325N/A throw new IllegalArgumentException("Missing '>' in "+s);
325N/A char ch = s.charAt(idx);
325N/A if(ch=='>')
325N/A return rawType.narrow(args.toArray(new JClass[args.size()]));
325N/A
325N/A if(ch!=',')
325N/A throw new IllegalArgumentException(s);
325N/A idx++;
325N/A }
325N/A
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * References to existing classes.
325N/A *
325N/A * <p>
325N/A * JReferencedClass is kept in a pool so that they are shared.
325N/A * There is one pool for each JCodeModel object.
325N/A *
325N/A * <p>
325N/A * It is impossible to cache JReferencedClass globally only because
325N/A * there is the _package() method, which obtains the owner JPackage
325N/A * object, which is scoped to JCodeModel.
325N/A */
325N/A private class JReferencedClass extends JClass implements JDeclaration {
325N/A private final Class<?> _class;
325N/A
325N/A JReferencedClass(Class<?> _clazz) {
325N/A super(JCodeModel.this);
325N/A this._class = _clazz;
325N/A assert !_class.isArray();
325N/A }
325N/A
325N/A public String name() {
325N/A return _class.getSimpleName().replace('$','.');
325N/A }
325N/A
325N/A public String fullName() {
325N/A return _class.getName().replace('$','.');
325N/A }
325N/A
325N/A public String binaryName() {
325N/A return _class.getName();
325N/A }
325N/A
325N/A public JClass outer() {
325N/A Class<?> p = _class.getDeclaringClass();
325N/A if(p==null) return null;
325N/A return ref(p);
325N/A }
325N/A
325N/A public JPackage _package() {
325N/A String name = fullName();
325N/A
325N/A // this type is array
325N/A if (name.indexOf('[') != -1)
325N/A return JCodeModel.this._package("");
325N/A
325N/A // other normal case
325N/A int idx = name.lastIndexOf('.');
325N/A if (idx < 0)
325N/A return JCodeModel.this._package("");
325N/A else
325N/A return JCodeModel.this._package(name.substring(0, idx));
325N/A }
325N/A
325N/A public JClass _extends() {
325N/A Class<?> sp = _class.getSuperclass();
325N/A if (sp == null) {
325N/A if(isInterface())
325N/A return owner().ref(Object.class);
325N/A return null;
325N/A } else
325N/A return ref(sp);
325N/A }
325N/A
325N/A public Iterator<JClass> _implements() {
325N/A final Class<?>[] interfaces = _class.getInterfaces();
325N/A return new Iterator<JClass>() {
325N/A private int idx = 0;
325N/A public boolean hasNext() {
325N/A return idx < interfaces.length;
325N/A }
325N/A public JClass next() {
325N/A return JCodeModel.this.ref(interfaces[idx++]);
325N/A }
325N/A public void remove() {
325N/A throw new UnsupportedOperationException();
325N/A }
325N/A };
325N/A }
325N/A
325N/A public boolean isInterface() {
325N/A return _class.isInterface();
325N/A }
325N/A
325N/A public boolean isAbstract() {
325N/A return Modifier.isAbstract(_class.getModifiers());
325N/A }
325N/A
325N/A public JPrimitiveType getPrimitiveType() {
325N/A Class<?> v = boxToPrimitive.get(_class);
325N/A if(v!=null)
325N/A return JType.parse(JCodeModel.this,v.getName());
325N/A else
325N/A return null;
325N/A }
325N/A
325N/A public boolean isArray() {
325N/A return false;
325N/A }
325N/A
325N/A public void declare(JFormatter f) {
325N/A }
325N/A
325N/A public JTypeVar[] typeParams() {
325N/A // TODO: does JDK 1.5 reflection provides these information?
325N/A return super.typeParams();
325N/A }
325N/A
325N/A protected JClass substituteParams(JTypeVar[] variables, List<JClass> bindings) {
325N/A // TODO: does JDK 1.5 reflection provides these information?
325N/A return this;
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Conversion from primitive type {@link Class} (such as {@link Integer#TYPE}
325N/A * to its boxed type (such as <tt>Integer.class</tt>)
325N/A */
325N/A public static final Map<Class<?>,Class<?>> primitiveToBox;
325N/A /**
325N/A * The reverse look up for {@link #primitiveToBox}
325N/A */
325N/A public static final Map<Class<?>,Class<?>> boxToPrimitive;
325N/A
325N/A static {
325N/A Map<Class<?>,Class<?>> m1 = new HashMap<Class<?>,Class<?>>();
325N/A Map<Class<?>,Class<?>> m2 = new HashMap<Class<?>,Class<?>>();
325N/A
325N/A m1.put(Boolean.class,Boolean.TYPE);
325N/A m1.put(Byte.class,Byte.TYPE);
325N/A m1.put(Character.class,Character.TYPE);
325N/A m1.put(Double.class,Double.TYPE);
325N/A m1.put(Float.class,Float.TYPE);
325N/A m1.put(Integer.class,Integer.TYPE);
325N/A m1.put(Long.class,Long.TYPE);
325N/A m1.put(Short.class,Short.TYPE);
325N/A m1.put(Void.class,Void.TYPE);
325N/A
325N/A for (Map.Entry<Class<?>, Class<?>> e : m1.entrySet())
325N/A m2.put(e.getValue(),e.getKey());
325N/A
325N/A boxToPrimitive = Collections.unmodifiableMap(m1);
325N/A primitiveToBox = Collections.unmodifiableMap(m2);
325N/A
325N/A }
325N/A}