0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.modules.visualweb.insync.java;
0043:
0044: import com.sun.rave.designtime.ContextMethod;
0045: import com.sun.source.tree.BlockTree;
0046: import com.sun.source.tree.ClassTree;
0047: import com.sun.source.tree.CompilationUnitTree;
0048: import com.sun.source.tree.IdentifierTree;
0049: import com.sun.source.tree.LiteralTree;
0050: import com.sun.source.tree.MethodTree;
0051: import com.sun.source.tree.MemberSelectTree;
0052: import com.sun.source.tree.Tree;
0053: import com.sun.source.tree.Tree.Kind;
0054: import com.sun.source.tree.VariableTree;
0055: import com.sun.source.util.TreePath;
0056: import com.sun.source.util.TreePathScanner;
0057: import java.util.ArrayList;
0058: import java.util.Collections;
0059: import java.util.HashMap;
0060: import java.util.List;
0061: import java.util.ListIterator;
0062: import java.util.Set;
0063: import javax.lang.model.element.Element;
0064: import javax.lang.model.element.ExecutableElement;
0065: import javax.lang.model.element.Modifier;
0066: import javax.lang.model.element.TypeElement;
0067: import javax.lang.model.element.VariableElement;
0068: import javax.lang.model.type.ArrayType;
0069: import javax.lang.model.type.DeclaredType;
0070: import javax.lang.model.type.TypeKind;
0071: import javax.lang.model.type.TypeMirror;
0072: import javax.lang.model.util.ElementFilter;
0073: import org.netbeans.api.java.source.CompilationInfo;
0074: import org.netbeans.api.java.source.ElementHandle;
0075: import org.netbeans.api.java.source.TreeMaker;
0076: import org.netbeans.api.java.source.WorkingCopy;
0077: import org.netbeans.modules.visualweb.insync.beans.Bean;
0078: import org.netbeans.modules.visualweb.insync.beans.BeansUnit;
0079: import org.netbeans.modules.visualweb.insync.beans.Naming;
0080: import org.netbeans.modules.visualweb.insync.faces.HtmlBean;
0081: import org.openide.filesystems.FileObject;
0082:
0083: /**
0084: *
0085: * @author jdeva
0086: */
0087: public class JavaClass {
0088: private ElementHandle<TypeElement> typeElementHandle;
0089: //JavaUnit javaUnit; //To obtain FileObject/JavaSource
0090: private FileObject fObj; //Temporary till we plug this into insync
0091: private String name;
0092:
0093: private enum MethodKind {
0094: NORMAL, DELEGATOR, EVENT
0095: }
0096:
0097: /** Creates a new instance of TypeElementAdapter */
0098: public JavaClass(TypeElement element, FileObject fObj) {
0099: this .fObj = fObj;
0100: typeElementHandle = ElementHandle.create(element);
0101: name = element.getQualifiedName().toString();
0102: }
0103:
0104: /*
0105: * Returns short name for the class
0106: */
0107: public String getShortName() {
0108: return name == null ? null : name.substring(name
0109: .lastIndexOf('.') + 1);
0110: }
0111:
0112: /*
0113: * Returns FQN for the class
0114: */
0115: public String getName() {
0116: return name;
0117: }
0118:
0119: public String getPackageName() {
0120: return name == null ? null : name.substring(0, name
0121: .lastIndexOf('.'));
0122: }
0123:
0124: /*
0125: * Return the file which contains this java class
0126: */
0127: public FileObject getFileObject() {
0128: return fObj;
0129: }
0130:
0131: /*
0132: * Checks if the passed in type as string is a super type of this class
0133: */
0134: public boolean isSubTypeOf(final String typeName) {
0135: Boolean result = (Boolean) ReadTaskWrapper.execute(
0136: new ReadTaskWrapper.Read() {
0137: public Object run(CompilationInfo cinfo) {
0138: TypeElement typeElement = typeElementHandle
0139: .resolve(cinfo);
0140: TypeMirror super Type = cinfo.getElements()
0141: .getTypeElement(typeName).asType();
0142: if (super Type.getKind() == TypeKind.DECLARED
0143: && cinfo.getTypes()
0144: .isSubtype(
0145: typeElement.asType(),
0146: super Type)) {
0147: return Boolean.TRUE;
0148: }
0149: return Boolean.FALSE;
0150: }
0151: }, fObj);
0152: return result.booleanValue();
0153: }
0154:
0155: /*
0156: * Return all the methods
0157: */
0158: public List<Method> getMethods() {
0159: return (List<Method>) ReadTaskWrapper.execute(
0160: new ReadTaskWrapper.Read() {
0161: public Object run(CompilationInfo cinfo) {
0162: List<Method> methods = new ArrayList<Method>();
0163: for (ExecutableElement method : getMethods(
0164: cinfo, null, null, null)) {
0165: methods.add(new Method(method,
0166: JavaClass.this ));
0167: }
0168: return methods;
0169: }
0170: }, fObj);
0171: }
0172:
0173: /*
0174: * Return all methods that has same return type and parameter types as
0175: * specified by arguments
0176: */
0177: public List<String> getMethodNames(final Class[] params,
0178: final Class retType) {
0179: return (List<String>) ReadTaskWrapper.execute(
0180: new ReadTaskWrapper.Read() {
0181: public Object run(CompilationInfo cinfo) {
0182: List<String> names = new ArrayList<String>();
0183: for (ExecutableElement method : getMethods(
0184: cinfo, null, params, retType)) {
0185: names
0186: .add(method.getSimpleName()
0187: .toString());
0188: }
0189: return names;
0190: }
0191: }, fObj);
0192: }
0193:
0194: /**
0195: * Return map of properties name and type information for all the properties
0196: * The value entry of map is a list of strings, the first item is the property's type and
0197: * the subsequent ones are the type parameters only in case of parameterized type.
0198: */
0199: public HashMap<String, List<String>> getPropertiesNameAndTypes() {
0200: return (HashMap<String, List<String>>) ReadTaskWrapper.execute(
0201: new ReadTaskWrapper.Read() {
0202: public Object run(CompilationInfo cinfo) {
0203: TypeElement typeElement = typeElementHandle
0204: .resolve(cinfo);
0205: HashMap<String, List<String>> nameAndtypes = new HashMap<String, List<String>>();
0206: for (ExecutableElement method : ElementFilter
0207: .methodsIn(typeElement
0208: .getEnclosedElements())) {
0209: if (isBeanGetter(method)) {
0210: TypeMirror type = method
0211: .getReturnType();
0212: String typeName = type.toString();
0213: List<String> typeNames = new ArrayList<String>();
0214:
0215: // In case of parameterized type, use the raw type
0216: if (isParameterizedType(type)) {
0217: typeName = cinfo.getTypes()
0218: .erasure(type).toString();
0219: addTypeParameters(type, typeNames);
0220: }
0221: typeNames.add(0, typeName);
0222: String name = Naming.propertyName(
0223: method.getSimpleName()
0224: .toString(), typeName
0225: .equals("boolean"));
0226: nameAndtypes.put(name, typeNames);
0227: }
0228: }
0229: return nameAndtypes;
0230: }
0231: }, fObj);
0232: }
0233:
0234: /**
0235: * Extracts type parameters for a paramterized type recursively
0236: */
0237: private void addTypeParameters(TypeMirror type,
0238: List<String> argTypeNames) {
0239: if (type.getKind() == TypeKind.DECLARED) {
0240: for (TypeMirror argType : ((DeclaredType) type)
0241: .getTypeArguments()) {
0242: if (isParameterizedType(argType)) {
0243: addTypeParameters(argType, argTypeNames);
0244: } else {
0245: argTypeNames.add(argType.toString());
0246: }
0247: }
0248: }
0249: }
0250:
0251: /*
0252: * Returns a handle corresponding to a field
0253: */
0254: public ElementHandle getField(final String name) {
0255: return (ElementHandle) ReadTaskWrapper.execute(
0256: new ReadTaskWrapper.Read() {
0257: public Object run(CompilationInfo cinfo) {
0258: VariableElement field = getField(cinfo, name);
0259: if (field != null) {
0260: return ElementHandle.create(field);
0261: }
0262: return null;
0263: }
0264: }, fObj);
0265: }
0266:
0267: /*
0268: * Inserts a field, getter and setter given the property name and type, boolean
0269: * flags to control the addition of getter/setter
0270: */
0271: private ClassTree addProperty(final String name, final Class type,
0272: final boolean getter, final boolean setter,
0273: ClassTree ctree, Tree tree, WorkingCopy wc) {
0274: TreeMaker make = wc.getTreeMaker();
0275: ClassTree newctree = ctree;
0276: VariableTree vtree = TreeMakerUtils.createPropertyField(wc,
0277: name, type);
0278:
0279: newctree = make.insertClassMember(newctree, ctree.getMembers()
0280: .indexOf(tree), vtree);
0281: MethodTree mtree = null;
0282: if (getter) {
0283: mtree = TreeMakerUtils.createPropertyGetterMethod(wc, name,
0284: type);
0285: newctree = make.insertClassMember(newctree, newctree
0286: .getMembers().indexOf(tree), mtree);
0287: }
0288: if (setter) {
0289: mtree = TreeMakerUtils.createPropertySetterMethod(wc, name,
0290: type);
0291: newctree = make.insertClassMember(newctree, newctree
0292: .getMembers().indexOf(tree), mtree);
0293: }
0294: return newctree;
0295: }
0296:
0297: /*
0298: * Deletes a field, getter and setter given the property name and type, boolean
0299: * flags to control the addition of getter/setter
0300: */
0301: private ClassTree removeProperty(final String name,
0302: ClassTree ctree, WorkingCopy wc) {
0303: TreeMaker make = wc.getTreeMaker();
0304: ClassTree newctree = ctree;
0305: VariableElement varElem = getField(wc, name);
0306: if (varElem != null) {
0307: newctree = make.removeClassMember(ctree, wc.getTrees()
0308: .getTree(varElem));
0309: }
0310: ExecutableElement getElem = getMethod(wc, Naming
0311: .getterName(name), new Class[0]);
0312: TypeMirror type = null;
0313: if (getElem != null) {
0314: type = getElem.getReturnType();
0315: newctree = make.removeClassMember(newctree, wc.getTrees()
0316: .getTree(getElem));
0317: ExecutableElement setElem = getMethod(wc, Naming
0318: .setterName(name), Collections
0319: .<TypeMirror> singletonList(type));
0320: if (setElem != null) {
0321: newctree = make.removeClassMember(newctree, wc
0322: .getTrees().getTree(setElem));
0323: }
0324: }
0325: return newctree;
0326: }
0327:
0328: public void addBeans(final List<Bean> beans) {
0329: WriteTaskWrapper.execute(new WriteTaskWrapper.Write() {
0330: public Object run(WorkingCopy wc) {
0331: TypeElement typeElement = typeElementHandle.resolve(wc);
0332: ClassTree ctree = wc.getTrees().getTree(typeElement);
0333: ClassTree oldTree = ctree;
0334: // Find the constructor
0335: // TBD: index selection logic, i.e where to add the field, getter and setter
0336: // For now insert before the ctor
0337: Tree ctor = getPublicConstructor(wc, ctree);
0338: BeansUnit beansUnit = null;
0339: BlockTree blockTree = null;
0340: for (Bean bean : beans) {
0341: ctree = addProperty(bean.getName(), bean.getType(),
0342: bean.isGetterRequired(), bean
0343: .isSetterRequired(), ctree, ctor,
0344: wc);
0345: beansUnit = bean.getUnit();
0346: if (beansUnit != null) {
0347: blockTree = beansUnit.getPropertiesInitMethod()
0348: .addPropertySetStatements(wc, bean,
0349: blockTree);
0350: }
0351: bean.setInserted(true);
0352: }
0353: if (beansUnit != null) {
0354: beansUnit.getCleanupMethod().addCleanupStatements(
0355: wc, beans);
0356: }
0357: if (oldTree != ctree) {
0358: wc.rewrite(oldTree, ctree);
0359: }
0360: return null;
0361: }
0362: }, fObj);
0363: }
0364:
0365: public void removeBeans(final List<Bean> beans) {
0366: WriteTaskWrapper.execute(new WriteTaskWrapper.Write() {
0367: public Object run(WorkingCopy wc) {
0368: TypeElement typeElement = typeElementHandle.resolve(wc);
0369: ClassTree ctree = wc.getTrees().getTree(typeElement);
0370: ClassTree oldTree = ctree;
0371: BeansUnit beansUnit = null;
0372: BlockTree blockTree = null;
0373: for (Bean bean : beans) {
0374: if (!(bean instanceof HtmlBean)) {
0375: ctree = removeProperty(bean.getName(), ctree,
0376: wc);
0377: beansUnit = bean.getUnit();
0378: if (beansUnit != null) {
0379: blockTree = beansUnit
0380: .getPropertiesInitMethod()
0381: .removeSetStatements(wc, bean,
0382: blockTree);
0383: }
0384: bean.setInserted(false);
0385: }
0386: }
0387: if (beansUnit != null) {
0388: beansUnit.getCleanupMethod()
0389: .removeCleanupStatements(wc, beans);
0390: }
0391: if (oldTree != ctree) {
0392: wc.rewrite(oldTree, ctree);
0393: }
0394: return null;
0395: }
0396: }, fObj);
0397: }
0398:
0399: /*
0400: * Renames field, getter and setter and its usage given the property new & old names, and
0401: * the list of files where the property could be used
0402: */
0403: public void renameProperty(final String name, final String newName,
0404: final List<FileObject> fObjs) {
0405: final HashMap<ElementHandle, String> elementAndNames = getElementHandlesToReplace(
0406: name, newName);
0407: WriteTaskWrapper.execute(new WriteTaskWrapper.Write() {
0408: public Object run(WorkingCopy wc) {
0409: new Refactor.ElementsRenamer(wc, elementAndNames).scan(
0410: wc.getCompilationUnit(), null);
0411: //To take care of VB expressions, Ex:- getValue(#{SessionBean1.personRowSet})
0412: renamePropertyBindingExpression(wc, name, newName);
0413: return null;
0414: }
0415: }, fObjs);
0416: }
0417:
0418: private HashMap<ElementHandle, String> getElementHandlesToReplace(
0419: final String name, final String newName) {
0420: return (HashMap<ElementHandle, String>) ReadTaskWrapper
0421: .execute(new ReadTaskWrapper.Read() {
0422: public Object run(CompilationInfo cinfo) {
0423: final HashMap<ElementHandle, String> elementAndNames = new HashMap<ElementHandle, String>();
0424: VariableElement varElem = getField(cinfo, name);
0425: if (varElem != null) {
0426: elementAndNames.put(ElementHandle
0427: .create(varElem), newName);
0428: }
0429: ExecutableElement getElem = getMethod(cinfo,
0430: Naming.getterName(name), new Class[0]);
0431: if (getElem != null) {
0432: elementAndNames.put(ElementHandle
0433: .create(getElem), Naming
0434: .getterName(newName));
0435: TypeMirror type = getElem.getReturnType();
0436: ExecutableElement setElem = getMethod(
0437: cinfo,
0438: Naming.setterName(name),
0439: Collections
0440: .<TypeMirror> singletonList(type));
0441: if (setElem != null) {
0442: elementAndNames.put(ElementHandle
0443: .create(setElem), Naming
0444: .setterName(newName));
0445: }
0446: }
0447: return elementAndNames;
0448: }
0449: }, fObj);
0450: }
0451:
0452: private void renamePropertyBindingExpression(WorkingCopy wc,
0453: String name, String newName) {
0454: String oldLiteral = "#{" + getShortName() + "." + name + "}"; //NOI18N
0455: String newLiteral = "#{" + getShortName() + "." + newName + "}"; //NOI18N
0456: new Refactor.LiteralRenamer(wc, oldLiteral, newLiteral).scan(wc
0457: .getCompilationUnit(), null);
0458: }
0459:
0460: /*
0461: * Internal method to add a method, returns element handle which can be cached by the caller
0462: */
0463: private Method addMethod(final ContextMethod cm,
0464: final String retType, final MethodKind kind) {
0465: Method method = null;
0466: method = (Method) WriteTaskWrapper.execute(
0467: new WriteTaskWrapper.Write() {
0468: public Object run(WorkingCopy wc) {
0469: Method m = getMethod(wc, cm.getName(), cm
0470: .getParameterTypes(), kind);
0471: if (m == null) {
0472: TreeMaker make = wc.getTreeMaker();
0473: TypeElement typeElement = typeElementHandle
0474: .resolve(wc);
0475: ClassTree ctree = wc.getTrees().getTree(
0476: typeElement);
0477: ClassTree newctree = ctree;
0478: MethodTree mtree = TreeMakerUtils
0479: .createMethod(wc, cm, retType);
0480: newctree = make
0481: .addClassMember(ctree, mtree);
0482: wc.rewrite(ctree, newctree);
0483: }
0484: return m;
0485: }
0486: }, fObj);
0487: //If the method is newly added, write task should be completed first before
0488: //we access the method
0489: if (method == null) {
0490: method = getMethod(cm.getName(), cm.getParameterTypes(),
0491: kind);
0492: }
0493: return method;
0494: }
0495:
0496: public Method addMethod(ContextMethod cm) {
0497: String retTypeName = null;
0498: if (cm.getReturnType() != null) {
0499: retTypeName = cm.getReturnType().getCanonicalName();
0500: }
0501: return addMethod(cm, retTypeName, MethodKind.NORMAL);
0502: }
0503:
0504: public Method addMethod(MethodInfo mInfo) {
0505: return addMethod(mInfo, mInfo.getReturnTypeName(),
0506: MethodKind.NORMAL);
0507: }
0508:
0509: public DelegatorMethod addDelegatorMethod(MethodInfo mInfo) {
0510: return (DelegatorMethod) addMethod(mInfo, mInfo
0511: .getReturnTypeName(), MethodKind.DELEGATOR);
0512: }
0513:
0514: public EventMethod addEventMethod(MethodInfo mInfo) {
0515: return (EventMethod) addMethod(mInfo,
0516: mInfo.getReturnTypeName(), MethodKind.EVENT);
0517: }
0518:
0519: /*
0520: * Removes a method corresponding to passed in element handle
0521: */
0522: void removeMethod(
0523: final ElementHandle<ExecutableElement> methodElementHandle) {
0524: WriteTaskWrapper.execute(new WriteTaskWrapper.Write() {
0525: public Object run(WorkingCopy wc) {
0526: ExecutableElement execElement = methodElementHandle
0527: .resolve(wc);
0528: TypeElement typeElement = typeElementHandle.resolve(wc);
0529: ClassTree ctree = wc.getTrees().getTree(typeElement);
0530: ClassTree newctree = ctree;
0531: newctree = wc.getTreeMaker().removeClassMember(ctree,
0532: wc.getTrees().getTree(execElement));
0533: wc.rewrite(ctree, newctree);
0534: return null;
0535: }
0536: }, fObj);
0537: }
0538:
0539: /*
0540: * Returns the first public constructor
0541: */
0542: private MethodTree getPublicConstructor(CompilationInfo cinfo,
0543: ClassTree ctree) {
0544: for (Tree tree : ctree.getMembers()) {
0545: if (Tree.Kind.METHOD == tree.getKind()) {
0546: MethodTree mtree = (MethodTree) tree;
0547: if (mtree.getName().toString().equals(Method.CTOR)
0548: && !cinfo.getTreeUtilities().isSynthetic(
0549: TreeUtils.getTreePath(cinfo, ctree))) {
0550: return mtree;
0551: }
0552: }
0553: }
0554: return null;
0555: }
0556:
0557: /*
0558: * Returns a method corresponding to a method by given name and parameter types
0559: */
0560: public Method getMethod(final String name, final Class[] params) {
0561: return (Method) ReadTaskWrapper.execute(
0562: new ReadTaskWrapper.Read() {
0563: public Object run(CompilationInfo cinfo) {
0564: return getMethod(cinfo, name, params,
0565: MethodKind.NORMAL);
0566: }
0567: }, fObj);
0568: }
0569:
0570: /*
0571: * Returns a method corresponding to a method by given name and parameter types
0572: */
0573: private Method getMethod(final String name, final Class[] params,
0574: final MethodKind kind) {
0575: return (Method) ReadTaskWrapper.execute(
0576: new ReadTaskWrapper.Read() {
0577: public Object run(CompilationInfo cinfo) {
0578: return getMethod(cinfo, name, params, kind);
0579: }
0580: }, fObj);
0581: }
0582:
0583: /*
0584: * Returns a event method
0585: */
0586: public EventMethod getEventMethod(final String name,
0587: final Class[] params) {
0588: return (EventMethod) ReadTaskWrapper.execute(
0589: new ReadTaskWrapper.Read() {
0590: public Object run(CompilationInfo cinfo) {
0591: return getMethod(cinfo, name, params,
0592: MethodKind.EVENT);
0593: }
0594: }, fObj);
0595: }
0596:
0597: /*
0598: * Returns a delegator method
0599: */
0600: public DelegatorMethod getDelegatorMethod(final String name,
0601: final Class[] params) {
0602: return (DelegatorMethod) ReadTaskWrapper.execute(
0603: new ReadTaskWrapper.Read() {
0604: public Object run(CompilationInfo cinfo) {
0605: return getMethod(cinfo, name, params,
0606: MethodKind.DELEGATOR);
0607: }
0608: }, fObj);
0609: }
0610:
0611: /*
0612: * Returns a public method corresponding to a method by given name and parameter types
0613: */
0614: public Method getPublicMethod(final String name,
0615: final Class[] params) {
0616: return (Method) ReadTaskWrapper.execute(
0617: new ReadTaskWrapper.Read() {
0618: public Object run(CompilationInfo cinfo) {
0619: ExecutableElement elem = getMethod(cinfo, name,
0620: params);
0621: if (elem != null
0622: && elem.getModifiers().contains(
0623: Modifier.PUBLIC)) {
0624: return new Method(elem, JavaClass.this );
0625: }
0626:
0627: return null;
0628: }
0629: }, fObj);
0630: }
0631:
0632: /*
0633: * Returns a method corresponding to a method by given name and parameter types
0634: */
0635: private Method getMethod(CompilationInfo cinfo, String name,
0636: Class[] params, MethodKind kind) {
0637: ExecutableElement elem = getMethod(cinfo, name, params);
0638: if (elem != null) {
0639: switch (kind) {
0640: case NORMAL:
0641: return new Method(elem, this );
0642: case DELEGATOR:
0643: return new DelegatorMethod(elem, this );
0644: case EVENT:
0645: return new EventMethod(elem, this );
0646: }
0647: }
0648: return null;
0649: }
0650:
0651: /*
0652: * Returns a element corresponding to a method by given name and parameter types
0653: */
0654: private ExecutableElement getMethod(CompilationInfo cinfo,
0655: String name, Class[] params) {
0656: if (params == null) {
0657: params = new Class[0];
0658: }
0659: if (name.equals(Method.CTOR)) {
0660: return getConstructor(cinfo, params);
0661: } else {
0662: List<ExecutableElement> methods = getMethods(cinfo, name,
0663: params, null);
0664: return (methods.size() == 1) ? methods.get(0) : null;
0665: }
0666: }
0667:
0668: /*
0669: * Returns element corresponding to a method by given parameter types
0670: */
0671: private ExecutableElement getConstructor(CompilationInfo cinfo,
0672: Class[] params) {
0673: TypeElement typeElement = typeElementHandle.resolve(cinfo);
0674: for (ExecutableElement method : ElementFilter
0675: .constructorsIn(typeElement.getEnclosedElements())) {
0676: if ((params == null || matchTypes(cinfo, method
0677: .getParameters(), params))) {
0678: return method;
0679: }
0680: }
0681: return null;
0682: }
0683:
0684: /*
0685: * Returns a element corresponding to a method by given name and parameter types(TypeMirror)
0686: */
0687: private ExecutableElement getMethod(CompilationInfo cinfo,
0688: String name, List<? extends TypeMirror> paramTypes) {
0689: TypeElement typeElement = typeElementHandle.resolve(cinfo);
0690: for (ExecutableElement method : ElementFilter
0691: .methodsIn(typeElement.getEnclosedElements())) {
0692: if ((name == null || method.getSimpleName().toString()
0693: .equals(name))
0694: && (paramTypes == null || matchTypes(cinfo, method
0695: .getParameters(), paramTypes))) {
0696: return method;
0697: }
0698: }
0699: return null;
0700: }
0701:
0702: /*
0703: * Returns all methods that match the passed in name, parameter types and return type
0704: */
0705: private List<ExecutableElement> getMethods(CompilationInfo cinfo,
0706: String name, Class[] params, Class retType) {
0707: List<ExecutableElement> methods = new ArrayList<ExecutableElement>();
0708: TypeElement typeElement = typeElementHandle.resolve(cinfo);
0709: for (ExecutableElement method : ElementFilter
0710: .methodsIn(typeElement.getEnclosedElements())) {
0711: if ((name == null || method.getSimpleName().toString()
0712: .equals(name))
0713: && (params == null || matchTypes(cinfo, method
0714: .getParameters(), params))
0715: && (retType == null || matchType(cinfo, method
0716: .getReturnType(), retType))) {
0717: methods.add(method);
0718: }
0719: }
0720: return methods;
0721: }
0722:
0723: /*
0724: * Returns a element corresponding to a field by given name
0725: */
0726: private VariableElement getField(CompilationInfo cinfo, String name) {
0727: TypeElement typeElement = typeElementHandle.resolve(cinfo);
0728: for (VariableElement var : ElementFilter.fieldsIn(typeElement
0729: .getEnclosedElements())) {
0730: if (var.getSimpleName().toString().equals(name)) {
0731: return var;
0732: }
0733: }
0734: return null;
0735: }
0736:
0737: /*
0738: * Returns true if the type matches cls type
0739: */
0740: private boolean matchType(CompilationInfo cinfo, TypeMirror type,
0741: Class cls) {
0742: String typeName = type.toString();
0743: //Use the raw type if the type represents paramaterized type
0744: TypeMirror compType = type;
0745: if (type.getKind() == TypeKind.ARRAY) {
0746: compType = ((ArrayType) type).getComponentType();
0747: }
0748: if (isParameterizedType(compType)) {
0749: typeName = cinfo.getTypes().erasure(type).toString();
0750: }
0751: if (cls.getCanonicalName().equals(typeName)) {
0752: return true;
0753: }
0754: return false;
0755: }
0756:
0757: /*
0758: * Returns true if the variable elements types matches params types
0759: */
0760: private boolean matchTypes(CompilationInfo cinfo,
0761: List<? extends VariableElement> varElems, Class[] params) {
0762: if (varElems.size() == params.length) {
0763: int i = 0;
0764: for (VariableElement varElem : varElems) {
0765: if (!matchType(cinfo, varElem.asType(), params[i++])) {
0766: return false;
0767: }
0768: }
0769: return true;
0770: }
0771: return false;
0772: }
0773:
0774: /*
0775: * Returns true if the variable elements types matches params types
0776: */
0777: private boolean matchTypes(CompilationInfo cinfo,
0778: List<? extends VariableElement> varElems,
0779: List<? extends TypeMirror> paramTypes) {
0780: if (varElems.size() == paramTypes.size()) {
0781: ListIterator<? extends VariableElement> elemsIter = varElems
0782: .listIterator();
0783: ListIterator<? extends TypeMirror> typesIter = paramTypes
0784: .listIterator();
0785: while (elemsIter.hasNext()) {
0786: if (!cinfo.getTypes().isSameType(
0787: elemsIter.next().asType(), typesIter.next())) {
0788: return false;
0789: }
0790: }
0791: return true;
0792: }
0793: return false;
0794: }
0795:
0796: /*
0797: * Returns true if the supplied method qualifies as a bean getter method
0798: */
0799: private boolean isBeanGetter(ExecutableElement method) {
0800: Set<Modifier> mods = method.getModifiers();
0801: // Must be public and can't be abstract or static
0802: if (!mods.contains(Modifier.PUBLIC)
0803: || mods.contains(Modifier.STATIC)
0804: || mods.contains(Modifier.ABSTRACT)) {
0805: return false;
0806: }
0807:
0808: // Must have zero parameters
0809: if (method.getParameters().size() > 0) {
0810: return false;
0811: }
0812:
0813: // Return type should be non void
0814: TypeMirror type = method.getReturnType();
0815: if (type.getKind() == TypeKind.VOID) {
0816: return false;
0817: }
0818:
0819: // Check if it is a valid getter name
0820: String name = Naming.propertyName(method.getSimpleName()
0821: .toString(), type.toString().equals("boolean"));
0822: if (name == null)
0823: return false;
0824:
0825: return true;
0826: }
0827:
0828: /*
0829: * Returns true if the type represents a parameterized type
0830: */
0831: private boolean isParameterizedType(TypeMirror type) {
0832: if (type.getKind() == TypeKind.DECLARED) {
0833: DeclaredType declType = (DeclaredType) type;
0834: if (declType.getTypeArguments().size() > 0) {
0835: return true;
0836: }
0837: }
0838: return false;
0839: }
0840:
0841: /**
0842: * Returns the JavaClass(TypeElement wrapper) for public class in the given file
0843: */
0844: public static JavaClass getJavaClass(final FileObject fObj) {
0845: return (JavaClass) ReadTaskWrapper.execute(
0846: new ReadTaskWrapper.Read() {
0847: public Object run(CompilationInfo cinfo) {
0848: CompilationUnitTree cunit = cinfo
0849: .getCompilationUnit();
0850: for (Tree tree : cunit.getTypeDecls()) {
0851: if (tree.getKind() == Tree.Kind.CLASS) {
0852: ClassTree clazz = (ClassTree) tree;
0853: if (clazz.getSimpleName().toString()
0854: .equals(fObj.getName())
0855: && clazz
0856: .getModifiers()
0857: .getFlags()
0858: .contains(
0859: Modifier.PUBLIC)) {
0860: TypeElement element = TreeUtils
0861: .getTypeElement(cinfo,
0862: clazz);
0863: if (element != null) {
0864: return new JavaClass(element,
0865: fObj);
0866: }
0867: }
0868: }
0869: }
0870: return null;
0871: }
0872: }, fObj);
0873: }
0874:
0875: /*
0876: * @return The usage status, it returns
0877: * UseStatus.init_use_only if the bean is used in _init() method
0878: * UseStatus.used if it is used elsewhere in .java in addition to _init() method
0879: * UseStatus.not_used if it is not used in
0880: */
0881: public UsageStatus isPropertyUsed(final String name,
0882: final List<FileObject> fObjs) {
0883: final HashMap<ElementHandle, String> elementAndNames = getElementHandlesToReplace(
0884: name, name);
0885: return (UsageStatus) WriteTaskWrapper.execute(
0886: new WriteTaskWrapper.Write() {
0887: public Object run(WorkingCopy wc) {
0888: ElementsUsageFinder usageFinder = new ElementsUsageFinder(
0889: wc, elementAndNames);
0890: usageFinder.scan(wc.getCompilationUnit(), null);
0891: UsageStatus result = usageFinder.getUseStatus();
0892: return result;
0893: }
0894: }, fObjs);
0895: }
0896:
0897: public enum UsageStatus {
0898: NOT_USED, INIT_USE_ONLY, USED;
0899: }
0900:
0901: /* Look for the occurence of list of elements and a EL expression string literal
0902: * in the given java source. It sets an instance field by name useStatus as following
0903: * UseStatus.init_use_only if the elements/literal is used in _init() method
0904: * UseStatus.used if elements/literal is used elsewhere in addition to _init() method
0905: * UseStatus.not_used if not used
0906: */
0907: class ElementsUsageFinder extends TreePathScanner<Tree, Void> {
0908: private final CompilationInfo cinfo;
0909: private HashMap<Element, String> elementAndNames = new HashMap<Element, String>();
0910: private UsageStatus useStatus = UsageStatus.NOT_USED;
0911: private String vbExpression;
0912:
0913: /** Creates a new instance of Refactor */
0914: public ElementsUsageFinder(CompilationInfo cinfo,
0915: HashMap<? extends ElementHandle, String> handleAndNames) {
0916: this .cinfo = cinfo;
0917: for (ElementHandle elemHandle : handleAndNames.keySet()) {
0918: Element elem = elemHandle.resolve(cinfo);
0919: elementAndNames.put(elem, handleAndNames
0920: .get(elemHandle));
0921: }
0922: //To take care of VB expressions, Ex:- getValue(#{SessionBean1.personRowSet})
0923: //renamePropertyBindingExpression(wc, name, newName);
0924: vbExpression = "#{" + getShortName() + "." + name + "}";
0925: }
0926:
0927: @Override
0928: public Tree visitIdentifier(IdentifierTree tree, Void v) {
0929: if (useStatus != UsageStatus.USED) {
0930: UsageStatus status = getUseStatus(getCurrentPath());
0931: if (status != useStatus
0932: && status != UsageStatus.NOT_USED) {
0933: useStatus = status;
0934: }
0935: return super .visitIdentifier(tree, v);
0936: }
0937: return null;
0938: }
0939:
0940: @Override
0941: public Tree visitMemberSelect(MemberSelectTree tree, Void v) {
0942: if (useStatus != UsageStatus.USED) {
0943: UsageStatus status = getUseStatus(getCurrentPath());
0944: if (status != useStatus
0945: && status != UsageStatus.NOT_USED) {
0946: useStatus = status;
0947: }
0948: return super .visitMemberSelect(tree, v);
0949: }
0950: return null;
0951: }
0952:
0953: @Override
0954: public Tree visitMethod(MethodTree tree, Void v) {
0955: if (useStatus != UsageStatus.USED
0956: && !canSkip(getCurrentPath())) {
0957: return super .visitMethod(tree, v);
0958: }
0959: return null;
0960: }
0961:
0962: @Override
0963: public Tree visitVariable(VariableTree tree, Void v) {
0964: if (useStatus != UsageStatus.USED
0965: && !canSkip(getCurrentPath())) {
0966: return super .visitVariable(tree, v);
0967: }
0968: return null;
0969: }
0970:
0971: public UsageStatus getUseStatus() {
0972: return useStatus;
0973: }
0974:
0975: @Override
0976: public Tree visitLiteral(LiteralTree tree, Void v) {
0977: if (useStatus != UsageStatus.USED) {
0978: UsageStatus status = getUseStatus(tree);
0979: if (status != useStatus
0980: && status != UsageStatus.NOT_USED) {
0981: useStatus = status;
0982: }
0983: return super .visitLiteral(tree, v);
0984: }
0985: return null;
0986: }
0987:
0988: private UsageStatus getUseStatus(LiteralTree tree) {
0989: if (tree.getKind() == Tree.Kind.STRING_LITERAL
0990: && vbExpression.equals(tree.getValue())) {
0991: if (getEnclosingMethodName(getCurrentPath()).equals(
0992: "_init")) {
0993: return UsageStatus.INIT_USE_ONLY;
0994: } else {
0995: return UsageStatus.USED;
0996: }
0997: } else {
0998: return UsageStatus.NOT_USED;
0999: }
1000: }
1001:
1002: private UsageStatus getUseStatus(TreePath path) {
1003: if (cinfo.getTreeUtilities().isSynthetic(path)) {
1004: return UsageStatus.NOT_USED;
1005: }
1006: Element el = cinfo.getTrees().getElement(path);
1007: if (el != null && elementAndNames.containsKey(el)) {
1008: if (getEnclosingMethodName(path).equals("_init")) {
1009: return UsageStatus.INIT_USE_ONLY;
1010: } else {
1011: return UsageStatus.USED;
1012: }
1013: }
1014: return UsageStatus.NOT_USED;
1015: }
1016:
1017: private boolean canSkip(TreePath path) {
1018: if (cinfo.getTreeUtilities().isSynthetic(path)) {
1019: return false;
1020: }
1021: Element el = cinfo.getTrees().getElement(path);
1022: if (el != null && elementAndNames.containsKey(el)) {
1023: TreePath declPath = cinfo.getTrees().getPath(el);
1024: if (declPath.getLeaf().equals(path.getLeaf())) {
1025: return true;
1026: }
1027: }
1028: return false;
1029: }
1030:
1031: private String getEnclosingMethodName(TreePath path) {
1032: while (path != null) {
1033: Tree leaf = path.getLeaf();
1034: if (leaf.getKind() == Kind.METHOD) {
1035: MethodTree mtree = (MethodTree) leaf;
1036: return mtree.getName().toString();
1037: }
1038: path = path.getParentPath();
1039: }
1040: return null;
1041: }
1042: }
1043: }
|