/** * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * Portions Copyright 2013-2017 Philip Helger + contributors * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.helger.jcodemodel; import static com.helger.jcodemodel.util.JCEqualsHelper.isEqual; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.helger.jcodemodel.util.JCHashCodeGenerator; /** * {@link JMethod} invocation */ public class JInvocation extends AbstractJExpressionImpl implements IJStatement, IJOwnedMaybe { private final JCodeModel m_aOwner; /** * Object expression upon which this method will be invoked, or null if this * is a constructor invocation */ private final IJGenerable m_aObject; /** * Name of the method to be invoked. Either this field is set, or * {@link #m_sMethod}, or {@link #m_aConstructorType} (in which case it's a * constructor invocation.) This allows {@link JMethod#name(String) the name * of the method to be changed later}. */ private final String m_sMethodName; private final JMethod m_sMethod; private final boolean m_bIsConstructor; /** * List of argument expressions for this method invocation */ private final List <IJExpression> _args = new ArrayList <> (); /** * If isConstructor==true, this field keeps the type to be created. */ private final AbstractJType m_aConstructorType; /** * Lazily created list of {@link JTypeVar}s. */ private List <JTypeVar> _typeVariables; /** * Invokes a method on an object. * * @param object * JExpression for the object upon which the named method will be * invoked, or null if none * @param name * Name of method to invoke */ protected JInvocation (@Nullable final IJExpression object, @Nonnull final String name) { // Not possible to determine an owner :( this (null, object, name); } protected JInvocation (@Nullable final IJExpression object, @Nonnull final JMethod method) { this (method.owner (), object, method); } /** * Invokes a static method on a class. * * @param aType * Parent type * @param sMethodName * Method name to be invoked */ protected JInvocation (@Nonnull final AbstractJClass aType, @Nonnull final String sMethodName) { this (aType.owner (), aType, sMethodName); } /** * Invokes a static method on a class. * * @param aType * Parent type * @param aMethod * Method to be invoked */ protected JInvocation (@Nonnull final AbstractJClass aType, @Nonnull final JMethod aMethod) { this (aType.owner (), aType, aMethod); } private JInvocation (@Nullable final JCodeModel owner, @Nullable final IJGenerable object, @Nonnull final String sName) { if (sName.indexOf ('.') >= 0) throw new IllegalArgumentException ("method name contains '.': " + sName); m_aOwner = owner; m_aObject = object; m_sMethodName = sName; m_sMethod = null; m_bIsConstructor = false; m_aConstructorType = null; } private JInvocation (@Nonnull final JCodeModel owner, @Nullable final IJGenerable object, @Nonnull final JMethod method) { m_aOwner = owner; m_aObject = object; m_sMethodName = null; m_sMethod = method; m_bIsConstructor = false; m_aConstructorType = null; } /** * Invokes a constructor of an object (i.e., creates a new object.) * * @param aConstructorType * Type of the object to be created. If this type is an array type, * added arguments are treated as array initializer. Thus you can * create an expression like <code>new int[]{1,2,3,4,5}</code>. */ protected JInvocation (@Nonnull final AbstractJType aConstructorType) { m_aOwner = aConstructorType.owner (); m_aObject = null; m_sMethodName = null; m_sMethod = null; m_bIsConstructor = true; m_aConstructorType = aConstructorType; } @Nullable public JCodeModel owner () { return m_aOwner; } public boolean isConstructor () { return m_bIsConstructor; } /** * Add an expression to this invocation's argument list * * @param arg * Argument to add to argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final IJExpression arg) { if (arg == null) throw new IllegalArgumentException ("argument may not be null"); _args.add (arg); return this; } /** * Adds a literal argument. Short for {@code arg(JExpr.lit(v))} * * @param v * Value to be added to the argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final boolean v) { return arg (JExpr.lit (v)); } /** * Adds a literal argument. Short for {@code arg(JExpr.lit(v))} * * @param v * Value to be added to the argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final char v) { return arg (JExpr.lit (v)); } /** * Adds a literal argument. Short for {@code arg(JExpr.lit(v))} * * @param v * Value to be added to the argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final double v) { return arg (JExpr.lit (v)); } /** * Adds a literal argument. Short for {@code arg(JExpr.lit(v))} * * @param v * Value to be added to the argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final float v) { return arg (JExpr.lit (v)); } /** * Adds a literal argument. Short for {@code arg(JExpr.lit(v))} * * @param v * Value to be added to the argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final int v) { return arg (JExpr.lit (v)); } /** * Adds a literal argument. Short for {@code arg(JExpr.lit(v))} * * @param v * Value to be added to the argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final long v) { return arg (JExpr.lit (v)); } /** * Adds a literal argument. Short for {@code arg(JExpr.lit(v))} * * @param v * Value to be added to the argument list * @return this for chaining */ @Nonnull public JInvocation arg (@Nonnull final String v) { return arg (JExpr.lit (v)); } /** * Returns all arguments of the invocation. * * @return If there's no arguments, an empty array will be returned. */ @Nonnull public IJExpression [] listArgs () { return _args.toArray (new IJExpression [_args.size ()]); } /** * Returns all arguments of the invocation. * * @return If there's no arguments, an empty list will be returned. */ @Nonnull public List <IJExpression> args () { return new ArrayList <> (_args); } @Nonnull private JCodeModel _narrowOwner () { final JCodeModel owner = owner (); if (owner == null) throw new IllegalStateException ("No owner is present, so this invocation cannot be generified!"); return owner; } @Nonnull public JInvocation narrow (@Nonnull final String name) { final JTypeVar v = new JTypeVar (_narrowOwner (), name); if (_typeVariables == null) _typeVariables = new ArrayList <> (3); _typeVariables.add (v); return this; } @Nonnull public JInvocation narrow (@Nonnull final Class <?> bound) { return narrow (_narrowOwner ().ref (bound)); } @Nonnull public JInvocation narrow (@Nonnull final AbstractJClass bound) { final JTypeVar v = new JTypeVarClass (bound); if (_typeVariables == null) _typeVariables = new ArrayList <> (3); _typeVariables.add (v); return this; } @Nonnull public List <JTypeVar> typeParamList () { if (_typeVariables == null) return Collections.<JTypeVar> emptyList (); return new ArrayList <> (_typeVariables); } private void _addTypeVars (@Nonnull final JFormatter f) { if (_typeVariables != null && !_typeVariables.isEmpty ()) { f.print ('<'); int nIndex = 0; for (final JTypeVar aTypeVar : _typeVariables) { if (nIndex++ > 0) f.print (','); // Use type here to get the import (if needed) f.type (aTypeVar); } f.print (JFormatter.CLOSE_TYPE_ARGS); } } private String methodName () { return m_sMethodName != null ? m_sMethodName : m_sMethod.name (); } public void generate (@Nonnull final JFormatter f) { if (m_bIsConstructor) { if (m_aConstructorType.isArray ()) { // [RESULT] new T[]{arg1,arg2,arg3,...}; f.print ("new").generable (m_aConstructorType); _addTypeVars (f); f.print ('{'); } else { // [RESULT] new T( f.print ("new").generable (m_aConstructorType); _addTypeVars (f); f.print ('('); } } else { final String name = methodName (); if (m_aObject != null) { // object.<generics> name ( f.generable (m_aObject).print ('.'); _addTypeVars (f); f.print (name).print ('('); } else { // name ( f.id (name).print ('('); } } // Method arguments f.generable (_args); // Close arg list if (m_bIsConstructor && m_aConstructorType.isArray ()) f.print ('}'); else f.print (')'); if (m_aConstructorType instanceof JDefinedClass && ((JDefinedClass) m_aConstructorType).isAnonymous ()) { ((JAnonymousClass) m_aConstructorType).declareBody (f); } } public void state (@Nonnull final JFormatter f) { f.generable (this).print (';').newline (); } private String typeFullName () { return m_aConstructorType != null ? m_aConstructorType.fullName () : ""; } @Override public boolean equals (final Object o) { if (o == this) return true; if (o == null || getClass () != o.getClass ()) return false; final JInvocation rhs = (JInvocation) o; if (!(isEqual (m_aObject, rhs.m_aObject) && isEqual (m_bIsConstructor, rhs.m_bIsConstructor) && (m_bIsConstructor || isEqual (methodName (), rhs.methodName ())) && isEqual (_args, rhs._args) && isEqual (typeFullName (), rhs.typeFullName ()))) { return false; } if (_typeVariables == null) return rhs._typeVariables == null; if (rhs._typeVariables == null) return false; if (_typeVariables.size () != rhs._typeVariables.size ()) return false; for (int i = 0; i < _typeVariables.size (); i++) { if (!isEqual (_typeVariables.get (i).fullName (), rhs._typeVariables.get (i).fullName ())) return false; } return true; } @Override public int hashCode () { JCHashCodeGenerator hashCodeGenerator = new JCHashCodeGenerator (this).append (m_aObject).append (m_bIsConstructor); if (!m_bIsConstructor) hashCodeGenerator = hashCodeGenerator.append (methodName ()); hashCodeGenerator = hashCodeGenerator.append (_args).append (typeFullName ()); if (_typeVariables != null) { hashCodeGenerator = hashCodeGenerator.append (_typeVariables.size ()); for (final JTypeVar typeVariable : _typeVariables) { hashCodeGenerator = hashCodeGenerator.append (typeVariable.fullName ()); } } return hashCodeGenerator.getHashCode (); } }