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-2018 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.lang.annotation.Annotation;
44  import java.util.ArrayList;
45  import java.util.Collection;
46  import java.util.Collections;
47  import java.util.List;
48  import java.util.Set;
49  import java.util.TreeSet;
50  
51  import javax.annotation.Nonnegative;
52  import javax.annotation.Nonnull;
53  import javax.annotation.Nullable;
54  
55  import com.helger.jcodemodel.util.ClassNameComparator;
56  import com.helger.jcodemodel.util.JCValueEnforcer;
57  
58  /**
59   * Java method.
60   */
61  public class JMethod extends AbstractJGenerifiableImpl implements IJAnnotatable, IJDocCommentable
62  {
63    /**
64     * Modifiers for this method
65     */
66    private final JMods m_aMods;
67  
68    /**
69     * Return type for this method
70     */
71    private AbstractJType m_aReturnType;
72  
73    /**
74     * Name of this method
75     */
76    private String m_sName;
77  
78    /**
79     * List of parameters for this method's declaration
80     */
81    private final List <JVar> m_aParams = new ArrayList <> ();
82  
83    /**
84     * Set of exceptions that this method may throw. A set instance lazily
85     * created.
86     */
87    private Set <AbstractJClass> m_aThrows;
88  
89    /**
90     * JBlock of statements that makes up the body this method
91     */
92    private JBlock m_aBody;
93  
94    private final JDefinedClass m_aOwningClass;
95  
96    /**
97     * javadoc comments for this JMethod
98     */
99    private JDocComment m_aJDoc;
100 
101   /**
102    * Variable parameter for this method's varargs declaration introduced in J2SE
103    * 1.5
104    */
105   private JVar m_aVarParam;
106 
107   /**
108    * Annotations on this variable. Lazily created.
109    */
110   private List <JAnnotationUse> m_aAnnotations;
111   /**
112    * To set the default value for the annotation member
113    */
114   private IJExpression m_aDefaultValue;
115 
116   /**
117    * Constructor for regular methods
118    *
119    * @param aOwningClass
120    *        Outer class. May not be <code>null</code>.
121    * @param nMods
122    *        Modifiers for this method's declaration
123    * @param aReturnType
124    *        Return type for the method. May not be <code>null</code>.
125    * @param sName
126    *        Name of this method. May neither be <code>null</code> nor empty.
127    */
128   protected JMethod (@Nonnull final JDefinedClass aOwningClass,
129                      final int nMods,
130                      @Nonnull final AbstractJType aReturnType,
131                      @Nonnull final String sName)
132   {
133     JCValueEnforcer.notNull (aOwningClass, "OwningClass");
134     JCValueEnforcer.notNull (aReturnType, "ReturnType");
135     JCValueEnforcer.notEmpty (sName, "Name");
136     m_aMods = JMods.forMethod (nMods);
137     m_aReturnType = aReturnType;
138     m_sName = sName;
139     m_aOwningClass = aOwningClass;
140   }
141 
142   /**
143    * Constructor for constructors
144    *
145    * @param nMods
146    *        Modifiers for this constructor's declaration
147    * @param aClass
148    *        Class containing this constructor. May not be <code>null</code>.
149    */
150   protected JMethod (final int nMods, @Nonnull final JDefinedClass aClass)
151   {
152     JCValueEnforcer.notNull (aClass, "Class");
153     m_aMods = JMods.forMethod (nMods);
154     m_aReturnType = null;
155     m_sName = aClass.name ();
156     m_aOwningClass = aClass;
157   }
158 
159   public boolean isConstructor ()
160   {
161     return m_aReturnType == null;
162   }
163 
164   @Nonnull
165   public Collection <AbstractJClass> getThrows ()
166   {
167     if (m_aThrows == null)
168       return Collections.emptySet ();
169     return Collections.unmodifiableSet (m_aThrows);
170   }
171 
172   /**
173    * Add an exception to the list of exceptions that this method may throw.
174    *
175    * @param aException
176    *        Name of an exception that this method may throw
177    * @return this
178    */
179   @Nonnull
180   public JMethod _throws (@Nonnull final AbstractJClass aException)
181   {
182     if (m_aThrows == null)
183       m_aThrows = new TreeSet <> (ClassNameComparator.getInstance ());
184     m_aThrows.add (aException);
185     return this;
186   }
187 
188   @Nonnull
189   public JMethod _throws (@Nonnull final Class <? extends Throwable> aException)
190   {
191     return _throws (m_aOwningClass.owner ().ref (aException));
192   }
193 
194   /**
195    * Returns the list of variable of this method.
196    *
197    * @return List of parameters of this method. This list is not modifiable.
198    */
199   @Nonnull
200   public List <JVar> params ()
201   {
202     return Collections.unmodifiableList (m_aParams);
203   }
204 
205   @Nonnull
206   public JVar paramAtIndex (@Nonnegative final int nIndex) throws IndexOutOfBoundsException
207   {
208     return m_aParams.get (nIndex);
209   }
210 
211   /**
212    * Add the specified variable to the list of parameters for this method
213    * signature.
214    *
215    * @param nMods
216    *        Java modifiers to be used
217    * @param aType
218    *        JType of the parameter being added
219    * @param sName
220    *        Name of the parameter being added
221    * @return New parameter variable of type {@link JVar}
222    */
223   @Nonnull
224   public JVar param (final int nMods, @Nonnull final AbstractJType aType, @Nonnull final String sName)
225   {
226     final JVarhtml#JVar">JVar aVar = new JVar (JMods.forVar (nMods), aType, sName, null);
227     m_aParams.add (aVar);
228     return aVar;
229   }
230 
231   @Nonnull
232   public JVar param (@Nonnull final AbstractJType aType, @Nonnull final String sName)
233   {
234     return param (JMod.NONE, aType, sName);
235   }
236 
237   @Nonnull
238   public JVar param (final int nMods, @Nonnull final Class <?> aType, @Nonnull final String sName)
239   {
240     return param (nMods, m_aOwningClass.owner ()._ref (aType), sName);
241   }
242 
243   @Nonnull
244   public JVar param (@Nonnull final Class <?> aType, @Nonnull final String sName)
245   {
246     return param (m_aOwningClass.owner ()._ref (aType), sName);
247   }
248 
249   /**
250    * Add the specified variable argument to the list of parameters for this
251    * method signature.
252    *
253    * @param aType
254    *        Type of the parameter being added.
255    * @param sName
256    *        Name of the parameter being added
257    * @return the variable parameter
258    * @throws IllegalStateException
259    *         If this method is called twice. varargs in J2SE 1.5 can appear only
260    *         once in the method signature.
261    */
262   @Nonnull
263   public JVar varParam (@Nonnull final Class <?> aType, @Nonnull final String sName)
264   {
265     return varParam (m_aOwningClass.owner ()._ref (aType), sName);
266   }
267 
268   /**
269    * Add the specified variable argument to the list of parameters for this
270    * method signature.
271    *
272    * @param aType
273    *        Type of the parameter being added.
274    * @param sName
275    *        Name of the parameter being added
276    * @return the variable parameter
277    * @throws IllegalStateException
278    *         If this method is called twice. varargs in J2SE 1.5 can appear only
279    *         once in the method signature.
280    */
281   @Nonnull
282   public JVar varParam (@Nonnull final AbstractJType aType, @Nonnull final String sName)
283   {
284     return varParam (JMod.NONE, aType, sName);
285   }
286 
287   /**
288    * Add the specified variable argument to the list of parameters for this
289    * method signature.
290    *
291    * @param nMods
292    *        nMods to use
293    * @param aType
294    *        Type of the parameter being added. Is automatically converted to an
295    *        array.
296    * @param sName
297    *        Name of the parameter being added
298    * @return the created variable parameter
299    * @throws IllegalStateException
300    *         If this method is called twice. varargs in J2SE 1.5 can appear only
301    *         once in the method signature.
302    */
303   @Nonnull
304   public JVar varParam (final int nMods, @Nonnull final Class <?> aType, @Nonnull final String sName)
305   {
306     return varParam (nMods, m_aOwningClass.owner ()._ref (aType), sName);
307   }
308 
309   /**
310    * Add the specified variable argument to the list of parameters for this
311    * method signature.
312    *
313    * @param nMods
314    *        nMods to use
315    * @param aType
316    *        Type of the parameter being added. Is automatically converted to an
317    *        array.
318    * @param sName
319    *        Name of the parameter being added
320    * @return the created variable parameter
321    * @throws IllegalStateException
322    *         If this method is called twice. varargs in J2SE 1.5 can appear only
323    *         once in the method signature.
324    */
325   @Nonnull
326   public JVar varParam (final int nMods, @Nonnull final AbstractJType aType, @Nonnull final String sName)
327   {
328     JCValueEnforcer.isFalse (hasVarArgs (),
329                              "Cannot have two varargs in a method,\n" +
330                                             "Check if varParam method of JMethod is" +
331                                             " invoked more than once");
332 
333     m_aVarParam = new JVar (JMods.forVar (nMods), aType.array (), sName, null);
334     return m_aVarParam;
335   }
336 
337   @Nullable
338   public JVar varParam ()
339   {
340     return m_aVarParam;
341   }
342 
343   /**
344    * @return <code>true</code> if there are any varargs declared for this method
345    *         signature.
346    */
347   public boolean hasVarArgs ()
348   {
349     return m_aVarParam != null;
350   }
351 
352   /**
353    * Returns the varags parameter type.
354    *
355    * @return If there's no vararg parameter type, null will be returned.
356    */
357   @Nullable
358   public AbstractJType listVarParamType ()
359   {
360     return m_aVarParam != null ? m_aVarParam.type () : null;
361   }
362 
363   /**
364    * Adds an annotation to this variable.
365    *
366    * @param aClazz
367    *        The annotation class to annotate the field with
368    * @return The created object. Never <code>null</code>.
369    */
370   @Nonnull
371   public JAnnotationUse annotate (@Nonnull final AbstractJClass aClazz)
372   {
373     if (m_aAnnotations == null)
374       m_aAnnotations = new ArrayList <> ();
375     final JAnnotationUsese.html#JAnnotationUse">JAnnotationUse a = new JAnnotationUse (aClazz);
376     m_aAnnotations.add (a);
377     return a;
378   }
379 
380   /**
381    * Adds an annotation to this variable.
382    *
383    * @param aClazz
384    *        The annotation class to annotate the field with
385    * @return The created object. Never <code>null</code>.
386    */
387   @Nonnull
388   public JAnnotationUse annotate (@Nonnull final Class <? extends Annotation> aClazz)
389   {
390     return annotate (owner ().ref (aClazz));
391   }
392 
393   @Nonnull
394   public Collection <JAnnotationUse> annotations ()
395   {
396     if (m_aAnnotations == null)
397       return Collections.emptyList ();
398     return Collections.unmodifiableList (m_aAnnotations);
399   }
400 
401   public String name ()
402   {
403     return m_sName;
404   }
405 
406   /**
407    * Changes the name of the method.
408    *
409    * @param sName
410    *        New name
411    */
412   public void name (@Nonnull final String sName)
413   {
414     JCValueEnforcer.notEmpty (sName, "Name");
415     m_sName = sName;
416   }
417 
418   /**
419    * @return the return type. Is <code>null</code> for constructors.
420    */
421   @Nullable
422   public AbstractJType type ()
423   {
424     return m_aReturnType;
425   }
426 
427   /**
428    * Overrides the return type.
429    *
430    * @param aReturnType
431    *        The type to set. Set to <code>null</code> to make this method a
432    *        constructor.
433    */
434   public void type (@Nullable final AbstractJType aReturnType)
435   {
436     m_aReturnType = aReturnType;
437   }
438 
439   /**
440    * Returns all the parameter types in an array.
441    *
442    * @return If there's no parameter, an empty array will be returned.
443    */
444   @Nonnull
445   public AbstractJType [] listParamTypes ()
446   {
447     final AbstractJTypehtml#AbstractJType">AbstractJType [] r = new AbstractJType [m_aParams.size ()];
448     for (int i = 0; i < r.length; i++)
449       r[i] = m_aParams.get (i).type ();
450     return r;
451   }
452 
453   /**
454    * Returns all the parameters in an array.
455    *
456    * @return If there's no parameter, an empty array will be returned.
457    */
458   @Nonnull
459   public JVar [] listParams ()
460   {
461     return m_aParams.toArray (new JVar [m_aParams.size ()]);
462   }
463 
464   /**
465    * Returns the variable parameter
466    *
467    * @return If there's no parameter, null will be returned.
468    * @deprecated Use {@link #varParam()} instead.
469    */
470   @Nullable
471   @Deprecated
472   public JVar listVarParam ()
473   {
474     return varParam ();
475   }
476 
477   /**
478    * Returns true if the method has the specified signature.
479    *
480    * @param argTypes
481    *        Signature to check
482    * @return <code>true</code> if this method has the provided signature
483    */
484   public boolean hasSignature (@Nonnull final AbstractJType [] argTypes)
485   {
486     final JVar [] aParams = listParams ();
487     if (aParams.length != argTypes.length)
488       return false;
489 
490     for (int i = 0; i < aParams.length; i++)
491       if (!aParams[i].type ().equals (argTypes[i]))
492         return false;
493 
494     return true;
495   }
496 
497   /**
498    * Get the block that makes up body of this method
499    *
500    * @return Body of method. Never <code>null</code>.
501    */
502   @Nonnull
503   public JBlock body ()
504   {
505     if (m_aBody == null)
506       m_aBody = new JBlock ();
507     return m_aBody;
508   }
509 
510   /**
511    * Specify the default value for this method
512    *
513    * @param aDefaultValue
514    *        Default value for the method
515    */
516   public void declareDefaultValue (@Nullable final IJExpression aDefaultValue)
517   {
518     m_aDefaultValue = aDefaultValue;
519   }
520 
521   @Nonnull
522   public JDocComment javadoc ()
523   {
524     if (m_aJDoc == null)
525       m_aJDoc = new JDocComment (owner ());
526     return m_aJDoc;
527   }
528 
529   @Override
530   public void declare (@Nonnull final IJFormatter f)
531   {
532     if (m_aJDoc != null)
533       f.generable (m_aJDoc);
534 
535     if (m_aAnnotations != null)
536       for (final JAnnotationUse a : m_aAnnotations)
537         f.generable (a).newline ();
538 
539     f.generable (m_aMods);
540 
541     // declare the generics parameters
542     super.declare (f);
543 
544     if (!isConstructor ())
545       f.generable (m_aReturnType);
546     f.id (m_sName).print ('(').indent ();
547     // when parameters are printed in new lines, we want them to be indented.
548     // there's a good chance no newlines happen, too, but just in case it does.
549     boolean first = true;
550     for (final JVar var : m_aParams)
551     {
552       if (!first)
553         f.print (',');
554       if (var.isAnnotated ())
555         f.newline ();
556       f.var (var);
557       first = false;
558     }
559     if (hasVarArgs ())
560     {
561       if (!first)
562         f.print (',');
563       for (final JAnnotationUse annotation : m_aVarParam.annotations ())
564         f.generable (annotation).newline ();
565       f.generable (m_aVarParam.mods ()).generable (m_aVarParam.type ().elementType ());
566       f.print ("... ");
567       f.id (m_aVarParam.name ());
568     }
569 
570     f.outdent ().print (')');
571     if (m_aThrows != null && !m_aThrows.isEmpty ())
572     {
573       f.newline ().indent ().print ("throws").generable (m_aThrows).newline ().outdent ();
574     }
575 
576     if (m_aDefaultValue != null)
577     {
578       // For annotation values
579       f.print ("default ");
580       f.generable (m_aDefaultValue);
581     }
582     if (m_aBody != null)
583     {
584       f.statement (m_aBody);
585     }
586     else
587     {
588       final boolean bIsDeclarationOnly = (m_aOwningClass.isInterface () && !m_aMods.isDefault ()) ||
589                                          m_aOwningClass.isAnnotationTypeDeclaration () ||
590                                          m_aMods.isAbstract () ||
591                                          m_aMods.isNative ();
592 
593       if (bIsDeclarationOnly)
594       {
595         f.print (';').newline ();
596       }
597       else
598       {
599         // Print an empty body for non-native, non-abstract methods
600         f.statement (new JBlock ());
601       }
602     }
603   }
604 
605   /**
606    * @return the current modifiers of this method. Always return non-null valid
607    *         object.
608    */
609   @Nonnull
610   public JMods mods ()
611   {
612     return m_aMods;
613   }
614 
615   /**
616    * @return The {@link JDefinedClass} to which this methods belongs. Never
617    *         <code>null</code>.
618    */
619   @Nonnull
620   public JDefinedClass owningClass ()
621   {
622     return m_aOwningClass;
623   }
624 
625   @Nonnull
626   public JCodeModel owner ()
627   {
628     return m_aOwningClass.owner ();
629   }
630 }