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;
public class AspectJElementHierarchy implements IHierarchy {
private static final long serialVersionUID = 6462734311117048620L;
private transient AsmManager asm;
protected IProgramElement root = null;
protected String configFile = null;
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) {
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;
}
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;
}
public IProgramElement findElementForType(String packageName, String typeName) {
synchronized (this) {
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) {
for (IProgramElement fileNode : pkg.getChildren()) {
IProgramElement cNode = findClassInNodes(fileNode.getChildren(), typeName, typeName);
if (cNode != null) {
typeMap.put(key, cNode);
return cNode;
}
}
}
}
return null;
}
public List<IProgramElement> findMatchingPackages(String packagename) {
List<IProgramElement> children = root.getChildren();
if (children.size() == 0) {
return Collections.emptyList();
}
if ((children.get(0)).getKind() == IProgramElement.Kind.SOURCE_FOLDER) {
String searchPackageName = (packagename == null ? "" : packagename);
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);
}
}
}
}
return matchingPackageNodes;
} else {
if (packagename == null) {
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) {
if (possiblePackage.getName().equals("binaries")) {
for (IProgramElement possiblePackage2 : possiblePackage.getChildren()) {
if (possiblePackage2.getKind() == IProgramElement.Kind.PACKAGE
&& possiblePackage2.getName().equals(packagename)) {
result.add(possiblePackage2);
break;
}
}
}
}
}
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;
}
public IProgramElement findElementForSourceFile(String sourceFile) {
try {
if (!isValid() || sourceFile == null) {
return IHierarchy.NO_STRUCTURE;
} else {
String correctedPath = asm.getCanonicalFilePath(new File(sourceFile));
IProgramElement node = (IProgramElement) findInFileMap(correctedPath);
if (node != null) {
return node;
} else {
return createFileStructureNode(correctedPath);
}
}
} catch (Exception e) {
return IHierarchy.NO_STRUCTURE;
}
}
public IProgramElement findElementForSourceLine(ISourceLocation location) {
try {
return findElementForSourceLine(asm.getCanonicalFilePath(location.getSourceFile()), location.getLine());
} catch (Exception e) {
return null;
}
}
public IProgramElement findElementForSourceLine(String sourceFilePath, int lineNumber) {
String canonicalSFP = asm.getCanonicalFilePath(new File(sourceFilePath));
IProgramElement node = findNodeForSourceFile(root, canonicalSFP);
if (node == null) {
return createFileStructureNode(sourceFilePath);
}
IProgramElement closernode = findCloserMatchForLineNumber(node, lineNumber);
if (closernode == null) {
return node;
} else {
return closernode;
}
}
public IProgramElement findNodeForSourceFile(IProgramElement node, String sourcefilePath) {
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;
} else {
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) {
int lastSlash = sourceFilePath.lastIndexOf('\\');
if (lastSlash == -1) {
lastSlash = sourceFilePath.lastIndexOf('/');
}
int i = sourceFilePath.lastIndexOf('!');
int j = sourceFilePath.indexOf(".class");
if (i > lastSlash && i != -1 && j != -1) {
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.addChild(NO_STRUCTURE);
return fileNode;
}
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) {
IProgramElement evenCloserMatch = findCloserMatchForLineNumber(child, lineno);
if (evenCloserMatch == null) {
return child;
} else {
return evenCloserMatch;
}
} else if (child.getKind().isType()) {
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) {
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()));
}
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);
}
public IProgramElement findElementForHandleOrCreate(String handle, boolean create) {
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)) {
IProgramElement childSearch = findElementForHandle(node, handle);
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) {
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);
}
}
}
public void updateHandleMap(Set<String> deletedFiles) {
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 "";
}
}