org.eclipse.ajdt.core.text.ITDAwareSelectionRequestor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ajdt.core.text.ITDAwareSelectionRequestor.java

Source

/*******************************************************************************
 * Copyright (c) 2009 SpringSource and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Andrew Eisenberg - initial API and implementation
 *******************************************************************************/

package org.eclipse.ajdt.core.text;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.aspectj.asm.IProgramElement.Kind;
import org.eclipse.ajdt.core.codeconversion.AspectsConvertingParser;
import org.eclipse.ajdt.core.codeconversion.AspectsConvertingParser.Replacement;
import org.eclipse.ajdt.core.javaelements.AJCompilationUnit;
import org.eclipse.ajdt.core.javaelements.IntertypeElement;
import org.eclipse.ajdt.core.javaelements.IntertypeElementInfo;
import org.eclipse.ajdt.core.model.AJProjectModelFacade;
import org.eclipse.ajdt.core.model.AJProjectModelFactory;
import org.eclipse.ajdt.core.model.AJRelationshipManager;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.codeassist.ISelectionRequestor;

/**
 * @author Andrew Eisenberg
 * @created Apr 28, 2009
 * 
 * A selection requestor that knows about ITDs.
 */
public class ITDAwareSelectionRequestor implements ISelectionRequestor {

    private AJProjectModelFacade model;
    private ICompilationUnit currentUnit;
    private Set<IJavaElement> accepted;

    private ArrayList<Replacement> replacements;
    private IJavaProject javaProject;

    public ITDAwareSelectionRequestor(AJProjectModelFacade model, ICompilationUnit currentUnit) {
        this.model = model;
        this.currentUnit = currentUnit;
        this.accepted = new HashSet<IJavaElement>();
    }

    public void setReplacements(ArrayList<Replacement> replacements) {
        this.replacements = replacements;
    }

    public void acceptError(CategorizedProblem error) {
        // can ignore
    }

    public void acceptField(char[] declaringTypePackageName, char[] declaringTypeName, char[] name,
            boolean isDeclaration, char[] uniqueKey, int start, int end) {
        try {
            IType targetType = findType(declaringTypePackageName, declaringTypeName);
            if (targetType == null) {
                // type couldn't be found.  this is really some kind of problem
                return;
            }
            List<IJavaElement> itds = ensureModel(targetType).getRelationshipsForElement(targetType,
                    AJRelationshipManager.ASPECT_DECLARATIONS);
            for (IJavaElement elt : itds) {
                if (matchedField(elt, name)) {
                    accepted.add(elt);
                    return;
                }
            }

            // if we are selecting inside of an ITD and the field being matched is a regular field, we find it here.
            IntertypeElement itd = maybeGetITD(start);
            if (itd != null) {
                IField field = targetType.getField(String.valueOf(name));
                if (field.exists()) {
                    accepted.add(field);
                    return;
                }
            }

            // now check to see if we actually found a field in an ITIT
            IJavaElement parent = targetType.getParent();
            if (parent.getElementType() == IJavaElement.TYPE) {
                // definitely an inner type.  If the outer type does not match,
                // then we know that this was from an ITIT
                char[] enclosingName = (parent.getElementName() + ".").toCharArray();
                if (!CharOperation.prefixEquals(enclosingName, declaringTypeName)) {
                    IField field = targetType.getField(String.valueOf(name));
                    if (field.exists()) {
                        accepted.add(field);
                    }
                }
            }
        } catch (JavaModelException e) {
        }
    }

    /**
     * for itits:
     * If the initial type finding returns null, then look for a dot (ie- an inner type) in the type name
     * Get the parent type and look to see if it has any aspect declarations on it of the name of the inner
     * type.  if so, then use that one
     */
    private IType findType(char[] declaringTypePackageName, char[] declaringTypeName) throws JavaModelException {
        if (javaProject == null) {
            javaProject = currentUnit.getJavaProject();
        }
        IType type = javaProject.findType(toQualifiedName(declaringTypePackageName, declaringTypeName));
        if (type != null) {
            return type;
        } else {
            // check to see if this type is an ITIT
            int index = CharOperation.lastIndexOf('.', declaringTypeName);
            if (index >= 0) {
                // definitely an inner type...now get the enclosing type to look for ITDs
                char[] enclosingTypeName = CharOperation.subarray(declaringTypeName, 0, index);
                char[] innerTypeName = CharOperation.subarray(declaringTypeName, index + 1,
                        declaringTypeName.length);
                IType enclosingType = javaProject
                        .findType(toQualifiedName(declaringTypePackageName, enclosingTypeName));
                if (enclosingType != null) {
                    String innerTypeStr = String.valueOf(innerTypeName);
                    IType innerType = enclosingType.getType(innerTypeStr);
                    if (innerType.exists()) {
                        // a standard inner type
                        // probably won't get here since should have been returned above
                        return innerType;
                    } else if (model.hasModel()) {
                        // now check to see if the type has any ITITs declared on it
                        List<IJavaElement> rels = model.getRelationshipsForElement(enclosingType,
                                AJRelationshipManager.ASPECT_DECLARATIONS);
                        for (IJavaElement rel : rels) {
                            if (rel.getElementType() == IJavaElement.TYPE
                                    && innerTypeStr.equals(rel.getElementName())) {
                                return (IType) rel;
                            }
                        }
                    }
                }
            }
        }
        // not found...probably an error or the model hasn't been built.
        return null;
    }

    private IntertypeElement maybeGetITD(int pos) throws JavaModelException {
        if (replacements != null && currentUnit instanceof AJCompilationUnit) {
            IJavaElement elt = currentUnit
                    .getElementAt(AspectsConvertingParser.translatePositionToBeforeChanges(pos, replacements));
            if (elt instanceof IntertypeElement) {
                return (IntertypeElement) elt;
            }
        }
        return null;
    }

    public void acceptMethod(char[] declaringTypePackageName, char[] declaringTypeName,
            String enclosingDeclaringTypeSignature, char[] selector, char[][] parameterPackageNames,
            char[][] parameterTypeNames, String[] parameterSignatures, char[][] typeParameterNames,
            char[][][] typeParameterBoundNames, boolean isConstructor, boolean isDeclaration, char[] uniqueKey,
            int start, int end) {
        try {
            IType targetType = findType(declaringTypePackageName, declaringTypeName);
            if (targetType == null) {
                return;
            }

            String[] simpleParameterSigs;
            if (parameterSignatures != null) {
                simpleParameterSigs = new String[parameterSignatures.length];
                for (int i = 0; i < parameterSignatures.length; i++) {
                    simpleParameterSigs[i] = toSimpleName(parameterSignatures[i]);
                }
            } else {
                simpleParameterSigs = null;
            }

            List<IJavaElement> itds = ensureModel(targetType).getRelationshipsForElement(targetType,
                    AJRelationshipManager.ASPECT_DECLARATIONS);
            for (IJavaElement elt : itds) {
                if (matchedMethod(elt, selector, simpleParameterSigs)) {
                    accepted.add(elt);
                    return;
                }
            }

            IntertypeElement itd = maybeGetITD(start);
            String selectorStr = String.valueOf(selector);
            if (itd != null && !isDeclaration) {
                // if we are selecting inside of an ITD and the method being matched is a regular method, we find it here.
                IMethod method = targetType.getMethod(selectorStr, parameterSignatures);
                if (method.exists()) {
                    accepted.add(method);
                    return;
                }
            }

            // still need to determine if the ITD declaration itself is being selected

            // now check to see if we actually found a method in an ITIT
            IJavaElement parent = targetType.getParent();
            if (parent.getElementType() == IJavaElement.TYPE) {
                // definitely an inner type.  If the outer type does not match,
                // then we know that this was from an ITIT
                char[] enclosingName = (parent.getElementName() + ".").toCharArray();
                if (!CharOperation.prefixEquals(enclosingName, declaringTypeName)) {
                    IMethod[] methods = targetType.getMethods();
                    for (IMethod method : methods) {
                        if (method.getElementName().equals(selectorStr)
                                && matchedParameters(simpleParameterSigs, method.getParameterTypes())) {
                            accepted.add(method);
                        }
                    }
                }
            }

        } catch (JavaModelException e) {
        }
    }

    public void acceptTypeParameter(char[] declaringTypePackageName, char[] declaringTypeName,
            char[] typeParameterName, boolean isDeclaration, int start, int end) {
        // can ignore
    }

    public void acceptMethodTypeParameter(char[] declaringTypePackageName, char[] declaringTypeName,
            char[] selector, int selectorStart, int selectorEnd, char[] typeParameterName, boolean isDeclaration,
            int start, int end) {
        // can ignore
    }

    public void acceptPackage(char[] packageName) {
        // can ignore
    }

    public void acceptType(char[] packageName, char[] annotationName, int modifiers, boolean isDeclaration,
            char[] genericTypeSignature, int start, int end) {
        try {
            int origStart = AspectsConvertingParser.translatePositionToBeforeChanges(start, replacements);
            int origEnd = AspectsConvertingParser.translatePositionToBeforeChanges(end, replacements);
            IntertypeElement itd = maybeGetITD(origStart);
            if (itd != null) {
                // find out if we are selecting the target type name part of an itd
                // itd.getNameRange() returns the range of the name, but excludes the target type.  Must subtract from there.  
                // Make assumption that there are no spaces
                // or comments between '.' and the rest of the name
                ISourceRange nameRange = itd.getNameRange();

                String itdName = itd.getElementName();
                int typeNameLength = Math.max(itdName.lastIndexOf('.'), 0);
                String typeName = itdName.substring(0, typeNameLength);

                int typeNameStart;
                if (itd.getAJKind() == Kind.INTER_TYPE_CONSTRUCTOR) {
                    typeNameStart = nameRange.getOffset();
                } else {
                    typeNameStart = nameRange.getOffset() - 1 - typeName.length();
                }
                // now determine if the selected section is completely contained within the type name
                if (contained(origStart, origEnd, typeNameStart, typeNameStart + typeNameLength)) {
                    IType targetType = itd.findTargetType();
                    if (targetType != null && targetType.getFullyQualifiedName('.')
                            .equals(toQualifiedName(packageName, annotationName))) {
                        accepted.add(targetType);
                    }
                }
            } else {

                // now check to see if we actually found an ITIT
                IType targetType = findType(packageName, annotationName);
                if (targetType != null) {
                    IJavaElement parent = targetType.getParent();
                    if (parent.getElementType() == IJavaElement.TYPE) {
                        // definitely an inner type.  If the outer type does not match,
                        // then we know that this was from an ITIT
                        char[] enclosingName = (parent.getElementName() + ".").toCharArray();
                        if (!CharOperation.prefixEquals(enclosingName, annotationName)) {
                            accepted.add(targetType);
                        }
                    }
                }
            }
        } catch (JavaModelException e) {
        }
    }

    private boolean contained(int selStart, int selEnd, int typeNameStart, int typeNameEnd) {
        return selStart >= typeNameStart && selEnd <= typeNameEnd;
    }

    private AJProjectModelFacade ensureModel(IJavaElement elt) {
        try {
            if (model.getProject().equals(elt.getJavaProject().getProject())) {
                return model;
            } else {
                return AJProjectModelFactory.getInstance().getModelForJavaElement(elt);
            }
        } catch (Exception e) {
            // catch NPE if elt is null, or core exception if not stored to disk yet.
            return model;
        }
    }

    private boolean matchedField(IJavaElement elt, char[] name) throws JavaModelException {
        if (elt instanceof IntertypeElement) {
            IntertypeElementInfo info = (IntertypeElementInfo) ((IntertypeElement) elt).getElementInfo();
            if (info.getAJKind() == Kind.INTER_TYPE_FIELD) {
                if (extractName(elt.getElementName()).equals(new String(name))) {
                    return true;
                }
            }
        }
        return false;
    }

    // This method checks to see if the selected method matches the 
    // method we are searching for.  However, we take some shortcuts here.
    // Rather than looking at qualified, resolved type signatures of both methods
    // we look at the simple type names of all paramters.  The reason for this 
    // is that the parameters on the elt argument may be unresolved (ie- simple names)
    // whereas the paramter signatures passed in may be resolved (ie- fully qualified).
    // Thus, there could be a match that wouldn't be found.
    // The solution is to compare simple names only.  The danger is that there might be false
    // positives with the match, but they would be rare and not particularly worrisome if 
    // they exist.
    private boolean matchedMethod(IJavaElement elt, char[] selector, String[] simpleParameterSigs)
            throws JavaModelException {
        if (elt instanceof IntertypeElement) {
            IntertypeElement itd = (IntertypeElement) elt;
            IntertypeElementInfo info = (IntertypeElementInfo) ((IntertypeElement) elt).getElementInfo();
            if (info.getAJKind() == Kind.INTER_TYPE_METHOD || info.getAJKind() == Kind.INTER_TYPE_CONSTRUCTOR) {
                if (extractName(elt.getElementName()).equals(String.valueOf(selector))) {
                    String[] itdParameterSigs = itd.getParameterTypes();
                    if (itdParameterSigs == null || simpleParameterSigs == null) {
                        return (itdParameterSigs == null || itdParameterSigs.length == 0)
                                && (simpleParameterSigs == null || simpleParameterSigs.length == 0);
                    }
                    if (itdParameterSigs.length == simpleParameterSigs.length) {
                        return matchedParameters(simpleParameterSigs, itdParameterSigs);
                    }
                }
            }
        }
        return false;
    }

    /**
     * Since we can't resolved unresolved type signatures coming from source
     * files, we only compare the simple names of the types.  99% of the time,
     * this is sufficient.
     *  
     * @param simpleParameterSigs
     * @param itdParameterSigs
     */
    protected boolean matchedParameters(String[] simpleParameterSigs, String[] itdParameterSigs) {
        if (itdParameterSigs.length == simpleParameterSigs.length) {
            for (int i = 0; i < itdParameterSigs.length; i++) {
                String simple = toSimpleName(itdParameterSigs[i]);
                if (!simple.equals(simpleParameterSigs[i])) {
                    return false;
                }
            }
        }
        return true;
    }

    private String toSimpleName(String signature) {
        String simple = Signature.getSignatureSimpleName(signature);
        int typeParamIndex = simple.indexOf('<');
        if (typeParamIndex > 0) {
            simple = simple.substring(0, typeParamIndex);
        }
        int dotIndex = simple.indexOf('.');
        if (dotIndex > 0) {
            simple = simple.substring(dotIndex + 1);
        }
        return simple;
    }

    private String toQualifiedName(char[] declaringTypePackageName, char[] declaringTypeName) {
        StringBuffer sb = new StringBuffer();
        sb.append(declaringTypePackageName);
        if (sb.length() > 0) {
            sb.append(".");
        }
        sb.append(declaringTypeName);
        return sb.toString();
    }

    private String extractName(String name) {

        String[] split = name.split("\\.");
        if (split.length <= 1) {
            return name;
        }
        int splitLength = split.length;
        // maybe this is a constructor ITD
        if (name.endsWith("_new")) {
            if ((split[splitLength - 2] + "_new").equals(split[splitLength - 1])) {
                return split[splitLength - 2];
            }
        } else if (name.endsWith("$new")) {
            return name.substring(0, name.length() - "$new".length());
        }
        return split[splitLength - 1];
    }

    public IJavaElement[] getElements() {
        return (IJavaElement[]) accepted.toArray(new IJavaElement[accepted.size()]);
    }

    /**
     * Converts from a 'Q' kind of signature to an 'L' kind of signature.
     * This is actually quite tricky.
     * @param signature
     * @return
     */
    // not used any more.  can likely delete
    //    private String resolveSignture(IType type, String signature) {
    //        String simple = Signature.getSignatureSimpleName(signature);
    //        String qual = Signature.getSignatureQualifier(signature);
    //        String[] typeParams = Signature.getTypeArguments(signature);
    //        int arrayCount = Signature.getArrayCount(signature);
    //        
    //        String fullyQual = qual != null && qual.length() > 0 ? qual + '.' + simple : simple;
    //        try {
    //            String[][] resolvedArr = type.resolveType(fullyQual);
    //            if (resolvedArr != null && resolvedArr.length > 0) {
    //                String resolved = (resolvedArr[0][0].length() > 0) ? 
    //                        resolvedArr[0][0] + "." + resolvedArr[0][1] : resolvedArr[0][1]; 
    //                String newSig = Signature.createTypeSignature(resolved, true);
    //                if (arrayCount > 0) {
    //                    newSig = Signature.createArraySignature(newSig, arrayCount);
    //                }
    //                
    //                // uggh...don't know if this will work
    //                if (typeParams != null && typeParams.length > 0) {
    //                    newSig = newSig.substring(0, newSig.length()-1) + "<";
    //                    for (int i = 0; i < typeParams.length; i++) {
    //                        typeParams[i] = resolveSignture(type, typeParams[i]);
    //                        newSig = newSig.substring(0, newSig.length()-1) + typeParams[i];
    //                    }
    //                    newSig += ">;";
    //                }
    //                return newSig;
    //            }
    //        } catch (JavaModelException e) {
    //        }
    //        
    //        // couldn't resolve
    //        return signature;
    //    }
    //
    //    /**
    //     * @param signature
    //     * @return true if this is an unresolved signature
    //     */
    //    private boolean isUnresolvedSignature(String signature) {
    //        int typeStart = 0;
    //        while (signature.length() < typeStart && signature.charAt(typeStart) == '[') {
    //            typeStart++;
    //        }
    //        return signature.charAt(typeStart) == 'Q';
    //    }
}