diff --git a/src/main/java/org/walkmod/javalang/compiler/types/SymbolTypesClassLoader.java b/src/main/java/org/walkmod/javalang/compiler/types/CachedClassLoader.java similarity index 56% rename from src/main/java/org/walkmod/javalang/compiler/types/SymbolTypesClassLoader.java rename to src/main/java/org/walkmod/javalang/compiler/types/CachedClassLoader.java index 39419d0..50a13ab 100644 --- a/src/main/java/org/walkmod/javalang/compiler/types/SymbolTypesClassLoader.java +++ b/src/main/java/org/walkmod/javalang/compiler/types/CachedClassLoader.java @@ -14,35 +14,59 @@ */ package org.walkmod.javalang.compiler.types; -import java.util.HashMap; -import java.util.Map; +import org.walkmod.javalang.ast.SymbolData; import org.walkmod.javalang.ast.type.Type; import org.walkmod.javalang.compiler.symbols.ASTSymbolTypeResolver; -import org.walkmod.javalang.compiler.symbols.SymbolType; -public class SymbolTypesClassLoader extends ClassLoader { +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + +public class CachedClassLoader extends ClassLoader { + + public static final Map> PRIMITIVES; + + static { + + Map> aux = new HashMap<>(); + // static block to resolve primitive classes + aux.put("boolean", boolean.class); + aux.put("int", int.class); + aux.put("long", long.class); + aux.put("double", double.class); + aux.put("char", char.class); + aux.put("float", float.class); + aux.put("short", short.class); + aux.put("byte", byte.class); + aux.put("void", void.class); + PRIMITIVES = Collections.unmodifiableMap(aux); + } + private Map> cache = new HashMap>(); - public SymbolTypesClassLoader(ClassLoader parent) { + public CachedClassLoader(IndexedURLClassLoader parent) { super(parent); - cache.put("boolean", boolean.class); - cache.put("int", int.class); - cache.put("long", long.class); - cache.put("double", double.class); - cache.put("char", char.class); - cache.put("float", float.class); - cache.put("short", short.class); - cache.put("byte", byte.class); - cache.put("void", void.class); + + cache.putAll(PRIMITIVES); + } public Class loadClass(Type t) throws ClassNotFoundException { - return ASTSymbolTypeResolver.getInstance().valueOf(t).getClazz(); } - public Class loadClass(SymbolType t) throws ClassNotFoundException { + + public List getPackageContents(String packageName) { + return ((IndexedURLClassLoader)getParent()).getPackageClasses(packageName); + } + + public List getSDKContents(String packageName) { + return ((IndexedURLClassLoader)getParent()).getSDKContents(packageName); + } + + public Class loadClass(SymbolData t) throws ClassNotFoundException { String name = t.getName(); Class cachedClass = cache.get(name); if (cachedClass == null) { diff --git a/src/main/java/org/walkmod/javalang/compiler/types/IndexedURLClassLoader.java b/src/main/java/org/walkmod/javalang/compiler/types/IndexedURLClassLoader.java new file mode 100644 index 0000000..49c67aa --- /dev/null +++ b/src/main/java/org/walkmod/javalang/compiler/types/IndexedURLClassLoader.java @@ -0,0 +1,116 @@ +package org.walkmod.javalang.compiler.types; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.AllPermission; +import java.security.CodeSource; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.ProtectionDomain; +import java.security.cert.Certificate; +import java.util.List; + + +/** + * A custom ClassLoader that indexes the contents of classpath elements, for faster class locating. + * + * The standard URLClassLoader does a linear scan of the classpath for each class or resource, which + * becomes prohibitively expensive for classpaths with many elements. + */ +public class IndexedURLClassLoader extends ClassLoader { + + /* The search path for classes and resources */ + private final IndexedURLClassPath ucp; + + public IndexedURLClassLoader(ClassLoader parent) { + // parent is the default system classloader, which we want to bypass entirely in + // the delegation hierarchy, so we make our parent that thing's parent instead. + super(parent.getParent()); + this.ucp = new IndexedURLClassPath(getClassPathURLs()); + } + + public URL[] getURLs() { + return this.ucp.getURLs(); + } + + public IndexedURLClassLoader(URL[] urls, ClassLoader parent) { + super(parent); + this.ucp = new IndexedURLClassPath(urls); + } + + public List getPackageClasses(String packageName) { + return ucp.listPackageContents(packageName); + } + + public List getSDKContents(String packageName) { + return ucp.listSDKContents(packageName); + } + + private static URL[] getClassPathURLs() { + try { + String[] paths = System.getProperties().getProperty("java.class.path").split(File.pathSeparator); + URL[] urls = new URL[paths.length]; + for (int i = 0; i < paths.length; ++i) { + urls[i] = new File(paths[i]).toURI().toURL(); + } + return urls; + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + @Override + public URL findResource(String name) { + return ucp.findResource(name); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + try { + String path = name.replace('.', '/').concat(".class"); + URL res = ucp.findResource(path); + if (res != null) { + int i = name.lastIndexOf('.'); + if (i != -1) { + String pkgname = name.substring(0, i); + // Check if package already loaded. + Package pkg = getPackage(pkgname); + if (pkg == null) { + definePackage(pkgname, null, null, null, null, null, null, null); + } + } + byte[] data = loadResource(res); + // Add a CodeSource via a ProtectionDomain, as code may use this to find its own jars. + CodeSource cs = new CodeSource(res, (Certificate[])null); + PermissionCollection pc = new Permissions(); + pc.add(new AllPermission()); + ProtectionDomain pd = new ProtectionDomain(cs, pc); + return defineClass(name, data, 0, data.length, pd); + } else { + throw new ClassNotFoundException(String.format("IndexedURLClassLoader failed to read class %s", name)); + } + } catch (IOException e) { + throw new ClassNotFoundException(String.format("IndexedURLClassLoader failed to read class %s", name), e); + } + } + + private byte[] loadResource(URL toDownload) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + byte[] chunk = new byte[4096]; + int bytesRead; + InputStream stream = toDownload.openStream(); + try { + while ((bytesRead = stream.read(chunk)) > 0) { + outputStream.write(chunk, 0, bytesRead); + } + } finally { + stream.close(); + } + return outputStream.toByteArray(); + } +} \ No newline at end of file diff --git a/src/main/java/org/walkmod/javalang/compiler/types/IndexedURLClassPath.java b/src/main/java/org/walkmod/javalang/compiler/types/IndexedURLClassPath.java new file mode 100644 index 0000000..1881e3a --- /dev/null +++ b/src/main/java/org/walkmod/javalang/compiler/types/IndexedURLClassPath.java @@ -0,0 +1,157 @@ +package org.walkmod.javalang.compiler.types; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * A modified URLClassPath that indexes the contents of classpath elements, for faster resource locating. + * + * The standard URLClassPath does a linear scan of the classpath for each resource, which becomes + * prohibitively expensive for classpaths with many elements. + */ +public class IndexedURLClassPath { + + private final URL[] urls; + private int lastIndexed = 0; + + private static URL RT_JAR; + // Map from resource name to URLClassPath to delegate loading that resource to. + private final PathTree index = new PathTree(); + + static { + + URL foundJar = null; + // static block to resolve java.lang package classes + String[] bootPath = System.getProperties().get("sun.boot.class.path").toString() + .split(File.pathSeparator); + for (String lib : bootPath) { + if (lib.endsWith("rt.jar")) { + File f = new File(lib); + try { + foundJar = f.toURI().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException("The java.lang classes cannot be loaded", e.getCause()); + } + } + } + RT_JAR = foundJar; + } + + public IndexedURLClassPath(final URL[] urls) { + this.urls = urls; + } + + + public URL findResource(final String name) { + URL delegate = index.get(name); + if (delegate == null) { + if (lastIndexed < urls.length) { + indexURLs(urls[lastIndexed]); + lastIndexed ++; + return findResource(name); + } + return null; + } + try { + return new URL(delegate, name); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + } + + + public List listPackageContents(final String packageName) { + + String packageFile = packageName.replaceAll("\\.", File.separator); + while (lastIndexed < urls.length) { + indexURLs(urls[lastIndexed]); + lastIndexed ++; + } + return index.list(packageFile); + } + + public List listSDKContents(final String packageName) { + String packageFile = packageName.replaceAll("\\.", File.separator); + indexURLs(RT_JAR); + return index.list(packageFile); + } + + + private void indexURLs(URL url) { + try { + if (!"file".equals(url.getProtocol())) { + throw new RuntimeException("Classpath element is not a file: " + url); + } + File root = new File(url.getPath()); + + if (root.isDirectory()) { + String rootPath = root.getPath(); + if (!rootPath.endsWith(File.separator)) { + rootPath = rootPath + File.separator; + } + addFilesToIndex(rootPath.length(), root, url); + } else if (root.isFile() && root.getName().endsWith(".jar")) { + JarFile jarFile = new JarFile(root); + try { + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + maybeIndexResource(name, url); + } + } finally { + jarFile.close(); + } + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private void addFilesToIndex(int basePrefixLen, File f, URL delegate) throws IOException { + if (f.isDirectory()) { + if (f.getPath().length() > basePrefixLen) { // Don't index the root itself. + String relPath = f.getPath().substring(basePrefixLen); + maybeIndexResource(relPath, delegate); + } + File[] directoryEntries = f.listFiles(); + + if (directoryEntries == null) { + throw new RuntimeException("The list of directories of " + f.getAbsolutePath() + " is null"); + } + for (int i = 0; i < directoryEntries.length; ++i) { + addFilesToIndex(basePrefixLen, directoryEntries[i], delegate); + } + } else { + String relPath = f.getPath().substring(basePrefixLen); + maybeIndexResource(relPath, delegate); + } + } + + public URL[] getURLs() { + return urls; + } + + /** + * Callers may request the directory itself as a resource, and may + * do so with or without trailing slashes. We do this in a while-loop + * in case the classpath element has multiple superfluous trailing slashes. + * @param relPath relative path + * @param delegate value to insert + */ + private void maybeIndexResource(String relPath, URL delegate) { + + if (!index.containsKey(relPath)) { + index.put(relPath, delegate); + if (relPath.endsWith(File.separator)) { + maybeIndexResource(relPath.substring(0, relPath.length() - File.separator.length()), delegate); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/org/walkmod/javalang/compiler/types/PathTree.java b/src/main/java/org/walkmod/javalang/compiler/types/PathTree.java new file mode 100644 index 0000000..757889a --- /dev/null +++ b/src/main/java/org/walkmod/javalang/compiler/types/PathTree.java @@ -0,0 +1,114 @@ +package org.walkmod.javalang.compiler.types; + +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +public class PathTree { + + private final Map> children; + private final TreeValue value; + + public PathTree() { + this(new HashMap>(), null); + } + + public PathTree(Map> children, TreeValue value) { + this.children = children; + this.value = value; + } + + public void put(String key, T value) { + insertTree(key, Arrays.asList(key.split(File.separator)), new TreeValue(key, value)); + } + + private void insertTree(String path, List keys, TreeValue value) { + if (keys.isEmpty()) { + return; + } + + String currentKey = keys.get(0); + PathTree tree = children.get(currentKey); + + if (tree != null) { + tree.insertTree(path, keys.subList(1, keys.size()), value); + } else { + TreeValue valueToInsert = null; + if (keys.size() == 1) { + valueToInsert = value; + } + + PathTree aux = new PathTree(new HashMap(), valueToInsert); + children.put(currentKey, aux); + aux.insertTree(path, keys.subList(1, keys.size()), value); + } + } + + public List list(String key) { + if ("".equals(key)) { + return findAll(Collections.emptyList()); + } + return findAll(Arrays.asList(key.split(File.separator))); + } + + private List findAll(List keys) { + List list = new LinkedList(); + if (keys.isEmpty()) { + if (value != null) { + list.add(value.path); + } + Iterator> it = children.values().iterator(); + while (it.hasNext()) { + PathTree tree = it.next(); + if (tree.value != null) { + list.add(tree.value.path); + } + } + } else { + String next = keys.get(0); + PathTree tree = children.get(next); + if (tree != null) { + return tree.findAll(keys.subList(1, keys.size())); + } + } + return list; + } + + public T get(String key) { + return findOne(Arrays.asList(key.split(File.separator))); + } + + public boolean containsKey(String key) { + return get(key) != null; + } + + private T findOne(List keys) { + if (keys.isEmpty()) { + if (value != null) { + return value.delegate; + } + return null; + } + String next = keys.get(0); + PathTree tree = children.get(next); + if (tree != null) { + return tree.findOne(keys.subList(1, keys.size())); + } + return null; + } + + class TreeValue { + final String path; + final T delegate; + + public TreeValue(String path, T delegate) { + this.path = path; + this.delegate = delegate; + } + } +} diff --git a/src/main/java/org/walkmod/javalang/compiler/types/TypesLoaderVisitor.java b/src/main/java/org/walkmod/javalang/compiler/types/TypesLoaderVisitor.java index 49b5c16..055a4ab 100644 --- a/src/main/java/org/walkmod/javalang/compiler/types/TypesLoaderVisitor.java +++ b/src/main/java/org/walkmod/javalang/compiler/types/TypesLoaderVisitor.java @@ -15,21 +15,11 @@ package org.walkmod.javalang.compiler.types; import java.io.File; -import java.io.IOException; import java.lang.reflect.Modifier; -import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; + import org.walkmod.javalang.ast.CompilationUnit; import org.walkmod.javalang.ast.ImportDeclaration; @@ -67,37 +57,14 @@ */ public class TypesLoaderVisitor extends VoidVisitorAdapter { - private enum FileFlag { - Directory, Readable; - - private final int mask; - - FileFlag() { - this.mask = 1 << ordinal(); - } - - public boolean isSetIn(int flags) { - return (mask & flags) != 0; - } - } - private String contextName = null; private String packageName = null; - private static SymbolTypesClassLoader classLoader = - new SymbolTypesClassLoader(Thread.currentThread().getContextClassLoader()); - - private static Set defaultJavaLangClasses = new HashSet(); - - private static Map> primitiveClasses = new HashMap>(); - - private static Map> jarFileClassNames = new HashMap>(); - - /** conceptually a Map> */ - private static Map fileFlagCache = new HashMap(); + private static CachedClassLoader classLoader = + new CachedClassLoader(new IndexedURLClassLoader(Thread.currentThread().getContextClassLoader())); - private static JarFile SDKJar; + private static ClassLoader applicationClassLoader = null; private SymbolTable symbolTable = null; @@ -107,23 +74,52 @@ public boolean isSetIn(int flags) { private Node startingNode = null; + private static List SDKFiles = new LinkedList<>(); + public TypesLoaderVisitor(SymbolTable symbolTable, SymbolActionProvider actionProvider, List actions) { this.symbolTable = symbolTable; this.actions = actions; this.actionProvider = actionProvider; - for (String defaultType : primitiveClasses.keySet()) { - SymbolType st = new SymbolType(primitiveClasses.get(defaultType)); + } + + private void loadPrimitives() { + for (String defaultType : CachedClassLoader.PRIMITIVES.keySet()) { + SymbolType st = new SymbolType(CachedClassLoader.PRIMITIVES.get(defaultType)); symbolTable.pushSymbol(defaultType, ReferenceType.TYPE, st, null, actions); } - for (String defaultType : defaultJavaLangClasses) { + } + + private void loadLangPackage() { + for(String sdkFile: SDKFiles) { + if (isClassFile(sdkFile) && !isAnonymousClass(sdkFile)) { + String asClass = sdkFile.replaceAll(File.separator, "\\."); + String fullName = asClass.substring(0, asClass.length() - 6); //extract .class + symbolTable.pushSymbol(resolveSymbolName(fullName, false, false), ReferenceType.TYPE, new SymbolType(fullName), null, actions); + } + } + } - SymbolType st = new SymbolType(defaultType); - symbolTable.pushSymbol(resolveSymbolName(defaultType, false, false), ReferenceType.TYPE, st, null, actions); + private void addTypes(List files, List actions, Node node) { + for (String classFile: files) { + if (isClassFile(classFile) && !isAnonymousClass(classFile)) { + String asClass = classFile.replaceAll(File.separator, "\\."); + String fullName = asClass.substring(0, asClass.length() - 6); //extract .class + addType(fullName, false, node, actions); + } } } + private boolean isClassFile(String classFile) { + return classFile.endsWith(".class"); + } + + private boolean isAnonymousClass(String classFile) { + String[] split = classFile.split("\\$\\d"); + return split.length > 1; + } + /** * Infers the simple name to be pushed into the symbol table * @@ -165,12 +161,23 @@ static String resolveSymbolName(final String name, final boolean imported, final } public void setClassLoader(ClassLoader cl) { - if (classLoader.getParent() != cl) { - classLoader = new SymbolTypesClassLoader(cl); + if (applicationClassLoader != cl) { + applicationClassLoader = cl; + if (cl instanceof URLClassLoader) { + URLClassLoader aux = (URLClassLoader) cl; + IndexedURLClassLoader icl = new IndexedURLClassLoader(aux.getURLs(), aux.getParent()); + classLoader = new CachedClassLoader(icl); + + } else { + classLoader = new CachedClassLoader(new IndexedURLClassLoader(cl)); + } + SDKFiles = classLoader.getSDKContents("java.lang"); } + + } - public static SymbolTypesClassLoader getClassLoader() { + public static CachedClassLoader getClassLoader() { return classLoader; } @@ -547,191 +554,16 @@ private void loadInnerClass(String name, boolean imported, Node node, List classNames = getClassNamesForJar(jarFile, jar, directory); - - for (String name : classNames) { - String[] split = name.split("\\$\\d"); - if (split.length == 1) { - addType(name, false, node, actions); - } - } - } - - private JarFile jarFile(File file) { - try { - return new JarFile(file); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - /** - * Note: only one of jarFile or jar is set - */ - private List getClassNamesForJar(File jarFile, JarFile jar, String directory) { - final String jarFilePath = jarFile != null ? jarFile.getPath() : jar.getName(); - final String key = jarFilePath + "@" + directory; - final List classNames = jarFileClassNames.get(key); - if (classNames != null) { - return classNames; - } - List allClassNames = jarFileClassNames.get(jarFilePath); - if (allClassNames == null) { - // creating a JarFile instance is costly so only do it when needed. - allClassNames = readClassNamesFromJar(jar != null ? jar : jarFile(jarFile)); - jarFileClassNames.put(jarFilePath, allClassNames); - } - final List selectedClassNames = selectClassNamesByPackage(allClassNames, directory.replace("/", ".")); - jarFileClassNames.put(key, selectedClassNames); - return selectedClassNames; - } - - private List readClassNamesFromJar(JarFile jar) { - List classNames = new ArrayList(); - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String name = entry.getName(); - - if (name.endsWith(".class")) { - - name = name.replaceAll("/", "."); - name = name.substring(0, name.length() - 6); - - classNames.add(name); - } - } - return Collections.unmodifiableList(classNames); - } - - private List selectClassNamesByPackage(List allClassNames, String packageName) { - List classNames = new ArrayList(); - for (String name : allClassNames) { - int index = name.indexOf(packageName); - - if (index != -1 && name.lastIndexOf(".") == packageName.length()) { - classNames.add(name); - } - } - return Collections.unmodifiableList(classNames); - } - private void loadClassesFromPackage(String packageName, List actions, Node node) { - - URL[] urls = ((URLClassLoader) classLoader.getParent()).getURLs(); - String directory = packageName.replaceAll("\\.", "/"); - - loadClassesFromJar(null, SDKJar, directory, node); - - for (URL url : urls) { - File file = new File(url.getFile()); - - final boolean isDirectory = isDirectory(file); - final boolean canRead = canRead(file); - if (!isDirectory && canRead) { - // it is a jar file - loadClassesFromJar(file, null, directory, node); - - } else if (isDirectory && canRead) { - File aux = new File(file, directory); - if (aux.exists() && isDirectory(aux)) { - File[] contents = aux.listFiles(); - if (contents != null) { - for (File resource : contents) { - if (resource.getName().endsWith(".class")) { - String simpleName = - resource.getName().substring(0, resource.getName().lastIndexOf(".class")); - String name = simpleName; - if (!"".equals(packageName)) { - name = packageName + "." + simpleName; - } - - String[] split = resource.getName().split("\\$\\d"); - if (split.length == 1) { - addType(name, false, node, actions); - } - - } - } - } - } - } - } - - } - - private boolean canRead(File file) { - return FileFlag.Readable.isSetIn(cachedFileFlags(file)); - } - - private boolean isDirectory(File file) { - return FileFlag.Directory.isSetIn(cachedFileFlags(file)); - } - - private int cachedFileFlags(File file) { - final String absolutePath = file.getAbsolutePath(); - Integer v = fileFlagCache.get(absolutePath); - if (v == null) { - v = (file.isDirectory() ? FileFlag.Directory.mask : 0) | (file.canRead() ? FileFlag.Readable.mask : 0); - fileFlagCache.put(absolutePath, v); - } - return v; - } - - static { - // static block to resolve java.lang package classes - String[] bootPath = System.getProperties().get("sun.boot.class.path").toString() - .split(Character.toString(File.pathSeparatorChar)); - for (String lib : bootPath) { - if (lib.endsWith("rt.jar")) { - File f = new File(lib); - try { - JarFile jar = new JarFile(f); - SDKJar = jar; - Enumeration entries = jar.entries(); - while (entries.hasMoreElements()) { - JarEntry entry = entries.nextElement(); - String name = entry.getName(); - - int index = name.indexOf("java/lang/"); - - if (index != -1 && name.lastIndexOf("/") == "java/lang/".length() - 1) { - - name = name.replaceAll("/", "."); - name = name.substring(0, name.length() - 6); - String[] split = name.split("\\$\\d"); - if (split.length == 1) { - defaultJavaLangClasses.add(split[0]); - } - } - } - - } catch (IOException e) { - throw new RuntimeException("The java.lang classes cannot be loaded", e.getCause()); - } - } - } - } - - static { - // static block to resolve primitive classes - primitiveClasses.put("boolean", boolean.class); - primitiveClasses.put("int", int.class); - primitiveClasses.put("long", long.class); - primitiveClasses.put("double", double.class); - primitiveClasses.put("char", char.class); - primitiveClasses.put("float", float.class); - primitiveClasses.put("short", short.class); - primitiveClasses.put("byte", byte.class); + addTypes(classLoader.getPackageContents(packageName), actions, node); } @Override public void visit(CompilationUnit cu, T context) { + loadPrimitives(); + loadLangPackage(); + if (cu.getPackage() != null) { contextName = cu.getPackage().getName().toString(); @@ -754,26 +586,6 @@ public void visit(CompilationUnit cu, T context) { startingNode = null; } - public Class loadClass(Type t) throws ClassNotFoundException { - - Class result = ASTSymbolTypeResolver.getInstance().valueOf(t).getClazz(); - if (result == null) { - throw new ClassNotFoundException("The class " + t.toString() + " is not found"); - } - return result; - } - - public String getFullName(TypeDeclaration type) { - String name = type.getName(); - Node parentNode = type.getParentNode(); - // if it is an inner class, we build the unique name - while (parentNode instanceof TypeDeclaration) { - name = ((TypeDeclaration) parentNode).getName() + "." + name; - parentNode = parentNode.getParentNode(); - } - return symbolTable.findSymbol(name, ReferenceType.TYPE).getType().getName(); - } - public void clear() { packageName = null; contextName = null; diff --git a/src/test/java/org/walkmod/javalang/compiler/types/PathTreeTest.java b/src/test/java/org/walkmod/javalang/compiler/types/PathTreeTest.java new file mode 100644 index 0000000..d4f86c9 --- /dev/null +++ b/src/test/java/org/walkmod/javalang/compiler/types/PathTreeTest.java @@ -0,0 +1,51 @@ +package org.walkmod.javalang.compiler.types; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; +import java.util.List; + +public class PathTreeTest { + + private static final String ROOT_KEY = "foo2"; + private static final String PACKAGE = ROOT_KEY + File.separator + "bar"; + private static final String HELLO_CLASS_KEY = PACKAGE + File.separator + "Hello.class"; + private static final String BYE_CLASS_KEY = PACKAGE + File.separator + "Bye.class"; + private String AUX_CLASS_KEY = ROOT_KEY + File.separator + "Aux.class"; + + @Test + public void testInsertion() { + + PathTree tree = createTreeWithData(); + + Assert.assertEquals(HELLO_CLASS_KEY, tree.get(HELLO_CLASS_KEY)); + Assert.assertEquals(BYE_CLASS_KEY, tree.get(BYE_CLASS_KEY)); + + } + + private PathTree createTreeWithData() { + PathTree tree = new PathTree(); + + tree.put(AUX_CLASS_KEY, AUX_CLASS_KEY); + tree.put(HELLO_CLASS_KEY, HELLO_CLASS_KEY); + tree.put(BYE_CLASS_KEY, BYE_CLASS_KEY); + return tree; + } + + @Test + public void testList() { + PathTree tree = createTreeWithData(); + List result = tree.list(""); + Assert.assertTrue(result.isEmpty()); + + result = tree.list(ROOT_KEY); + Assert.assertTrue(result.contains(AUX_CLASS_KEY)); + + result = tree.list(PACKAGE); + Assert.assertTrue(result.contains(HELLO_CLASS_KEY)); + Assert.assertTrue(result.contains(BYE_CLASS_KEY)); + + } + +}