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<T1,T2,...>, 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 '<T1,T2,...,Tn>'
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 }