View Javadoc
1   /**
2    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3    *
4    * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
5    * Portions Copyright 2013-2016 Philip Helger + contributors
6    *
7    * The contents of this file are subject to the terms of either the GNU
8    * General Public License Version 2 only ("GPL") or the Common Development
9    * and Distribution License("CDDL") (collectively, the "License").  You
10   * may not use this file except in compliance with the License.  You can
11   * obtain a copy of the License at
12   * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
13   * or packager/legal/LICENSE.txt.  See the License for the specific
14   * language governing permissions and limitations under the License.
15   *
16   * When distributing the software, include this License Header Notice in each
17   * file and include the License file at packager/legal/LICENSE.txt.
18   *
19   * GPL Classpath Exception:
20   * Oracle designates this particular file as subject to the "Classpath"
21   * exception as provided by Oracle in the GPL Version 2 section of the License
22   * file that accompanied this code.
23   *
24   * Modifications:
25   * If applicable, add the following below the License Header, with the fields
26   * enclosed by brackets [] replaced by your own identifying information:
27   * "Portions Copyright [year] [name of copyright owner]"
28   *
29   * Contributor(s):
30   * If you wish your version of this file to be governed by only the CDDL or
31   * only the GPL Version 2, indicate your decision by adding "[Contributor]
32   * elects to include this software in this distribution under the [CDDL or GPL
33   * Version 2] license."  If you don't indicate a single choice of license, a
34   * recipient has the option to distribute your version of this file under
35   * either the CDDL, the GPL Version 2 or to extend the choice of license to
36   * its licensees as provided above.  However, if you add GPL Version 2 code
37   * and therefore, elected the GPL Version 2 license, then the option applies
38   * only if the new code is made subject to such option by the copyright
39   * holder.
40   */
41  package com.helger.jcodemodel;
42  
43  import java.io.File;
44  import java.io.IOException;
45  import java.io.PrintStream;
46  import java.nio.charset.Charset;
47  import java.util.ArrayList;
48  import java.util.Collections;
49  import java.util.HashMap;
50  import java.util.Iterator;
51  import java.util.List;
52  import java.util.Map;
53  
54  import javax.annotation.Nonnegative;
55  import javax.annotation.Nonnull;
56  import javax.annotation.Nullable;
57  import javax.annotation.concurrent.NotThreadSafe;
58  import javax.lang.model.element.TypeElement;
59  import javax.lang.model.util.Elements;
60  
61  import com.helger.jcodemodel.meta.CodeModelBuildingException;
62  import com.helger.jcodemodel.meta.ErrorTypeFound;
63  import com.helger.jcodemodel.meta.JCodeModelJavaxLangModelAdapter;
64  import com.helger.jcodemodel.util.JCSecureLoader;
65  import com.helger.jcodemodel.util.JCValueEnforcer;
66  import com.helger.jcodemodel.writer.FileCodeWriter;
67  import com.helger.jcodemodel.writer.ProgressCodeWriter;
68  
69  /**
70   * Root of the code DOM.
71   * <p>
72   * Here's your typical CodeModel application.
73   *
74   * <pre>
75   * JCodeModel cm = new JCodeModel();
76   *
77   * // generate source code by populating the 'cm' tree.
78   * cm._class(...);
79   * ...
80   *
81   * // write them out
82   * cm.build(new File("."));
83   * </pre>
84   * <p>
85   * Every CodeModel node is always owned by one {@link JCodeModel} object at any
86   * given time (which can be often accessed by the <tt>owner()</tt> method.) As
87   * such, when you generate Java code, most of the operation works in a top-down
88   * fashion. For example, you create a class from {@link JCodeModel}, which gives
89   * you a {@link JDefinedClass}. Then you invoke a method on it to generate a new
90   * method, which gives you {@link JMethod}, and so on. There are a few
91   * exceptions to this, most notably building {@link IJExpression}s, but
92   * generally you work with CodeModel in a top-down fashion. Because of this
93   * design, most of the CodeModel classes aren't directly instanciable.
94   * <h2>Where to go from here?</h2>
95   * <p>
96   * Most of the time you'd want to populate new type definitions in a
97   * {@link JCodeModel}. See {@link #_class(String, EClassType)}.
98   */
99  public final class JCodeModel
100 {
101   /**
102    * Conversion from primitive type {@link Class} (such as {@link Integer#TYPE})
103    * to its boxed type (such as <tt>Integer.class</tt>). It's an unmodifiable
104    * map.
105    */
106   public static final Map <Class <?>, Class <?>> primitiveToBox;
107 
108   /**
109    * The reverse look up for {@link #primitiveToBox}. It's an unmodifiable map.
110    */
111   public static final Map <Class <?>, Class <?>> boxToPrimitive;
112 
113   static
114   {
115     final Map <Class <?>, Class <?>> m1 = new HashMap <Class <?>, Class <?>> ();
116     final Map <Class <?>, Class <?>> m2 = new HashMap <Class <?>, Class <?>> ();
117 
118     m1.put (Boolean.class, Boolean.TYPE);
119     m1.put (Byte.class, Byte.TYPE);
120     m1.put (Character.class, Character.TYPE);
121     m1.put (Double.class, Double.TYPE);
122     m1.put (Float.class, Float.TYPE);
123     m1.put (Integer.class, Integer.TYPE);
124     m1.put (Long.class, Long.TYPE);
125     m1.put (Short.class, Short.TYPE);
126     m1.put (Void.class, Void.TYPE);
127 
128     // Swap keys and values
129     for (final Map.Entry <Class <?>, Class <?>> e : m1.entrySet ())
130       m2.put (e.getValue (), e.getKey ());
131 
132     boxToPrimitive = Collections.unmodifiableMap (m1);
133     primitiveToBox = Collections.unmodifiableMap (m2);
134   }
135 
136   /** The packages that this JCodeWriter contains. */
137   private final Map <String, JPackage> m_aPackages = new HashMap <String, JPackage> ();
138 
139   /** All JReferencedClasses are pooled here. */
140   private final Map <Class <?>, JReferencedClass> m_aRefClasses = new HashMap <Class <?>, JReferencedClass> ();
141 
142   /** Obtains a reference to the special "null" type. */
143   public final JNullType NULL = new JNullType (this);
144   // primitive types
145   public final JPrimitiveType BOOLEAN = new JPrimitiveType (this, "boolean", Boolean.class, true);
146   public final JPrimitiveType BYTE = new JPrimitiveType (this, "byte", Byte.class, true);
147   public final JPrimitiveType CHAR = new JPrimitiveType (this, "char", Character.class, true);
148   public final JPrimitiveType DOUBLE = new JPrimitiveType (this, "double", Double.class, true);
149   public final JPrimitiveType FLOAT = new JPrimitiveType (this, "float", Float.class, true);
150   public final JPrimitiveType INT = new JPrimitiveType (this, "int", Integer.class, true);
151   public final JPrimitiveType LONG = new JPrimitiveType (this, "long", Long.class, true);
152   public final JPrimitiveType SHORT = new JPrimitiveType (this, "short", Short.class, true);
153   public final JPrimitiveType VOID = new JPrimitiveType (this, "void", Void.class, false);
154 
155   protected static boolean getFileSystemCaseSensitivity ()
156   {
157     try
158     {
159       // let the system property override, in case the user really
160       // wants to override.
161       if (System.getProperty ("com.sun.codemodel.FileSystemCaseSensitive") != null)
162         return true;
163 
164       // Add special override to differentiate if Sun implementation is also in
165       // scope
166       if (System.getProperty ("com.helger.jcodemodel.FileSystemCaseSensitive") != null)
167         return true;
168     }
169     catch (final Exception e)
170     {
171       // Fall through
172     }
173 
174     // on Unix, it's case sensitive.
175     return File.separatorChar == '/';
176   }
177 
178   /**
179    * If the flag is true, we will consider two classes "Foo" and "foo" as a
180    * collision.
181    */
182   protected final boolean isCaseSensitiveFileSystem = getFileSystemCaseSensitivity ();
183 
184   /**
185    * Cached for {@link #wildcard()}.
186    */
187   private AbstractJClass m_aWildcard;
188 
189   /** The charset used for building the output - null means system default */
190   private Charset m_aBuildingCharset = null;
191 
192   /** The newline string to be used. Defaults to system default */
193   private String m_sBuildingNewLine = AbstractCodeWriter.getDefaultNewLine ();
194 
195   public JCodeModel ()
196   {}
197 
198   /**
199    * Add a package to the list of packages to be generated
200    *
201    * @param name
202    *        Name of the package. Use "" to indicate the root package.
203    * @return Newly generated package
204    */
205   @Nonnull
206   public JPackage _package (@Nonnull final String name)
207   {
208     JPackage p = m_aPackages.get (name);
209     if (p == null)
210     {
211       p = new JPackage (name, this);
212       m_aPackages.put (name, p);
213     }
214     return p;
215   }
216 
217   @Nonnull
218   public final JPackage rootPackage ()
219   {
220     return _package ("");
221   }
222 
223   /**
224    * @return an iterator that walks the packages defined using this code writer.
225    */
226   @Nonnull
227   public Iterator <JPackage> packages ()
228   {
229     return m_aPackages.values ().iterator ();
230   }
231 
232   /**
233    * Creates a new generated class.
234    *
235    * @param nMods
236    *        Modifiers to use
237    * @param sFullyQualifiedClassName
238    *        FQCN
239    * @param eClassType
240    *        Class type to use (enum/class/interface/annotation)
241    * @return New {@link JDefinedClass}
242    * @exception JClassAlreadyExistsException
243    *            When the specified class/interface was already created.
244    */
245   @Nonnull
246   public JDefinedClass _class (final int nMods,
247                                @Nonnull final String sFullyQualifiedClassName,
248                                @Nonnull final EClassType eClassType) throws JClassAlreadyExistsException
249   {
250     final int nIdx = sFullyQualifiedClassName.lastIndexOf ('.');
251     if (nIdx < 0)
252       return rootPackage ()._class (nMods, sFullyQualifiedClassName, eClassType);
253     return _package (sFullyQualifiedClassName.substring (0, nIdx))._class (nMods,
254                                                                            sFullyQualifiedClassName.substring (nIdx +
255                                                                                                                1),
256                                                                            eClassType);
257   }
258 
259   /**
260    * Creates a new generated class.
261    *
262    * @param sFullyQualifiedClassName
263    *        FQCN
264    * @return New {@link JDefinedClass}
265    * @exception JClassAlreadyExistsException
266    *            When the specified class/interface was already created.
267    */
268   @Nonnull
269   public JDefinedClass _class (@Nonnull final String sFullyQualifiedClassName) throws JClassAlreadyExistsException
270   {
271     return _class (sFullyQualifiedClassName, EClassType.CLASS);
272   }
273 
274   /**
275    * Creates a new generated class.
276    *
277    * @param nMods
278    *        Modifiers to use
279    * @param sFullyQualifiedClassName
280    *        FQCN
281    * @return New {@link JDefinedClass}
282    * @exception JClassAlreadyExistsException
283    *            When the specified class/interface was already created.
284    */
285   @Nonnull
286   public JDefinedClass _class (final int nMods,
287                                @Nonnull final String sFullyQualifiedClassName) throws JClassAlreadyExistsException
288   {
289     return _class (nMods, sFullyQualifiedClassName, EClassType.CLASS);
290   }
291 
292   /**
293    * Creates a new generated class.
294    *
295    * @param sFullyQualifiedClassName
296    *        FQCN
297    * @param eClassType
298    *        Class type to use (enum/class/interface/annotation)
299    * @return New {@link JDefinedClass}
300    * @exception JClassAlreadyExistsException
301    *            When the specified class/interface was already created.
302    */
303   @Nonnull
304   public JDefinedClass _class (@Nonnull final String sFullyQualifiedClassName,
305                                @Nonnull final EClassType eClassType) throws JClassAlreadyExistsException
306   {
307     return _class (JMod.PUBLIC, sFullyQualifiedClassName, eClassType);
308   }
309 
310   /**
311    * Creates a dummy, unknown {@link JDirectClass} that represents a given name.
312    * <br>
313    * This method is useful when the code generation needs to include the
314    * user-specified class that may or may not exist, and only thing known about
315    * it is a class name.
316    *
317    * @param sName
318    *        The fully qualified name of the class. When using type parameters
319    *        please use {@link #parseType(String)} instead!
320    * @return New {@link JDirectClass}
321    */
322   @Nonnull
323   public JDirectClass directClass (@Nonnull final String sName)
324   {
325     return directClass (EClassType.CLASS, sName);
326   }
327 
328   /**
329    * Creates a dummy, unknown {@link JDirectClass} that represents a given name.
330    * <br>
331    * This method is useful when the code generation needs to include the
332    * user-specified class that may or may not exist, and only thing known about
333    * it is a class name.
334    *
335    * @param eClassType
336    *        Class type to use.
337    * @param sName
338    *        The fully qualified name of the class. When using type parameters
339    *        please use {@link #parseType(String)} instead!
340    * @return New {@link JDirectClass}
341    */
342   @Nonnull
343   public JDirectClass directClass (@Nonnull final EClassType eClassType, @Nonnull final String sName)
344   {
345     return new JDirectClass (this, null, eClassType, sName);
346   }
347 
348   /**
349    * Creates a dummy, error {@link AbstractJClass} that can only be referenced
350    * from hidden classes.
351    * <p>
352    * This method is useful when the code generation needs to include some error
353    * class that should never leak into actually written code.
354    * <p>
355    * Error-types represents holes or place-holders that can't be filled.
356    * References to error-classes can be used in hidden class-models. Such
357    * classes should never be actually written but can be somehow used during
358    * code generation. Use {@code JCodeModel#buildsErrorTypeRefs} method to test
359    * if your generated Java-sources contains references to error-types.
360    * <p>
361    * You should probably always check generated code with
362    * {@code JCodeModel#buildsErrorTypeRefs} method if you use any error-types.
363    * <p>
364    * Most of error-types methods throws {@code JErrorClassUsedException}
365    * unchecked exceptions. Be careful and use {@link AbstractJType#isError()
366    * AbstractJType#isError} method to check for error-types before actually
367    * using it's methods.
368    *
369    * @param sMessage
370    *        some free form text message to identify source of error
371    * @return New {@link JErrorClass}
372    * @see JCodeModel#buildsErrorTypeRefs()
373    * @see JErrorClass
374    */
375   @Nonnull
376   public JErrorClass errorClass (@Nonnull final String sMessage)
377   {
378     return errorClass (sMessage, null);
379   }
380 
381   /**
382    * Creates a dummy, error {@link AbstractJClass} that can only be referenced
383    * from hidden classes.
384    * <p>
385    * This method is useful when the code generation needs to include some error
386    * class that should never leak into actually written code.
387    * <p>
388    * Error-types represents holes or place-holders that can't be filled.
389    * References to error-classes can be used in hidden class-models. Such
390    * classes should never be actually written but can be somehow used during
391    * code generation. Use {@code JCodeModel#buildsErrorTypeRefs} method to test
392    * if your generated Java-sources contains references to error-types.
393    * <p>
394    * You should probably always check generated code with
395    * {@code JCodeModel#buildsErrorTypeRefs} method if you use any error-types.
396    * <p>
397    * Most of error-types methods throws {@code JErrorClassUsedException}
398    * unchecked exceptions. Be careful and use {@link AbstractJType#isError()
399    * AbstractJType#isError} method to check for error-types before actually
400    * using it's methods.
401    *
402    * @param sName
403    *        name of missing class if it is known
404    * @param sMessage
405    *        some free form text message to identify source of error
406    * @return New {@link JErrorClass}
407    * @see JCodeModel#buildsErrorTypeRefs()
408    * @see JErrorClass
409    */
410   @Nonnull
411   public JErrorClass errorClass (@Nonnull final String sMessage, @Nullable final String sName)
412   {
413     return new JErrorClass (this, sMessage, sName);
414   }
415 
416   /**
417    * Check if any error-types leaked into output Java-sources.
418    *
419    * @return <code>true</code> if so
420    * @see JCodeModel#errorClass(String)
421    */
422   public boolean buildsErrorTypeRefs ()
423   {
424     final JPackage [] pkgs = m_aPackages.values ().toArray (new JPackage [m_aPackages.size ()]);
425     // avoid concurrent modification exception
426     for (final JPackage pkg : pkgs)
427     {
428       if (pkg.buildsErrorTypeRefs ())
429         return true;
430     }
431     return false;
432   }
433 
434   /**
435    * Gets a reference to the already created generated class.
436    *
437    * @param sFullyQualifiedClassName
438    *        FQCN
439    * @return <code>null</code> If the class is not yet created.
440    * @see JPackage#_getClass(String)
441    */
442   @Nullable
443   public JDefinedClass _getClass (@Nonnull final String sFullyQualifiedClassName)
444   {
445     final int nIndex = sFullyQualifiedClassName.lastIndexOf ('.');
446     if (nIndex < 0)
447       return rootPackage ()._getClass (sFullyQualifiedClassName);
448     return _package (sFullyQualifiedClassName.substring (0, nIndex))
449                                                                     ._getClass (sFullyQualifiedClassName.substring (nIndex +
450                                                                                                                     1));
451   }
452 
453   /**
454    * Creates a new anonymous class.
455    *
456    * @param aBaseClass
457    *        Base class
458    * @return New {@link JAnonymousClass}
459    */
460   @Nonnull
461   public JAnonymousClass anonymousClass (@Nonnull final AbstractJClass aBaseClass)
462   {
463     return new JAnonymousClass (aBaseClass);
464   }
465 
466   /**
467    * Creates a new anonymous class.
468    *
469    * @param aBaseClass
470    *        Base class
471    * @return New {@link JAnonymousClass}
472    */
473   @Nonnull
474   public JAnonymousClass anonymousClass (@Nonnull final Class <?> aBaseClass)
475   {
476     return anonymousClass (ref (aBaseClass));
477   }
478 
479   /**
480    * @return The default charset used for building. <code>null</code> means
481    *         system default.
482    */
483   @Nullable
484   public Charset getBuildingCharset ()
485   {
486     return m_aBuildingCharset;
487   }
488 
489   /**
490    * Set the charset to be used for emitting files.
491    *
492    * @param aCharset
493    *        The charset to be used. May be <code>null</code> to indicate the use
494    *        of the system default.
495    * @return this for chaining
496    */
497   @Nonnull
498   public JCodeModel setBuildingCharset (@Nullable final Charset aCharset)
499   {
500     m_aBuildingCharset = aCharset;
501     return this;
502   }
503 
504   /**
505    * @return The newline string to be used. Defaults to system default
506    */
507   public String getBuildingNewLine ()
508   {
509     return m_sBuildingNewLine;
510   }
511 
512   /**
513    * Set the new line string to be used for emitting source files.
514    *
515    * @param sNewLine
516    *        The new line string to be used. May neither be <code>null</code> nor
517    *        empty.
518    * @return this for chaining
519    */
520   @Nonnull
521   public JCodeModel setBuildingNewLine (@Nonnull final String sNewLine)
522   {
523     JCValueEnforcer.notEmpty (sNewLine, "NewLine");
524     m_sBuildingNewLine = sNewLine;
525     return this;
526   }
527 
528   /**
529    * Generates Java source code. A convenience method for
530    * <code>build(destDir,destDir,status)</code>.
531    *
532    * @param destDir
533    *        source files and resources are generated into this directory.
534    * @param status
535    *        if non-null, progress indication will be sent to this stream.
536    * @throws IOException
537    *         on IO error
538    */
539   public void build (@Nonnull final File destDir, @Nullable final PrintStream status) throws IOException
540   {
541     build (destDir, destDir, status);
542   }
543 
544   /**
545    * Generates Java source code. A convenience method that calls
546    * {@link #build(AbstractCodeWriter,AbstractCodeWriter)}.
547    *
548    * @param srcDir
549    *        Java source files are generated into this directory.
550    * @param resourceDir
551    *        Other resource files are generated into this directory.
552    * @param status
553    *        Progress stream. May be <code>null</code>.
554    * @throws IOException
555    *         on IO error if non-null, progress indication will be sent to this
556    *         stream.
557    */
558   public void build (@Nonnull final File srcDir,
559                      @Nonnull final File resourceDir,
560                      @Nullable final PrintStream status) throws IOException
561   {
562     AbstractCodeWriter res = new FileCodeWriter (resourceDir, m_aBuildingCharset, m_sBuildingNewLine);
563     AbstractCodeWriter src = new FileCodeWriter (srcDir, m_aBuildingCharset, m_sBuildingNewLine);
564     if (status != null)
565     {
566       src = new ProgressCodeWriter (src, status);
567       res = new ProgressCodeWriter (res, status);
568     }
569     build (src, res);
570   }
571 
572   /**
573    * A convenience method for <code>build(destDir,System.out)</code>.
574    *
575    * @param destDir
576    *        source files and resources are generated into this directory.
577    * @throws IOException
578    *         on IO error
579    */
580   public void build (@Nonnull final File destDir) throws IOException
581   {
582     build (destDir, System.out);
583   }
584 
585   /**
586    * A convenience method for <code>build(srcDir,resourceDir,System.out)</code>.
587    *
588    * @param srcDir
589    *        Java source files are generated into this directory.
590    * @param resourceDir
591    *        Other resource files are generated into this directory.
592    * @throws IOException
593    *         on IO error
594    */
595   public void build (@Nonnull final File srcDir, @Nonnull final File resourceDir) throws IOException
596   {
597     build (srcDir, resourceDir, System.out);
598   }
599 
600   /**
601    * A convenience method for <code>build(out,out)</code>.
602    *
603    * @param out
604    *        Source code and resource writer
605    * @throws IOException
606    *         on IO error
607    */
608   public void build (@Nonnull final AbstractCodeWriter out) throws IOException
609   {
610     build (out, out);
611   }
612 
613   /**
614    * Generates Java source code.
615    *
616    * @param source
617    *        Source code writer
618    * @param resource
619    *        Resource writer
620    * @throws IOException
621    *         on IO error
622    */
623   public void build (@Nonnull final AbstractCodeWriter source,
624                      @Nonnull final AbstractCodeWriter resource) throws IOException
625   {
626     try
627     {
628       final JPackage [] pkgs = m_aPackages.values ().toArray (new JPackage [m_aPackages.size ()]);
629       // avoid concurrent modification exception
630       for (final JPackage pkg : pkgs)
631         pkg.build (source, resource);
632     }
633     finally
634     {
635       source.close ();
636       resource.close ();
637     }
638   }
639 
640   /**
641    * @return the number of files to be generated if {@link #build} is invoked
642    *         now.
643    */
644   @Nonnegative
645   public int countArtifacts ()
646   {
647     int r = 0;
648     final JPackage [] pkgs = m_aPackages.values ().toArray (new JPackage [m_aPackages.size ()]);
649     // avoid concurrent modification exception
650     for (final JPackage pkg : pkgs)
651       r += pkg.countArtifacts ();
652     return r;
653   }
654 
655   /**
656    * Obtains a reference to an existing class from its Class object.
657    * <p>
658    * The parameter may not be primitive.
659    *
660    * @param clazz
661    *        Existing class to reference
662    * @return Singleton reference to this class. Might be a
663    *         {@link JReferencedClass} or a {@link JArrayClass}
664    * @see #_ref(Class) for the version that handles more cases.
665    */
666   @Nonnull
667   public AbstractJClass ref (@Nonnull final Class <?> clazz)
668   {
669     JReferencedClass aRefClass = m_aRefClasses.get (clazz);
670     if (aRefClass == null)
671     {
672       if (clazz.isPrimitive ())
673       {
674         // Cannot return BYTE etc. because the return type does not match
675         throw new IllegalArgumentException (clazz + " is a primitive");
676       }
677 
678       if (clazz.isArray ())
679       {
680         final Class <?> aComponentType = clazz.getComponentType ();
681         // Component type may be a primitive!
682         return new JArrayClass (this, _ref (aComponentType));
683       }
684 
685       aRefClass = new JReferencedClass (this, clazz);
686       m_aRefClasses.put (clazz, aRefClass);
687     }
688     return aRefClass;
689   }
690 
691   /**
692    * Obtains a reference to a processable class from its TypeElement
693    * description.
694    * <p>
695    * Annotation processors can get access of {@link TypeElement} objects during
696    * annotation processing. These TypeElement objects can be used with
697    * jcodemodel as a references to classes.
698    * <p>
699    * This method result-class definition can never include references to
700    * "error"-types. Error-types araise during annotation processing when
701    * something is not fully defined.
702    * <p>
703    * You can post-pond annotation processing for later stages of annotation
704    * processor hoping that all error-types will become defined on some
705    * annotation processing stage at last. You can catch {@link ErrorTypeFound}
706    * exception to achieve this.
707    *
708    * @param element
709    *        Processable class to reference
710    * @param elementUtils
711    *        Utility functions to handle Element-objects
712    * @return Singleton reference to this class.
713    * @throws ErrorTypeFound
714    *         if some classes are not fully defined during annotation processing.
715    * @throws CodeModelBuildingException
716    *         In case of an internal error (?)
717    * @see JCodeModelJavaxLangModelAdapter
718    * @see #refWithErrorTypes(TypeElement,Elements)
719    */
720   @Nonnull
721   public JDefinedClass ref (@Nonnull final TypeElement element,
722                             @Nonnull final Elements elementUtils) throws ErrorTypeFound, CodeModelBuildingException
723   {
724     final JCodeModelJavaxLangModelAdapter adapter = new JCodeModelJavaxLangModelAdapter (this, elementUtils);
725     return adapter.getClass (element);
726   }
727 
728   /**
729    * Obtains a reference to a processable class from its TypeElement
730    * description.
731    * <p>
732    * Annotation processors can get access of TypeElement objects during
733    * annotation processing. These TypeElement objects can be used with
734    * jcodemodel as a references to classes.
735    * <p>
736    * This method result-class definition can include references to
737    * "error"-types. Error-types araise during annotation processing when
738    * something is not fully defined.
739    * <p>
740    * Sometimes direct treatment of error-types is required. You can use
741    * {@link AbstractJType#isError()} and
742    * {@link JCodeModel#buildsErrorTypeRefs()} methods to handle error-types and
743    * to prevent error-types to leak into generated code.
744    *
745    * @param element
746    *        Processable class to reference
747    * @param elementUtils
748    *        Utility functions to handle Element-objects
749    * @return Singleton reference to this class.
750    * @throws CodeModelBuildingException
751    *         In case of an internal error (?)
752    * @see JCodeModelJavaxLangModelAdapter
753    * @see #ref(TypeElement, Elements)
754    * @see JErrorClass
755    * @see #buildsErrorTypeRefs()
756    */
757   @Nonnull
758   public JDefinedClass refWithErrorTypes (@Nonnull final TypeElement element,
759                                           @Nonnull final Elements elementUtils) throws CodeModelBuildingException
760   {
761     final JCodeModelJavaxLangModelAdapter adapter = new JCodeModelJavaxLangModelAdapter (this, elementUtils);
762     return adapter.getClassWithErrorTypes (element);
763   }
764 
765   /**
766    * Like {@link #ref(Class)} but also handling primitive types!
767    *
768    * @param c
769    *        Class to be referenced
770    * @return primitive or class
771    */
772   @Nonnull
773   public AbstractJType _ref (@Nonnull final Class <?> c)
774   {
775     if (c.isPrimitive ())
776       return AbstractJType.parse (this, c.getName ());
777     return ref (c);
778   }
779 
780   /**
781    * Obtains a reference to an existing class from its fully-qualified class
782    * name. <br>
783    * First, this method attempts to load the class of the given name. If that
784    * fails, we assume that the class is derived straight from {@link Object},
785    * and return a {@link AbstractJClass}.
786    *
787    * @param sFullyQualifiedClassName
788    *        FQCN
789    * @return Singleton reference to this class. Might be a
790    *         {@link JReferencedClass} or a {@link JArrayClass} or a
791    *         {@link JDirectClass}
792    */
793   @Nonnull
794   public AbstractJClass ref (@Nonnull final String sFullyQualifiedClassName)
795   {
796     try
797     {
798       // try the context class loader first
799       return ref (JCSecureLoader.getContextClassLoader ().loadClass (sFullyQualifiedClassName));
800     }
801     catch (final ClassNotFoundException e)
802     {
803       // fall through
804     }
805 
806     // then the default mechanism.
807     try
808     {
809       return ref (Class.forName (sFullyQualifiedClassName));
810     }
811     catch (final ClassNotFoundException e)
812     {
813       // fall through
814     }
815 
816     // assume it's not visible to us.
817     return new JDirectClass (this, null, EClassType.CLASS, sFullyQualifiedClassName);
818   }
819 
820   /**
821    * @return Singleton {@link AbstractJClass} representation for "?", which is
822    *         equivalent to "? extends Object".
823    */
824   @Nonnull
825   public AbstractJClass wildcard ()
826   {
827     if (m_aWildcard == null)
828       m_aWildcard = ref (Object.class).wildcard ();
829     return m_aWildcard;
830   }
831 
832   /**
833    * Obtains a type object from a type name.
834    * <p>
835    * This method handles primitive types, arrays, and existing {@link Class}es.
836    *
837    * @param name
838    *        Type name to parse
839    * @return The internal representation of the specified name. Might be a
840    *         {@link JArrayClass}, a {@link JPrimitiveType}, a
841    *         {@link JReferencedClass}, a {@link JNarrowedClass}
842    */
843   @Nonnull
844   public AbstractJType parseType (@Nonnull final String name)
845   {
846     // array
847     if (name.endsWith ("[]"))
848     {
849       // Simply remove trailing "[]"
850       return parseType (name.substring (0, name.length () - 2)).array ();
851     }
852 
853     // try primitive type
854     try
855     {
856       return AbstractJType.parse (this, name);
857     }
858     catch (final IllegalArgumentException e)
859     {
860       // Not a primitive type
861     }
862 
863     // existing class
864     return new TypeNameParser (name).parseTypeName ();
865   }
866 
867   @NotThreadSafe
868   private final class TypeNameParser
869   {
870     private final String m_sTypeName;
871     private int m_nIdx;
872 
873     public TypeNameParser (@Nonnull final String s)
874     {
875       m_sTypeName = s;
876     }
877 
878     /**
879      * Parses a type name token T (which can be potentially of the form
880      * T&lt;T1,T2,...&gt;, or "? extends/super T".)
881      *
882      * @return The parsed type name
883      */
884     @Nonnull
885     AbstractJClass parseTypeName ()
886     {
887       final int nStart = m_nIdx;
888 
889       if (m_sTypeName.charAt (m_nIdx) == '?')
890       {
891         // wildcard
892         m_nIdx++;
893         _skipWs ();
894 
895         final String head = m_sTypeName.substring (m_nIdx);
896         if (head.startsWith ("extends"))
897         {
898           // 7 == "extends".length
899           m_nIdx += 7;
900           _skipWs ();
901           return parseTypeName ().wildcard ();
902         }
903 
904         if (head.startsWith ("super"))
905         {
906           // 5 == "super".length
907           m_nIdx += 5;
908           _skipWs ();
909           return parseTypeName ().wildcardSuper ();
910         }
911 
912         // not supported
913         throw new IllegalArgumentException ("only extends/super can follow ?, but found " +
914                                             m_sTypeName.substring (m_nIdx));
915       }
916 
917       while (m_nIdx < m_sTypeName.length ())
918       {
919         final char ch = m_sTypeName.charAt (m_nIdx);
920         if (Character.isJavaIdentifierStart (ch) || Character.isJavaIdentifierPart (ch) || ch == '.')
921           m_nIdx++;
922         else
923           break;
924       }
925 
926       final AbstractJClass clazz = ref (m_sTypeName.substring (nStart, m_nIdx));
927       return _parseSuffix (clazz);
928     }
929 
930     /**
931      * Parses additional left-associative suffixes, like type arguments and
932      * array specifiers.
933      */
934     @Nonnull
935     private AbstractJClass _parseSuffix (@Nonnull final AbstractJClass clazz)
936     {
937       if (m_nIdx == m_sTypeName.length ())
938       {
939         // hit EOL
940         return clazz;
941       }
942 
943       final char ch = m_sTypeName.charAt (m_nIdx);
944 
945       if (ch == '<')
946         return _parseSuffix (_parseArguments (clazz));
947 
948       if (ch == '[')
949       {
950         if (m_sTypeName.charAt (m_nIdx + 1) == ']')
951         {
952           m_nIdx += 2;
953           return _parseSuffix (clazz.array ());
954         }
955         throw new IllegalArgumentException ("Expected ']' but found " + m_sTypeName.substring (m_nIdx + 1));
956       }
957 
958       return clazz;
959     }
960 
961     /**
962      * Skips whitespaces
963      */
964     private void _skipWs ()
965     {
966       while (Character.isWhitespace (m_sTypeName.charAt (m_nIdx)) && m_nIdx < m_sTypeName.length ())
967         m_nIdx++;
968     }
969 
970     /**
971      * Parses '&lt;T1,T2,...,Tn&gt;'
972      *
973      * @return the index of the character next to '>'
974      */
975     @Nonnull
976     private AbstractJClass _parseArguments (@Nonnull final AbstractJClass rawType)
977     {
978       if (m_sTypeName.charAt (m_nIdx) != '<')
979         throw new IllegalArgumentException ();
980       m_nIdx++;
981 
982       final List <AbstractJClass> args = new ArrayList <AbstractJClass> ();
983 
984       while (true)
985       {
986         args.add (parseTypeName ());
987         if (m_nIdx == m_sTypeName.length ())
988           throw new IllegalArgumentException ("Missing '>' in " + m_sTypeName);
989         final char ch = m_sTypeName.charAt (m_nIdx);
990         if (ch == '>')
991           return rawType.narrow (args);
992 
993         if (ch != ',')
994           throw new IllegalArgumentException (m_sTypeName);
995         m_nIdx++;
996       }
997     }
998   }
999 }