1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.codemodel.internal;
  27 
  28 import java.lang.annotation.Annotation;
  29 import java.util.ArrayList;
  30 import java.util.List;
  31 import java.util.Set;
  32 import java.util.TreeSet;
  33 import java.util.Collections;
  34 import java.util.Collection;
  35 
  36 import com.sun.codemodel.internal.util.ClassNameComparator;
  37 
  38 /**
  39  * Java method.
  40  */
  41 public class JMethod extends JGenerifiableImpl implements JDeclaration, JAnnotatable, JDocCommentable {
  42 
  43         /**
  44          * Modifiers for this method
  45          */
  46         private JMods mods;
  47 
  48         /**
  49          * Return type for this method
  50          */
  51         private JType type = null;
  52 
  53         /**
  54          * Name of this method
  55          */
  56         private String name = null;
  57 
  58         /**
  59          * List of parameters for this method's declaration
  60          */
  61         private final List<JVar> params = new ArrayList<JVar>();
  62 
  63         /**
  64          * Set of exceptions that this method may throw.
  65      * A set instance lazily created.
  66          */
  67         private Set<JClass> _throws;
  68 
  69         /**
  70          * JBlock of statements that makes up the body this method
  71          */
  72         private JBlock body = null;
  73 
  74         private JDefinedClass outer;
  75 
  76         /**
  77          * javadoc comments for this JMethod
  78          */
  79         private JDocComment jdoc = null;
  80 
  81         /**
  82          * Variable parameter for this method's varargs declaration
  83          * introduced in J2SE 1.5
  84          */
  85         private JVar varParam = null;
  86 
  87     /**
  88      * Annotations on this variable. Lazily created.
  89      */
  90     private List<JAnnotationUse> annotations = null;
  91 
  92 
  93         private boolean isConstructor() {
  94                 return type == null;
  95         }
  96 
  97     /** To set the default value for the
  98      *  annotation member
  99      */
 100     private JExpression defaultValue = null;
 101 
 102 
 103         /**
 104          * JMethod constructor
 105          *
 106          * @param mods
 107          *        Modifiers for this method's declaration
 108          *
 109          * @param type
 110          *        Return type for the method
 111          *
 112          * @param name
 113          *        Name of this method
 114          */
 115         JMethod(JDefinedClass outer, int mods, JType type, String name) {
 116                 this.mods = JMods.forMethod(mods);
 117                 this.type = type;
 118                 this.name = name;
 119                 this.outer = outer;
 120         }
 121 
 122         /**
 123          * Constructor constructor
 124          *
 125          * @param mods
 126          *        Modifiers for this constructor's declaration
 127          *
 128          * @param _class
 129          *        JClass containing this constructor
 130          */
 131         JMethod(int mods, JDefinedClass _class) {
 132                 this.mods = JMods.forMethod(mods);
 133                 this.type = null;
 134                 this.name = _class.name();
 135                 this.outer = _class;
 136         }
 137 
 138     private Set<JClass> getThrows() {
 139         if(_throws==null)
 140             _throws = new TreeSet<JClass>(ClassNameComparator.theInstance);
 141         return _throws;
 142     }
 143 
 144         /**
 145          * Add an exception to the list of exceptions that this
 146          * method may throw.
 147          *
 148          * @param exception
 149          *        Name of an exception that this method may throw
 150          */
 151         public JMethod _throws(JClass exception) {
 152         getThrows().add(exception);
 153                 return this;
 154         }
 155 
 156         public JMethod _throws(Class<? extends Throwable> exception) {
 157                 return _throws(outer.owner().ref(exception));
 158         }
 159 
 160         /**
 161          * Returns the list of variable of this method.
 162          *
 163          * @return List of parameters of this method. This list is not modifiable.
 164          */
 165         public List<JVar> params() {
 166                 return Collections.<JVar>unmodifiableList(params);
 167         }
 168 
 169         /**
 170          * Add the specified variable to the list of parameters
 171          * for this method signature.
 172          *
 173          * @param type
 174          *        JType of the parameter being added
 175          *
 176          * @param name
 177          *        Name of the parameter being added
 178          *
 179          * @return New parameter variable
 180          */
 181         public JVar param(int mods, JType type, String name) {
 182                 JVar v = new JVar(JMods.forVar(mods), type, name, null);
 183                 params.add(v);
 184                 return v;
 185         }
 186 
 187         public JVar param(JType type, String name) {
 188                 return param(JMod.NONE, type, name);
 189         }
 190 
 191         public JVar param(int mods, Class<?> type, String name) {
 192                 return param(mods, outer.owner()._ref(type), name);
 193         }
 194 
 195         public JVar param(Class<?> type, String name) {
 196                 return param(outer.owner()._ref(type), name);
 197         }
 198 
 199         /**
 200          * @see #varParam(JType, String)
 201          */
 202         public JVar varParam(Class<?> type, String name) {
 203         return varParam(outer.owner()._ref(type),name);
 204     }
 205 
 206     /**
 207      * Add the specified variable argument to the list of parameters
 208      * for this method signature.
 209      *
 210      * @param type
 211      *      Type of the parameter being added.
 212      *
 213      * @param name
 214      *        Name of the parameter being added
 215      *
 216      * @return the variable parameter
 217      *
 218      * @throws IllegalStateException
 219      *      If this method is called twice.
 220      *      varargs in J2SE 1.5 can appear only once in the
 221      *      method signature.
 222      */
 223     public JVar varParam(JType type, String name) {
 224                 if (!hasVarArgs()) {
 225 
 226             varParam =
 227                                 new JVar(
 228                                         JMods.forVar(JMod.NONE),
 229                                         type.array(),
 230                                         name,
 231                                         null);
 232                         return varParam;
 233                 } else {
 234                         throw new IllegalStateException(
 235                                 "Cannot have two varargs in a method,\n"
 236                                         + "Check if varParam method of JMethod is"
 237                                         + " invoked more than once");
 238 
 239                 }
 240 
 241         }
 242 
 243     /**
 244      * Adds an annotation to this variable.
 245      * @param clazz
 246      *          The annotation class to annotate the field with
 247      */
 248     public JAnnotationUse annotate(JClass clazz){
 249         if(annotations==null)
 250            annotations = new ArrayList<JAnnotationUse>();
 251         JAnnotationUse a = new JAnnotationUse(clazz);
 252         annotations.add(a);
 253         return a;
 254     }
 255 
 256     /**
 257      * Adds an annotation to this variable.
 258      *
 259      * @param clazz
 260      *          The annotation class to annotate the field with
 261      */
 262     public JAnnotationUse annotate(Class <? extends Annotation> clazz){
 263         return annotate(owner().ref(clazz));
 264     }
 265 
 266     public <W extends JAnnotationWriter> W annotate2(Class<W> clazz) {
 267         return TypedAnnotationWriter.create(clazz,this);
 268     }
 269 
 270     public Collection<JAnnotationUse> annotations() {
 271         if (annotations == null)
 272             annotations = new ArrayList<JAnnotationUse>();
 273         return Collections.unmodifiableList(annotations);
 274     }
 275 
 276     /**
 277          * Check if there are any varargs declared
 278          * for this method signature.
 279          */
 280         public boolean hasVarArgs() {
 281                 return this.varParam!=null;
 282         }
 283 
 284         public String name() {
 285                 return name;
 286         }
 287 
 288     /**
 289      * Changes the name of the method.
 290      */
 291     public void name(String n) {
 292         this.name = n;
 293     }
 294 
 295     /**
 296          * Returns the return type.
 297          */
 298         public JType type() {
 299                 return type;
 300         }
 301 
 302     /**
 303      * Overrides the return type.
 304      */
 305     public void type(JType t) {
 306         this.type = t;
 307     }
 308 
 309     /**
 310          * Returns all the parameter types in an array.
 311          * @return
 312          *      If there's no parameter, an empty array will be returned.
 313          */
 314         public JType[] listParamTypes() {
 315                 JType[] r = new JType[params.size()];
 316                 for (int i = 0; i < r.length; i++)
 317                         r[i] = params.get(i).type();
 318                 return r;
 319         }
 320 
 321         /**
 322          * Returns  the varags parameter type.
 323          * @return
 324          * If there's no vararg parameter type, null will be returned.
 325          */
 326         public JType listVarParamType() {
 327                 if (varParam != null)
 328                         return varParam.type();
 329                 else
 330                         return null;
 331         }
 332 
 333         /**
 334          * Returns all the parameters in an array.
 335          * @return
 336          *      If there's no parameter, an empty array will be returned.
 337          */
 338         public JVar[] listParams() {
 339                 return params.toArray(new JVar[params.size()]);
 340         }
 341 
 342         /**
 343          * Returns the variable parameter
 344          * @return
 345          *      If there's no parameter, null will be returned.
 346          */
 347         public JVar listVarParam() {
 348                 return varParam;
 349         }
 350 
 351         /**
 352          * Returns true if the method has the specified signature.
 353          */
 354         public boolean hasSignature(JType[] argTypes) {
 355                 JVar[] p = listParams();
 356                 if (p.length != argTypes.length)
 357                         return false;
 358 
 359                 for (int i = 0; i < p.length; i++)
 360                         if (!p[i].type().equals(argTypes[i]))
 361                                 return false;
 362 
 363                 return true;
 364         }
 365 
 366         /**
 367          * Get the block that makes up body of this method
 368          *
 369          * @return Body of method
 370          */
 371         public JBlock body() {
 372                 if (body == null)
 373                         body = new JBlock();
 374                 return body;
 375         }
 376 
 377     /**
 378      * Specify the default value for this annotation member
 379      * @param value
 380      *           Default value for the annotation member
 381      *
 382      */
 383     public void declareDefaultValue(JExpression value){
 384         this.defaultValue = value;
 385     }
 386 
 387         /**
 388          * Creates, if necessary, and returns the class javadoc for this
 389          * JDefinedClass
 390          *
 391          * @return JDocComment containing javadocs for this class
 392          */
 393         public JDocComment javadoc() {
 394                 if (jdoc == null)
 395                         jdoc = new JDocComment(owner());
 396                 return jdoc;
 397         }
 398 
 399         public void declare(JFormatter f) {
 400                 if (jdoc != null)
 401                         f.g(jdoc);
 402 
 403         if (annotations != null){
 404             for (JAnnotationUse a : annotations)
 405                 f.g(a).nl();
 406         }
 407 
 408         f.g(mods);
 409 
 410         // declare the generics parameters
 411                 super.declare(f);
 412 
 413                 if (!isConstructor())
 414                         f.g(type);
 415                 f.id(name).p('(').i();
 416         // when parameters are printed in new lines, we want them to be indented.
 417         // there's a good chance no newlines happen, too, but just in case it does.
 418                 boolean first = true;
 419         for (JVar var : params) {
 420             if (!first)
 421                 f.p(',');
 422             if(var.isAnnotated())
 423                 f.nl();
 424             f.b(var);
 425             first = false;
 426         }
 427                 if (hasVarArgs()) {
 428                         if (!first)
 429                                 f.p(',');
 430                         f.g(varParam.type().elementType());
 431                         f.p("... ");
 432                         f.id(varParam.name());
 433                 }
 434 
 435                 f.o().p(')');
 436                 if (_throws!=null && !_throws.isEmpty()) {
 437                         f.nl().i().p("throws").g(_throws).nl().o();
 438                 }
 439 
 440         if (defaultValue != null) {
 441             f.p("default ");
 442             f.g(defaultValue);
 443         }
 444                 if (body != null) {
 445                         f.s(body);
 446                 } else if (
 447                         !outer.isInterface() && !outer.isAnnotationTypeDeclaration() && !mods.isAbstract() && !mods.isNative()) {
 448                         // Print an empty body for non-native, non-abstract methods
 449                         f.s(new JBlock());
 450                 } else {
 451                         f.p(';').nl();
 452                 }
 453         }
 454 
 455     /**
 456      * @return
 457      *      the current modifiers of this method.
 458      *      Always return non-null valid object.
 459      */
 460     public JMods mods() {
 461         return mods;
 462     }
 463 
 464     /**
 465      * @deprecated use {@link #mods()}
 466      */
 467     public JMods getMods() {
 468                 return mods;
 469         }
 470 
 471         protected JCodeModel owner() {
 472                 return outer.owner();
 473         }
 474 }