/******************************************************************** * Copyright (c) 2006 Contributors. All rights reserved. * This program and the accompanying materials are made available * under the terms of the Eclipse Public License v 2.0 * which accompanies this distribution and is available at * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt * * Contributors: IBM Corporation - initial API and implementation * Helen Hawkins - initial version *******************************************************************/ package org.aspectj.asm.internal; import java.io.File; import java.util.List; import org.aspectj.asm.AsmManager; import org.aspectj.asm.IElementHandleProvider; import org.aspectj.asm.IProgramElement; import org.aspectj.bridge.ISourceLocation; /** * Creates JDT-like handles, for example * * method with string argument: <tjp{Demo.java[Demo~main~\[QString; method with generic argument: * <pkg{MyClass.java[MyClass~myMethod~QList\<QString;>; an aspect: <pkg*A1.aj}A1 advice with Integer arg: * <pkg*A8.aj}A8&afterReturning&QInteger; method call: <pkg*A10.aj[C~m1?method-call(void pkg.C.m2()) * */ public class JDTLikeHandleProvider implements IElementHandleProvider { private final AsmManager asm; private static final char[] empty = new char[] {}; private static final char[] countDelim = new char[] { HandleProviderDelimiter.COUNT.getDelimiter() }; private static final String backslash = "\\"; private static final String emptyString = ""; public JDTLikeHandleProvider(AsmManager asm) { this.asm = asm; } public void initialize() { // nothing to do } public String createHandleIdentifier(IProgramElement ipe) { // AjBuildManager.setupModel --> top of the tree is either // or the .lst file if (ipe == null || (ipe.getKind().equals(IProgramElement.Kind.FILE_JAVA) && ipe.getName().equals(""))) { return ""; } else if (ipe.getHandleIdentifier(false) != null) { // have already created the handle for this ipe // therefore just return it return ipe.getHandleIdentifier(); } else if (ipe.getKind().equals(IProgramElement.Kind.FILE_LST)) { String configFile = asm.getHierarchy().getConfigFile(); int start = configFile.lastIndexOf(File.separator); int end = configFile.lastIndexOf(".lst"); if (end != -1) { configFile = configFile.substring(start + 1, end); } else { configFile = new StringBuilder("=").append(configFile.substring(start + 1)).toString(); } ipe.setHandleIdentifier(configFile); return configFile; } else if (ipe.getKind() == IProgramElement.Kind.SOURCE_FOLDER) { StringBuilder sb = new StringBuilder(); sb.append(createHandleIdentifier(ipe.getParent())).append("/"); // pr249216 - escape any embedded slashes String folder = ipe.getName(); if (folder.endsWith("/")) { folder = folder.substring(0, folder.length() - 1); } if (folder.contains("/")) { folder = folder.replace("/", "\\/"); } sb.append(folder); String handle = sb.toString(); ipe.setHandleIdentifier(handle); return handle; } IProgramElement parent = ipe.getParent(); if (parent != null && parent.getKind().equals(IProgramElement.Kind.IMPORT_REFERENCE)) { // want to miss out '#import declaration' in the handle parent = ipe.getParent().getParent(); } StringBuilder handle = new StringBuilder(); // add the handle for the parent handle.append(createHandleIdentifier(parent)); // add the correct delimiter for this ipe handle.append(HandleProviderDelimiter.getDelimiter(ipe)); // add the name and any parameters unless we're an initializer // (initializer's names are '...') if (!ipe.getKind().equals(IProgramElement.Kind.INITIALIZER)) { if (ipe.getKind() == IProgramElement.Kind.CLASS && ipe.getName().endsWith("{..}")) { // format: 'new Runnable() {..}' but its anon-y-mouse // dont append anything, there may be a count to follow though (!) } else { if (ipe.getKind() == IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR) { handle.append(ipe.getName()).append("_new").append(getParameters(ipe)); } else { // if (ipe.getKind() == IProgramElement.Kind.PACKAGE && ipe.getName().equals("DEFAULT")) { // // the delimiter will be in there, but skip the word DEFAULT as it is just a placeholder // } else { if (ipe.getKind().isDeclareAnnotation()) { // escape the @ (pr249216c9) handle.append("declare \\@").append(ipe.getName().substring(9)).append(getParameters(ipe)); } else { if (ipe.getFullyQualifiedName() != null) { handle.append(ipe.getFullyQualifiedName()); } else { handle.append(ipe.getName()); } handle.append(getParameters(ipe)); } } // } } } // add the count, for example '!2' if its the second ipe of its // kind in the aspect handle.append(getCount(ipe)); ipe.setHandleIdentifier(handle.toString()); return handle.toString(); } private String getParameters(IProgramElement ipe) { if (ipe.getParameterSignatures() == null || ipe.getParameterSignatures().isEmpty()) { return ""; } List sourceRefs = ipe.getParameterSignaturesSourceRefs(); List parameterTypes = ipe.getParameterSignatures(); StringBuilder sb = new StringBuilder(); if (sourceRefs != null) { for (String sourceRef : sourceRefs) { sb.append(HandleProviderDelimiter.getDelimiter(ipe)); sb.append(sourceRef); } } else { for (char[] element : parameterTypes) { sb.append(HandleProviderDelimiter.getDelimiter(ipe)); sb.append(NameConvertor.createShortName(element, false, false)); } } return sb.toString(); } /** * Determine a count to be suffixed to the handle, this is only necessary for identical looking entries at the same level in the * model (for example two anonymous class declarations). The format is ! where n will be greater than 2. * * @param ipe the program element for which the handle is being constructed * @return a char suffix that will either be empty or of the form "!" */ private char[] getCount(IProgramElement ipe) { // TODO could optimize this code char[] byteCodeName = ipe.getBytecodeName().toCharArray(); if (ipe.getKind().isInterTypeMember()) { int count = 1; List kids = ipe.getParent().getChildren(); for (IProgramElement object : kids) { if (object.equals(ipe)) { break; } if (object.getKind().isInterTypeMember()) { if (object.getName().equals(ipe.getName()) && getParameters(object).equals(getParameters(ipe))) { String existingHandle = object.getHandleIdentifier(); int suffixPosition = existingHandle.indexOf('!'); if (suffixPosition != -1) { count = Integer.parseInt(existingHandle.substring(suffixPosition + 1)) + 1; } else { if (count == 1) { count = 2; } } } } } if (count > 1) { return CharOperation.concat(countDelim, Integer.toString(count).toCharArray()); } } else if (ipe.getKind().isDeclare()) { // // look at peer declares int count = computeCountBasedOnPeers(ipe); if (count > 1) { return CharOperation.concat(countDelim, Integer.toString(count).toCharArray()); } } else if (ipe.getKind().equals(IProgramElement.Kind.ADVICE)) { // Look at any peer advice int count = 1; List kids = ipe.getParent().getChildren(); String ipeSig = ipe.getBytecodeSignature(); // remove return type from the signature - it should not be included in the comparison int idx = 0; ipeSig = shortenIpeSig(ipeSig); for (IProgramElement object : kids) { if (object.equals(ipe)) { break; } if (object.getKind() == ipe.getKind()) { if (object.getName().equals(ipe.getName())) { String sig1 = object.getBytecodeSignature(); if (sig1 != null && (idx = sig1.indexOf(")")) != -1) { sig1 = sig1.substring(0, idx); } // this code needs a speed overhaul... and some proper tests // Two static parts because one may be enclosing jpsp (269522) if (sig1 != null) { if (sig1.contains("Lorg/aspectj/lang")) { if (sig1.endsWith("Lorg/aspectj/lang/JoinPoint$StaticPart;")) { sig1 = sig1.substring(0, sig1.lastIndexOf("Lorg/aspectj/lang/JoinPoint$StaticPart;")); } if (sig1.endsWith("Lorg/aspectj/lang/JoinPoint;")) { sig1 = sig1.substring(0, sig1.lastIndexOf("Lorg/aspectj/lang/JoinPoint;")); } if (sig1.endsWith("Lorg/aspectj/lang/JoinPoint$StaticPart;")) { sig1 = sig1.substring(0, sig1.lastIndexOf("Lorg/aspectj/lang/JoinPoint$StaticPart;")); } } } if (sig1 == null && ipeSig == null || (sig1 != null && sig1.equals(ipeSig))) { String existingHandle = object.getHandleIdentifier(); int suffixPosition = existingHandle.indexOf('!'); if (suffixPosition != -1) { count = Integer.valueOf(existingHandle.substring(suffixPosition + 1)) + 1; } else { if (count == 1) { count = 2; } } } } } } if (count > 1) { return CharOperation.concat(countDelim, Integer.toString(count).toCharArray()); } } else if (ipe.getKind().equals(IProgramElement.Kind.INITIALIZER)) { // return String.valueOf(++initializerCounter).toCharArray(); // Look at any peer advice int count = 1; List kids = ipe.getParent().getChildren(); String ipeSig = ipe.getBytecodeSignature(); // remove return type from the signature - it should not be included in the comparison int idx = 0; ipeSig = shortenIpeSig(ipeSig); for (IProgramElement object : kids) { if (object.equals(ipe)) { break; } if (object.getKind() == ipe.getKind()) { if (object.getName().equals(ipe.getName())) { String sig1 = object.getBytecodeSignature(); if (sig1 != null && (idx = sig1.indexOf(")")) != -1) { sig1 = sig1.substring(0, idx); } // this code needs a speed overhaul... and some proper tests // Two static parts because one may be enclosing jpsp (269522) if (sig1 != null) { if (sig1.contains("Lorg/aspectj/lang")) { if (sig1.endsWith("Lorg/aspectj/lang/JoinPoint$StaticPart;")) { sig1 = sig1.substring(0, sig1.lastIndexOf("Lorg/aspectj/lang/JoinPoint$StaticPart;")); } if (sig1.endsWith("Lorg/aspectj/lang/JoinPoint;")) { sig1 = sig1.substring(0, sig1.lastIndexOf("Lorg/aspectj/lang/JoinPoint;")); } if (sig1.endsWith("Lorg/aspectj/lang/JoinPoint$StaticPart;")) { sig1 = sig1.substring(0, sig1.lastIndexOf("Lorg/aspectj/lang/JoinPoint$StaticPart;")); } } } if (sig1 == null && ipeSig == null || (sig1 != null && sig1.equals(ipeSig))) { String existingHandle = object.getHandleIdentifier(); int suffixPosition = existingHandle.indexOf('!'); if (suffixPosition != -1) { count = Integer.valueOf(existingHandle.substring(suffixPosition + 1)) + 1; } else { if (count == 1) { count = 2; } } } } } } // if (count > 1) { return Integer.toString(count).toCharArray(); // return CharOperation.concat(countDelim, new Integer(count).toString().toCharArray()); // } } else if (ipe.getKind().equals(IProgramElement.Kind.CODE)) { int index = CharOperation.lastIndexOf('!', byteCodeName); if (index != -1) { return convertCount(CharOperation.subarray(byteCodeName, index + 1, byteCodeName.length)); } } else if (ipe.getKind() == IProgramElement.Kind.CLASS) { // depends on previous children int count = 1; List kids = ipe.getParent().getChildren(); if (ipe.getName().endsWith("{..}")) { // only depends on previous anonymous children, name irrelevant for (IProgramElement object : kids) { if (object.equals(ipe)) { break; } if (object.getKind() == ipe.getKind()) { if (object.getName().endsWith("{..}")) { String existingHandle = object.getHandleIdentifier(); int suffixPosition = existingHandle.lastIndexOf('!'); int lastSquareBracket = existingHandle.lastIndexOf('['); // type delimiter if (suffixPosition != -1 && lastSquareBracket < suffixPosition) { // pr260384 count = Integer.valueOf(existingHandle.substring(suffixPosition + 1)) + 1; } else { if (count == 1) { count = 2; } } } } } } else { for (IProgramElement object : kids) { if (object.equals(ipe)) { break; } if (object.getKind() == ipe.getKind()) { if (object.getName().equals(ipe.getName())) { String existingHandle = object.getHandleIdentifier(); int suffixPosition = existingHandle.lastIndexOf('!'); int lastSquareBracket = existingHandle.lastIndexOf('['); // type delimiter if (suffixPosition != -1 && lastSquareBracket < suffixPosition) { // pr260384 count = Integer.valueOf(existingHandle.substring(suffixPosition + 1)) + 1; } else { if (count == 1) { count = 2; } } } } } } if (count > 1) { return CharOperation.concat(countDelim, Integer.toString(count).toCharArray()); } } return empty; } private String shortenIpeSig(String ipeSig) { int idx; if (ipeSig != null && ((idx = ipeSig.indexOf(")")) != -1)) { ipeSig = ipeSig.substring(0, idx); } if (ipeSig != null) { if (ipeSig.contains("Lorg/aspectj/lang")) { if (ipeSig.endsWith("Lorg/aspectj/lang/JoinPoint$StaticPart;")) { ipeSig = ipeSig.substring(0, ipeSig.lastIndexOf("Lorg/aspectj/lang/JoinPoint$StaticPart;")); } if (ipeSig.endsWith("Lorg/aspectj/lang/JoinPoint;")) { ipeSig = ipeSig.substring(0, ipeSig.lastIndexOf("Lorg/aspectj/lang/JoinPoint;")); } if (ipeSig.endsWith("Lorg/aspectj/lang/JoinPoint$StaticPart;")) { ipeSig = ipeSig.substring(0, ipeSig.lastIndexOf("Lorg/aspectj/lang/JoinPoint$StaticPart;")); } } } return ipeSig; } private int computeCountBasedOnPeers(IProgramElement ipe) { int count = 1; for (IProgramElement object : ipe.getParent().getChildren()) { if (object.equals(ipe)) { break; } if (object.getKind() == ipe.getKind()) { if (object.getKind().toString().equals(ipe.getKind().toString())) { String existingHandle = object.getHandleIdentifier(); int suffixPosition = existingHandle.indexOf('!'); if (suffixPosition != -1) { count = Integer.valueOf(existingHandle.substring(suffixPosition + 1)) + 1; } else { if (count == 1) { count = 2; } } } } } return count; } /** * Only returns the count if it's not equal to 1 */ private char[] convertCount(char[] c) { if ((c.length == 1 && c[0] != ' ' && c[0] != '1') || c.length > 1) { return CharOperation.concat(countDelim, c); } return empty; } public String getFileForHandle(String handle) { IProgramElement node = asm.getHierarchy().getElement(handle); if (node != null) { return asm.getCanonicalFilePath(node.getSourceLocation().getSourceFile()); } else if (handle.charAt(0) == HandleProviderDelimiter.ASPECT_CU.getDelimiter() || handle.charAt(0) == HandleProviderDelimiter.COMPILATIONUNIT.getDelimiter()) { // it's something like *MyAspect.aj or {MyClass.java. In other words // it's a file node that's been created with no children and no // parent return backslash + handle.substring(1); } return emptyString; } public int getLineNumberForHandle(String handle) { IProgramElement node = asm.getHierarchy().getElement(handle); if (node != null) { return node.getSourceLocation().getLine(); } else if (handle.charAt(0) == HandleProviderDelimiter.ASPECT_CU.getDelimiter() || handle.charAt(0) == HandleProviderDelimiter.COMPILATIONUNIT.getDelimiter()) { // it's something like *MyAspect.aj or {MyClass.java. In other words // it's a file node that's been created with no children and no // parent return 1; } return -1; } public int getOffSetForHandle(String handle) { IProgramElement node = asm.getHierarchy().getElement(handle); if (node != null) { return node.getSourceLocation().getOffset(); } else if (handle.charAt(0) == HandleProviderDelimiter.ASPECT_CU.getDelimiter() || handle.charAt(0) == HandleProviderDelimiter.COMPILATIONUNIT.getDelimiter()) { // it's something like *MyAspect.aj or {MyClass.java. In other words // it's a file node that's been created with no children and no // parent return 0; } return -1; } public String createHandleIdentifier(ISourceLocation location) { IProgramElement node = asm.getHierarchy().findElementForSourceLine(location); if (node != null) { return createHandleIdentifier(node); } return null; } public String createHandleIdentifier(File sourceFile, int line, int column, int offset) { IProgramElement node = asm.getHierarchy().findElementForOffSet(sourceFile.getAbsolutePath(), line, offset); if (node != null) { return createHandleIdentifier(node); } return null; } public boolean dependsOnLocation() { // handles are independent of soureLocations therefore return false return false; } }