jaxws/src/share/classes/com/sun/codemodel/internal/JCodeModel.java
changeset 8 474761f14bca
child 2678 57cf2a1c1a05
--- /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 '&lt;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);
+
+    }
+}