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-2015 Philip Helger
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 static com.helger.jcodemodel.util.JCEqualsHelper.isEqual;
44  
45  import java.util.ArrayList;
46  import java.util.Collections;
47  import java.util.List;
48  
49  import javax.annotation.Nonnull;
50  import javax.annotation.Nullable;
51  
52  import com.helger.jcodemodel.util.JCHashCodeGenerator;
53  
54  /**
55   * {@link JMethod} invocation
56   */
57  public class JInvocation extends AbstractJExpressionImpl implements IJStatement, IJOwnedMaybe
58  {
59    private final JCodeModel m_aOwner;
60  
61    /**
62     * Object expression upon which this method will be invoked, or null if this
63     * is a constructor invocation
64     */
65    private final IJGenerable m_aObject;
66  
67    /**
68     * Name of the method to be invoked. Either this field is set, or
69     * {@link #m_sMethod}, or {@link #m_aConstructorType} (in which case it's a constructor
70     * invocation.) This allows {@link JMethod#name(String) the name of the method
71     * to be changed later}.
72     */
73    private final String m_sMethodName;
74  
75    private final JMethod m_sMethod;
76  
77    private final boolean m_bIsConstructor;
78  
79    /**
80     * List of argument expressions for this method invocation
81     */
82    private final List <IJExpression> _args = new ArrayList <IJExpression> ();
83  
84    /**
85     * If isConstructor==true, this field keeps the type to be created.
86     */
87    private final AbstractJType m_aConstructorType;
88  
89    /**
90     * Lazily created list of {@link JTypeVar}s.
91     */
92    private List <JTypeVar> _typeVariables;
93  
94    /**
95     * Invokes a method on an object.
96     *
97     * @param object
98     *        JExpression for the object upon which the named method will be
99     *        invoked, or null if none
100    * @param name
101    *        Name of method to invoke
102    */
103   protected JInvocation (@Nullable final IJExpression object, @Nonnull final String name)
104   {
105     // Not possible to determine an owner :(
106     this (null, object, name);
107   }
108 
109   protected JInvocation (@Nullable final IJExpression object, @Nonnull final JMethod method)
110   {
111     this (method.owner (), object, method);
112   }
113 
114   /**
115    * Invokes a static method on a class.
116    *
117    * @param aType
118    *        Parent type
119    * @param sMethodName
120    *        Method name to be invoked
121    */
122   protected JInvocation (@Nonnull final AbstractJClass aType, @Nonnull final String sMethodName)
123   {
124     this (aType.owner (), aType, sMethodName);
125   }
126 
127   /**
128    * Invokes a static method on a class.
129    *
130    * @param aType
131    *        Parent type
132    * @param aMethod
133    *        Method to be invoked
134    */
135   protected JInvocation (@Nonnull final AbstractJClass aType, @Nonnull final JMethod aMethod)
136   {
137     this (aType.owner (), aType, aMethod);
138   }
139 
140   private JInvocation (@Nullable final JCodeModel owner, @Nullable final IJGenerable object, @Nonnull final String sName)
141   {
142     if (sName.indexOf ('.') >= 0)
143       throw new IllegalArgumentException ("method name contains '.': " + sName);
144     m_aOwner = owner;
145     m_aObject = object;
146     m_sMethodName = sName;
147     m_sMethod = null;
148     m_bIsConstructor = false;
149     m_aConstructorType = null;
150   }
151 
152   private JInvocation (@Nonnull final JCodeModel owner,
153                        @Nullable final IJGenerable object,
154                        @Nonnull final JMethod method)
155   {
156     m_aOwner = owner;
157     m_aObject = object;
158     m_sMethodName = null;
159     m_sMethod = method;
160     m_bIsConstructor = false;
161     m_aConstructorType = null;
162   }
163 
164   /**
165    * Invokes a constructor of an object (i.e., creates a new object.)
166    *
167    * @param aConstructorType
168    *        Type of the object to be created. If this type is an array type,
169    *        added arguments are treated as array initializer. Thus you can
170    *        create an expression like <code>new int[]{1,2,3,4,5}</code>.
171    */
172   protected JInvocation (@Nonnull final AbstractJType aConstructorType)
173   {
174     m_aOwner = aConstructorType.owner ();
175     m_aObject = null;
176     m_sMethodName = null;
177     m_sMethod = null;
178     m_bIsConstructor = true;
179     m_aConstructorType = aConstructorType;
180   }
181 
182   @Nullable
183   public JCodeModel owner ()
184   {
185     return m_aOwner;
186   }
187 
188   public boolean isConstructor ()
189   {
190     return m_bIsConstructor;
191   }
192 
193   /**
194    * Add an expression to this invocation's argument list
195    *
196    * @param arg
197    *        Argument to add to argument list
198    * @return this for chaining
199    */
200   @Nonnull
201   public JInvocation arg (@Nonnull final IJExpression arg)
202   {
203     if (arg == null)
204       throw new IllegalArgumentException ("argument may not be null");
205     _args.add (arg);
206     return this;
207   }
208 
209   /**
210    * Adds a literal argument. Short for {@code arg(JExpr.lit(v))}
211    *
212    * @param v
213    *        Value to be added to the argument list
214    * @return this for chaining
215    */
216   @Nonnull
217   public JInvocation arg (@Nonnull final boolean v)
218   {
219     return arg (JExpr.lit (v));
220   }
221 
222   /**
223    * Adds a literal argument. Short for {@code arg(JExpr.lit(v))}
224    *
225    * @param v
226    *        Value to be added to the argument list
227    * @return this for chaining
228    */
229   @Nonnull
230   public JInvocation arg (@Nonnull final char v)
231   {
232     return arg (JExpr.lit (v));
233   }
234 
235   /**
236    * Adds a literal argument. Short for {@code arg(JExpr.lit(v))}
237    *
238    * @param v
239    *        Value to be added to the argument list
240    * @return this for chaining
241    */
242   @Nonnull
243   public JInvocation arg (@Nonnull final double v)
244   {
245     return arg (JExpr.lit (v));
246   }
247 
248   /**
249    * Adds a literal argument. Short for {@code arg(JExpr.lit(v))}
250    *
251    * @param v
252    *        Value to be added to the argument list
253    * @return this for chaining
254    */
255   @Nonnull
256   public JInvocation arg (@Nonnull final float v)
257   {
258     return arg (JExpr.lit (v));
259   }
260 
261   /**
262    * Adds a literal argument. Short for {@code arg(JExpr.lit(v))}
263    *
264    * @param v
265    *        Value to be added to the argument list
266    * @return this for chaining
267    */
268   @Nonnull
269   public JInvocation arg (@Nonnull final int v)
270   {
271     return arg (JExpr.lit (v));
272   }
273 
274   /**
275    * Adds a literal argument. Short for {@code arg(JExpr.lit(v))}
276    *
277    * @param v
278    *        Value to be added to the argument list
279    * @return this for chaining
280    */
281   @Nonnull
282   public JInvocation arg (@Nonnull final long v)
283   {
284     return arg (JExpr.lit (v));
285   }
286 
287   /**
288    * Adds a literal argument. Short for {@code arg(JExpr.lit(v))}
289    *
290    * @param v
291    *        Value to be added to the argument list
292    * @return this for chaining
293    */
294   @Nonnull
295   public JInvocation arg (@Nonnull final String v)
296   {
297     return arg (JExpr.lit (v));
298   }
299 
300   /**
301    * Returns all arguments of the invocation.
302    *
303    * @return If there's no arguments, an empty array will be returned.
304    */
305   @Nonnull
306   public IJExpression [] listArgs ()
307   {
308     return _args.toArray (new IJExpression [_args.size ()]);
309   }
310 
311   /**
312    * Returns all arguments of the invocation.
313    *
314    * @return If there's no arguments, an empty list will be returned.
315    */
316   @Nonnull
317   public List <IJExpression> args ()
318   {
319     return new ArrayList <IJExpression> (_args);
320   }
321 
322   @Nonnull
323   private JCodeModel _narrowOwner ()
324   {
325     final JCodeModel owner = owner ();
326     if (owner == null)
327       throw new IllegalStateException ("No owner is present, so this invocation cannot be generified!");
328     return owner;
329   }
330 
331   @Nonnull
332   public JInvocation narrow (@Nonnull final String name)
333   {
334     final JTypeVar v = new JTypeVar (_narrowOwner (), name);
335     if (_typeVariables == null)
336       _typeVariables = new ArrayList <JTypeVar> (3);
337     _typeVariables.add (v);
338     return this;
339   }
340 
341   @Nonnull
342   public JInvocation narrow (@Nonnull final Class <?> bound)
343   {
344     return narrow (_narrowOwner ().ref (bound));
345   }
346 
347   @Nonnull
348   public JInvocation narrow (@Nonnull final AbstractJClass bound)
349   {
350     final JTypeVar v = new JTypeVarClass (bound);
351     if (_typeVariables == null)
352       _typeVariables = new ArrayList <JTypeVar> (3);
353     _typeVariables.add (v);
354     return this;
355   }
356 
357   @Nonnull
358   public List <JTypeVar> typeParamList ()
359   {
360     if (_typeVariables == null)
361       return Collections.<JTypeVar> emptyList ();
362     return new ArrayList <JTypeVar> (_typeVariables);
363   }
364 
365   private void _addTypeVars (@Nonnull final JFormatter f)
366   {
367     if (_typeVariables != null && !_typeVariables.isEmpty ())
368     {
369       f.print ('<');
370       int nIndex = 0;
371       for (final JTypeVar aTypeVar : _typeVariables)
372       {
373         if (nIndex++ > 0)
374           f.print (',');
375         // Use type here to get the import (if needed)
376         f.type (aTypeVar);
377       }
378       f.print (JFormatter.CLOSE_TYPE_ARGS);
379     }
380   }
381 
382   private String methodName ()
383   {
384     return m_sMethodName != null ? m_sMethodName : m_sMethod.name ();
385   }
386 
387   public void generate (@Nonnull final JFormatter f)
388   {
389     if (m_bIsConstructor)
390     {
391       if (m_aConstructorType.isArray ())
392       {
393         // [RESULT] new T[]{arg1,arg2,arg3,...};
394         f.print ("new").generable (m_aConstructorType);
395         _addTypeVars (f);
396         f.print ('{');
397       }
398       else
399       {
400         // [RESULT] new T(
401         f.print ("new").generable (m_aConstructorType);
402         _addTypeVars (f);
403         f.print ('(');
404       }
405     }
406     else
407     {
408       final String name = methodName ();
409 
410       if (m_aObject != null)
411       {
412         // object.<generics> name (
413         f.generable (m_aObject).print ('.');
414         _addTypeVars (f);
415         f.print (name).print ('(');
416       }
417       else
418       {
419         // name (
420         f.id (name).print ('(');
421       }
422     }
423 
424     // Method arguments
425     f.generable (_args);
426 
427     // Close arg list
428     if (m_bIsConstructor && m_aConstructorType.isArray ())
429       f.print ('}');
430     else
431       f.print (')');
432 
433     if (m_aConstructorType instanceof JDefinedClass && ((JDefinedClass) m_aConstructorType).isAnonymous ())
434     {
435       ((JAnonymousClass) m_aConstructorType).declareBody (f);
436     }
437   }
438 
439   public void state (@Nonnull final JFormatter f)
440   {
441     f.generable (this).print (';').newline ();
442   }
443 
444   private String typeFullName ()
445   {
446     return m_aConstructorType != null ? m_aConstructorType.fullName () : "";
447   }
448 
449   @Override
450   public boolean equals (final Object o)
451   {
452     if (o == this)
453       return true;
454     if (o == null || getClass () != o.getClass ())
455       return false;
456     final JInvocation rhs = (JInvocation) o;
457     if (!(isEqual (m_aObject, rhs.m_aObject) &&
458           isEqual (m_bIsConstructor, rhs.m_bIsConstructor) &&
459           (m_bIsConstructor || isEqual (methodName (), rhs.methodName ())) &&
460           isEqual (_args, rhs._args) &&
461           isEqual (typeFullName (), rhs.typeFullName ())))
462     {
463       return false;
464     }
465     if (_typeVariables == null)
466       return rhs._typeVariables == null;
467     if (rhs._typeVariables == null)
468       return false;
469     if (_typeVariables.size () != rhs._typeVariables.size ())
470       return false;
471     for (int i = 0; i < _typeVariables.size (); i++)
472     {
473       if (!isEqual (_typeVariables.get (i).fullName (), rhs._typeVariables.get (i).fullName ()))
474         return false;
475     }
476     return true;
477   }
478 
479   @Override
480   public int hashCode ()
481   {
482     JCHashCodeGenerator hashCodeGenerator = new JCHashCodeGenerator (this).append (m_aObject).append (m_bIsConstructor);
483     if (!m_bIsConstructor)
484       hashCodeGenerator = hashCodeGenerator.append (methodName ());
485     hashCodeGenerator = hashCodeGenerator.append (_args).append (typeFullName ());
486     if (_typeVariables != null)
487     {
488       hashCodeGenerator = hashCodeGenerator.append (_typeVariables.size ());
489       for (final JTypeVar typeVariable : _typeVariables)
490       {
491         hashCodeGenerator = hashCodeGenerator.append (typeVariable.fullName ());
492       }
493     }
494     return hashCodeGenerator.getHashCode ();
495   }
496 }