--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxws/src/share/classes/com/sun/codemodel/internal/JCodeModel.java Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,641 @@
+/*
+ * Copyright 2006 Sun Microsystems, Inc. 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. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.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.
+ *
+ * <p>
+ * Here's your typical CodeModel application.
+ *
+ * <pre>
+ * JCodeModel cm = new JCodeModel();
+ *
+ * // generate source code by populating the 'cm' tree.
+ * cm._class(...);
+ * ...
+ *
+ * // write them out
+ * cm.build(new File("."));
+ * </pre>
+ *
+ * <p>
+ * Every CodeModel node is always owned by one {@link JCodeModel} object
+ * at any given time (which can be often accesesd by the <tt>owner()</tt> 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.
+ *
+ *
+ * <h2>Where to go from here?</h2>
+ * <p>
+ * 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<String,JPackage> packages = new HashMap<String,JPackage>();
+
+ /** All JReferencedClasses are pooled here. */
+ private final HashMap<Class,JReferencedClass> refClasses = new HashMap<Class,JReferencedClass>();
+
+
+ /** Obtains a reference to the special "null" type. */
+ public final JNullType NULL = new JNullType(this);
+ // primitive types
+ public final JPrimitiveType VOID = new JPrimitiveType(this,"void", Void.class);
+ public final JPrimitiveType BOOLEAN = new JPrimitiveType(this,"boolean",Boolean.class);
+ public final JPrimitiveType BYTE = new JPrimitiveType(this,"byte", Byte.class);
+ public final JPrimitiveType SHORT = new JPrimitiveType(this,"short", Short.class);
+ public final JPrimitiveType CHAR = new JPrimitiveType(this,"char", Character.class);
+ public final JPrimitiveType INT = new JPrimitiveType(this,"int", Integer.class);
+ public final JPrimitiveType FLOAT = new JPrimitiveType(this,"float", Float.class);
+ public final JPrimitiveType LONG = new JPrimitiveType(this,"long", Long.class);
+ public final JPrimitiveType DOUBLE = new JPrimitiveType(this,"double", Double.class);
+
+ /**
+ * If the flag is true, we will consider two classes "Foo" and "foo"
+ * as a collision.
+ */
+ protected static final boolean isCaseSensitiveFileSystem = getFileSystemCaseSensitivity();
+
+ private static boolean getFileSystemCaseSensitivity() {
+ try {
+ // let the system property override, in case the user really
+ // wants to override.
+ if( System.getProperty("com.sun.codemodel.internal.FileSystemCaseSensitive")!=null )
+ return true;
+ } catch( Exception e ) {}
+
+ // on Unix, it's case sensitive.
+ return (File.separatorChar == '/');
+ }
+
+
+ public JCodeModel() {}
+
+ /**
+ * Add a package to the list of packages to be generated
+ *
+ * @param name
+ * Name of the package. Use "" to indicate the root package.
+ *
+ * @return Newly generated package
+ */
+ public JPackage _package(String name) {
+ JPackage p = packages.get(name);
+ if (p == null) {
+ p = new JPackage(name, this);
+ packages.put(name, p);
+ }
+ return p;
+ }
+
+ public final JPackage rootPackage() {
+ return _package("");
+ }
+
+ /**
+ * Returns an iterator that walks the packages defined using this code
+ * writer.
+ */
+ public Iterator<JPackage> packages() {
+ return packages.values().iterator();
+ }
+
+ /**
+ * Creates a new generated class.
+ *
+ * @exception JClassAlreadyExistsException
+ * When the specified class/interface was already created.
+ */
+ public JDefinedClass _class(String fullyqualifiedName) throws JClassAlreadyExistsException {
+ return _class(fullyqualifiedName,ClassType.CLASS);
+ }
+
+ /**
+ * Creates a dummy, unknown {@link JClass} that represents a given name.
+ *
+ * <p>
+ * 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(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( JMod.PUBLIC, fullyqualifiedName.substring(idx+1), 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 <code>build(destDir,destDir,System.out)</code>.
+ *
+ * @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 <code>build(destDir,System.out)</code>.
+ */
+ public void build( File destDir ) throws IOException {
+ build(destDir,System.out);
+ }
+
+ /**
+ * A convenience method for <code>build(srcDir,resourceDir,System.out)</code>.
+ */
+ public void build( File srcDir, File resourceDir ) throws IOException {
+ build(srcDir,resourceDir,System.out);
+ }
+
+ /**
+ * A convenience method for <code>build(out,out)</code>.
+ */
+ 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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.
+ *
+ * <p>
+ * 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<s.length()) {
+ char ch = s.charAt(idx);
+ if(Character.isJavaIdentifierStart(ch)
+ || Character.isJavaIdentifierPart(ch)
+ || ch=='.')
+ idx++;
+ else
+ break;
+ }
+
+ JClass clazz = ref(s.substring(start,idx));
+
+ if(idx==s.length())
+ return clazz; // hit EOL
+
+ char ch = s.charAt(idx);
+
+ if(ch=='<')
+ return parseArguments(clazz);
+
+ return clazz;
+ }
+
+ /**
+ * Skips whitespaces
+ */
+ private void ws() {
+ while(Character.isWhitespace(s.charAt(idx)) && idx<s.length())
+ idx++;
+ }
+
+ /**
+ * Parses '<T1,T2,...,Tn>'
+ *
+ * @return the index of the character next to '>'
+ */
+ private JClass parseArguments(JClass rawType) throws ClassNotFoundException {
+ if(s.charAt(idx)!='<')
+ throw new IllegalArgumentException();
+ idx++;
+
+ List<JClass> args = new ArrayList<JClass>();
+
+ while(true) {
+ args.add(parseTypeName());
+ if(idx==s.length())
+ throw new IllegalArgumentException("Missing '>' in "+s);
+ char ch = s.charAt(idx);
+ if(ch=='>')
+ return rawType.narrow(args.toArray(new JClass[args.size()]));
+
+ if(ch!=',')
+ throw new IllegalArgumentException(s);
+ idx++;
+ }
+
+ }
+ }
+
+ /**
+ * References to existing classes.
+ *
+ * <p>
+ * JReferencedClass is kept in a pool so that they are shared.
+ * There is one pool for each JCodeModel object.
+ *
+ * <p>
+ * 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 Iterator<JClass> _implements() {
+ final Class[] interfaces = _class.getInterfaces();
+ return new Iterator<JClass>() {
+ private int idx = 0;
+ public boolean hasNext() {
+ return idx < interfaces.length;
+ }
+ public JClass next() {
+ return JCodeModel.this.ref(interfaces[idx++]);
+ }
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public boolean isInterface() {
+ return _class.isInterface();
+ }
+
+ public boolean isAbstract() {
+ return Modifier.isAbstract(_class.getModifiers());
+ }
+
+ public JPrimitiveType getPrimitiveType() {
+ Class v = boxToPrimitive.get(_class);
+ if(v!=null)
+ return JType.parse(JCodeModel.this,v.getName());
+ else
+ return null;
+ }
+
+ public boolean isArray() {
+ return false;
+ }
+
+ public void declare(JFormatter f) {
+ }
+
+ public JTypeVar[] typeParams() {
+ // TODO: does JDK 1.5 reflection provides these information?
+ return super.typeParams();
+ }
+
+ protected JClass substituteParams(JTypeVar[] variables, List<JClass> bindings) {
+ // TODO: does JDK 1.5 reflection provides these information?
+ return this;
+ }
+ }
+
+ /**
+ * Conversion from primitive type {@link Class} (such as {@link Integer#TYPE}
+ * to its boxed type (such as <tt>Integer.class</tt>)
+ */
+ public static final Map<Class,Class> primitiveToBox;
+ /**
+ * The reverse look up for {@link #primitiveToBox}
+ */
+ public static final Map<Class,Class> boxToPrimitive;
+
+ static {
+ Map<Class,Class> m1 = new HashMap<Class,Class>();
+ Map<Class,Class> m2 = new HashMap<Class,Class>();
+
+ m1.put(Boolean.class,Boolean.TYPE);
+ m1.put(Byte.class,Byte.TYPE);
+ m1.put(Character.class,Character.TYPE);
+ m1.put(Double.class,Double.TYPE);
+ m1.put(Float.class,Float.TYPE);
+ m1.put(Integer.class,Integer.TYPE);
+ m1.put(Long.class,Long.TYPE);
+ m1.put(Short.class,Short.TYPE);
+ m1.put(Void.class,Void.TYPE);
+
+ for (Map.Entry<Class, Class> e : m1.entrySet())
+ m2.put(e.getValue(),e.getKey());
+
+ boxToPrimitive = Collections.unmodifiableMap(m1);
+ primitiveToBox = Collections.unmodifiableMap(m2);
+
+ }
+}