| /******************************************************************************* |
| * 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()]); |
| } |
| |
| public void acceptModule(char[] arg0, char[] arg1, int arg2, int arg3) { |
| // TODO Auto-generated method stub |
| |
| } |
| |
| /** |
| * 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'; |
| // } |
| } |