aboutsummaryrefslogtreecommitdiffstats
path: root/asm/src/main
diff options
context:
space:
mode:
authorAndy Clement <aclement@pivotal.io>2019-01-23 19:36:37 -0800
committerAndy Clement <aclement@pivotal.io>2019-01-23 19:36:37 -0800
commitd9cd0d44e8c42f06e13b033ae1cc269d3e7f7c29 (patch)
tree821258f70f7f28e3e96faa82379708a038c84128 /asm/src/main
parent9803f22ec4337779057f4ec2ace35f2d6483d6dd (diff)
downloadaspectj-d9cd0d44e8c42f06e13b033ae1cc269d3e7f7c29.tar.gz
aspectj-d9cd0d44e8c42f06e13b033ae1cc269d3e7f7c29.zip
mavenizing asm module - wip
Diffstat (limited to 'asm/src/main')
-rw-r--r--asm/src/main/java/org/aspectj/asm/AsmManager.java1305
-rw-r--r--asm/src/main/java/org/aspectj/asm/HierarchyWalker.java37
-rw-r--r--asm/src/main/java/org/aspectj/asm/IElementHandleProvider.java65
-rw-r--r--asm/src/main/java/org/aspectj/asm/IHierarchy.java127
-rw-r--r--asm/src/main/java/org/aspectj/asm/IHierarchyListener.java25
-rw-r--r--asm/src/main/java/org/aspectj/asm/IModelFilter.java32
-rw-r--r--asm/src/main/java/org/aspectj/asm/IProgramElement.java456
-rw-r--r--asm/src/main/java/org/aspectj/asm/IRelationship.java103
-rw-r--r--asm/src/main/java/org/aspectj/asm/IRelationshipMap.java87
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/AspectJElementHierarchy.java697
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/CharOperation.java205
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/HandleProviderDelimiter.java143
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/JDTLikeHandleProvider.java474
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/NameConvertor.java253
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/ProgramElement.java852
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/Relationship.java86
-rw-r--r--asm/src/main/java/org/aspectj/asm/internal/RelationshipMap.java150
17 files changed, 5097 insertions, 0 deletions
diff --git a/asm/src/main/java/org/aspectj/asm/AsmManager.java b/asm/src/main/java/org/aspectj/asm/AsmManager.java
new file mode 100644
index 000000000..2ab34c862
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/AsmManager.java
@@ -0,0 +1,1305 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * Andy Clement incremental support and switch on/off state
+ * ******************************************************************/
+
+package org.aspectj.asm;
+
+import java.io.BufferedWriter;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.aspectj.asm.internal.AspectJElementHierarchy;
+import org.aspectj.asm.internal.HandleProviderDelimiter;
+import org.aspectj.asm.internal.JDTLikeHandleProvider;
+import org.aspectj.asm.internal.RelationshipMap;
+import org.aspectj.bridge.ISourceLocation;
+import org.aspectj.util.IStructureModel;
+
+/**
+ * The Abstract Structure Model (ASM) represents the containment hierarchy and crosscutting structure map for AspectJ programs. It
+ * is used by IDE views such as the document outline, and by other tools such as ajdoc to show both AspectJ declarations and
+ * crosscutting links, such as which advice affects which join point shadows.
+ *
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public class AsmManager implements IStructureModel {
+
+ // For testing ONLY
+ public static boolean recordingLastActiveStructureModel = true;
+ public static AsmManager lastActiveStructureModel;
+ public static boolean forceSingletonBehaviour = false;
+
+ // SECRETAPI asc pull the secret options together into a system API you lazy fool
+ public static boolean attemptIncrementalModelRepairs = false;
+ // Dumping the model is expensive
+ public static boolean dumpModelPostBuild = false;
+ // For offline debugging, you can now ask for the AsmManager to
+ // dump the model - see the method setReporting()
+ private static boolean dumpModel = false;
+ private static boolean dumpRelationships = false;
+ private static boolean dumpDeltaProcessing = false;
+ private static IModelFilter modelFilter = null;
+ private static String dumpFilename = "";
+ private static boolean reporting = false;
+
+ private static boolean completingTypeBindings = false;
+
+ private final List<IHierarchyListener> structureListeners = new ArrayList<IHierarchyListener>();
+
+ // The model is 'manipulated' by the AjBuildManager.setupModel() code which
+ // trashes all the
+ // fields when setting up a new model for a batch build.
+ // Due to the requirements of incremental compilation we need to tie some of
+ // the info
+ // below to the AjState for a compilation and recover it if switching
+ // between projects.
+ protected IHierarchy hierarchy;
+
+ /*
+ * Map from String > String - it maps absolute paths for inpath dirs/jars to workspace relative paths suitable for handle
+ * inclusion
+ */
+ protected Map<File, String> inpathMap;
+ private IRelationshipMap mapper;
+ private IElementHandleProvider handleProvider;
+
+ private final CanonicalFilePathMap canonicalFilePathMap = new CanonicalFilePathMap();
+ // Record the Set<File> for which the model has been modified during the
+ // last incremental build
+ private final Set<File> lastBuildChanges = new HashSet<File>();
+
+ // Record the Set<File> of aspects that wove the files listed in lastBuildChanges
+ final Set<File> aspectsWeavingInLastBuild = new HashSet<File>();
+
+ // static {
+ // setReporting("c:/model.nfo",true,true,true,true);
+ // }
+
+ private AsmManager() {
+ }
+
+ public static AsmManager createNewStructureModel(Map<File, String> inpathMap) {
+ if (forceSingletonBehaviour && lastActiveStructureModel != null) {
+ return lastActiveStructureModel;
+ }
+ AsmManager asm = new AsmManager();
+ asm.inpathMap = inpathMap;
+ asm.hierarchy = new AspectJElementHierarchy(asm);
+ asm.mapper = new RelationshipMap();
+ asm.handleProvider = new JDTLikeHandleProvider(asm);
+ // call initialize on the handleProvider when we create a new ASM
+ // to give handleProviders the chance to reset any state
+ asm.handleProvider.initialize();
+ asm.resetDeltaProcessing();
+ setLastActiveStructureModel(asm);
+ return asm;
+ }
+
+ public IHierarchy getHierarchy() {
+ return hierarchy;
+ }
+
+ public IRelationshipMap getRelationshipMap() {
+ return mapper;
+ }
+
+ public void fireModelUpdated() {
+ notifyListeners();
+ if (dumpModelPostBuild && hierarchy.getConfigFile() != null) {
+ writeStructureModel(hierarchy.getConfigFile());
+ }
+ }
+
+ /**
+ * Constructs map each time it's called.
+ */
+ public HashMap<Integer, List<IProgramElement>> getInlineAnnotations(String sourceFile, boolean showSubMember,
+ boolean showMemberAndType) {
+
+ if (!hierarchy.isValid()) {
+ return null;
+ }
+
+ HashMap<Integer, List<IProgramElement>> annotations = new HashMap<Integer, List<IProgramElement>>();
+ IProgramElement node = hierarchy.findElementForSourceFile(sourceFile);
+ if (node == IHierarchy.NO_STRUCTURE) {
+ return null;
+ } else {
+ IProgramElement fileNode = node;
+ ArrayList<IProgramElement> peNodes = new ArrayList<IProgramElement>();
+ getAllStructureChildren(fileNode, peNodes, showSubMember, showMemberAndType);
+ for (Iterator<IProgramElement> it = peNodes.iterator(); it.hasNext();) {
+ IProgramElement peNode = it.next();
+ List<IProgramElement> entries = new ArrayList<IProgramElement>();
+ entries.add(peNode);
+ ISourceLocation sourceLoc = peNode.getSourceLocation();
+ if (null != sourceLoc) {
+ Integer hash = new Integer(sourceLoc.getLine());
+ List<IProgramElement> existingEntry = annotations.get(hash);
+ if (existingEntry != null) {
+ entries.addAll(existingEntry);
+ }
+ annotations.put(hash, entries);
+ }
+ }
+ return annotations;
+ }
+ }
+
+ private void getAllStructureChildren(IProgramElement node, List<IProgramElement> result, boolean showSubMember,
+ boolean showMemberAndType) {
+ List<IProgramElement> children = node.getChildren();
+ if (node.getChildren() == null) {
+ return;
+ }
+ for (IProgramElement next : children) {
+ List<IRelationship> rels = mapper.get(next);
+ if (next != null
+ && ((next.getKind() == IProgramElement.Kind.CODE && showSubMember) || (next.getKind() != IProgramElement.Kind.CODE && showMemberAndType))
+ && rels != null && rels.size() > 0) {
+ result.add(next);
+ }
+ getAllStructureChildren(next, result, showSubMember, showMemberAndType);
+ }
+ }
+
+ public void addListener(IHierarchyListener listener) {
+ structureListeners.add(listener);
+ }
+
+ public void removeStructureListener(IHierarchyListener listener) {
+ structureListeners.remove(listener);
+ }
+
+ // this shouldn't be needed - but none of the people that add listeners
+ // in the test suite ever remove them. AMC added this to be called in
+ // setup() so that the test cases would cease leaking listeners and go
+ // back to executing at a reasonable speed.
+ public void removeAllListeners() {
+ structureListeners.clear();
+ }
+
+ private void notifyListeners() {
+ for (IHierarchyListener listener : structureListeners) {
+ listener.elementsUpdated(hierarchy);
+ }
+ }
+
+ public IElementHandleProvider getHandleProvider() {
+ return handleProvider;
+ }
+
+ public void setHandleProvider(IElementHandleProvider handleProvider) {
+ this.handleProvider = handleProvider;
+ }
+
+ public void writeStructureModel(String configFilePath) {
+ try {
+ String filePath = genExternFilePath(configFilePath);
+ FileOutputStream fos = new FileOutputStream(filePath);
+ ObjectOutputStream s = new ObjectOutputStream(fos);
+ s.writeObject(hierarchy); // Store the program element tree
+ s.writeObject(mapper); // Store the relationships
+ s.flush();
+ fos.flush();
+ fos.close();
+ s.close();
+ } catch (IOException e) {
+ // System.err.println("AsmManager: Unable to write structure model: "
+ // +configFilePath+" because of:");
+ // e.printStackTrace();
+ }
+ }
+
+ /**
+ * @param configFilePath path to an ".lst" file
+ */
+ public void readStructureModel(String configFilePath) {
+ boolean hierarchyReadOK = false;
+ try {
+ if (configFilePath == null) {
+ hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
+ } else {
+ String filePath = genExternFilePath(configFilePath);
+ FileInputStream in = new FileInputStream(filePath);
+ ObjectInputStream s = new ObjectInputStream(in);
+ hierarchy = (AspectJElementHierarchy) s.readObject();
+ ((AspectJElementHierarchy) hierarchy).setAsmManager(this);
+ hierarchyReadOK = true;
+ mapper = (RelationshipMap) s.readObject();
+ s.close();
+ }
+ } catch (FileNotFoundException fnfe) {
+ // That is OK
+ hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
+ } catch (EOFException eofe) {
+ // Might be an old format sym file that is missing its relationships
+ if (!hierarchyReadOK) {
+ System.err.println("AsmManager: Unable to read structure model: " + configFilePath + " because of:");
+ eofe.printStackTrace();
+ hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
+ }
+ } catch (Exception e) {
+ // System.err.println("AsmManager: Unable to read structure model: "+
+ // configFilePath+" because of:");
+ // e.printStackTrace();
+ hierarchy.setRoot(IHierarchy.NO_STRUCTURE);
+ } finally {
+ notifyListeners();
+ }
+ }
+
+ private String genExternFilePath(String configFilePath) {
+ // sometimes don't have ".lst"
+ if (configFilePath.lastIndexOf(".lst") != -1) {
+ configFilePath = configFilePath.substring(0, configFilePath.lastIndexOf(".lst"));
+ }
+ return configFilePath + ".ajsym";
+ }
+
+ public String getCanonicalFilePath(File f) {
+ return canonicalFilePathMap.get(f);
+ }
+
+ public CanonicalFilePathMap getCanonicalFilePathMap() {
+ return canonicalFilePathMap;
+ }
+
+ private static class CanonicalFilePathMap {
+ private static final int MAX_SIZE = 4000;
+
+ private final Map<String, String> pathMap = new HashMap<String, String>(20);
+
+ // // guards to ensure correctness and liveness
+ // private boolean cacheInUse = false;
+ // private boolean stopRequested = false;
+ //
+ // private synchronized boolean isCacheInUse() {
+ // return cacheInUse;
+ // }
+ //
+ // private synchronized void setCacheInUse(boolean val) {
+ // cacheInUse = val;
+ // if (val) {
+ // notifyAll();
+ // }
+ // }
+ //
+ // private synchronized boolean isStopRequested() {
+ // return stopRequested;
+ // }
+ //
+ // private synchronized void requestStop() {
+ // stopRequested = true;
+ // }
+ //
+ // /**
+ // * Begin prepopulating the map by adding an entry from
+ // * file.getPath -> file.getCanonicalPath for each file in
+ // * the list. Do this on a background thread.
+ // * @param files
+ // */
+ // public void prepopulate(final List files) {
+ // stopRequested = false;
+ // setCacheInUse(false);
+ // if (pathMap.size() > MAX_SIZE) {
+ // pathMap.clear();
+ // }
+ // new Thread() {
+ // public void run() {
+ // System.out.println("Starting cache population: " +
+ // System.currentTimeMillis());
+ // Iterator it = files.iterator();
+ // while (!isStopRequested() && it.hasNext()) {
+ // File f = (File)it.next();
+ // if (pathMap.get(f.getPath()) == null) {
+ // // may reuse cache across compiles from ides...
+ // try {
+ // pathMap.put(f.getPath(),f.getCanonicalPath());
+ // } catch (IOException ex) {
+ // pathMap.put(f.getPath(),f.getPath());
+ // }
+ // }
+ // }
+ // System.out.println("Cached " + files.size());
+ // setCacheInUse(true);
+ // System.out.println("Cache populated: " + System.currentTimeMillis());
+ // }
+ // }.start();
+ // }
+ //
+ // /**
+ // * Stop pre-populating the cache - our customers are ready to use it.
+ // * If there are any cache misses from this point on, we'll populate
+ // the
+ // * cache as we go.
+ // * The handover is done this way to ensure that only one thread is
+ // ever
+ // * accessing the cache, and that we minimize synchronization.
+ // */
+ // public synchronized void handover() {
+ // if (!isCacheInUse()) {
+ // requestStop();
+ // try {
+ // while (!isCacheInUse()) wait();
+ // } catch (InterruptedException intEx) { } // just continue
+ // }
+ // }
+
+ public String get(File f) {
+ // if (!cacheInUse) { // unsynchronized test - should never be
+ // parallel
+ // // threads at this point
+ // throw new IllegalStateException(
+ // "Must take ownership of cache before using by calling " +
+ // "handover()");
+ // }
+ String ret = pathMap.get(f.getPath());
+ if (ret == null) {
+ try {
+ ret = f.getCanonicalPath();
+ } catch (IOException ioEx) {
+ ret = f.getPath();
+ }
+ pathMap.put(f.getPath(), ret);
+ if (pathMap.size() > MAX_SIZE) {
+ pathMap.clear();
+ }
+ }
+ return ret;
+ }
+ }
+
+ // SECRETAPI
+ public static void setReporting(String filename, boolean dModel, boolean dRels, boolean dDeltaProcessing, boolean deletefile) {
+ reporting = true;
+ dumpModel = dModel;
+ dumpRelationships = dRels;
+ dumpDeltaProcessing = dDeltaProcessing;
+ if (deletefile) {
+ new File(filename).delete();
+ }
+ dumpFilename = filename;
+ }
+
+ public static void setReporting(String filename, boolean dModel, boolean dRels, boolean dDeltaProcessing, boolean deletefile,
+ IModelFilter aFilter) {
+ setReporting(filename, dModel, dRels, dDeltaProcessing, deletefile);
+ modelFilter = aFilter;
+ }
+
+ public static boolean isReporting() {
+ return reporting;
+ }
+
+ public static void setDontReport() {
+ reporting = false;
+ dumpDeltaProcessing = false;
+ dumpModel = false;
+ dumpRelationships = false;
+ }
+
+ // NB. If the format of this report changes then the model tests
+ // (@see org.aspectj.systemtest.model.ModelTestCase) will fail in
+ // their comparison. The tests are assuming that both the model
+ // and relationship map are reported and as a consequence single
+ // testcases test that both the model and relationship map are correct.
+ public void reportModelInfo(String reasonForReport) {
+ if (!dumpModel && !dumpRelationships) {
+ return;
+ }
+ try {
+ FileWriter fw = new FileWriter(dumpFilename, true);
+ BufferedWriter bw = new BufferedWriter(fw);
+ if (dumpModel) {
+ bw.write("=== MODEL STATUS REPORT ========= " + reasonForReport + "\n");
+ dumptree(bw, hierarchy.getRoot(), 0);
+
+ bw.write("=== END OF MODEL REPORT =========\n");
+ }
+ if (dumpRelationships) {
+ bw.write("=== RELATIONSHIPS REPORT ========= " + reasonForReport + "\n");
+ dumprels(bw);
+ bw.write("=== END OF RELATIONSHIPS REPORT ==\n");
+ }
+ Properties p = summarizeModel().getProperties();
+ Enumeration<Object> pkeyenum = p.keys();
+ bw.write("=== Properties of the model and relationships map =====\n");
+ while (pkeyenum.hasMoreElements()) {
+ String pkey = (String) pkeyenum.nextElement();
+ bw.write(pkey + "=" + p.getProperty(pkey) + "\n");
+ }
+ bw.flush();
+ fw.close();
+ } catch (IOException e) {
+ System.err.println("InternalError: Unable to report model information:");
+ e.printStackTrace();
+ }
+ }
+
+ public static void dumptree(Writer w, IProgramElement node, int indent) throws IOException {
+ for (int i = 0; i < indent; i++) {
+ w.write(" ");
+ }
+ String loc = "";
+ if (node != null) {
+ if (node.getSourceLocation() != null) {
+ loc = node.getSourceLocation().toString();
+ if (modelFilter != null) {
+ loc = modelFilter.processFilelocation(loc);
+ }
+ }
+ }
+ w.write(node + " [" + (node == null ? "null" : node.getKind().toString()) + "] " + loc + "\n");
+ if (node != null) {
+ for (IProgramElement child : node.getChildren()) {
+ dumptree(w, child, indent + 2);
+ }
+ }
+ }
+
+ public static void dumptree(IProgramElement node, int indent) throws IOException {
+ for (int i = 0; i < indent; i++) {
+ System.out.print(" ");
+ }
+ String loc = "";
+ if (node != null) {
+ if (node.getSourceLocation() != null) {
+ loc = node.getSourceLocation().toString();
+ }
+ }
+ System.out.println(node + " [" + (node == null ? "null" : node.getKind().toString()) + "] " + loc);
+ if (node != null) {
+ for (IProgramElement child : node.getChildren()) {
+ dumptree(child, indent + 2);
+ }
+ }
+ }
+
+ public void dumprels(Writer w) throws IOException {
+ int ctr = 1;
+ Set<String> entries = mapper.getEntries();
+ for (String hid : entries) {
+ List<IRelationship> rels = mapper.get(hid);
+ for (IRelationship ir : rels) {
+ List<String> targets = ir.getTargets();
+ for (String thid : targets) {
+ StringBuffer sb = new StringBuffer();
+ if (modelFilter == null || modelFilter.wantsHandleIds()) {
+ sb.append("Hid:" + (ctr++) + ":");
+ }
+ sb.append("(targets=" + targets.size() + ") " + hid + " (" + ir.getName() + ") " + thid + "\n");
+ w.write(sb.toString());
+ }
+ }
+ }
+ }
+
+ private void dumprelsStderr(String key) {
+ System.err.println("Relationships dump follows: " + key);
+ int ctr = 1;
+ Set<String> entries = mapper.getEntries();
+ for (String hid : entries) {
+ for (IRelationship ir : mapper.get(hid)) {
+ List<String> targets = ir.getTargets();
+ for (String thid : targets) {
+ System.err.println("Hid:" + (ctr++) + ":(targets=" + targets.size() + ") " + hid + " (" + ir.getName() + ") "
+ + thid);
+ }
+ }
+ }
+ System.err.println("End of relationships dump for: " + key);
+ }
+
+ // ===================== DELTA PROCESSING CODE ============== start
+ // ==========//
+
+ /**
+ * Removes the hierarchy structure for the specified files from the structure model. Returns true if it deleted anything
+ */
+ public boolean removeStructureModelForFiles(Writer fw, Collection<File> files) throws IOException {
+
+ boolean modelModified = false;
+
+ Set<String> deletedNodes = new HashSet<String>();
+ for (File fileForCompilation : files) {
+ String correctedPath = getCanonicalFilePath(fileForCompilation);
+ IProgramElement progElem = (IProgramElement) hierarchy.findInFileMap(correctedPath);
+ if (progElem != null) {
+ // Found it, let's remove it
+ if (dumpDeltaProcessing) {
+ fw.write("Deleting " + progElem + " node for file " + fileForCompilation + "\n");
+ }
+ removeNode(progElem);
+ lastBuildChanges.add(fileForCompilation);
+ deletedNodes.add(getCanonicalFilePath(progElem.getSourceLocation().getSourceFile()));
+ if (!hierarchy.removeFromFileMap(correctedPath)) {
+ throw new RuntimeException("Whilst repairing model, couldn't remove entry for file: " + correctedPath
+ + " from the filemap");
+ }
+ modelModified = true;
+ }
+ }
+ if (modelModified) {
+ hierarchy.updateHandleMap(deletedNodes);
+ }
+ return modelModified;
+ }
+
+ public void processDelta(Collection<File> files_tobecompiled, Set<File> files_added, Set<File> files_deleted) {
+
+ try {
+ Writer fw = null;
+
+ // Are we recording this ?
+ if (dumpDeltaProcessing) {
+ FileWriter filew = new FileWriter(dumpFilename, true);
+ fw = new BufferedWriter(filew);
+ fw.write("=== Processing delta changes for the model ===\n");
+ fw.write("Files for compilation:#" + files_tobecompiled.size() + ":" + files_tobecompiled + "\n");
+ fw.write("Files added :#" + files_added.size() + ":" + files_added + "\n");
+ fw.write("Files deleted :#" + files_deleted.size() + ":" + files_deleted + "\n");
+ }
+
+ long stime = System.currentTimeMillis();
+
+ // Let's remove all the files that are deleted on this compile
+ removeStructureModelForFiles(fw, files_deleted);
+ long etime1 = System.currentTimeMillis(); // etime1-stime = time to
+ // fix up the model
+
+ repairRelationships(fw);
+ long etime2 = System.currentTimeMillis(); // etime2-stime = time to
+ // repair the
+ // relationship map
+
+ removeStructureModelForFiles(fw, files_tobecompiled);
+
+ if (dumpDeltaProcessing) {
+ fw.write("===== Delta Processing timing ==========\n");
+ fw.write("Hierarchy=" + (etime1 - stime) + "ms Relationshipmap=" + (etime2 - etime1) + "ms\n");
+ fw.write("===== Traversal ========================\n");
+ // fw.write("Source handles processed="+srchandlecounter+"\n");
+ // fw.write("Target handles processed="+tgthandlecounter+"\n");
+ fw.write("========================================\n");
+ fw.flush();
+ fw.close();
+
+ }
+ reportModelInfo("After delta processing");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private String getTypeNameFromHandle(String handle, Map<String, String> cache) {
+ try {
+ String typename = cache.get(handle);
+ if (typename != null) {
+ return typename;
+ }
+ // inpath handle - but for which type?
+ // let's do it the slow way, we can optimize this with a cache perhaps
+ int hasPackage = handle.indexOf(HandleProviderDelimiter.PACKAGEFRAGMENT.getDelimiter());
+ int typeLocation = handle.indexOf(HandleProviderDelimiter.TYPE.getDelimiter());
+ if (typeLocation == -1) {
+ typeLocation = handle.indexOf(HandleProviderDelimiter.ASPECT_TYPE.getDelimiter());
+ }
+ if (typeLocation == -1) {
+ // unexpected - time to give up
+ return "";
+ }
+ StringBuffer qualifiedTypeNameFromHandle = new StringBuffer();
+ if (hasPackage != -1) {
+ int classfileLoc = handle.indexOf(HandleProviderDelimiter.CLASSFILE.getDelimiter(), hasPackage);
+ qualifiedTypeNameFromHandle.append(handle.substring(hasPackage + 1, classfileLoc));
+ qualifiedTypeNameFromHandle.append('.');
+ }
+ qualifiedTypeNameFromHandle.append(handle.substring(typeLocation + 1));
+ typename = qualifiedTypeNameFromHandle.toString();
+ cache.put(handle, typename);
+ return typename;
+ } catch (StringIndexOutOfBoundsException sioobe) {
+ // debug for 330170
+ System.err.println("Handle processing problem, the handle is: " + handle);
+ sioobe.printStackTrace(System.err);
+ return "";
+ }
+ }
+
+ /**
+ * two kinds of relationships
+ *
+ * A affects B B affectedBy A
+ *
+ * Both of these relationships are added when 'B' is modified. Concrete examples are 'advises/advisedby' or
+ * 'annotates/annotatedby'.
+ *
+ * What we need to do is when 'B' is going to be woven, remove all relationships that may reoccur when it is woven. So - remove
+ * 'affects' relationships where the target is 'B', remove all 'affectedBy' relationships where the source is 'B'.
+ *
+ */
+ public void removeRelationshipsTargettingThisType(String typename) {
+ boolean debug = false;
+ if (debug) {
+ System.err.println(">>removeRelationshipsTargettingThisType " + typename);
+ }
+ String pkg = null;
+ String type = typename;
+ int lastSep = typename.lastIndexOf('.');
+ if (lastSep != -1) {
+ pkg = typename.substring(0, lastSep);
+ type = typename.substring(lastSep + 1);
+ }
+ boolean didsomething = false;
+ IProgramElement typeNode = hierarchy.findElementForType(pkg, type);
+
+ // Reasons for that being null:
+ // 1. the file has fundamental errors and so doesn't exist in the model
+ // (-proceedOnError probably forced us to weave)
+ if (typeNode == null) {
+ return;
+ }
+
+ Set<String> sourcesToRemove = new HashSet<String>();
+ Map<String, String> handleToTypenameCache = new HashMap<String, String>();
+ // Iterate over the source handles in the relationships map, the aim
+ // here is to remove any 'affected by'
+ // relationships where the source of the relationship is the specified
+ // type (since it will be readded
+ // when the type is woven)
+ Set<String> sourcehandlesSet = mapper.getEntries();
+ List<IRelationship> relationshipsToRemove = new ArrayList<IRelationship>();
+ for (String hid : sourcehandlesSet) {
+ if (isPhantomHandle(hid)) {
+ // inpath handle - but for which type?
+ // TODO promote cache for reuse during one whole model update
+ if (!getTypeNameFromHandle(hid, handleToTypenameCache).equals(typename)) {
+ continue;
+ }
+ }
+ IProgramElement sourceElement = hierarchy.getElement(hid);
+ if (sourceElement == null || sameType(hid, sourceElement, typeNode)) {
+ // worth continuing as there may be a relationship to remove
+ relationshipsToRemove.clear();
+ List<IRelationship> relationships = mapper.get(hid);
+ for (IRelationship relationship : relationships) {
+ if (relationship.getKind() == IRelationship.Kind.USES_POINTCUT) {
+ continue; // these relationships are added at compile
+ }
+ // time, argh
+ if (relationship.isAffects()) {
+ continue; // we want 'affected by' relationships - (e.g.
+ }
+ // advised by)
+ relationshipsToRemove.add(relationship); // all the relationships can
+ // be removed, regardless of
+ // the target(s)
+ }
+ // Now, were any relationships emptied during that processing
+ // and so need removing for this source handle
+ if (relationshipsToRemove.size() > 0) {
+ didsomething = true;
+ if (relationshipsToRemove.size() == relationships.size()) {
+ sourcesToRemove.add(hid);
+ } else {
+ for (int i = 0; i < relationshipsToRemove.size(); i++) {
+ relationships.remove(relationshipsToRemove.get(i));
+ }
+ }
+ }
+ }
+ }
+ // Remove sources that have no valid relationships any more
+ for (String hid : sourcesToRemove) {
+ // System.err.println(
+ // " source handle: all relationships have gone for "+hid);
+ mapper.removeAll(hid);
+ IProgramElement ipe = hierarchy.getElement(hid);
+ if (ipe != null) {
+ // If the relationship was hanging off a 'code' node, delete it.
+ if (ipe.getKind().equals(IProgramElement.Kind.CODE)) {
+ if (debug) {
+ System.err.println(" source handle: it was code node, removing that as well... code=" + ipe + " parent="
+ + ipe.getParent());
+ }
+ removeSingleNode(ipe);
+ }
+ }
+ }
+
+ if (debug) {
+ dumprelsStderr("after processing 'affectedby'");
+ }
+ if (didsomething) { // did we do anything?
+ sourcesToRemove.clear();
+ // removing 'affects' relationships
+ if (debug) {
+ dumprelsStderr("before processing 'affects'");
+ }
+ // Iterate over the source handles in the relationships map
+ sourcehandlesSet = mapper.getEntries();
+ for (String hid : sourcehandlesSet) {
+ relationshipsToRemove.clear();
+ List<IRelationship> relationships = mapper.get(hid);
+ for (IRelationship rel : relationships) {
+ if (rel.getKind() == IRelationship.Kind.USES_POINTCUT) {
+ continue; // these relationships are added at compile
+ }
+ // time, argh
+ if (!rel.isAffects()) {
+ continue;
+ }
+ List<String> targets = rel.getTargets();
+ List<String> targetsToRemove = new ArrayList<String>();
+
+ // find targets that target the type we are interested in,
+ // they need removing
+ for (String targethid : targets) {
+ if (isPhantomHandle(hid) && !getTypeNameFromHandle(hid, handleToTypenameCache).equals(typename)) {
+ continue;
+ }
+ // Does this point to the same type?
+ IProgramElement existingTarget = hierarchy.getElement(targethid);
+ if (existingTarget == null || sameType(targethid, existingTarget, typeNode)) {
+ targetsToRemove.add(targethid);
+ }
+ }
+
+ if (targetsToRemove.size() != 0) {
+ if (targetsToRemove.size() == targets.size()) {
+ relationshipsToRemove.add(rel);
+ } else {
+ // Remove all the targets that are no longer valid
+ for (String togo : targetsToRemove) {
+ targets.remove(togo);
+ }
+ }
+ }
+ }
+ // Now, were any relationships emptied during that processing
+ // and so need removing for this source handle
+ if (relationshipsToRemove.size() > 0) {
+ // Are we removing *all* of the relationships for this
+ // source handle?
+ if (relationshipsToRemove.size() == relationships.size()) {
+ sourcesToRemove.add(hid);
+ } else {
+ for (int i = 0; i < relationshipsToRemove.size(); i++) {
+ relationships.remove(relationshipsToRemove.get(i));
+ }
+ }
+ }
+ }
+ // Remove sources that have no valid relationships any more
+ for (String hid : sourcesToRemove) {
+ // System.err.println(
+ // " source handle: all relationships have gone for "+hid);
+ mapper.removeAll(hid);
+ IProgramElement ipe = hierarchy.getElement(hid);
+ if (ipe != null) {
+ // If the relationship was hanging off a 'code' node, delete
+ // it.
+ if (ipe.getKind().equals(IProgramElement.Kind.CODE)) {
+ if (debug) {
+ System.err.println(" source handle: it was code node, removing that as well... code=" + ipe
+ + " parent=" + ipe.getParent());
+ }
+ removeSingleNode(ipe);
+ }
+ }
+ }
+ if (debug) {
+ dumprelsStderr("after processing 'affects'");
+ }
+ }
+
+ if (debug) {
+ System.err.println("<<removeRelationshipsTargettingThisFile");
+ }
+ }
+
+ /**
+ * Return true if the target element is in the type specified.
+ */
+ private boolean sameType(String hid, IProgramElement target, IProgramElement type) {
+ IProgramElement containingType = target;
+ if (target == null) {
+ throw new RuntimeException("target can't be null!");
+ }
+ if (type == null) {
+ throw new RuntimeException("type can't be null!");
+ }
+ if (target.getKind().isSourceFile() || target.getKind().isFile()) { // isFile() covers pr263487
+ // @AJ aspect with broken relationship endpoint - we couldn't find
+ // the real
+ // endpoint (the declare parents or ITD or similar) so defaulted to
+ // the
+ // first line of the source file...
+
+ // FRAGILE
+ // Let's assume the worst, and that it is the same type if the
+ // source files
+ // are the same. This will break for multiple top level types in a
+ // file...
+ if (target.getSourceLocation() == null) {
+ return false; // these four possibilities should really be FIXED
+ }
+ // so we don't have this situation
+ if (type.getSourceLocation() == null) {
+ return false;
+ }
+ if (target.getSourceLocation().getSourceFile() == null) {
+ return false;
+ }
+ if (type.getSourceLocation().getSourceFile() == null) {
+ return false;
+ }
+ return (target.getSourceLocation().getSourceFile().equals(type.getSourceLocation().getSourceFile()));
+ }
+ try {
+ while (!containingType.getKind().isType()) {
+ containingType = containingType.getParent();
+ }
+ } catch (Throwable t) {
+ // Example:
+ // java.lang.RuntimeException: Exception whilst walking up from target X.class kind=(file)
+ // hid=(=importProb/binaries<x(X.class)
+ throw new RuntimeException("Exception whilst walking up from target " + target.toLabelString() + " kind=("
+ + target.getKind() + ") hid=(" + target.getHandleIdentifier() + ")", t);
+ }
+ return (type.equals(containingType));
+ }
+
+ /**
+ * @param handle a JDT like handle, following the form described in AsmRelationshipProvider.findOrFakeUpNode
+ * @return true if the handle contains ';' - the char indicating that it is a phantom handle
+ */
+ private boolean isPhantomHandle(String handle) {
+ int phantomMarker = handle.indexOf(HandleProviderDelimiter.PHANTOM.getDelimiter());
+ return phantomMarker != -1
+ && handle.charAt(phantomMarker - 1) == HandleProviderDelimiter.PACKAGEFRAGMENTROOT.getDelimiter();
+ }
+
+ /**
+ * Go through all the relationships in the model, if any endpoints no longer exist (the node it points to has been deleted from
+ * the model) then delete the relationship.
+ */
+ private void repairRelationships(Writer fw) {
+ try {
+ // IHierarchy model = AsmManager.getDefault().getHierarchy();
+ // TODO Speed this code up by making this assumption:
+ // the only piece of the handle that is interesting is the file
+ // name. We are working at file granularity, if the
+ // file does not exist (i.e. its not in the filemap) then any handle
+ // inside that file cannot exist.
+ if (dumpDeltaProcessing) {
+ fw.write("Repairing relationships map:\n");
+ }
+
+ // Now sort out the relationships map
+ // IRelationshipMap irm = AsmManager.getDefault().getRelationshipMap();
+ Set<String> sourcesToRemove = new HashSet<String>();
+ Set<String> nonExistingHandles = new HashSet<String>(); // Cache of handles that we
+ // *know* are invalid
+// int srchandlecounter = 0;
+// int tgthandlecounter = 0;
+
+ // Iterate over the source handles in the relationships map
+ Set<String> keyset = mapper.getEntries(); // These are source handles
+ for (String hid : keyset) {
+// srchandlecounter++;
+
+ // Do we already know this handle points to nowhere?
+ if (nonExistingHandles.contains(hid)) {
+ sourcesToRemove.add(hid);
+ } else if (!isPhantomHandle(hid)) {
+ // We better check if it actually exists
+ IProgramElement existingElement = hierarchy.getElement(hid);
+ if (dumpDeltaProcessing) {
+ fw.write("Looking for handle [" + hid + "] in model, found: " + existingElement + "\n");
+ }
+ // Did we find it?
+ if (existingElement == null) {
+ // No, so delete this relationship
+ sourcesToRemove.add(hid);
+ nonExistingHandles.add(hid); // Speed up a bit you swine
+ } else {
+ // Ok, so the source is valid, what about the targets?
+ List<IRelationship> relationships = mapper.get(hid);
+ List<IRelationship> relationshipsToRemove = new ArrayList<IRelationship>();
+ // Iterate through the relationships against this source
+ // handle
+ for (Iterator<IRelationship> reliter = relationships.iterator(); reliter.hasNext();) {
+ IRelationship rel = reliter.next();
+ List<String> targets = rel.getTargets();
+ List<String> targetsToRemove = new ArrayList<String>();
+
+ // Iterate through the targets for this relationship
+ for (Iterator<String> targetIter = targets.iterator(); targetIter.hasNext();) {
+ String targethid = targetIter.next();
+// tgthandlecounter++;
+ // Do we already know it doesn't exist?
+ if (nonExistingHandles.contains(targethid)) {
+ if (dumpDeltaProcessing) {
+ fw.write("Target handle [" + targethid + "] for srchid[" + hid + "]rel[" + rel.getName()
+ + "] does not exist\n");
+ }
+ targetsToRemove.add(targethid);
+ } else if (!isPhantomHandle(targethid)) {
+ // We better check
+ IProgramElement existingTarget = hierarchy.getElement(targethid);
+ if (existingTarget == null) {
+ if (dumpDeltaProcessing) {
+ fw.write("Target handle [" + targethid + "] for srchid[" + hid + "]rel["
+ + rel.getName() + "] does not exist\n");
+ }
+ targetsToRemove.add(targethid);
+ nonExistingHandles.add(targethid);
+ }
+ }
+ }
+
+ // Do we have some targets that need removing?
+ if (targetsToRemove.size() != 0) {
+ // Are we removing *all* of the targets for this
+ // relationship (i.e. removing the relationship)
+ if (targetsToRemove.size() == targets.size()) {
+ if (dumpDeltaProcessing) {
+ fw.write("No targets remain for srchid[" + hid + "] rel[" + rel.getName()
+ + "]: removing it\n");
+ }
+ relationshipsToRemove.add(rel);
+ } else {
+ // Remove all the targets that are no longer
+ // valid
+ for (String togo : targetsToRemove) {
+ targets.remove(togo);
+ }
+ // Should have already been caught above,
+ // but lets double check ...
+ if (targets.size() == 0) {
+ if (dumpDeltaProcessing) {
+ fw.write("No targets remain for srchid[" + hid + "] rel[" + rel.getName()
+ + "]: removing it\n");
+ }
+ relationshipsToRemove.add(rel); // TODO
+ // Should
+ // only
+ // remove
+ // this
+ // relationship
+ // for
+ // the
+ // srchid
+ // ?
+ }
+ }
+ }
+ }
+ // Now, were any relationships emptied during that
+ // processing and so need removing for this source
+ // handle
+ if (relationshipsToRemove.size() > 0) {
+ // Are we removing *all* of the relationships for
+ // this source handle?
+ if (relationshipsToRemove.size() == relationships.size()) {
+ // We know they are all going to go, so just
+ // delete the source handle.
+ sourcesToRemove.add(hid);
+ } else {
+ // MEMORY LEAK - we don't remove the
+ // relationships !!
+ for (int i = 0; i < relationshipsToRemove.size(); i++) {
+ IRelationship irel = relationshipsToRemove.get(i);
+ verifyAssumption(mapper.remove(hid, irel), "Failed to remove relationship " + irel.getName()
+ + " for shid " + hid);
+ }
+ List<IRelationship> rels = mapper.get(hid);
+ if (rels == null || rels.size() == 0) {
+ sourcesToRemove.add(hid);
+ }
+ }
+ }
+ }
+ }
+ }
+ // Remove sources that have no valid relationships any more
+ for (Iterator<String> srciter = sourcesToRemove.iterator(); srciter.hasNext();) {
+ String hid = srciter.next();
+ mapper.removeAll(hid);
+ IProgramElement ipe = hierarchy.getElement(hid);
+ if (ipe != null) {
+ // If the relationship was hanging off a 'code' node, delete
+ // it.
+ if (ipe.getKind().equals(IProgramElement.Kind.CODE)) {
+ // System.err.println("Deleting code node");
+ removeSingleNode(ipe);
+ }
+ }
+ }
+ } catch (IOException ioe) {
+ System.err.println("Failed to repair relationships:");
+ ioe.printStackTrace();
+ }
+ }
+
+ /**
+ * Removes a specified program element from the structure model. We go to the parent of the program element, ask for all its
+ * children and remove the node we want to delete from the list of children.
+ */
+ private void removeSingleNode(IProgramElement progElem) {
+ if (progElem == null) {
+ throw new IllegalStateException("AsmManager.removeNode(): programElement unexpectedly null");
+ }
+ boolean deleteOK = false;
+ IProgramElement parent = progElem.getParent();
+ List<IProgramElement> kids = parent.getChildren();
+ for (int i = 0, max = kids.size(); i < max; i++) {
+ if (kids.get(i).equals(progElem)) {
+ kids.remove(i);
+ deleteOK = true;
+ break;
+ }
+ }
+ if (!deleteOK) {
+ System.err.println("unexpectedly failed to delete node from model. hid=" + progElem.getHandleIdentifier());
+ }
+ }
+
+ /**
+ * Removes a specified program element from the structure model. Two processing stages:
+ * <p>
+ * First: We go to the parent of the program element, ask for all its children and remove the node we want to delete from the
+ * list of children.
+ * <p>
+ * Second:We check if that parent has any other children. If it has no other children and it is either a CODE node or a PACKAGE
+ * node, we delete it too.
+ */
+ private void removeNode(IProgramElement progElem) {
+
+ // StringBuffer flightrecorder = new StringBuffer();
+ try {
+ // flightrecorder.append("In removeNode, about to chuck away: "+
+ // progElem+"\n");
+ if (progElem == null) {
+ throw new IllegalStateException("AsmManager.removeNode(): programElement unexpectedly null");
+ }
+ // boolean deleteOK = false;
+ IProgramElement parent = progElem.getParent();
+ // flightrecorder.append("Parent of it is "+parent+"\n");
+ List<IProgramElement> kids = parent.getChildren();
+ // flightrecorder.append("Which has "+kids.size()+" kids\n");
+ for (int i = 0; i < kids.size(); i++) {
+ // flightrecorder.append("Comparing with "+kids.get(i)+"\n");
+ if (kids.get(i).equals(progElem)) {
+ kids.remove(i);
+ // flightrecorder.append("Removing it\n");
+ // deleteOK=true;
+ break;
+ }
+ }
+ // verifyAssumption(deleteOK,flightrecorder.toString());
+ // Are there any kids left for this node?
+ if (parent.getChildren().size() == 0
+ && parent.getParent() != null
+ && (parent.getKind().equals(IProgramElement.Kind.CODE) || parent.getKind().equals(IProgramElement.Kind.PACKAGE))) {
+ // This node is on its own, we should trim it too *as long as
+ // its not a structural node* which we currently check by
+ // making sure its a code node
+ // We should trim if it
+ // System.err.println("Deleting parent:"+parent);
+ removeNode(parent);
+ }
+ } catch (NullPointerException npe) {
+ // Occurred when commenting out other 2 ras classes in wsif??
+ // reproducable?
+ // System.err.println(flightrecorder.toString());
+ npe.printStackTrace();
+ }
+ }
+
+ public static void verifyAssumption(boolean b, String info) {
+ if (!b) {
+ System.err.println("=========== ASSERTION IS NOT TRUE =========v");
+ System.err.println(info);
+ Thread.dumpStack();
+ System.err.println("=========== ASSERTION IS NOT TRUE =========^");
+ throw new RuntimeException("Assertion is false");
+ }
+ }
+
+ public static void verifyAssumption(boolean b) {
+ if (!b) {
+ Thread.dumpStack();
+ throw new RuntimeException("Assertion is false");
+ }
+ }
+
+ // ===================== DELTA PROCESSING CODE ============== end
+ // ==========//
+
+ /**
+ * A ModelInfo object captures basic information about the structure model. It is used for testing and producing debug info.
+ */
+ public static class ModelInfo {
+ private final Hashtable<String, Integer> nodeTypeCount = new Hashtable<String, Integer>();
+ private final Properties extraProperties = new Properties();
+
+ private ModelInfo(IHierarchy hierarchy, IRelationshipMap relationshipMap) {
+ IProgramElement ipe = hierarchy.getRoot();
+ walkModel(ipe);
+ recordStat("FileMapSize", new Integer(hierarchy.getFileMapEntrySet().size()).toString());
+ recordStat("RelationshipMapSize", new Integer(relationshipMap.getEntries().size()).toString());
+ }
+
+ private void walkModel(IProgramElement ipe) {
+ countNode(ipe);
+ for (IProgramElement child : ipe.getChildren()) {
+ walkModel(child);
+ }
+ }
+
+ private void countNode(IProgramElement ipe) {
+ String node = ipe.getKind().toString();
+ Integer ctr = nodeTypeCount.get(node);
+ if (ctr == null) {
+ nodeTypeCount.put(node, new Integer(1));
+ } else {
+ ctr = new Integer(ctr.intValue() + 1);
+ nodeTypeCount.put(node, ctr);
+ }
+ }
+
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append("Model node summary:\n");
+ Enumeration<String> nodeKeys = nodeTypeCount.keys();
+ while (nodeKeys.hasMoreElements()) {
+ String key = nodeKeys.nextElement();
+ Integer ct = nodeTypeCount.get(key);
+ sb.append(key + "=" + ct + "\n");
+ }
+ sb.append("Model stats:\n");
+ Enumeration<Object> ks = extraProperties.keys();
+ while (ks.hasMoreElements()) {
+ String k = (String) ks.nextElement();
+ String v = extraProperties.getProperty(k);
+ sb.append(k + "=" + v + "\n");
+ }
+ return sb.toString();
+ }
+
+ public Properties getProperties() {
+ Properties p = new Properties();
+ for (Map.Entry<String, Integer> entry : nodeTypeCount.entrySet()) {
+ p.setProperty(entry.getKey(), entry.getValue().toString());
+ }
+ p.putAll(extraProperties);
+ return p;
+ }
+
+ public void recordStat(String string, String string2) {
+ extraProperties.setProperty(string, string2);
+ }
+
+ }
+
+ public ModelInfo summarizeModel() {
+ return new ModelInfo(getHierarchy(), getRelationshipMap());
+ }
+
+ /**
+ * Set to indicate whether we are currently building a structure model, should be set up front.
+ */
+ // public static void setCreatingModel(boolean b) {
+ // creatingModel = b;
+ // }
+ //
+ // /**
+ // * returns true if we are currently generating a structure model, enables guarding of expensive operations on an empty/null
+ // * model.
+ // */
+ // public static boolean isCreatingModel() {
+ // return creatingModel;
+ // }
+ public static void setCompletingTypeBindings(boolean b) {
+ completingTypeBindings = b;
+ }
+
+ public static boolean isCompletingTypeBindings() {
+ return completingTypeBindings;
+ }
+
+ // public void setRelationshipMap(IRelationshipMap irm) {
+ // mapper = irm;
+ // }
+ //
+ // public void setHierarchy(IHierarchy ih) {
+ // hierarchy = ih;
+ // }
+
+ public void resetDeltaProcessing() {
+ lastBuildChanges.clear();
+ aspectsWeavingInLastBuild.clear();
+ }
+
+ /**
+ * @return the Set of files for which the structure model was modified (they may have been removed or otherwise rebuilt). Set is
+ * empty for a full build.
+ */
+ public Set<File> getModelChangesOnLastBuild() {
+ return lastBuildChanges;
+ }
+
+ /**
+ * @return the Set of aspects that wove files on the last build (either incremental or full build)
+ */
+ public Set<File> getAspectsWeavingFilesOnLastBuild() {
+ return aspectsWeavingInLastBuild;
+ }
+
+ public void addAspectInEffectThisBuild(File f) {
+ aspectsWeavingInLastBuild.add(f);
+ }
+
+ public static void setLastActiveStructureModel(AsmManager structureModel) {
+ if (recordingLastActiveStructureModel) {
+ lastActiveStructureModel = structureModel;
+ }
+ }
+
+ public String getHandleElementForInpath(String binaryPath) {
+ return inpathMap.get(new File(binaryPath));
+ }
+}
diff --git a/asm/src/main/java/org/aspectj/asm/HierarchyWalker.java b/asm/src/main/java/org/aspectj/asm/HierarchyWalker.java
new file mode 100644
index 000000000..7629f7c68
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/HierarchyWalker.java
@@ -0,0 +1,37 @@
+/* *******************************************************************
+ * Copyright (c) 2003,2010 Contributors
+ * 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:
+ * Mik Kersten initial implementation
+ * Andy Clement
+ * ******************************************************************/
+
+package org.aspectj.asm;
+
+/**
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public abstract class HierarchyWalker {
+
+ public HierarchyWalker() {
+ }
+
+ protected void preProcess(IProgramElement node) {
+ }
+
+ protected void postProcess(IProgramElement node) {
+ }
+
+ public IProgramElement process(IProgramElement node) {
+ preProcess(node);
+ node.walk(this);
+ postProcess(node);
+ return node;
+ }
+}
diff --git a/asm/src/main/java/org/aspectj/asm/IElementHandleProvider.java b/asm/src/main/java/org/aspectj/asm/IElementHandleProvider.java
new file mode 100644
index 000000000..c071f0337
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/IElementHandleProvider.java
@@ -0,0 +1,65 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * ******************************************************************/
+
+package org.aspectj.asm;
+
+import java.io.File;
+
+import org.aspectj.bridge.ISourceLocation;
+
+/**
+ * Adapter used to uniquely identify program element handles. Can be implemented and overridden in @see{AsmManager} in order to
+ * provide IDE-specific mechanisms of identifying elements. For example, AJDT uses workspace-relative paths that are understood by
+ * its JavaCore class.
+ *
+ * @author Mik Kersten
+ */
+public interface IElementHandleProvider {
+
+ /**
+ * @return a String uniquely identifying this element
+ */
+ public String createHandleIdentifier(ISourceLocation location);
+
+ /**
+ * @return a String uniquely identifying this element
+ */
+ public String createHandleIdentifier(File sourceFile, int line, int column, int offset);
+
+ /**
+ * @return a String uniquely identifying this element
+ */
+ public String createHandleIdentifier(IProgramElement ipe);
+
+ /**
+ * NOTE: this is necessary for the current implementation to look up nodes, but we may want to consider removing it.
+ *
+ * @return a String corresponding to the
+ */
+ public String getFileForHandle(String handle);
+
+ /**
+ * NOTE: this is necessary for the current implementation to look up nodes, but we may want to consider removing it.
+ *
+ * @return the line number corresponding to this handel
+ */
+ public int getLineNumberForHandle(String handle);
+
+ public int getOffSetForHandle(String handle);
+
+ /**
+ * Initializes handle provider state.
+ *
+ * The initializer is invoked when a new ASM is created on a full build.
+ */
+ public void initialize();
+}
diff --git a/asm/src/main/java/org/aspectj/asm/IHierarchy.java b/asm/src/main/java/org/aspectj/asm/IHierarchy.java
new file mode 100644
index 000000000..6674cdaea
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/IHierarchy.java
@@ -0,0 +1,127 @@
+/* *******************************************************************
+ * Copyright (c) 2003,2010 Contributors
+ * 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:
+ * Mik Kersten initial implementation
+ * Andy Clement
+ * ******************************************************************/
+package org.aspectj.asm;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.aspectj.asm.internal.ProgramElement;
+import org.aspectj.bridge.ISourceLocation;
+
+/**
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public interface IHierarchy extends Serializable {
+
+ public static final IProgramElement NO_STRUCTURE = new ProgramElement(null, "<build to view structure>",
+ IProgramElement.Kind.ERROR, null);
+
+ public IProgramElement getElement(String handle);
+
+ public IProgramElement getRoot();
+
+ public void setRoot(IProgramElement root);
+
+ public void addToFileMap(String canonicalFilePath, IProgramElement compilationUnitProgramElement);
+
+ public boolean removeFromFileMap(String canonicalFilePath);
+
+ public void setFileMap(HashMap<String, IProgramElement> fileMap);
+
+ public Object findInFileMap(Object key);
+
+ public Set<Map.Entry<String, IProgramElement>> getFileMapEntrySet();
+
+ public boolean isValid();
+
+ public IProgramElement findElementForHandle(String handle);
+
+ public IProgramElement findElementForHandleOrCreate(String handle, boolean create);
+
+ /**
+ * Returns the first match
+ *
+ * @param parent
+ * @param kind not null
+ * @return null if not found
+ */
+ public IProgramElement findElementForSignature(IProgramElement parent, IProgramElement.Kind kind, String signature);
+
+ /**
+ * Returns the first match
+ *
+ * @param parent
+ * @param kind not null
+ * @return null if not found
+ */
+ public IProgramElement findElementForLabel(IProgramElement parent, IProgramElement.Kind kind, String label);
+
+ /**
+ * @param packageName if null default package is searched
+ * @param className can't be null
+ */
+ public IProgramElement findElementForType(String packageName, String typeName);
+
+ /**
+ * @param sourceFilePath modified to '/' delimited path for consistency
+ * @return a new structure node for the file if it was not found in the model
+ */
+ public IProgramElement findElementForSourceFile(String sourceFile);
+
+ /**
+ * TODO: discriminate columns
+ */
+ public IProgramElement findElementForSourceLine(ISourceLocation location);
+
+ /**
+ * Never returns null
+ *
+ * @param sourceFilePath canonicalized path for consistency
+ * @param lineNumber if 0 or 1 the corresponding file node will be returned
+ * @return a new structure node for the file if it was not found in the model
+ */
+ public IProgramElement findElementForSourceLine(String sourceFilePath, int lineNumber);
+
+ public IProgramElement findElementForOffSet(String sourceFilePath, int lineNumber, int offSet);
+
+ public String getConfigFile();
+
+ public void setConfigFile(String configFile);
+
+ public void flushTypeMap();
+
+ public void flushHandleMap();
+
+ public void updateHandleMap(Set<String> deletedFiles);
+
+ /**
+ * For a specified node, check if any of the children more accurately represent the specified line.
+ *
+ * @param node where to start looking
+ * @param lineno the line number
+ * @return any closer match below 'node' or null if nothing is a more accurate match
+ */
+ public IProgramElement findCloserMatchForLineNumber(IProgramElement node, int lineno);
+
+ /**
+ * Discover the node representing a particular source file.
+ *
+ * @param node where in the model to start looking (usually the root on the initial call)
+ * @param sourcefilePath the source file being searched for
+ * @return the node representing that source file or null if it cannot be found
+ */
+ public IProgramElement findNodeForSourceFile(IProgramElement node, String sourcefilePath);
+} \ No newline at end of file
diff --git a/asm/src/main/java/org/aspectj/asm/IHierarchyListener.java b/asm/src/main/java/org/aspectj/asm/IHierarchyListener.java
new file mode 100644
index 000000000..12bf724da
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/IHierarchyListener.java
@@ -0,0 +1,25 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * ******************************************************************/
+
+package org.aspectj.asm;
+
+import java.util.EventListener;
+
+/**
+ * Compiler listeners get notified of structure model update events.
+ *
+ * @author Mik Kersten
+ */
+public interface IHierarchyListener extends EventListener {
+
+ public void elementsUpdated(IHierarchy rootNode);
+}
diff --git a/asm/src/main/java/org/aspectj/asm/IModelFilter.java b/asm/src/main/java/org/aspectj/asm/IModelFilter.java
new file mode 100644
index 000000000..cfae6e7b7
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/IModelFilter.java
@@ -0,0 +1,32 @@
+/* *******************************************************************
+ * Copyright (c) 2006 Contributors
+ * 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:
+ * Andy Clement IBM initial implementation
+ * ******************************************************************/
+package org.aspectj.asm;
+
+/**
+ * When dumping the model out (for debugging/testing), various parts of it can be passed through this filter. Currently it is used
+ * to ensure the source locations we dump out are independent of sandbox directory.
+ *
+ * @author Andy Clement
+ */
+public interface IModelFilter {
+
+ /**
+ * Called when about to dump out an absolute file location, enabling it to be altered (eg.
+ * c:/temp/ajcsSandbox/foo/ajctemp.12323/<BLAH> could become TEST_SANDBOX/<BLAH>
+ */
+ String processFilelocation(String loc);
+
+ /**
+ * When the relationship map is dumped, lines are prefixed with a handle ID. Return true if you want these, false if you do not.
+ */
+ boolean wantsHandleIds();
+}
diff --git a/asm/src/main/java/org/aspectj/asm/IProgramElement.java b/asm/src/main/java/org/aspectj/asm/IProgramElement.java
new file mode 100644
index 000000000..19e6d95ac
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/IProgramElement.java
@@ -0,0 +1,456 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * ******************************************************************/
+
+package org.aspectj.asm;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.ISourceLocation;
+
+/**
+ * Represents program elements in the AspectJ containment hierarchy.
+ *
+ * @author Mik Kersten
+ */
+public interface IProgramElement extends Serializable {
+
+ public List<IProgramElement> getChildren();
+
+ public void setChildren(List<IProgramElement> children);
+
+ public void addChild(IProgramElement child);
+
+ public boolean removeChild(IProgramElement child);
+
+ // Extra stuff
+ // Could be just a string but may prove more useful as an object in the long
+ // run ...
+ public static class ExtraInformation implements Serializable {
+ private static final long serialVersionUID = -3880735494840820638L;
+ private String extraInfo;
+
+ public ExtraInformation() {
+ extraInfo = "";
+ }
+
+ public void setExtraAdviceInformation(String string) {
+ extraInfo = string;
+ }
+
+ public String getExtraAdviceInformation() {
+ return extraInfo;
+ }
+
+ public String toString() {
+ return "ExtraInformation: [" + extraInfo + "]";
+ }
+ }
+
+ public void setExtraInfo(ExtraInformation info);
+
+ public ExtraInformation getExtraInfo();
+
+ public IProgramElement getParent();
+
+ public void setParent(IProgramElement parent);
+
+ public void setParentTypes(List<String> parentTypes);
+
+ public List<String> getParentTypes();
+
+ public String getName();
+
+ public void setName(String name);
+
+ public String getDetails();
+
+ public void setDetails(String details);
+
+ public IProgramElement.Kind getKind();
+
+ public void setKind(Kind kind);
+
+ public List<IProgramElement.Modifiers> getModifiers();
+
+ public void setModifiers(int i);
+
+ public Accessibility getAccessibility();
+
+ public String getDeclaringType(); // TODO: remove (Emacs uses it)
+
+ public String getPackageName();
+
+ /**
+ * @param method
+ * return types or field types
+ */
+ public void setCorrespondingType(String returnType);
+
+ /**
+ * This correponds to both method return types and field types.
+ */
+ public String getCorrespondingType();
+
+ public String getCorrespondingType(boolean getFullyQualifiedType);
+
+ public String toSignatureString();
+
+ public String toSignatureString(boolean getFullyQualifiedArgTypes);
+
+ public void setRunnable(boolean value);
+
+ public boolean isRunnable();
+
+ public boolean isImplementor();
+
+ public void setImplementor(boolean value);
+
+ public boolean isOverrider();
+
+ public void setOverrider(boolean value);
+
+ public IMessage getMessage();
+
+ public void setMessage(IMessage message);
+
+ public ISourceLocation getSourceLocation();
+
+ public void setSourceLocation(ISourceLocation sourceLocation);
+
+ public String toString();
+
+ /**
+ * @return the javadoc comment for this program element, null if not available
+ */
+ public String getFormalComment();
+
+ public void setFormalComment(String comment);
+
+ /**
+ * Includes information about the origin of the node.
+ */
+ public String toLinkLabelString();
+
+ public String toLinkLabelString(boolean getFullyQualifiedArgTypes);
+
+ /**
+ * Includes name, parameter types (if any) and details (if any).
+ */
+ public String toLabelString();
+
+ public String toLabelString(boolean getFullyQualifiedArgTypes);
+
+ public List<String> getParameterNames();
+
+ public void setParameterNames(List<String> list);
+
+ public List<char[]> getParameterSignatures();
+
+ public List<String> getParameterSignaturesSourceRefs();
+
+ public void setParameterSignatures(List<char[]> list, List<String> paramSourceRefs);
+
+ public List<char[]> getParameterTypes();
+
+ /**
+ * The format of the string handle is not specified, but is stable across compilation sessions.
+ *
+ * @return a string representation of this element
+ */
+ public String getHandleIdentifier();
+
+ public String getHandleIdentifier(boolean create);
+
+ public void setHandleIdentifier(String handle);
+
+ /**
+ * @return a string representation of this node and all of its children (recursive)
+ */
+ public String toLongString();
+
+ public String getBytecodeName();
+
+ public String getBytecodeSignature();
+
+ public void setBytecodeName(String bytecodeName);
+
+ public void setBytecodeSignature(String bytecodeSignature);
+
+ /**
+ * @return the full signature of this element, as it appears in the source
+ */
+ public String getSourceSignature();
+
+ public void setSourceSignature(String string);
+
+ public IProgramElement walk(HierarchyWalker walker);
+
+ public AsmManager getModel();
+
+ public int getRawModifiers();
+
+ /**
+ * Uses "typesafe enum" pattern.
+ */
+ public static class Modifiers implements Serializable {
+
+ private static final long serialVersionUID = -8279300899976607927L;
+
+ public static final Modifiers STATIC = new Modifiers("static", 0x0008);
+ public static final Modifiers FINAL = new Modifiers("final", 0x0010);
+ public static final Modifiers ABSTRACT = new Modifiers("abstract", 0x0400);
+ public static final Modifiers SYNCHRONIZED = new Modifiers("synchronized", 0x0020);
+ public static final Modifiers VOLATILE = new Modifiers("volatile", 0x0040);
+ public static final Modifiers STRICTFP = new Modifiers("strictfp", 0x0800);
+ public static final Modifiers TRANSIENT = new Modifiers("transient", 0x0080);
+ public static final Modifiers NATIVE = new Modifiers("native", 0x0100);
+ public static final Modifiers[] ALL = { STATIC, FINAL, ABSTRACT, SYNCHRONIZED, VOLATILE, STRICTFP, TRANSIENT, NATIVE };
+ private final String name;
+ private final int bit;
+
+ private Modifiers(String name, int bit) {
+ this.name = name;
+ this.bit = bit;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public int getBit() {
+ return bit;
+ }
+
+ // The 4 declarations below are necessary for serialization
+ private static int nextOrdinal = 0;
+ private final int ordinal = nextOrdinal++;
+
+ private Object readResolve() throws ObjectStreamException {
+ return ALL[ordinal];
+ }
+ }
+
+ /**
+ * Uses "typesafe enum" pattern.
+ */
+ public static class Accessibility implements Serializable {
+
+ private static final long serialVersionUID = 5371838588180918519L;
+
+ public static final Accessibility PUBLIC = new Accessibility("public");
+ public static final Accessibility PACKAGE = new Accessibility("package");
+ public static final Accessibility PROTECTED = new Accessibility("protected");
+ public static final Accessibility PRIVATE = new Accessibility("private");
+ public static final Accessibility PRIVILEGED = new Accessibility("privileged");
+ public static final Accessibility[] ALL = { PUBLIC, PACKAGE, PROTECTED, PRIVATE, PRIVILEGED };
+ private final String name;
+
+ private Accessibility(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ // The 4 declarations below are necessary for serialization
+ private static int nextOrdinal = 0;
+ private final int ordinal = nextOrdinal++;
+
+ private Object readResolve() throws ObjectStreamException {
+ return ALL[ordinal];
+ }
+ }
+
+ /**
+ * Uses "typesafe enum" pattern.
+ */
+ public static class Kind implements Serializable {
+
+ private static final long serialVersionUID = -1963553877479266124L;
+
+ public static final Kind PROJECT = new Kind("project");
+ public static final Kind PACKAGE = new Kind("package");
+ public static final Kind FILE = new Kind("file");
+ public static final Kind FILE_JAVA = new Kind("java source file");
+ public static final Kind FILE_ASPECTJ = new Kind("aspect source file");
+ public static final Kind FILE_LST = new Kind("build configuration file");
+ public static final Kind IMPORT_REFERENCE = new Kind("import reference");
+ public static final Kind CLASS = new Kind("class");
+ public static final Kind INTERFACE = new Kind("interface");
+ public static final Kind ASPECT = new Kind("aspect");
+ public static final Kind ENUM = new Kind("enum");
+ public static final Kind ENUM_VALUE = new Kind("enumvalue");
+ public static final Kind ANNOTATION = new Kind("annotation");
+ public static final Kind INITIALIZER = new Kind("initializer");
+ public static final Kind INTER_TYPE_FIELD = new Kind("inter-type field");
+ public static final Kind INTER_TYPE_METHOD = new Kind("inter-type method");
+ public static final Kind INTER_TYPE_CONSTRUCTOR = new Kind("inter-type constructor");
+ public static final Kind INTER_TYPE_PARENT = new Kind("inter-type parent");
+ public static final Kind CONSTRUCTOR = new Kind("constructor");
+ public static final Kind METHOD = new Kind("method");
+ public static final Kind FIELD = new Kind("field");
+ public static final Kind POINTCUT = new Kind("pointcut");
+ public static final Kind ADVICE = new Kind("advice");
+ public static final Kind DECLARE_PARENTS = new Kind("declare parents");
+ public static final Kind DECLARE_WARNING = new Kind("declare warning");
+ public static final Kind DECLARE_ERROR = new Kind("declare error");
+ public static final Kind DECLARE_SOFT = new Kind("declare soft");
+ public static final Kind DECLARE_PRECEDENCE = new Kind("declare precedence");
+ public static final Kind CODE = new Kind("code");
+ public static final Kind ERROR = new Kind("error");
+ public static final Kind DECLARE_ANNOTATION_AT_CONSTRUCTOR = new Kind("declare @constructor");
+ public static final Kind DECLARE_ANNOTATION_AT_FIELD = new Kind("declare @field");
+ public static final Kind DECLARE_ANNOTATION_AT_METHOD = new Kind("declare @method");
+ public static final Kind DECLARE_ANNOTATION_AT_TYPE = new Kind("declare @type");
+ public static final Kind SOURCE_FOLDER = new Kind("source folder");
+ public static final Kind PACKAGE_DECLARATION = new Kind("package declaration");
+
+ public static final Kind[] ALL = { PROJECT, PACKAGE, FILE, FILE_JAVA, FILE_ASPECTJ, FILE_LST, IMPORT_REFERENCE, CLASS,
+ INTERFACE, ASPECT, ENUM, ENUM_VALUE, ANNOTATION, INITIALIZER, INTER_TYPE_FIELD, INTER_TYPE_METHOD,
+ INTER_TYPE_CONSTRUCTOR, INTER_TYPE_PARENT, CONSTRUCTOR, METHOD, FIELD, POINTCUT, ADVICE, DECLARE_PARENTS,
+ DECLARE_WARNING, DECLARE_ERROR, DECLARE_SOFT, DECLARE_PRECEDENCE, CODE, ERROR, DECLARE_ANNOTATION_AT_CONSTRUCTOR,
+ DECLARE_ANNOTATION_AT_FIELD, DECLARE_ANNOTATION_AT_METHOD, DECLARE_ANNOTATION_AT_TYPE, SOURCE_FOLDER,
+ PACKAGE_DECLARATION
+
+ };
+
+ public static Kind getKindForString(String kindString) {
+ for (int i = 0; i < ALL.length; i++) {
+ if (ALL[i].toString().equals(kindString)) {
+ return ALL[i];
+ }
+ }
+ return ERROR;
+ }
+
+ private final String name;
+
+ private Kind(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public static List<Kind> getNonAJMemberKinds() {
+ List<Kind> list = new ArrayList<Kind>();
+ list.add(METHOD);
+ list.add(ENUM_VALUE);
+ list.add(FIELD);
+ list.add(CONSTRUCTOR);
+ return list;
+ }
+
+ public boolean isMember() {
+ return this == FIELD || this == METHOD || this == CONSTRUCTOR || this == POINTCUT || this == ADVICE
+ || this == ENUM_VALUE;
+ }
+
+ public boolean isInterTypeMember() {
+ return this == INTER_TYPE_CONSTRUCTOR || this == INTER_TYPE_FIELD || this == INTER_TYPE_METHOD;
+ }
+
+ public boolean isType() {
+ return this == CLASS || this == INTERFACE || this == ASPECT || this == ANNOTATION || this == ENUM;
+ }
+
+ public boolean isSourceFile() {
+ return this == FILE_ASPECTJ || this == FILE_JAVA;
+ }
+
+ public boolean isFile() {
+ return this == FILE;
+ }
+
+ public boolean isDeclare() {
+ return name.startsWith("declare");
+ }
+
+ public boolean isDeclareAnnotation() {
+ return name.startsWith("declare @");
+ }
+
+ public boolean isDeclareParents() {
+ return name.startsWith("declare parents");
+ }
+
+ public boolean isDeclareSoft() {
+ return name.startsWith("declare soft");
+ }
+
+ public boolean isDeclareWarning() {
+ return name.startsWith("declare warning");
+ }
+
+ public boolean isDeclareError() {
+ return name.startsWith("declare error");
+ }
+
+ public boolean isDeclarePrecedence() {
+ return name.startsWith("declare precedence");
+ }
+
+ // The 4 declarations below are necessary for serialization
+ private static int nextOrdinal = 0;
+ private final int ordinal = nextOrdinal++;
+
+ private Object readResolve() throws ObjectStreamException {
+ return ALL[ordinal];
+ }
+
+ public boolean isPackageDeclaration() {
+ return this == PACKAGE_DECLARATION;
+ }
+
+ }
+
+ public void setAnnotationStyleDeclaration(boolean b);
+
+ public boolean isAnnotationStyleDeclaration();
+
+ /**
+ * @param fullyQualifiedannotationType
+ * the annotation type, eg. p.q.r.Foo
+ */
+ public void setAnnotationType(String fullyQualifiedannotationType);
+
+ /**
+ * @return the fully qualified annotation type, eg. p.q.r.Foo
+ */
+ public String getAnnotationType();
+
+ public String[] getRemovedAnnotationTypes();
+
+ public Map<String, List<String>> getDeclareParentsMap();
+
+ public void setDeclareParentsMap(Map<String, List<String>> newmap);
+
+ public void addFullyQualifiedName(String fqname);
+
+ public String getFullyQualifiedName();
+
+ public void setAnnotationRemover(boolean isRemover);
+
+ public boolean isAnnotationRemover();
+
+ /**
+ * @return the return type of a method or type of a field in signature form (e.g. Ljava/lang/String;)
+ */
+ public String getCorrespondingTypeSignature();
+} \ No newline at end of file
diff --git a/asm/src/main/java/org/aspectj/asm/IRelationship.java b/asm/src/main/java/org/aspectj/asm/IRelationship.java
new file mode 100644
index 000000000..86633cf36
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/IRelationship.java
@@ -0,0 +1,103 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * ******************************************************************/
+package org.aspectj.asm;
+
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * A relationship has a name (e.g. 'declare warning') and some set of affected targets.
+ *
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public interface IRelationship extends Serializable {
+
+ public String getName();
+
+ public Kind getKind();
+
+ public void addTarget(String handle);
+
+ public List<String> getTargets();
+
+ public String getSourceHandle();
+
+ public boolean hasRuntimeTest();
+
+ public boolean isAffects();
+
+ public static class Kind implements Serializable { // typesafe enum
+
+ private static final long serialVersionUID = -2691351740214705220L;
+
+ public static final Kind DECLARE_WARNING = new Kind("declare warning");
+ public static final Kind DECLARE_ERROR = new Kind("declare error");
+ public static final Kind ADVICE_AROUND = new Kind("around advice");
+ public static final Kind ADVICE_AFTERRETURNING = new Kind("after returning advice");
+ public static final Kind ADVICE_AFTERTHROWING = new Kind("after throwing advice");
+ public static final Kind ADVICE_AFTER = new Kind("after advice");
+ public static final Kind ADVICE_BEFORE = new Kind("before advice");
+ public static final Kind ADVICE = new Kind("advice");
+ public static final Kind DECLARE = new Kind("declare");
+ public static final Kind DECLARE_INTER_TYPE = new Kind("inter-type declaration");
+ public static final Kind USES_POINTCUT = new Kind("uses pointcut");
+ public static final Kind DECLARE_SOFT = new Kind("declare soft");
+
+ public static final Kind[] ALL = { DECLARE_WARNING, DECLARE_ERROR, ADVICE_AROUND, ADVICE_AFTERRETURNING,
+ ADVICE_AFTERTHROWING, ADVICE_AFTER, ADVICE_BEFORE, ADVICE, DECLARE, DECLARE_INTER_TYPE, USES_POINTCUT, DECLARE_SOFT };
+
+ private final String name;
+
+ public boolean isDeclareKind() {
+ return this == DECLARE_WARNING || this == DECLARE_ERROR || this == DECLARE || this == DECLARE_INTER_TYPE
+ || this == DECLARE_SOFT;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Return the Kind of the relationship that is passed in by name.
+ *
+ * @param stringFormOfRelationshipKind the relationship name, eg. 'declare warning', 'declare error', etc.
+ * @return the Kind instance
+ */
+ public static Kind getKindFor(String stringFormOfRelationshipKind) {
+ for (int i = 0; i < ALL.length; i++) {
+ if (ALL[i].name.equals(stringFormOfRelationshipKind)) {
+ return ALL[i];
+ }
+ }
+ return null;
+ }
+
+ private Kind(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ // The 4 declarations below are necessary for serialization
+ private static int nextOrdinal = 0;
+ private final int ordinal = nextOrdinal++;
+
+ private Object readResolve() throws ObjectStreamException {
+ return ALL[ordinal];
+ }
+ }
+
+}
diff --git a/asm/src/main/java/org/aspectj/asm/IRelationshipMap.java b/asm/src/main/java/org/aspectj/asm/IRelationshipMap.java
new file mode 100644
index 000000000..e4159dce8
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/IRelationshipMap.java
@@ -0,0 +1,87 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * ******************************************************************/
+
+package org.aspectj.asm;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Maps from a program element handles to a list of relationships between that element and other program elements. Each element in
+ * the list or relationships is uniquely identified by a kind and a relationship name. For example, the advice affecting a
+ * particular shadow (e.g. method call) can be retrieved by calling <CODE>get</CODE> on the handle for that method. Symmetrically
+ * the method call shadows that an advice affects can be retrieved.
+ * <p>
+ *
+ * <p>
+ * The elements can be stored and looked up as IProgramElement(s), in which cases the element corresponding to the handle is looked
+ * up in the containment hierarchy.
+ *
+ * <p>
+ * put/get methods taking IProgramElement as a parameter are for convenience only. They work identically to calling their
+ * counterparts with IProgramElement.getIdentifierHandle()
+ *
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public interface IRelationshipMap extends Serializable {
+
+ /**
+ * @return list of relationships or null if the source element has no relationships
+ */
+ public List<IRelationship> get(IProgramElement sourceProgramElement);
+
+ /**
+ * @return list of relationships or null if the source element has no relationships
+ */
+ public List<IRelationship> get(String sourceHandle);
+
+ /**
+ * Return a relationship matching the kind and name for the given element.
+ *
+ * @return null if the relationship is not found.
+ */
+ public IRelationship get(IProgramElement source, IRelationship.Kind kind, String relationshipName, boolean runtimeTest,
+ boolean createIfMissing);
+
+ /**
+ * Return a relationship matching the kind and name for the given element.
+ *
+ * @return null if the relationship is not found.
+ */
+ public IRelationship get(IProgramElement source, IRelationship.Kind kind, String relationshipName);
+
+ /**
+ * Return a relationship matching the kind and name for the given element. Creates the relationship if not found.
+ *
+ * @return null if the relationship is not found.
+ */
+ public IRelationship get(String source, IRelationship.Kind kind, String relationshipName, boolean runtimeTest,
+ boolean createIfMissing);
+
+ public void put(IProgramElement source, IRelationship relationship);
+
+ public void put(String handle, IRelationship relationship);
+
+ public boolean remove(String handle, IRelationship relationship);
+
+ public void removeAll(String source);
+
+ /**
+ * Clear all of the relationships in the map.
+ */
+ public void clear();
+
+ public Set<String> getEntries();
+
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/AspectJElementHierarchy.java b/asm/src/main/java/org/aspectj/asm/internal/AspectJElementHierarchy.java
new file mode 100644
index 000000000..6019964c6
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/AspectJElementHierarchy.java
@@ -0,0 +1,697 @@
+/* *******************************************************************
+ * Copyright (c) 2003 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * Andy Clement Extensions for better IDE representation
+ * ******************************************************************/
+
+package org.aspectj.asm.internal;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.aspectj.asm.AsmManager;
+import org.aspectj.asm.IHierarchy;
+import org.aspectj.asm.IProgramElement;
+import org.aspectj.bridge.ISourceLocation;
+import org.aspectj.bridge.SourceLocation;
+
+/**
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public class AspectJElementHierarchy implements IHierarchy {
+
+ private static final long serialVersionUID = 6462734311117048620L;
+
+ private transient AsmManager asm;
+ protected IProgramElement root = null;
+ protected String configFile = null;
+
+ // Access to the handleMap and typeMap are now synchronized - at least the find methods and the updateHandleMap function
+ // see pr305788
+ private Map<String, IProgramElement> fileMap = null;
+ private Map<String, IProgramElement> handleMap = new HashMap<String, IProgramElement>();
+ private Map<String, IProgramElement> typeMap = null;
+
+ public AspectJElementHierarchy(AsmManager asm) {
+ this.asm = asm;
+ }
+
+ public IProgramElement getElement(String handle) {
+ return findElementForHandleOrCreate(handle, false);
+ }
+
+ public void setAsmManager(AsmManager asm) { // used when deserializing
+ this.asm = asm;
+ }
+
+ public IProgramElement getRoot() {
+ return root;
+ }
+
+ public String toSummaryString() {
+ StringBuilder s = new StringBuilder();
+ s.append("FileMap has " + fileMap.size() + " entries\n");
+ s.append("HandleMap has " + handleMap.size() + " entries\n");
+ s.append("TypeMap has " + handleMap.size() + " entries\n");
+ s.append("FileMap:\n");
+ for (Map.Entry<String, IProgramElement> fileMapEntry : fileMap.entrySet()) {
+ s.append(fileMapEntry).append("\n");
+ }
+ s.append("TypeMap:\n");
+ for (Map.Entry<String, IProgramElement> typeMapEntry : typeMap.entrySet()) {
+ s.append(typeMapEntry).append("\n");
+ }
+ s.append("HandleMap:\n");
+ for (Map.Entry<String, IProgramElement> handleMapEntry : handleMap.entrySet()) {
+ s.append(handleMapEntry).append("\n");
+ }
+ return s.toString();
+ }
+
+ public void setRoot(IProgramElement root) {
+ this.root = root;
+ handleMap = new HashMap<String, IProgramElement>();
+ typeMap = new HashMap<String, IProgramElement>();
+ }
+
+ public void addToFileMap(String key, IProgramElement value) {
+ fileMap.put(key, value);
+ }
+
+ public boolean removeFromFileMap(String canonicalFilePath) {
+ return fileMap.remove(canonicalFilePath) != null;
+ }
+
+ public void setFileMap(HashMap<String, IProgramElement> fileMap) {
+ this.fileMap = fileMap;
+ }
+
+ public Object findInFileMap(Object key) {
+ return fileMap.get(key);
+ }
+
+ public Set<Map.Entry<String, IProgramElement>> getFileMapEntrySet() {
+ return fileMap.entrySet();
+ }
+
+ public boolean isValid() {
+ return root != null && fileMap != null;
+ }
+
+ /**
+ * Returns the first match
+ *
+ * @param parent
+ * @param kind not null
+ * @return null if not found
+ */
+ public IProgramElement findElementForSignature(IProgramElement parent, IProgramElement.Kind kind, String signature) {
+ for (IProgramElement node : parent.getChildren()) {
+ if (node.getKind() == kind && signature.equals(node.toSignatureString())) {
+ return node;
+ } else {
+ IProgramElement childSearch = findElementForSignature(node, kind, signature);
+ if (childSearch != null) {
+ return childSearch;
+ }
+ }
+ }
+ return null;
+ }
+
+ public IProgramElement findElementForLabel(IProgramElement parent, IProgramElement.Kind kind, String label) {
+ for (IProgramElement node : parent.getChildren()) {
+ if (node.getKind() == kind && label.equals(node.toLabelString())) {
+ return node;
+ } else {
+ IProgramElement childSearch = findElementForLabel(node, kind, label);
+ if (childSearch != null) {
+ return childSearch;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Find the entry in the model that represents a particular type.
+ *
+ * @param packageName the package in which the type is declared or null for the default package
+ * @param typeName the name of the type
+ * @return the IProgramElement representing the type, or null if not found
+ */
+ public IProgramElement findElementForType(String packageName, String typeName) {
+
+ synchronized (this) {
+ // Build a cache key and check the cache
+ StringBuilder keyb = (packageName == null) ? new StringBuilder() : new StringBuilder(packageName);
+ keyb.append(".").append(typeName);
+ String key = keyb.toString();
+ IProgramElement cachedValue = typeMap.get(key);
+ if (cachedValue != null) {
+ return cachedValue;
+ }
+
+ List<IProgramElement> packageNodes = findMatchingPackages(packageName);
+
+ for (IProgramElement pkg : packageNodes) {
+ // this searches each file for a class
+ for (IProgramElement fileNode : pkg.getChildren()) {
+ IProgramElement cNode = findClassInNodes(fileNode.getChildren(), typeName, typeName);
+ if (cNode != null) {
+ typeMap.put(key, cNode);
+ return cNode;
+ }
+ }
+ }
+ }
+ return null;
+
+ // IProgramElement packageNode = null;
+ // if (packageName == null) {
+ // packageNode = root;
+ // } else {
+ // if (root == null)
+ // return null;
+ // List kids = root.getChildren();
+ // if (kids == null) {
+ // return null;
+ // }
+ // for (Iterator it = kids.iterator(); it.hasNext() && packageNode == null;) {
+ // IProgramElement node = (IProgramElement) it.next();
+ // if (packageName.equals(node.getName())) {
+ // packageNode = node;
+ // }
+ // }
+ // if (packageNode == null) {
+ // return null;
+ // }
+ // }
+
+ // // this searches each file for a class
+ // for (Iterator it = packageNode.getChildren().iterator(); it.hasNext();) {
+ // IProgramElement fileNode = (IProgramElement) it.next();
+ // IProgramElement cNode = findClassInNodes(fileNode.getChildren(), typeName, typeName);
+ // if (cNode != null) {
+ // typeMap.put(key, cNode);
+ // return cNode;
+ // }
+ // }
+ // return null;
+ }
+
+ /**
+ * Look for any package nodes matching the specified package name. There may be multiple in the case where the types within a
+ * package are split across source folders.
+ *
+ * @param packagename the packagename being searched for
+ * @return a list of package nodes that match that name
+ */
+ public List<IProgramElement> findMatchingPackages(String packagename) {
+ List<IProgramElement> children = root.getChildren();
+ // The children might be source folders or packages
+ if (children.size() == 0) {
+ return Collections.emptyList();
+ }
+ if ((children.get(0)).getKind() == IProgramElement.Kind.SOURCE_FOLDER) {
+ String searchPackageName = (packagename == null ? "" : packagename); // default package means match on ""
+ // dealing with source folders
+ List<IProgramElement> matchingPackageNodes = new ArrayList<IProgramElement>();
+ for (IProgramElement sourceFolder : children) {
+ List<IProgramElement> possiblePackageNodes = sourceFolder.getChildren();
+ for (IProgramElement possiblePackageNode : possiblePackageNodes) {
+ if (possiblePackageNode.getKind() == IProgramElement.Kind.PACKAGE) {
+ if (possiblePackageNode.getName().equals(searchPackageName)) {
+ matchingPackageNodes.add(possiblePackageNode);
+ }
+ }
+ }
+ }
+ // 'binaries' will be checked automatically by the code above as it is represented as a SOURCE_FOLDER
+ return matchingPackageNodes;
+ } else {
+ // dealing directly with packages below the root, no source folders. Therefore at most one
+ // thing to return in the list
+ if (packagename == null) {
+ // default package
+ List<IProgramElement> result = new ArrayList<IProgramElement>();
+ result.add(root);
+ return result;
+ }
+ List<IProgramElement> result = new ArrayList<IProgramElement>();
+ for (IProgramElement possiblePackage : children) {
+ if (possiblePackage.getKind() == IProgramElement.Kind.PACKAGE && possiblePackage.getName().equals(packagename)) {
+ result.add(possiblePackage);
+ }
+ if (possiblePackage.getKind() == IProgramElement.Kind.SOURCE_FOLDER) { // might be 'binaries'
+ if (possiblePackage.getName().equals("binaries")) {
+ for (IProgramElement possiblePackage2 : possiblePackage.getChildren()) {
+ if (possiblePackage2.getKind() == IProgramElement.Kind.PACKAGE
+ && possiblePackage2.getName().equals(packagename)) {
+ result.add(possiblePackage2);
+ break; // ok to break here, can't be another entry under binaries
+ }
+ }
+ }
+ }
+ }
+ if (result.isEmpty()) {
+ return Collections.emptyList();
+ } else {
+ return result;
+ }
+ }
+ }
+
+ private IProgramElement findClassInNodes(Collection<IProgramElement> nodes, String name, String typeName) {
+ String baseName;
+ String innerName;
+ int dollar = name.indexOf('$');
+ if (dollar == -1) {
+ baseName = name;
+ innerName = null;
+ } else {
+ baseName = name.substring(0, dollar);
+ innerName = name.substring(dollar + 1);
+ }
+
+ for (IProgramElement classNode : nodes) {
+ if (!classNode.getKind().isType()) {
+ List<IProgramElement> kids = classNode.getChildren();
+ if (kids != null && !kids.isEmpty()) {
+ IProgramElement node = findClassInNodes(kids, name, typeName);
+ if (node != null) {
+ return node;
+ }
+ }
+ } else {
+ if (baseName.equals(classNode.getName())) {
+ if (innerName == null) {
+ return classNode;
+ } else {
+ return findClassInNodes(classNode.getChildren(), innerName, typeName);
+ }
+ } else if (name.equals(classNode.getName())) {
+ return classNode;
+ } else if (typeName.equals(classNode.getBytecodeSignature())) {
+ return classNode;
+ } else if (classNode.getChildren() != null && !classNode.getChildren().isEmpty()) {
+ IProgramElement node = findClassInNodes(classNode.getChildren(), name, typeName);
+ if (node != null) {
+ return node;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param sourceFilePath modified to '/' delimited path for consistency
+ * @return a new structure node for the file if it was not found in the model
+ */
+ public IProgramElement findElementForSourceFile(String sourceFile) {
+ try {
+ if (!isValid() || sourceFile == null) {
+ return IHierarchy.NO_STRUCTURE;
+ } else {
+ String correctedPath = asm.getCanonicalFilePath(new File(sourceFile));
+ // StructureNode node = (StructureNode)getFileMap().get(correctedPath);//findFileNode(filePath, model);
+ IProgramElement node = (IProgramElement) findInFileMap(correctedPath);// findFileNode(filePath, model);
+ if (node != null) {
+ return node;
+ } else {
+ return createFileStructureNode(correctedPath);
+ }
+ }
+ } catch (Exception e) {
+ return IHierarchy.NO_STRUCTURE;
+ }
+ }
+
+ /**
+ * TODO: discriminate columns
+ */
+ public IProgramElement findElementForSourceLine(ISourceLocation location) {
+ try {
+ return findElementForSourceLine(asm.getCanonicalFilePath(location.getSourceFile()), location.getLine());
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Never returns null
+ *
+ * @param sourceFilePath canonicalized path for consistency
+ * @param lineNumber if 0 or 1 the corresponding file node will be returned
+ * @return a new structure node for the file if it was not found in the model
+ */
+ public IProgramElement findElementForSourceLine(String sourceFilePath, int lineNumber) {
+ String canonicalSFP = asm.getCanonicalFilePath(new File(sourceFilePath));
+ // Used to do this:
+ // IProgramElement node2 = findNodeForSourceLineHelper(root, canonicalSFP, lineNumber, -1);
+
+ // Find the relevant source file node first
+ IProgramElement node = findNodeForSourceFile(root, canonicalSFP);
+ if (node == null) {
+ return createFileStructureNode(sourceFilePath);
+ }
+
+ // Check if there is a more accurate child node of that source file node:
+ IProgramElement closernode = findCloserMatchForLineNumber(node, lineNumber);
+ if (closernode == null) {
+ return node;
+ } else {
+ return closernode;
+ }
+ }
+
+ /**
+ * Discover the node representing a particular source file.
+ *
+ * @param node where in the model to start looking (usually the root on the initial call)
+ * @param sourcefilePath the source file being searched for
+ * @return the node representing that source file or null if it cannot be found
+ */
+ public IProgramElement findNodeForSourceFile(IProgramElement node, String sourcefilePath) {
+ // 1. why is <root> a sourcefile node?
+ // 2. should isSourceFile() return true for a FILE that is a .class file...?
+ if ((node.getKind().isSourceFile() && !node.getName().equals("<root>")) || node.getKind().isFile()) {
+ ISourceLocation nodeLoc = node.getSourceLocation();
+ if (nodeLoc != null && asm.getCanonicalFilePath(nodeLoc.getSourceFile()).equals(sourcefilePath)) {
+ return node;
+ }
+ return null; // no need to search children of a source file node
+ } else {
+ // check the children
+ for (IProgramElement child : node.getChildren()) {
+ IProgramElement foundit = findNodeForSourceFile(child, sourcefilePath);
+ if (foundit != null) {
+ return foundit;
+ }
+ }
+ return null;
+ }
+ }
+
+ public IProgramElement findElementForOffSet(String sourceFilePath, int lineNumber, int offSet) {
+ String canonicalSFP = asm.getCanonicalFilePath(new File(sourceFilePath));
+ IProgramElement node = findNodeForSourceLineHelper(root, canonicalSFP, lineNumber, offSet);
+ if (node != null) {
+ return node;
+ } else {
+ return createFileStructureNode(sourceFilePath);
+ }
+ }
+
+ private IProgramElement createFileStructureNode(String sourceFilePath) {
+ // SourceFilePath might have originated on windows on linux...
+ int lastSlash = sourceFilePath.lastIndexOf('\\');
+ if (lastSlash == -1) {
+ lastSlash = sourceFilePath.lastIndexOf('/');
+ }
+ // '!' is used like in URLs "c:/blahblah/X.jar!a/b.class"
+ int i = sourceFilePath.lastIndexOf('!');
+ int j = sourceFilePath.indexOf(".class");
+ if (i > lastSlash && i != -1 && j != -1) {
+ // we are a binary aspect in the default package
+ lastSlash = i;
+ }
+ String fileName = sourceFilePath.substring(lastSlash + 1);
+ IProgramElement fileNode = new ProgramElement(asm, fileName, IProgramElement.Kind.FILE_JAVA, new SourceLocation(new File(
+ sourceFilePath), 1, 1), 0, null, null);
+ // fileNode.setSourceLocation();
+ fileNode.addChild(NO_STRUCTURE);
+ return fileNode;
+ }
+
+ /**
+ * For a specified node, check if any of the children more accurately represent the specified line.
+ *
+ * @param node where to start looking
+ * @param lineno the line number
+ * @return any closer match below 'node' or null if nothing is a more accurate match
+ */
+ public IProgramElement findCloserMatchForLineNumber(IProgramElement node, int lineno) {
+ if (node == null || node.getChildren() == null) {
+ return null;
+ }
+ for (IProgramElement child : node.getChildren()) {
+ ISourceLocation childLoc = child.getSourceLocation();
+ if (childLoc != null) {
+ if (childLoc.getLine() <= lineno && childLoc.getEndLine() >= lineno) {
+ // This child is a better match for that line number
+ IProgramElement evenCloserMatch = findCloserMatchForLineNumber(child, lineno);
+ if (evenCloserMatch == null) {
+ return child;
+ } else {
+ return evenCloserMatch;
+ }
+ } else if (child.getKind().isType()) { // types are a bit clueless about where they are... do other nodes have
+ // similar problems??
+ IProgramElement evenCloserMatch = findCloserMatchForLineNumber(child, lineno);
+ if (evenCloserMatch != null) {
+ return evenCloserMatch;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ private IProgramElement findNodeForSourceLineHelper(IProgramElement node, String sourceFilePath, int lineno, int offset) {
+ if (matches(node, sourceFilePath, lineno, offset) && !hasMoreSpecificChild(node, sourceFilePath, lineno, offset)) {
+ return node;
+ }
+
+ if (node != null) {
+ for (IProgramElement child : node.getChildren()) {
+ IProgramElement foundNode = findNodeForSourceLineHelper(child, sourceFilePath, lineno, offset);
+ if (foundNode != null) {
+ return foundNode;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private boolean matches(IProgramElement node, String sourceFilePath, int lineNumber, int offSet) {
+ // try {
+ // if (node != null && node.getSourceLocation() != null)
+ // System.err.println("====\n1: " +
+ // sourceFilePath + "\n2: " +
+ // node.getSourceLocation().getSourceFile().getCanonicalPath().equals(sourceFilePath)
+ // );
+ ISourceLocation nodeSourceLocation = (node != null ? node.getSourceLocation() : null);
+ return node != null
+ && nodeSourceLocation != null
+ && nodeSourceLocation.getSourceFile().getAbsolutePath().equals(sourceFilePath)
+ && ((offSet != -1 && nodeSourceLocation.getOffset() == offSet) || offSet == -1)
+ && ((nodeSourceLocation.getLine() <= lineNumber && nodeSourceLocation.getEndLine() >= lineNumber) || (lineNumber <= 1 && node
+ .getKind().isSourceFile()));
+ // } catch (IOException ioe) {
+ // return false;
+ // }
+ }
+
+ private boolean hasMoreSpecificChild(IProgramElement node, String sourceFilePath, int lineNumber, int offSet) {
+ for (IProgramElement child : node.getChildren()) {
+ if (matches(child, sourceFilePath, lineNumber, offSet)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public String getConfigFile() {
+ return configFile;
+ }
+
+ public void setConfigFile(String configFile) {
+ this.configFile = configFile;
+ }
+
+ public IProgramElement findElementForHandle(String handle) {
+ return findElementForHandleOrCreate(handle, true);
+ }
+
+ // TODO: optimize this lookup
+ // only want to create a file node if can't find the IPE if called through
+ // findElementForHandle() to mirror behaviour before pr141730
+ public IProgramElement findElementForHandleOrCreate(String handle, boolean create) {
+ // try the cache first...
+ IProgramElement ipe = null;
+ synchronized (this) {
+ ipe = handleMap.get(handle);
+ if (ipe != null) {
+ return ipe;
+ }
+ ipe = findElementForHandle(root, handle);
+ if (ipe == null && create) {
+ ipe = createFileStructureNode(getFilename(handle));
+ }
+ if (ipe != null) {
+ cache(handle, ipe);
+ }
+ }
+ return ipe;
+ }
+
+ private IProgramElement findElementForHandle(IProgramElement parent, String handle) {
+ for (IProgramElement node : parent.getChildren()) {
+ String nodeHid = node.getHandleIdentifier();
+ if (handle.equals(nodeHid)) {
+ return node;
+ } else {
+ if (handle.startsWith(nodeHid)) {
+ // it must be down here if it is anywhere
+ IProgramElement childSearch = findElementForHandle(node, handle);
+ if (childSearch != null) {
+ return childSearch;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ //
+ // private IProgramElement findElementForBytecodeInfo(
+ // IProgramElement node,
+ // String parentName,
+ // String name,
+ // String signature) {
+ // for (Iterator it = node.getChildren().iterator(); it.hasNext(); ) {
+ // IProgramElement curr = (IProgramElement)it.next();
+ // if (parentName.equals(curr.getParent().getBytecodeName())
+ // && name.equals(curr.getBytecodeName())
+ // && signature.equals(curr.getBytecodeSignature())) {
+ // return node;
+ // } else {
+ // IProgramElement childSearch = findElementForBytecodeInfo(curr, parentName, name, signature);
+ // if (childSearch != null) return childSearch;
+ // }
+ // }
+ // return null;
+ // }
+
+ protected void cache(String handle, IProgramElement pe) {
+ if (!AsmManager.isCompletingTypeBindings()) {
+ handleMap.put(handle, pe);
+ }
+ }
+
+ public void flushTypeMap() {
+ typeMap.clear();
+
+ }
+
+ public void flushHandleMap() {
+ handleMap.clear();
+ }
+
+ public void flushFileMap() {
+ fileMap.clear();
+ }
+
+ public void forget(IProgramElement compilationUnitNode, IProgramElement typeNode) {
+ String k = null;
+ synchronized (this) {
+ // handle map
+ // type map
+ for (Map.Entry<String, IProgramElement> typeMapEntry : typeMap.entrySet()) {
+ if (typeMapEntry.getValue() == typeNode) {
+ k = typeMapEntry.getKey();
+ break;
+ }
+ }
+ if (k != null) {
+ typeMap.remove(k);
+ }
+ }
+
+ if (compilationUnitNode != null) {
+ k = null;
+ for (Map.Entry<String, IProgramElement> entry : fileMap.entrySet()) {
+ if (entry.getValue() == compilationUnitNode) {
+ k = entry.getKey();
+ break;
+ }
+ }
+ if (k != null) {
+ fileMap.remove(k);
+ }
+ }
+ }
+
+ // TODO rename this method ... it does more than just the handle map
+ public void updateHandleMap(Set<String> deletedFiles) {
+ // Only delete the entries we need to from the handle map - for performance reasons
+ List<String> forRemoval = new ArrayList<String>();
+ Set<String> k = null;
+ synchronized (this) {
+ k = handleMap.keySet();
+ for (String handle : k) {
+ IProgramElement ipe = handleMap.get(handle);
+ if (ipe == null) {
+ System.err.println("handleMap expectation not met, where is the IPE for " + handle);
+ }
+ if (ipe == null || deletedFiles.contains(getCanonicalFilePath(ipe))) {
+ forRemoval.add(handle);
+ }
+ }
+ for (String handle : forRemoval) {
+ handleMap.remove(handle);
+ }
+ forRemoval.clear();
+ k = typeMap.keySet();
+ for (String typeName : k) {
+ IProgramElement ipe = typeMap.get(typeName);
+ if (deletedFiles.contains(getCanonicalFilePath(ipe))) {
+ forRemoval.add(typeName);
+ }
+ }
+ for (String typeName : forRemoval) {
+ typeMap.remove(typeName);
+ }
+ forRemoval.clear();
+ }
+ for (Map.Entry<String, IProgramElement> entry : fileMap.entrySet()) {
+ String filePath = entry.getKey();
+ if (deletedFiles.contains(getCanonicalFilePath(entry.getValue()))) {
+ forRemoval.add(filePath);
+ }
+ }
+ for (String filePath : forRemoval) {
+ fileMap.remove(filePath);
+ }
+ }
+
+ private String getFilename(String hid) {
+ return asm.getHandleProvider().getFileForHandle(hid);
+ }
+
+ private String getCanonicalFilePath(IProgramElement ipe) {
+ if (ipe.getSourceLocation() != null) {
+ return asm.getCanonicalFilePath(ipe.getSourceLocation().getSourceFile());
+ }
+ return "";
+ }
+
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/CharOperation.java b/asm/src/main/java/org/aspectj/asm/internal/CharOperation.java
new file mode 100644
index 000000000..bf5330a11
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/CharOperation.java
@@ -0,0 +1,205 @@
+/********************************************************************
+ * Copyright (c) 2006 Contributors. 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://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ * Helen Hawkins - initial version
+ *******************************************************************/
+package org.aspectj.asm.internal;
+
+/**
+ * Taken from org.aspectj.org.eclipse.jdt.core.compiler.CharOperation
+ *
+ */
+public class CharOperation {
+
+ public static final char[][] NO_CHAR_CHAR = new char[0][];
+
+ public static final char[] NO_CHAR = new char[0];
+
+ /**
+ * Taken from org.aspectj.org.eclipse.jdt.core.compiler.CharOperation
+ */
+ public static final char[] subarray(char[] array, int start, int end) {
+ if (end == -1)
+ end = array.length;
+ if (start > end)
+ return null;
+ if (start < 0)
+ return null;
+ if (end > array.length)
+ return null;
+
+ char[] result = new char[end - start];
+ System.arraycopy(array, start, result, 0, end - start);
+ return result;
+ }
+
+ public static final char[][] subarray(char[][] array, int start, int end) {
+ if (end == -1)
+ end = array.length;
+ if (start > end)
+ return null;
+ if (start < 0)
+ return null;
+ if (end > array.length)
+ return null;
+
+ char[][] result = new char[end - start][];
+ System.arraycopy(array, start, result, 0, end - start);
+ return result;
+ }
+
+ public static final char[][] splitOn(char divider, char[] array) {
+ int length = array == null ? 0 : array.length;
+ if (length == 0)
+ return NO_CHAR_CHAR;
+
+ int wordCount = 1;
+ for (int i = 0; i < length; i++)
+ if (array[i] == divider)
+ wordCount++;
+ char[][] split = new char[wordCount][];
+ int last = 0, currentWord = 0;
+ for (int i = 0; i < length; i++) {
+ if (array[i] == divider) {
+ split[currentWord] = new char[i - last];
+ System.arraycopy(array, last, split[currentWord++], 0, i - last);
+ last = i + 1;
+ }
+ }
+ split[currentWord] = new char[length - last];
+ System.arraycopy(array, last, split[currentWord], 0, length - last);
+ return split;
+ }
+
+ /**
+ * Taken from org.aspectj.org.eclipse.jdt.core.compiler.CharOperation
+ */
+ public static final int lastIndexOf(char toBeFound, char[] array) {
+ for (int i = array.length; --i >= 0;)
+ if (toBeFound == array[i])
+ return i;
+ return -1;
+ }
+
+ /**
+ * Taken from org.aspectj.org.eclipse.jdt.core.compiler.CharOperation
+ */
+ public static final int indexOf(char toBeFound, char[] array) {
+ for (int i = 0; i < array.length; i++)
+ if (toBeFound == array[i])
+ return i;
+ return -1;
+ }
+
+ /**
+ * Taken from org.aspectj.org.eclipse.jdt.core.compiler.CharOperation
+ */
+ public static final char[] concat(char[] first, char[] second) {
+ if (first == null)
+ return second;
+ if (second == null)
+ return first;
+
+ int length1 = first.length;
+ int length2 = second.length;
+ char[] result = new char[length1 + length2];
+ System.arraycopy(first, 0, result, 0, length1);
+ System.arraycopy(second, 0, result, length1, length2);
+ return result;
+ }
+
+ /**
+ * Taken from org.aspectj.org.eclipse.jdt.core.compiler.CharOperation
+ */
+ public static final boolean equals(char[] first, char[] second) {
+ if (first == second)
+ return true;
+ if (first == null || second == null)
+ return false;
+ if (first.length != second.length)
+ return false;
+
+ for (int i = first.length; --i >= 0;)
+ if (first[i] != second[i])
+ return false;
+ return true;
+ }
+
+ final static public String toString(char[][] array) {
+ char[] result = concatWith(array, '.');
+ return new String(result);
+ }
+
+ public static final char[] concatWith(char[][] array, char separator) {
+ int length = array == null ? 0 : array.length;
+ if (length == 0)
+ return CharOperation.NO_CHAR;
+
+ int size = length - 1;
+ int index = length;
+ while (--index >= 0) {
+ if (array[index].length == 0)
+ size--;
+ else
+ size += array[index].length;
+ }
+ if (size <= 0)
+ return CharOperation.NO_CHAR;
+ char[] result = new char[size];
+ index = length;
+ while (--index >= 0) {
+ length = array[index].length;
+ if (length > 0) {
+ System.arraycopy(array[index], 0, result, (size -= length), length);
+ if (--size >= 0)
+ result[size] = separator;
+ }
+ }
+ return result;
+ }
+
+ public static final int hashCode(char[] array) {
+ int length = array.length;
+ int hash = length == 0 ? 31 : array[0];
+ if (length < 8) {
+ for (int i = length; --i > 0;)
+ hash = (hash * 31) + array[i];
+ } else {
+ // 8 characters is enough to compute a decent hash code, don't waste time examining every character
+ for (int i = length - 1, last = i > 16 ? i - 16 : 0; i > last; i -= 2)
+ hash = (hash * 31) + array[i];
+ }
+ return hash & 0x7FFFFFFF;
+ }
+
+ public static final boolean equals(char[][] first, char[][] second) {
+ if (first == second)
+ return true;
+ if (first == null || second == null)
+ return false;
+ if (first.length != second.length)
+ return false;
+
+ for (int i = first.length; --i >= 0;)
+ if (!equals(first[i], second[i]))
+ return false;
+ return true;
+ }
+
+ /**
+ * Taken from org.aspectj.org.eclipse.jdt.core.compiler.CharOperation
+ */
+ public static final void replace(char[] array, char toBeReplaced, char replacementChar) {
+ if (toBeReplaced != replacementChar) {
+ for (int i = 0, max = array.length; i < max; i++) {
+ if (array[i] == toBeReplaced)
+ array[i] = replacementChar;
+ }
+ }
+ }
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/HandleProviderDelimiter.java b/asm/src/main/java/org/aspectj/asm/internal/HandleProviderDelimiter.java
new file mode 100644
index 000000000..9bbfc307c
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/HandleProviderDelimiter.java
@@ -0,0 +1,143 @@
+/********************************************************************
+ * Copyright (c) 2006 Contributors. 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://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ * Helen Hawkins - initial version
+ *******************************************************************/
+package org.aspectj.asm.internal;
+
+import org.aspectj.asm.IProgramElement;
+
+/**
+ * Uses "typesafe enum" pattern.
+ */
+public class HandleProviderDelimiter {
+
+ // taken from JavaElement
+ public static final HandleProviderDelimiter JAVAPROJECT = new HandleProviderDelimiter('=');
+ public static final HandleProviderDelimiter PACKAGEFRAGMENT = new HandleProviderDelimiter('<');
+ public static final HandleProviderDelimiter FIELD = new HandleProviderDelimiter('^');
+ public static final HandleProviderDelimiter METHOD = new HandleProviderDelimiter('~');
+ public static final HandleProviderDelimiter INITIALIZER = new HandleProviderDelimiter('|');
+ public static final HandleProviderDelimiter COMPILATIONUNIT = new HandleProviderDelimiter('{');
+ public static final HandleProviderDelimiter CLASSFILE = new HandleProviderDelimiter('(');
+ public static final HandleProviderDelimiter TYPE = new HandleProviderDelimiter('[');
+ public static final HandleProviderDelimiter IMPORTDECLARATION = new HandleProviderDelimiter('#');
+ public static final HandleProviderDelimiter COUNT = new HandleProviderDelimiter('!');
+ public static final HandleProviderDelimiter ESCAPE = new HandleProviderDelimiter('\\');
+ public static final HandleProviderDelimiter PACKAGEDECLARATION = new HandleProviderDelimiter('%');
+ public static final HandleProviderDelimiter PACKAGEFRAGMENTROOT = new HandleProviderDelimiter('/');
+ // these below are not currently used because no iprogramelement.kind
+ // equivalent
+ public static final HandleProviderDelimiter LOCALVARIABLE = new HandleProviderDelimiter('@');
+ public static final HandleProviderDelimiter TYPE_PARAMETER = new HandleProviderDelimiter(']');
+
+ // AspectJ specific ones
+ public static final HandleProviderDelimiter ASPECT_CU = new HandleProviderDelimiter('*');
+ public static final HandleProviderDelimiter ADVICE = new HandleProviderDelimiter('&');
+ public static final HandleProviderDelimiter ASPECT_TYPE = new HandleProviderDelimiter('\'');
+ public static final HandleProviderDelimiter CODEELEMENT = new HandleProviderDelimiter('?');
+ public static final HandleProviderDelimiter ITD_FIELD = new HandleProviderDelimiter(',');
+ public static final HandleProviderDelimiter ITD = new HandleProviderDelimiter(')');
+ public static final HandleProviderDelimiter DECLARE = new HandleProviderDelimiter('`');
+ public static final HandleProviderDelimiter POINTCUT = new HandleProviderDelimiter('"');
+
+ public static final HandleProviderDelimiter PHANTOM = new HandleProviderDelimiter(';');
+
+ private static char empty = ' ';
+ private final char delim;
+
+ private HandleProviderDelimiter(char delim) {
+ this.delim = delim;
+ }
+
+ /**
+ * Returns the delimiter for the HandleProviderDelimiter, for example ASPECT returns '*' and METHOD returns '~'
+ */
+ public char getDelimiter() {
+ return delim;
+ }
+
+ /**
+ * Returns the delimiter for the given IProgramElement for example if the IProgramElement is an aspect returns '*' and if the
+ * IProgramElement is a method returns '~'
+ */
+ public static char getDelimiter(IProgramElement ipe) {
+ IProgramElement.Kind kind = ipe.getKind();
+ if (kind.equals(IProgramElement.Kind.PROJECT)) {
+ return JAVAPROJECT.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.PACKAGE)) {
+ return PACKAGEFRAGMENT.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.FILE_JAVA)) {
+ if (ipe.getName().endsWith(".aj")) {
+ return ASPECT_CU.getDelimiter();
+ } else {
+ return COMPILATIONUNIT.getDelimiter();
+ }
+ } else if (kind.equals(IProgramElement.Kind.FILE_ASPECTJ)) {
+ return ASPECT_CU.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.IMPORT_REFERENCE)) {
+ return IMPORTDECLARATION.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.PACKAGE_DECLARATION)) {
+ return PACKAGEDECLARATION.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.CLASS) || kind.equals(IProgramElement.Kind.INTERFACE)
+ || kind.equals(IProgramElement.Kind.ENUM) || kind.equals(IProgramElement.Kind.ANNOTATION)) {
+ return TYPE.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.ASPECT)) {
+ if (ipe.isAnnotationStyleDeclaration()) {
+ return TYPE.getDelimiter();
+ } else {
+ return ASPECT_TYPE.getDelimiter();
+ }
+ } else if (kind.equals(IProgramElement.Kind.INITIALIZER)) {
+ return INITIALIZER.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.INTER_TYPE_FIELD)) {
+ return ITD_FIELD.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.INTER_TYPE_METHOD) || kind.equals(IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR)
+ || kind.equals(IProgramElement.Kind.INTER_TYPE_PARENT)) {
+ return ITD.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.CONSTRUCTOR) || kind.equals(IProgramElement.Kind.METHOD)) {
+ return METHOD.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.FIELD) || kind.equals(IProgramElement.Kind.ENUM_VALUE)) {
+ return FIELD.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.POINTCUT)) {
+ if (ipe.isAnnotationStyleDeclaration()) {
+ return METHOD.getDelimiter();
+ } else {
+ return POINTCUT.getDelimiter();
+ }
+ } else if (kind.equals(IProgramElement.Kind.ADVICE)) {
+ if (ipe.isAnnotationStyleDeclaration()) {
+ return METHOD.getDelimiter();
+ } else {
+ return ADVICE.getDelimiter();
+ }
+ } else if (kind.equals(IProgramElement.Kind.DECLARE_PARENTS) || kind.equals(IProgramElement.Kind.DECLARE_WARNING)
+ || kind.equals(IProgramElement.Kind.DECLARE_ERROR) || kind.equals(IProgramElement.Kind.DECLARE_SOFT)
+ || kind.equals(IProgramElement.Kind.DECLARE_PRECEDENCE)
+ || kind.equals(IProgramElement.Kind.DECLARE_ANNOTATION_AT_CONSTRUCTOR)
+ || kind.equals(IProgramElement.Kind.DECLARE_ANNOTATION_AT_FIELD)
+ || kind.equals(IProgramElement.Kind.DECLARE_ANNOTATION_AT_METHOD)
+ || kind.equals(IProgramElement.Kind.DECLARE_ANNOTATION_AT_TYPE)) {
+ return DECLARE.getDelimiter();
+ } else if (kind.equals(IProgramElement.Kind.CODE)) {
+ return CODEELEMENT.getDelimiter();
+ } else if (kind == IProgramElement.Kind.FILE) {
+ if (ipe.getName().endsWith(".class")) {
+ return CLASSFILE.getDelimiter();
+ } else if (ipe.getName().endsWith(".aj")) {
+ return ASPECT_CU.getDelimiter();
+ } else if (ipe.getName().endsWith(".java")) {
+ return COMPILATIONUNIT.getDelimiter();
+ } else {
+ return empty;
+ }
+ }
+ return empty;
+ }
+
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/JDTLikeHandleProvider.java b/asm/src/main/java/org/aspectj/asm/internal/JDTLikeHandleProvider.java
new file mode 100644
index 000000000..3751dfc1b
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/JDTLikeHandleProvider.java
@@ -0,0 +1,474 @@
+/********************************************************************
+ * Copyright (c) 2006 Contributors. 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://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ * Helen Hawkins - initial version
+ *******************************************************************/
+package org.aspectj.asm.internal;
+
+import java.io.File;
+import java.util.Iterator;
+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
+ // <root> or the .lst file
+ if (ipe == null || (ipe.getKind().equals(IProgramElement.Kind.FILE_JAVA) && ipe.getName().equals("<root>"))) {
+ 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 StringBuffer("=").append(configFile.substring(start + 1)).toString();
+ }
+ ipe.setHandleIdentifier(configFile);
+ return configFile;
+ } else if (ipe.getKind() == IProgramElement.Kind.SOURCE_FOLDER) {
+ StringBuffer sb = new StringBuffer();
+ 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.indexOf("/") != -1) {
+ 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();
+ }
+
+ StringBuffer handle = new StringBuffer();
+ // 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 (!<n>)
+ } 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<String> sourceRefs = ipe.getParameterSignaturesSourceRefs();
+ List<char[]> parameterTypes = ipe.getParameterSignatures();
+ StringBuffer sb = new StringBuffer();
+ if (sourceRefs != null) {
+ for (int i = 0; i < sourceRefs.size(); i++) {
+ String sourceRef = sourceRefs.get(i);
+ 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 !<n> 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 "!<n>"
+ */
+ private char[] getCount(IProgramElement ipe) {
+ // TODO could optimize this code
+ char[] byteCodeName = ipe.getBytecodeName().toCharArray();
+
+ if (ipe.getKind().isInterTypeMember()) {
+ int count = 1;
+ List<IProgramElement> kids = ipe.getParent().getChildren();
+ for (Iterator<IProgramElement> iterator = kids.iterator(); iterator.hasNext();) {
+ IProgramElement object = iterator.next();
+ 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 = new Integer(existingHandle.substring(suffixPosition + 1)).intValue() + 1;
+ } else {
+ if (count == 1) {
+ count = 2;
+ }
+ }
+ }
+ }
+ }
+ if (count > 1) {
+ return CharOperation.concat(countDelim, new Integer(count).toString().toCharArray());
+ }
+ } else if (ipe.getKind().isDeclare()) {
+ // // look at peer declares
+ int count = computeCountBasedOnPeers(ipe);
+ if (count > 1) {
+ return CharOperation.concat(countDelim, new Integer(count).toString().toCharArray());
+ }
+ } else if (ipe.getKind().equals(IProgramElement.Kind.ADVICE)) {
+ // Look at any peer advice
+ int count = 1;
+ List<IProgramElement> 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.indexOf("Lorg/aspectj/lang") != -1) {
+ 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 = new Integer(existingHandle.substring(suffixPosition + 1)).intValue() + 1;
+ } else {
+ if (count == 1) {
+ count = 2;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (count > 1) {
+ return CharOperation.concat(countDelim, new Integer(count).toString().toCharArray());
+ }
+ } else if (ipe.getKind().equals(IProgramElement.Kind.INITIALIZER)) {
+ // return String.valueOf(++initializerCounter).toCharArray();
+ // Look at any peer advice
+ int count = 1;
+ List<IProgramElement> 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.indexOf("Lorg/aspectj/lang") != -1) {
+ 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 = new Integer(existingHandle.substring(suffixPosition + 1)).intValue() + 1;
+ } else {
+ if (count == 1) {
+ count = 2;
+ }
+ }
+ }
+ }
+ }
+ }
+ // if (count > 1) {
+ return new Integer(count).toString().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<IProgramElement> 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 = new Integer(existingHandle.substring(suffixPosition + 1)).intValue() + 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 = new Integer(existingHandle.substring(suffixPosition + 1)).intValue() + 1;
+ } else {
+ if (count == 1) {
+ count = 2;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (count > 1) {
+ return CharOperation.concat(countDelim, new Integer(count).toString().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.indexOf("Lorg/aspectj/lang") != -1) {
+ 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 = new Integer(existingHandle.substring(suffixPosition + 1)).intValue() + 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;
+ }
+
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/NameConvertor.java b/asm/src/main/java/org/aspectj/asm/internal/NameConvertor.java
new file mode 100644
index 000000000..340d41586
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/NameConvertor.java
@@ -0,0 +1,253 @@
+/********************************************************************
+ * Copyright (c) 2006 Contributors. 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://eclipse.org/legal/epl-v10.html
+ *
+ * Contributors: IBM Corporation - initial API and implementation
+ * Helen Hawkins - initial version
+ *******************************************************************/
+package org.aspectj.asm.internal;
+
+public class NameConvertor {
+
+ private static final char BOOLEAN = 'Z';
+ private static final char BYTE = 'B';
+ private static final char CHAR = 'C';
+ private static final char DOUBLE = 'D';
+ private static final char FLOAT = 'F';
+ private static final char INT = 'I';
+ private static final char LONG = 'J';
+ private static final char SHORT = 'S';
+ private static final char ARRAY = '[';
+ private static final char RESOLVED = 'L';
+ private static final char UNRESOLVED = 'Q';
+
+ public static final char PARAMETERIZED = 'P';
+
+ private static final char[] BOOLEAN_NAME = new char[] { 'b', 'o', 'o', 'l', 'e', 'a', 'n' };
+ private static final char[] BYTE_NAME = new char[] { 'b', 'y', 't', 'e' };
+ private static final char[] CHAR_NAME = new char[] { 'c', 'h', 'a', 'r' };
+ private static final char[] DOUBLE_NAME = new char[] { 'd', 'o', 'u', 'b', 'l', 'e' };
+ private static final char[] FLOAT_NAME = new char[] { 'f', 'l', 'o', 'a', 't' };
+ private static final char[] INT_NAME = new char[] { 'i', 'n', 't' };
+ private static final char[] LONG_NAME = new char[] { 'l', 'o', 'n', 'g' };
+ private static final char[] SHORT_NAME = new char[] { 's', 'h', 'o', 'r', 't' };
+
+ private static final char[] SQUARE_BRACKETS = new char[] { '[', ']' };
+ private static final char[] GREATER_THAN = new char[] { '>' };
+ private static final char[] LESS_THAN = new char[] { '<' };
+ private static final char[] COMMA = new char[] { ',' };
+ private static final char[] BACKSLASH_LESSTHAN = new char[] { '\\', '<' };
+ private static final char[] SEMICOLON = new char[] { ';' };
+
+ /**
+ * Creates a readable name from the given char array, for example, given 'I' returns 'int'. Moreover, given
+ * 'Ljava/lang/String;<Ljava/lang/String;>' returns 'java.lang.String<java.lang.String>'
+ */
+ public static char[] convertFromSignature(char[] c) {
+ int lt = CharOperation.indexOf('<', c);
+ int sc = CharOperation.indexOf(';', c);
+ int gt = CharOperation.indexOf('>', c);
+
+ int smallest = 0;
+ if (lt == -1 && sc == -1 && gt == -1) {
+ // we have something like 'Ljava/lang/String' or 'I'
+ return getFullyQualifiedTypeName(c);
+ } else if (lt != -1 && (sc == -1 || lt <= sc) && (gt == -1 || lt <= gt)) {
+ // we have something like 'Ljava/lang/String<I'
+ smallest = lt;
+ } else if (sc != -1 && (lt == -1 || sc <= lt) && (gt == -1 || sc <= gt)) {
+ // we have something like 'Ljava/lang/String;I'
+ smallest = sc;
+ } else {
+ // we have something like '>;'
+ smallest = gt;
+ }
+ char[] first = CharOperation.subarray(c, 0, smallest);
+ char[] second = CharOperation.subarray(c, smallest + 1, c.length);
+ if (smallest == 0 && first.length == 0 && c[0] == '>') {
+ // c = {'>',';'} therefore we just want to return '>' to
+ // close the generic signature
+ return GREATER_THAN;
+ } else if (first.length == 1 && second.length == 0) {
+ return first;
+ } else if (second.length == 0 || (second.length == 1 && second[0] == ';')) {
+ // we've reached the end of the array, therefore only care about
+ // the first part
+ return convertFromSignature(first);
+ } else if (smallest == lt) {
+ // if c = 'Ljava/lang/String;<I' then first = 'Ljava/Lang/String;' and
+ // second = 'I'. Want to end up with 'Ljava.lang.String<I' and so add
+ // the '<' back.
+ char[] inclLT = CharOperation.concat(convertFromSignature(first), LESS_THAN);
+ return CharOperation.concat(inclLT, convertFromSignature(second));
+ } else if (smallest == gt) {
+ char[] inclLT = CharOperation.concat(convertFromSignature(first), GREATER_THAN);
+ return CharOperation.concat(inclLT, convertFromSignature(second));
+ } else if (second.length != 2) {
+ // if c = 'Ljava/lang/Sting;LMyClass' then first = 'Ljava/lang/String'
+ // and second = 'LMyClass'. Want to end up with 'java.lang.String,MyClass
+ // so want to add a ','. However, only want to do this if we're in the
+ // middle of a '<...>'
+ char[] inclComma = CharOperation.concat(convertFromSignature(first), COMMA);
+ return CharOperation.concat(inclComma, convertFromSignature(second));
+ }
+ return CharOperation.concat(convertFromSignature(first), convertFromSignature(second));
+ }
+
+ /**
+ * Given a char array, returns the type name for this. For example 'I' returns 'int', 'Ljava/lang/String' returns
+ * 'java.lang.String' and '[Ljava/lang/String' returns 'java.lang.String[]'
+ *
+ * NOTE: Doesn't go any deaper so given 'Ljava/lang/String;<Ljava/lang/String;>' it would return
+ * 'java.lang.String;<Ljava.lang.String;>', however, only called with something like 'Ljava/lang/String'
+ */
+ private static char[] getFullyQualifiedTypeName(char[] c) {
+ if (c.length == 0) {
+ return c;
+ }
+ if (c[0] == BOOLEAN) {
+ return BOOLEAN_NAME;
+ } else if (c[0] == BYTE) {
+ return BYTE_NAME;
+ } else if (c[0] == CHAR) {
+ return CHAR_NAME;
+ } else if (c[0] == DOUBLE) {
+ return DOUBLE_NAME;
+ } else if (c[0] == FLOAT) {
+ return FLOAT_NAME;
+ } else if (c[0] == INT) {
+ return INT_NAME;
+ } else if (c[0] == LONG) {
+ return LONG_NAME;
+ } else if (c[0] == SHORT) {
+ return SHORT_NAME;
+ } else if (c[0] == ARRAY) {
+ return CharOperation.concat(getFullyQualifiedTypeName(CharOperation.subarray(c, 1, c.length)), SQUARE_BRACKETS);
+ } else {
+ char[] type = CharOperation.subarray(c, 1, c.length);
+ CharOperation.replace(type, '/', '.');
+ return type;
+ }
+ }
+
+ // public static char[] createShortName(char[] c) {
+ // return createShortName(c, false);
+ // }
+
+ /**
+ * Given 'Ppkg/MyGenericClass<Ljava/lang/String;Ljava/lang/Integer;>;' will return 'QMyGenericClass<QString;QInteger;>;'
+ */
+ public static char[] createShortName(char[] c, boolean haveFullyQualifiedAtLeastOneThing, boolean needsFullyQualifiedFirstEntry) {
+ if (c[0] == '[') {
+ char[] ret = CharOperation.concat(
+ new char[] { '\\', '[' },
+ createShortName(CharOperation.subarray(c, 1, c.length), haveFullyQualifiedAtLeastOneThing,
+ needsFullyQualifiedFirstEntry));
+ return ret;
+ } else if (c[0] == '+') {
+ char[] ret = CharOperation.concat(
+ new char[] { '+' },
+ createShortName(CharOperation.subarray(c, 1, c.length), haveFullyQualifiedAtLeastOneThing,
+ needsFullyQualifiedFirstEntry));
+ return ret;
+ } else if (c[0] == '*') {
+ return c; // c is *>;
+ }
+ int lt = CharOperation.indexOf('<', c);
+ int sc = CharOperation.indexOf(';', c);
+ int gt = CharOperation.indexOf('>', c);
+
+ int smallest = 0;
+ if (lt == -1 && sc == -1 && gt == -1) {
+ // we have something like 'Ljava/lang/String' or 'I'
+ if (!needsFullyQualifiedFirstEntry) {
+ return getTypeName(c, true);
+ } else {
+ return getTypeName(c, haveFullyQualifiedAtLeastOneThing);
+ }
+ } else if (lt != -1 && (sc == -1 || lt <= sc) && (gt == -1 || lt <= gt)) {
+ // we have something like 'Ljava/lang/String<I'
+ smallest = lt;
+ } else if (sc != -1 && (lt == -1 || sc <= lt) && (gt == -1 || sc <= gt)) {
+ // we have something like 'Ljava/lang/String;I'
+ smallest = sc;
+ } else {
+ // we have something like '>;'
+ smallest = gt;
+ }
+ char[] first = CharOperation.subarray(c, 0, smallest);
+ char[] second = CharOperation.subarray(c, smallest + 1, c.length);
+ if (smallest == 0 && first.length == 0 && c[0] == '>') {
+ // c = {'>',';'} therefore we just want to return c to
+ // close the generic signature
+ return c;
+ } else if (first.length == 1 && second.length == 0) {
+ return first;
+ } else if (second.length == 0 || (second.length == 1 && second[0] == ';')) {
+ // we've reached the end of the array, therefore only care about
+ // the first part
+ return CharOperation.concat(createShortName(first, haveFullyQualifiedAtLeastOneThing, needsFullyQualifiedFirstEntry),
+ new char[] { ';' });
+ } else if (smallest == lt) {
+ // if c = 'Ljava/lang/String;<I' then first = 'Ljava/Lang/String;' and
+ // second = 'I'. Want to end up with 'LString<I' and so add
+ // the '<' back.
+ char[] inclLT = CharOperation.concat(createShortName(first, haveFullyQualifiedAtLeastOneThing, true),
+ BACKSLASH_LESSTHAN);
+ return CharOperation.concat(inclLT, createShortName(second, true, false));
+ } else if (smallest == gt) {
+ char[] inclLT = CharOperation.concat(
+ createShortName(first, haveFullyQualifiedAtLeastOneThing, needsFullyQualifiedFirstEntry), GREATER_THAN);
+ return CharOperation.concat(inclLT, createShortName(second, true, false));
+ } else {
+ // if c = 'Ljava/lang/Sting;LMyClass;' then first = 'Ljava/lang/String'
+ // and second = 'LMyClass;'. Want to end up with 'QString;QMyClass;
+ // so add the ';' back
+ char[] firstTypeParam = CharOperation.concat(createShortName(first, haveFullyQualifiedAtLeastOneThing, false),
+ SEMICOLON);
+ return CharOperation.concat(firstTypeParam, createShortName(second, true, false));
+ }
+ }
+
+ // public static char[] getTypeName(char[] name) {
+ // return getTypeName(name, false);
+ // }
+
+ /**
+ * Convert a typename into its handle form. There are various cases to consider here - many are discussed in pr249216. The flag
+ * allreadyFQd indicates if we've already included a fq'd name in what we are creating - if we have then further references
+ * should not be fq'd and can be the short name (so java.util.Set becomes just Set).
+ *
+ */
+
+ /**
+ * Given 'Qjava/lang/String;' returns 'QString;'
+ */
+ public static char[] getTypeName(char[] name, boolean haveFullyQualifiedAtLeastOneThing) {
+ if (!haveFullyQualifiedAtLeastOneThing) {
+ if (name[0] == RESOLVED || name[0] == PARAMETERIZED) {
+ char[] sub = CharOperation.subarray(name, 1, name.length);
+ CharOperation.replace(sub, '/', '.');
+ return CharOperation.concat(new char[] { UNRESOLVED }, sub);
+ } else {
+ char[] sub = CharOperation.subarray(name, 1, name.length);
+ CharOperation.replace(sub, '/', '.');
+ return CharOperation.concat(new char[] { name[0] }, sub);
+ }
+ } else {
+ int i = CharOperation.lastIndexOf('/', name);
+ if (i != -1) {
+ if (name[0] == RESOLVED || name[0] == PARAMETERIZED) {
+ return CharOperation.concat(new char[] { UNRESOLVED }, CharOperation.subarray(name, i + 1, name.length));
+ } else {
+ return CharOperation.concat(new char[] { name[0] }, CharOperation.subarray(name, i + 1, name.length));
+ }
+ }
+ }
+ return name;
+ }
+
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/ProgramElement.java b/asm/src/main/java/org/aspectj/asm/internal/ProgramElement.java
new file mode 100644
index 000000000..51aafd936
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/ProgramElement.java
@@ -0,0 +1,852 @@
+/* *******************************************************************
+ * Copyright (c) 2003,2010 Contributors.
+ * 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:
+ * Mik Kersten initial implementation
+ * Andy Clement, IBM, SpringSource Extensions for better IDE representation
+ * ******************************************************************/
+
+package org.aspectj.asm.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.aspectj.asm.AsmManager;
+import org.aspectj.asm.HierarchyWalker;
+import org.aspectj.asm.IProgramElement;
+import org.aspectj.bridge.IMessage;
+import org.aspectj.bridge.ISourceLocation;
+
+/**
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public class ProgramElement implements IProgramElement {
+
+ public transient AsmManager asm; // which structure model is this node part of
+ private static final long serialVersionUID = 171673495267384449L;
+ public static boolean shortITDNames = true;
+
+ private final static String UNDEFINED = "<undefined>";
+ private final static int AccPublic = 0x0001;
+ private final static int AccPrivate = 0x0002;
+ private final static int AccProtected = 0x0004;
+ private final static int AccPrivileged = 0x0006; // XXX is this right?
+ private final static int AccStatic = 0x0008;
+ private final static int AccFinal = 0x0010;
+ private final static int AccSynchronized = 0x0020;
+ private final static int AccVolatile = 0x0040;
+ private final static int AccTransient = 0x0080;
+ private final static int AccNative = 0x0100;
+ // private final static int AccInterface = 0x0200;
+ private final static int AccAbstract = 0x0400;
+ // private final static int AccStrictfp = 0x0800;
+
+ protected String name;
+ private Kind kind;
+ protected IProgramElement parent = null;
+ protected List<IProgramElement> children = Collections.emptyList();
+ public Map<String, Object> kvpairs = Collections.emptyMap();
+ protected ISourceLocation sourceLocation = null;
+ public int modifiers;
+ private String handle = null;
+
+ public AsmManager getModel() {
+ return asm;
+ }
+
+ /** Used during deserialization */
+ public ProgramElement() {
+ }
+
+ /** Use to create program element nodes that do not correspond to source locations */
+ public ProgramElement(AsmManager asm, String name, Kind kind, List<IProgramElement> children) {
+ this.asm = asm;
+ if (asm == null && !name.equals("<build to view structure>")) {
+ throw new RuntimeException();
+ }
+ this.name = name;
+ this.kind = kind;
+ if (children != null) {
+ setChildren(children);
+ }
+ }
+
+ public ProgramElement(AsmManager asm, String name, IProgramElement.Kind kind, ISourceLocation sourceLocation, int modifiers,
+ String comment, List<IProgramElement> children) {
+ this(asm, name, kind, children);
+ this.sourceLocation = sourceLocation;
+ setFormalComment(comment);
+ // if (comment!=null && comment.length()>0) formalComment = comment;
+ this.modifiers = modifiers;
+ }
+
+ public int getRawModifiers() {
+ return this.modifiers;
+ }
+
+ public List<IProgramElement.Modifiers> getModifiers() {
+ return genModifiers(this.modifiers);
+ }
+
+ public Accessibility getAccessibility() {
+ return genAccessibility(this.modifiers);
+ }
+
+ public void setDeclaringType(String t) {
+ if (t != null && t.length() > 0) {
+ fixMap();
+ kvpairs.put("declaringType", t);
+ }
+ }
+
+ public String getDeclaringType() {
+ String dt = (String) kvpairs.get("declaringType");
+ if (dt == null) {
+ return ""; // assumption that not having one means "" is at HtmlDecorator line 111
+ }
+ return dt;
+ }
+
+ public String getPackageName() {
+ if (kind == Kind.PACKAGE) {
+ return getName();
+ }
+ if (getParent() == null) {
+ return "";
+ }
+ return getParent().getPackageName();
+ }
+
+ public Kind getKind() {
+ return kind;
+ }
+
+ public boolean isCode() {
+ return kind.equals(Kind.CODE);
+ }
+
+ public ISourceLocation getSourceLocation() {
+ return sourceLocation;
+ }
+
+ // not really sure why we have this setter ... how can we be in the situation where we didn't
+ // know the location when we built the node but we learned it later on?
+ public void setSourceLocation(ISourceLocation sourceLocation) {
+ // this.sourceLocation = sourceLocation;
+ }
+
+ public IMessage getMessage() {
+ return (IMessage) kvpairs.get("message");
+ // return message;
+ }
+
+ public void setMessage(IMessage message) {
+ fixMap();
+ kvpairs.put("message", message);
+ // this.message = message;
+ }
+
+ public IProgramElement getParent() {
+ return parent;
+ }
+
+ public void setParent(IProgramElement parent) {
+ this.parent = parent;
+ }
+
+ public boolean isMemberKind() {
+ return kind.isMember();
+ }
+
+ public void setRunnable(boolean value) {
+ fixMap();
+ if (value) {
+ kvpairs.put("isRunnable", "true");
+ } else {
+ kvpairs.remove("isRunnable");
+ // this.runnable = value;
+ }
+ }
+
+ public boolean isRunnable() {
+ return kvpairs.get("isRunnable") != null;
+ // return runnable;
+ }
+
+ public boolean isImplementor() {
+ return kvpairs.get("isImplementor") != null;
+ // return implementor;
+ }
+
+ public void setImplementor(boolean value) {
+ fixMap();
+ if (value) {
+ kvpairs.put("isImplementor", "true");
+ } else {
+ kvpairs.remove("isImplementor");
+ // this.implementor = value;
+ }
+ }
+
+ public boolean isOverrider() {
+ return kvpairs.get("isOverrider") != null;
+ // return overrider;
+ }
+
+ public void setOverrider(boolean value) {
+ fixMap();
+ if (value) {
+ kvpairs.put("isOverrider", "true");
+ } else {
+ kvpairs.remove("isOverrider");
+ // this.overrider = value;
+ }
+ }
+
+ public String getFormalComment() {
+ return (String) kvpairs.get("formalComment");
+ // return formalComment;
+ }
+
+ public String toString() {
+ return toLabelString();
+ }
+
+ private static List<IProgramElement.Modifiers> genModifiers(int modifiers) {
+ List<IProgramElement.Modifiers> modifiersList = new ArrayList<IProgramElement.Modifiers>();
+ if ((modifiers & AccStatic) != 0) {
+ modifiersList.add(IProgramElement.Modifiers.STATIC);
+ }
+ if ((modifiers & AccFinal) != 0) {
+ modifiersList.add(IProgramElement.Modifiers.FINAL);
+ }
+ if ((modifiers & AccSynchronized) != 0) {
+ modifiersList.add(IProgramElement.Modifiers.SYNCHRONIZED);
+ }
+ if ((modifiers & AccVolatile) != 0) {
+ modifiersList.add(IProgramElement.Modifiers.VOLATILE);
+ }
+ if ((modifiers & AccTransient) != 0) {
+ modifiersList.add(IProgramElement.Modifiers.TRANSIENT);
+ }
+ if ((modifiers & AccNative) != 0) {
+ modifiersList.add(IProgramElement.Modifiers.NATIVE);
+ }
+ if ((modifiers & AccAbstract) != 0) {
+ modifiersList.add(IProgramElement.Modifiers.ABSTRACT);
+ }
+ return modifiersList;
+ }
+
+ public static IProgramElement.Accessibility genAccessibility(int modifiers) {
+ if ((modifiers & AccPublic) != 0) {
+ return IProgramElement.Accessibility.PUBLIC;
+ }
+ if ((modifiers & AccPrivate) != 0) {
+ return IProgramElement.Accessibility.PRIVATE;
+ }
+ if ((modifiers & AccProtected) != 0) {
+ return IProgramElement.Accessibility.PROTECTED;
+ }
+ if ((modifiers & AccPrivileged) != 0) {
+ return IProgramElement.Accessibility.PRIVILEGED;
+ } else {
+ return IProgramElement.Accessibility.PACKAGE;
+ }
+ }
+
+ public String getBytecodeName() {
+ String s = (String) kvpairs.get("bytecodeName");
+ if (s == null) {
+ return UNDEFINED;
+ }
+ return s;
+ }
+
+ public void setBytecodeName(String s) {
+ fixMap();
+ kvpairs.put("bytecodeName", s);
+ }
+
+ public void setBytecodeSignature(String s) {
+ fixMap();
+ // Different kinds of format here. The one worth compressing starts with a '(':
+ // (La/b/c/D;Le/f/g/G;)Ljava/lang/String;
+ // maybe want to avoid generics initially.
+ // boolean worthCompressing = s.charAt(0) == '(' && s.indexOf('<') == -1 && s.indexOf('P') == -1; // starts parentheses and
+ // no
+ // // generics
+ // if (worthCompressing) {
+ // kvpairs.put("bytecodeSignatureCompressed", asm.compress(s));
+ // } else {
+ kvpairs.put("bytecodeSignature", s);
+ // }
+ }
+
+ public String getBytecodeSignature() {
+ String s = (String) kvpairs.get("bytecodeSignature");
+ // if (s == null) {
+ // List compressed = (List) kvpairs.get("bytecodeSignatureCompressed");
+ // if (compressed != null) {
+ // return asm.decompress(compressed, '/');
+ // }
+ // }
+ // if (s==null) return UNDEFINED;
+ return s;
+ }
+
+ public String getSourceSignature() {
+ return (String) kvpairs.get("sourceSignature");
+ }
+
+ public void setSourceSignature(String string) {
+ fixMap();
+ // System.err.println(name+" SourceSig=>"+string);
+ kvpairs.put("sourceSignature", string);
+ // sourceSignature = string;
+ }
+
+ public void setKind(Kind kind) {
+ this.kind = kind;
+ }
+
+ public void setCorrespondingType(String s) {
+ fixMap();
+ kvpairs.put("returnType", s);
+ // this.returnType = s;
+ }
+
+ public void setParentTypes(List<String> ps) {
+ fixMap();
+ kvpairs.put("parentTypes", ps);
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<String> getParentTypes() {
+ return (List<String>) (kvpairs == null ? null : kvpairs.get("parentTypes"));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setAnnotationType(String fullyQualifiedAnnotationType) {
+ fixMap();
+ kvpairs.put("annotationType", fullyQualifiedAnnotationType);
+ }
+
+ public void setAnnotationRemover(boolean isRemover) {
+ fixMap();
+ kvpairs.put("annotationRemover", isRemover);
+ }
+
+ public String getAnnotationType() {
+ if (isAnnotationRemover()) {
+ return null;
+ }
+ return (String) (kvpairs == null ? null : kvpairs.get("annotationType"));
+ }
+
+ public boolean isAnnotationRemover() {
+ if (kvpairs == null) {
+ return false;
+ }
+ Boolean b = (Boolean) kvpairs.get("annotationRemover");
+ if (b == null) {
+ return false;
+ }
+ return b.booleanValue();
+ }
+
+ public String[] getRemovedAnnotationTypes() {
+ if (!isAnnotationRemover()) {
+ return null;
+ }
+ String annotype = (String) (kvpairs == null ? null : kvpairs.get("annotationType"));
+ if (annotype == null) {
+ return null;
+ } else {
+ return new String[] { annotype };
+ }
+ }
+
+ public String getCorrespondingType() {
+ return getCorrespondingType(false);
+ }
+
+ public String getCorrespondingTypeSignature() {
+ String typename = (String) kvpairs.get("returnType");
+ if (typename == null) {
+ return null;
+ }
+ return nameToSignature(typename);
+ }
+
+ public static String nameToSignature(String name) {
+ int len = name.length();
+ if (len < 8) {
+ if (name.equals("byte")) {
+ return "B";
+ }
+ if (name.equals("char")) {
+ return "C";
+ }
+ if (name.equals("double")) {
+ return "D";
+ }
+ if (name.equals("float")) {
+ return "F";
+ }
+ if (name.equals("int")) {
+ return "I";
+ }
+ if (name.equals("long")) {
+ return "J";
+ }
+ if (name.equals("short")) {
+ return "S";
+ }
+ if (name.equals("boolean")) {
+ return "Z";
+ }
+ if (name.equals("void")) {
+ return "V";
+ }
+ if (name.equals("?")) {
+ return name;
+ }
+ }
+ if (name.endsWith("[]")) {
+ return "[" + nameToSignature(name.substring(0, name.length() - 2));
+ }
+ if (len != 0) {
+ // check if someone is calling us with something that is a signature already
+ assert name.charAt(0) != '[';
+
+ if (name.indexOf("<") == -1) {
+ // not parameterized
+ return new StringBuilder("L").append(name.replace('.', '/')).append(';').toString();
+ } else {
+ StringBuffer nameBuff = new StringBuffer();
+ int nestLevel = 0;
+ nameBuff.append("L");
+ for (int i = 0; i < name.length(); i++) {
+ char c = name.charAt(i);
+ switch (c) {
+ case '.':
+ nameBuff.append('/');
+ break;
+ case '<':
+ nameBuff.append("<");
+ nestLevel++;
+ StringBuffer innerBuff = new StringBuffer();
+ while (nestLevel > 0) {
+ c = name.charAt(++i);
+ if (c == '<') {
+ nestLevel++;
+ }
+ if (c == '>') {
+ nestLevel--;
+ }
+ if (c == ',' && nestLevel == 1) {
+ nameBuff.append(nameToSignature(innerBuff.toString()));
+ innerBuff = new StringBuffer();
+ } else {
+ if (nestLevel > 0) {
+ innerBuff.append(c);
+ }
+ }
+ }
+ nameBuff.append(nameToSignature(innerBuff.toString()));
+ nameBuff.append('>');
+ break;
+ case '>':
+ throw new IllegalStateException("Should by matched by <");
+ case ',':
+ throw new IllegalStateException("Should only happen inside <...>");
+ default:
+ nameBuff.append(c);
+ }
+ }
+ nameBuff.append(";");
+ return nameBuff.toString();
+ }
+ } else {
+ throw new IllegalArgumentException("Bad type name: " + name);
+ }
+ }
+
+ public String getCorrespondingType(boolean getFullyQualifiedType) {
+ String returnType = (String) kvpairs.get("returnType");
+ if (returnType == null) {
+ returnType = "";
+ }
+ if (getFullyQualifiedType) {
+ return returnType;
+ }
+ return trim(returnType);
+ }
+
+ /**
+ * Trim down fully qualified types to their short form (e.g. a.b.c.D<e.f.G> becomes D<G>)
+ */
+ public static String trim(String fqname) {
+ int i = fqname.indexOf("<");
+ if (i == -1) {
+ int lastdot = fqname.lastIndexOf('.');
+ if (lastdot == -1) {
+ return fqname;
+ } else {
+ return fqname.substring(lastdot + 1);
+ }
+ }
+ char[] charArray = fqname.toCharArray();
+ StringBuilder candidate = new StringBuilder(charArray.length);
+ StringBuilder complete = new StringBuilder(charArray.length);
+ for (char c : charArray) {
+ switch (c) {
+ case '.':
+ candidate.setLength(0);
+ break;
+ case '<':
+ case ',':
+ case '>':
+ complete.append(candidate).append(c);
+ candidate.setLength(0);
+ break;
+ default:
+ candidate.append(c);
+ }
+ }
+ complete.append(candidate);
+ return complete.toString();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List<IProgramElement> getChildren() {
+ return children;
+ }
+
+ public void setChildren(List<IProgramElement> children) {
+ this.children = children;
+ if (children == null) {
+ return;
+ }
+ for (Iterator<IProgramElement> it = children.iterator(); it.hasNext();) {
+ (it.next()).setParent(this);
+ }
+ }
+
+ public void addChild(IProgramElement child) {
+ if (children == null || children == Collections.EMPTY_LIST) {
+ children = new ArrayList<IProgramElement>();
+ }
+ children.add(child);
+ child.setParent(this);
+ }
+
+ public void addChild(int position, IProgramElement child) {
+ if (children == null || children == Collections.EMPTY_LIST) {
+ children = new ArrayList<IProgramElement>();
+ }
+ children.add(position, child);
+ child.setParent(this);
+ }
+
+ public boolean removeChild(IProgramElement child) {
+ child.setParent(null);
+ return children.remove(child);
+ }
+
+ public void setName(String string) {
+ name = string;
+ }
+
+ public IProgramElement walk(HierarchyWalker walker) {
+ if (children != null) {
+ for (IProgramElement child : children) {
+ walker.process(child);
+ }
+ }
+ return this;
+ }
+
+ public String toLongString() {
+ final StringBuffer buffer = new StringBuffer();
+ HierarchyWalker walker = new HierarchyWalker() {
+ private int depth = 0;
+
+ public void preProcess(IProgramElement node) {
+ for (int i = 0; i < depth; i++) {
+ buffer.append(' ');
+ }
+ buffer.append(node.toString());
+ buffer.append('\n');
+ depth += 2;
+ }
+
+ public void postProcess(IProgramElement node) {
+ depth -= 2;
+ }
+ };
+ walker.process(this);
+ return buffer.toString();
+ }
+
+ public void setModifiers(int i) {
+ this.modifiers = i;
+ }
+
+ /**
+ * Convenience mechanism for setting new modifiers which do not require knowledge of the private internal representation
+ *
+ * @param newModifier
+ */
+ public void addModifiers(IProgramElement.Modifiers newModifier) {
+ modifiers |= newModifier.getBit();
+ }
+
+ public String toSignatureString() {
+ return toSignatureString(true);
+ }
+
+ public String toSignatureString(boolean getFullyQualifiedArgTypes) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(name);
+
+ List<char[]> ptypes = getParameterTypes();
+ if (ptypes != null && (!ptypes.isEmpty() || this.kind.equals(IProgramElement.Kind.METHOD))
+ || this.kind.equals(IProgramElement.Kind.CONSTRUCTOR) || this.kind.equals(IProgramElement.Kind.ADVICE)
+ || this.kind.equals(IProgramElement.Kind.POINTCUT) || this.kind.equals(IProgramElement.Kind.INTER_TYPE_METHOD)
+ || this.kind.equals(IProgramElement.Kind.INTER_TYPE_CONSTRUCTOR)) {
+ sb.append('(');
+ for (Iterator<char[]> it = ptypes.iterator(); it.hasNext();) {
+ char[] arg = it.next();
+ if (getFullyQualifiedArgTypes) {
+ sb.append(arg);
+ } else {
+ int index = CharOperation.lastIndexOf('.', arg);
+ if (index != -1) {
+ sb.append(CharOperation.subarray(arg, index + 1, arg.length));
+ } else {
+ sb.append(arg);
+ }
+ }
+ if (it.hasNext()) {
+ sb.append(",");
+ }
+ }
+ sb.append(')');
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * TODO: move the "parent != null"==>injar heuristic to more explicit
+ */
+ public String toLinkLabelString() {
+ return toLinkLabelString(true);
+ }
+
+ public String toLinkLabelString(boolean getFullyQualifiedArgTypes) {
+ String label;
+ if (kind == Kind.CODE || kind == Kind.INITIALIZER) {
+ label = parent.getParent().getName() + ": ";
+ } else if (kind.isInterTypeMember()) {
+ if (shortITDNames) {
+ // if (name.indexOf('.')!=-1) return toLabelString().substring(name.indexOf('.')+1);
+ label = "";
+ } else {
+ int dotIndex = name.indexOf('.');
+ if (dotIndex != -1) {
+ return parent.getName() + ": " + toLabelString().substring(dotIndex + 1);
+ } else {
+ label = parent.getName() + '.';
+ }
+ }
+ } else if (kind == Kind.CLASS || kind == Kind.ASPECT || kind == Kind.INTERFACE) {
+ label = "";
+ } else if (kind.equals(Kind.DECLARE_PARENTS)) {
+ label = "";
+ } else {
+ if (parent != null) {
+ label = parent.getName() + '.';
+ } else {
+ label = "injar aspect: ";
+ }
+ }
+ label += toLabelString(getFullyQualifiedArgTypes);
+ return label;
+ }
+
+ public String toLabelString() {
+ return toLabelString(true);
+ }
+
+ public String toLabelString(boolean getFullyQualifiedArgTypes) {
+ String label = toSignatureString(getFullyQualifiedArgTypes);
+ String details = getDetails();
+ if (details != null) {
+ label += ": " + details;
+ }
+ return label;
+ }
+
+ public String getHandleIdentifier() {
+ return getHandleIdentifier(true);
+ }
+
+ public String getHandleIdentifier(boolean create) {
+ String h = handle;
+ if (null == handle && create) {
+ if (asm == null && name.equals("<build to view structure>")) {
+ h = "<build to view structure>";
+ } else {
+ try {
+ h = asm.getHandleProvider().createHandleIdentifier(this);
+ } catch (ArrayIndexOutOfBoundsException aioobe) {
+ throw new RuntimeException("AIOOBE whilst building handle for " + this, aioobe);
+ }
+ }
+ }
+ setHandleIdentifier(h);
+ return h;
+ }
+
+ public void setHandleIdentifier(String handle) {
+ this.handle = handle;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<String> getParameterNames() {
+ List<String> parameterNames = (List<String>) kvpairs.get("parameterNames");
+ return parameterNames;
+ }
+
+ public void setParameterNames(List<String> list) {
+ if (list == null || list.size() == 0) {
+ return;
+ }
+ fixMap();
+ kvpairs.put("parameterNames", list);
+ // parameterNames = list;
+ }
+
+ public List<char[]> getParameterTypes() {
+ List<char[]> l = getParameterSignatures();
+ if (l == null || l.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<char[]> params = new ArrayList<char[]>();
+ for (Iterator<char[]> iter = l.iterator(); iter.hasNext();) {
+ char[] param = iter.next();
+ params.add(NameConvertor.convertFromSignature(param));
+ }
+ return params;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<char[]> getParameterSignatures() {
+ List<char[]> parameters = (List<char[]>) kvpairs.get("parameterSigs");
+ return parameters;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<String> getParameterSignaturesSourceRefs() {
+ List<String> parameters = (List<String>) kvpairs.get("parameterSigsSourceRefs");
+ return parameters;
+ }
+
+ /**
+ * Set the parameter signatures for this method/constructor. The bit flags tell us if any were not singletypereferences in the
+ * the source. A singletypereference would be 'String' - whilst a qualifiedtypereference would be 'java.lang.String' - this has
+ * an effect on the handles.
+ */
+ public void setParameterSignatures(List<char[]> list, List<String> sourceRefs) {
+ fixMap();
+ if (list == null || list.size() == 0) {
+ kvpairs.put("parameterSigs", Collections.EMPTY_LIST);
+ } else {
+ kvpairs.put("parameterSigs", list);
+ }
+ if (sourceRefs != null && sourceRefs.size() != 0) {
+ kvpairs.put("parameterSigsSourceRefs", sourceRefs);
+ }
+ }
+
+ public String getDetails() {
+ String details = (String) kvpairs.get("details");
+ return details;
+ }
+
+ public void setDetails(String string) {
+ fixMap();
+ kvpairs.put("details", string);
+ }
+
+ public void setFormalComment(String txt) {
+ if (txt != null && txt.length() > 0) {
+ fixMap();
+ kvpairs.put("formalComment", txt);
+ }
+ }
+
+ private void fixMap() {
+ if (kvpairs == Collections.EMPTY_MAP) {
+ kvpairs = new HashMap<String, Object>();
+ }
+ }
+
+ public void setExtraInfo(ExtraInformation info) {
+ fixMap();
+ kvpairs.put("ExtraInformation", info);
+ }
+
+ public ExtraInformation getExtraInfo() {
+ return (ExtraInformation) kvpairs.get("ExtraInformation");
+ }
+
+ public boolean isAnnotationStyleDeclaration() {
+ return kvpairs.get("annotationStyleDeclaration") != null;
+ }
+
+ public void setAnnotationStyleDeclaration(boolean b) {
+ if (b) {
+ fixMap();
+ kvpairs.put("annotationStyleDeclaration", "true");
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public Map<String, List<String>> getDeclareParentsMap() {
+ Map<String, List<String>> s = (Map<String, List<String>>) kvpairs.get("declareparentsmap");
+ return s;
+ }
+
+ public void setDeclareParentsMap(Map<String, List<String>> newmap) {
+ fixMap();
+ kvpairs.put("declareparentsmap", newmap);
+ }
+
+ public void addFullyQualifiedName(String fqname) {
+ fixMap();
+ kvpairs.put("itdfqname", fqname);
+ }
+
+ public String getFullyQualifiedName() {
+ return (String) kvpairs.get("itdfqname");
+ }
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/Relationship.java b/asm/src/main/java/org/aspectj/asm/internal/Relationship.java
new file mode 100644
index 000000000..8bd4a7e66
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/Relationship.java
@@ -0,0 +1,86 @@
+/* *******************************************************************
+ * Copyright (c) 2003,2010 Contributors
+ * 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:
+ * Mik Kersten initial implementation
+ * Andy Clement Extensions for better IDE representation
+ * ******************************************************************/
+package org.aspectj.asm.internal;
+
+import java.util.List;
+
+import org.aspectj.asm.IRelationship;
+
+/**
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public class Relationship implements IRelationship {
+
+ private static final long serialVersionUID = 3855166397957609120L;
+
+ private String name;
+ private Kind kind;
+ private boolean isAffects;
+ private String sourceHandle;
+ private List<String> targets;
+ private boolean hasRuntimeTest;
+
+ public Relationship(String name, Kind kind, String sourceHandle, List<String> targets, boolean runtimeTest) {
+ this.name = name;
+ this.isAffects = name.equals("advises") || name.equals("declares on") || name.equals("softens")
+ || name.equals("matched by") || name.equals("declared on") || name.equals("annotates");
+ this.kind = kind;
+ this.sourceHandle = sourceHandle;
+ this.targets = targets;
+ this.hasRuntimeTest = runtimeTest;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Kind getKind() {
+ return kind;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public String getSourceHandle() {
+ return sourceHandle;
+ }
+
+ // TODO should be a Set and not a list
+ public List<String> getTargets() {
+ return targets;
+ }
+
+ public void addTarget(String handle) {
+ if (targets.contains(handle)) {
+ return;
+ }
+ targets.add(handle);
+ }
+
+ public boolean hasRuntimeTest() {
+ return hasRuntimeTest;
+ }
+
+ /**
+ * Return the direction of the relationship. It might be affects or affected-by. The direction enables the incremental model
+ * repair code to do the right thing.
+ *
+ * @return true if is an affects relationship: advises/declareson/softens/matchedby/declaredon/annotates
+ */
+ public boolean isAffects() {
+ return isAffects;
+ }
+
+}
diff --git a/asm/src/main/java/org/aspectj/asm/internal/RelationshipMap.java b/asm/src/main/java/org/aspectj/asm/internal/RelationshipMap.java
new file mode 100644
index 000000000..1fea7bb85
--- /dev/null
+++ b/asm/src/main/java/org/aspectj/asm/internal/RelationshipMap.java
@@ -0,0 +1,150 @@
+/* *******************************************************************
+ * Copyright (c) 2003,2010 Contributors
+ * 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:
+ * Mik Kersten initial implementation
+ * Andy Clement
+ * ******************************************************************/
+
+package org.aspectj.asm.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.aspectj.asm.IProgramElement;
+import org.aspectj.asm.IRelationship;
+import org.aspectj.asm.IRelationship.Kind;
+import org.aspectj.asm.IRelationshipMap;
+
+/**
+ * @author Mik Kersten
+ * @author Andy Clement
+ */
+public class RelationshipMap extends HashMap<String, List<IRelationship>> implements IRelationshipMap {
+
+ private static final long serialVersionUID = 496638323566589643L;
+
+ public RelationshipMap() {
+ }
+
+ public List<IRelationship> get(String handle) {
+ List<IRelationship> relationships = super.get(handle);
+ if (relationships == null) {
+ return null;
+ } else {
+ return relationships;
+ }
+ }
+
+ public List<IRelationship> get(IProgramElement source) {
+ return get(source.getHandleIdentifier());
+ }
+
+ public IRelationship get(String source, IRelationship.Kind kind, String relationshipName, boolean runtimeTest,
+ boolean createIfMissing) {
+ List<IRelationship> relationships = get(source);
+ if (relationships == null) {
+ if (!createIfMissing) {
+ return null;
+ }
+ relationships = new ArrayList<IRelationship>();
+ IRelationship rel = new Relationship(relationshipName, kind, source, new ArrayList<String>(), runtimeTest);
+ relationships.add(rel);
+
+ super.put(source, relationships);
+ return rel;
+ } else {
+ for (Iterator<IRelationship> it = relationships.iterator(); it.hasNext();) {
+ IRelationship curr = it.next();
+ if (curr.getKind() == kind && curr.getName().equals(relationshipName) && curr.hasRuntimeTest() == runtimeTest) {
+ return curr;
+ }
+ }
+ if (createIfMissing) {
+ // At this point we did find some relationships for 'source' but not one that looks like what we are
+ // after (either the kind or the name or the dynamictests setting don't match)
+ IRelationship rel = new Relationship(relationshipName, kind, source, new ArrayList<String>(), runtimeTest);
+ relationships.add(rel);
+ return rel;
+ }
+ }
+ return null;
+ }
+
+ public IRelationship get(IProgramElement source, IRelationship.Kind kind, String relationshipName, boolean runtimeTest,
+ boolean createIfMissing) {
+ return get(source.getHandleIdentifier(), kind, relationshipName, runtimeTest, createIfMissing);
+ }
+
+ public IRelationship get(IProgramElement source, Kind kind, String relationshipName) {
+ return get(source, kind, relationshipName, false, true);
+ }
+
+ public boolean remove(String source, IRelationship relationship) {
+ List<IRelationship> list = super.get(source);
+ if (list != null) {
+ return list.remove(relationship);
+ // boolean matched = false;
+ // for (Iterator it = list.iterator(); it.hasNext(); ) {
+ // IRelationship curr = (IRelationship)it.next();
+ // if (curr.getName().equals(relationship.getName())) {
+ // curr.getTargets().addAll(relationship.getTargets());
+ // matched = true;
+ // }
+ // }
+ // if (!matched) list.remove(relationship);
+ }
+ return false;
+ }
+
+ public void removeAll(String source) {
+ super.remove(source);
+ }
+
+ public void put(String source, IRelationship relationship) {
+ List<IRelationship> existingRelationships = super.get(source);
+ if (existingRelationships == null) {
+ // new entry
+ existingRelationships = new ArrayList<IRelationship>();
+ existingRelationships.add(relationship);
+ super.put(source, existingRelationships);
+ } else {
+ boolean matched = false;
+ for (IRelationship existingRelationship : existingRelationships) {
+ if (existingRelationship.getName().equals(relationship.getName())
+ && existingRelationship.getKind() == relationship.getKind()) {
+ existingRelationship.getTargets().addAll(relationship.getTargets());
+ matched = true;
+ }
+ }
+ if (matched) {
+ // bug?
+ System.err.println("matched = true");
+ }
+ if (matched) {
+ existingRelationships.add(relationship); // Is this a bug, will it give us double entries?
+ }
+ }
+ }
+
+ public void put(IProgramElement source, IRelationship relationship) {
+ put(source.getHandleIdentifier(), relationship);
+ }
+
+ public void clear() {
+ super.clear();
+ }
+
+ public Set<String> getEntries() {
+ return keySet();
+ }
+
+}