/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package ca.weblite.netbeans.mirah.lexer; import ca.weblite.asm.WLMirahCompiler; import ca.weblite.netbeans.mirah.RecompileQueue; import ca.weblite.netbeans.mirah.support.spi.MirahExtenderImplementation; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.WeakHashMap; import java.util.jar.JarOutputStream; import java.util.logging.Logger; import java.util.prefs.Preferences; import java.util.regex.Pattern; import java.util.zip.ZipEntry; //import javax.lang.model.element.ElementKind; import javax.swing.event.ChangeListener; import javax.swing.text.Document; import javax.tools.Diagnostic; import mirah.lang.ast.ClassDefinition; import mirah.lang.ast.FieldDeclaration; import mirah.lang.ast.InterfaceDeclaration; import mirah.lang.ast.MacroDefinition; import mirah.lang.ast.MethodDefinition; import mirah.lang.ast.Node; import mirah.lang.ast.NodeScanner; import mirah.lang.ast.Package; import mirah.lang.ast.Script; import mirah.lang.ast.StaticMethodDefinition; import mirah.lang.ast.StringCodeSource; import mirah.lang.ast.Super; import org.codehaus.plexus.util.FileUtils; import org.mirah.jvm.mirrors.debug.DebuggerInterface; import org.mirah.tool.Mirahc; import org.mirah.typer.ResolvedType; import org.mirah.typer.TypeFuture; import org.mirah.typer.TypeListener; import org.mirah.util.Context; import org.mirah.util.SimpleDiagnostics; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.project.JavaProjectConstants; import org.netbeans.api.project.FileOwnerQuery; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectUtils; import org.netbeans.api.project.SourceGroup; import org.netbeans.api.project.Sources; import org.netbeans.modules.csl.api.ElementKind; import org.netbeans.modules.csl.api.Error; import org.netbeans.modules.csl.api.Severity; import org.netbeans.modules.csl.spi.ParserResult; import org.netbeans.modules.parsing.api.Snapshot; import org.netbeans.modules.parsing.api.Task; import org.netbeans.modules.parsing.spi.ParseException; import org.netbeans.modules.parsing.spi.Parser; import org.netbeans.modules.parsing.spi.SourceModificationEvent; import org.openide.filesystems.FileChangeAdapter; import org.openide.filesystems.FileEvent; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.util.Exceptions; /** * * @author shannah */ public class MirahParser extends Parser { private static WeakHashMap<Document, DocumentDebugger> documentDebuggers = new WeakHashMap<Document, DocumentDebugger>(); private static WeakHashMap<Document, String> lastContent = new WeakHashMap<Document, String>(); private Snapshot snapshot; private MirahParseDiagnostics diag; private NBMirahParserResult result; private static final Logger LOG = Logger.getLogger(MirahParser.class.getCanonicalName()); private static int count = 0; public static DocumentDebugger getDocumentDebugger(Document doc) { return documentDebuggers.get(doc); } private static WeakHashMap<Document, Queue<Runnable>> parserCallbacks = new WeakHashMap<Document, Queue<Runnable>>(); public static void addCallback(Document d, Runnable r) { synchronized (parserCallbacks) { if (!parserCallbacks.containsKey(d)) { parserCallbacks.put(d, new LinkedList<Runnable>()); } parserCallbacks.get(d).add(r); } } private static void fireOnParse(Document d) { List<Runnable> tasks = new ArrayList<Runnable>(); synchronized (parserCallbacks) { Queue<Runnable> queue = parserCallbacks.get(d); if (queue != null) { tasks.addAll(queue); } parserCallbacks.remove(d); } for (Runnable r : tasks) { r.run(); } } String lastSource = ""; @Override public void parse(Snapshot snapshot, Task task, SourceModificationEvent sme) throws ParseException { String oldContent = lastContent.get( snapshot.getSource().getDocument(false) ); String newContent = snapshot.getText().toString(); boolean changed = oldContent == null || !oldContent.equals(newContent); if (sme.sourceChanged() && changed) { lastContent.put( snapshot.getSource().getDocument(false), newContent ); reparse(snapshot); } else if ( result == null ){ result = new NBMirahParserResult(snapshot, diag); getBlocks(result, newContent); } } public void reparse(Snapshot snapshot) throws ParseException { reparse(snapshot, snapshot.getText().toString()); } private void copyIfChanged(File sourceRoot, File destRoot, File sourceFile) throws IOException { if (sourceFile.getName().endsWith(".class")) { String relativePath = sourceFile.getPath().substring(sourceRoot.getPath().length()); if (relativePath.indexOf(File.separator) == 0) { relativePath = relativePath.substring(File.separator.length()); } File destFile = new File(destRoot, relativePath); if (destFile.exists() && destFile.lastModified() < sourceFile.lastModified()) { FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream(sourceFile); fos = new FileOutputStream(destFile); FileUtil.copy(fis, fos); } finally { if (fis != null) { try { fis.close(); } catch (Exception ex) { } } if (fos != null) { try { fos.close(); } catch (Exception ex) { } } } } } else if (sourceFile.isDirectory()) { for (File child : sourceFile.listFiles()) { copyIfChanged(sourceRoot, destRoot, child); } } } void getBlocks(final NBMirahParserResult res, String content){ mirah.impl.MirahParser parser = new mirah.impl.MirahParser(); final LinkedList<NBMirahParserResult.Block> blockStack = new LinkedList<NBMirahParserResult.Block>(); Object ast = null; try { ast = parser.parse(new StringCodeSource(snapshot.getSource().getFileObject().getName(), content)); } catch ( Throwable ex){ //ex.printStackTrace(); return; } if ( ast instanceof Node ){ Node node = (Node)ast; node.accept(new NodeScanner(){ @Override public boolean enterClassDefinition(ClassDefinition node, Object arg) { NBMirahParserResult.Block block = null; if ( blockStack.isEmpty()){ block = res.addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.CLASS); } else { block = blockStack.peek().addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.CLASS); } blockStack.push(block); return super.enterClassDefinition(node, arg); } @Override public Object exitClassDefinition(ClassDefinition node, Object arg) { blockStack.pop(); return super.exitClassDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public boolean enterMethodDefinition(MethodDefinition node, Object arg) { NBMirahParserResult.Block block = null; if (blockStack.isEmpty()){ block = res.addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.METHOD); } else { block = blockStack.peek().addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.METHOD); } blockStack.push(block); return super.enterMethodDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public Object exitMethodDefinition(MethodDefinition node, Object arg) { blockStack.pop(); return super.exitMethodDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public boolean enterInterfaceDeclaration(InterfaceDeclaration node, Object arg) { NBMirahParserResult.Block block = null; if (blockStack.isEmpty()){ block = res.addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.INTERFACE); } else { block = blockStack.peek().addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.INTERFACE); } blockStack.push(block); return super.enterInterfaceDeclaration(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public Object exitInterfaceDeclaration(InterfaceDeclaration node, Object arg) { blockStack.pop(); return super.exitInterfaceDeclaration(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public boolean enterStaticMethodDefinition(StaticMethodDefinition node, Object arg) { NBMirahParserResult.Block block = null; if (blockStack.isEmpty()){ block = res.addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.METHOD); } else { block = blockStack.peek().addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.METHOD); } blockStack.push(block); return super.enterStaticMethodDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public Object exitStaticMethodDefinition(StaticMethodDefinition node, Object arg) { blockStack.pop(); return super.exitStaticMethodDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } /* @Override public boolean enterScript(Script node, Object arg) { NBMirahParserResult.Block block = res.addBlock("Mirah File", node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.OTHER); blockStack.push(block); return super.enterScript(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public Object exitScript(Script node, Object arg) { blockStack.pop(); return super.exitScript(node, arg); //To change body of generated methods, choose Tools | Templates. } */ @Override public boolean enterPackage(Package node, Object arg) { if ( !blockStack.isEmpty() ){ blockStack.pop(); } NBMirahParserResult.Block block = null; if (blockStack.isEmpty()){ block = res.addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.PACKAGE); } else { block = blockStack.peek().addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.PACKAGE); } blockStack.push(block); return super.enterPackage(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public boolean enterFieldDeclaration(FieldDeclaration node, Object arg) { NBMirahParserResult.Block block = null; if (blockStack.isEmpty()){ block = res.addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.FIELD); } else { block = blockStack.peek().addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.FIELD); } blockStack.push(block); return super.enterFieldDeclaration(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public Object exitFieldDeclaration(FieldDeclaration node, Object arg) { blockStack.pop(); return super.exitFieldDeclaration(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public boolean enterMacroDefinition(MacroDefinition node, Object arg) { NBMirahParserResult.Block block = null; if (blockStack.isEmpty()){ block = res.addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.METHOD); } else { block = blockStack.peek().addBlock(node.name().identifier(), node.position().startChar(), node.position().endChar()-node.position().startChar(), "", ElementKind.METHOD); } blockStack.push(block); return super.enterMacroDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } @Override public Object exitMacroDefinition(MacroDefinition node, Object arg) { blockStack.pop(); return super.exitMacroDefinition(node, arg); //To change body of generated methods, choose Tools | Templates. } }, null); } } public void reparse(Snapshot snapshot, String content) throws ParseException { this.snapshot = snapshot; diag = new MirahParseDiagnostics(); NBMirahParserResult parserResult = new NBMirahParserResult(snapshot, diag); result = parserResult; getBlocks(parserResult, content); WLMirahCompiler compiler = new WLMirahCompiler(); FileObject src = snapshot.getSource().getFileObject(); Project project = FileOwnerQuery.getOwner(src); FileObject projectDirectory = project.getProjectDirectory(); FileObject buildDir = projectDirectory.getFileObject("build"); Preferences projPrefs = ProjectUtils.getPreferences(project, MirahExtenderImplementation.class, true); String projectType = projPrefs.get("project_type", "unknown"); System.out.println("Project type is "+projectType); if ( "maven".equals(projectType)){ try { // It's a maven project so we want to build our sources to a different location FileObject cacheDir = ProjectUtils.getCacheDirectory(project, MirahExtenderImplementation.class); buildDir = cacheDir.getFileObject("build"); if ( buildDir == null ){ buildDir = cacheDir.createFolder("build"); } } catch (IOException ex) { Exceptions.printStackTrace(ex); } } ClassPath compileClassPath = ClassPath.getClassPath(src, ClassPath.COMPILE); String compileClassPathStr = ""; if (compileClassPath != null) { compileClassPathStr = compileClassPath.toString(); } ClassPath buildClassPath = ClassPath.getClassPath(src, ClassPath.EXECUTE); String buildClassPathStr = ""; if (buildClassPath != null) { buildClassPathStr = buildClassPath.toString(); } ClassPath srcClassPath = ClassPath.getClassPath(src, ClassPath.SOURCE); String srcClassPathStr = ""; if (srcClassPath != null) { srcClassPathStr = srcClassPath.toString(); } String changedSourcePaths = RecompileQueue.getProjectQueue(project).getAndClearChangedSourcePaths(); if (changedSourcePaths != null) { Set<String> set = new HashSet<String>(); set.addAll(Arrays.asList(changedSourcePaths.split(Pattern.quote(File.pathSeparator)))); set.addAll(Arrays.asList(srcClassPathStr.split(Pattern.quote(File.pathSeparator)))); StringBuilder sb = new StringBuilder(); for (String p : set) { sb.append(p).append(File.pathSeparator); } srcClassPathStr = sb.substring(0, sb.length() - File.pathSeparator.length()); } compiler.setSourcePath(srcClassPathStr); String dest = buildClassPathStr; FileObject mirahDir = null; try { if (buildDir == null) { buildDir = projectDirectory.createFolder("build"); } mirahDir = buildDir.getFileObject("mirah"); if (mirahDir == null) { mirahDir = buildDir.createFolder("mirah"); } File javaStubDir = new File(buildDir.getPath(), "mirah_tmp" + File.separator + "java_stub_dir"); javaStubDir.mkdirs(); compiler.setJavaStubDirectory(javaStubDir); dest = mirahDir.getPath(); } catch (IOException ex) { } compiler.setDestinationDirectory(new File(dest)); compiler.setDiagnostics(diag); List<String> paths = new ArrayList<String>(); if (!"".equals(srcClassPathStr)) { paths.add(srcClassPathStr); } if (!"".equals(buildClassPathStr)) { paths.add(buildClassPathStr); } if (!"".equals(compileClassPathStr)) { paths.add(compileClassPathStr); } StringBuilder classPath = new StringBuilder(); for (String path : paths) { classPath.append(path); classPath.append(File.pathSeparator); } String macroPath = new StringBuilder() .append(classPath.toString()) .append(buildDir.getPath()) .append(File.separator) .append("mirah_tmp") .append(File.separator) .append("macros") .append(File.separator) .append("classes") .append(File.pathSeparator) .toString(); FileObject macroJarDir = projectDirectory.getFileObject("lib/mirah/macros"); if ( macroJarDir != null ){ File jarDir = FileUtil.toFile(macroJarDir); File[] jars = jarDir.listFiles(new FilenameFilter(){ @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }); if ( jars != null ){ for ( File jar : jars ){ macroPath += jar.getAbsolutePath() + File.pathSeparator; } } } if (macroPath.length() >= 1) { macroPath = macroPath.substring(0, macroPath.length() - 1); } String cp = "."; if (classPath.length() >= 1) { cp = classPath.toString().substring(0, classPath.length() - 1); } compiler.setClassPath(macroPath + File.pathSeparator + cp); compiler.setMacroClassPath(macroPath); DocumentDebugger debugger = new DocumentDebugger(); compiler.setDebugger(debugger); ClassPath bootClassPath = ClassPath.getClassPath(src, ClassPath.BOOT); String bootClassPathStr = ""; if (bootClassPath != null) { bootClassPathStr = bootClassPath.toString(); } if (!"".equals(bootClassPathStr)) { compiler.setBootClassPath(bootClassPathStr); } String srcText = content; FileObject fakeFileRoot = getRoot(src); String relPath = FileUtil.getRelativePath(fakeFileRoot, src); relPath = relPath.substring(0, relPath.lastIndexOf(".")); compiler.addFakeFile(relPath, srcText); FileChangeAdapter fileChangeListener = null; try { compiler.compile(new String[0]); if (mirahDir != null) { for (FileObject compileRoot : compileClassPath.getRoots()) { if (!compileRoot.getPath().endsWith(".jar") && compileRoot.isFolder() && !mirahDir.equals(compileRoot)) { copyIfChanged(new File(mirahDir.getPath()), new File(compileRoot.getPath()), new File(mirahDir.getPath())); } } } if ("maven".equals(projectType)){ // If its a maven project, we need to copy the build files into System.out.println("This is a MAVEN PROJECT"); FileObject libDir = projectDirectory.getFileObject("lib"); if ( libDir == null ){ libDir = projectDirectory.createFolder("lib"); } FileObject mirahTmpClassesDir = libDir.getFileObject("mirah-tmp-classes"); if ( mirahTmpClassesDir == null ){ mirahTmpClassesDir = libDir.createFolder("mirah-tmp-classes"); } File jarFile = new File(FileUtil.toFile(libDir), "mirah-tmp-classes.jar"); FileUtils.copyDirectoryStructureIfModified(new File(dest), FileUtil.toFile(mirahTmpClassesDir)); createJar(FileUtil.toFile(mirahTmpClassesDir), FileUtil.toFile(mirahTmpClassesDir).getPath(), jarFile); } } catch (Exception ex) { ex.printStackTrace(); } synchronized (documentDebuggers) { Document doc = snapshot.getSource().getDocument(true); if (debugger.resolvedTypes.size() > 0) { debugger.compiler = compiler.getMirahc(); documentDebuggers.put(doc, debugger); fireOnParse(doc); } } } private FileObject getRoot(FileObject file) { Project project = FileOwnerQuery.getOwner(file); Sources sources = ProjectUtils.getSources(project); for (SourceGroup sourceGroup : sources.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA)) { FileObject root = sourceGroup.getRootFolder(); if (FileUtil.isParentOf(root, file) || root.equals(file)) { return root; } } return ClassPath.getClassPath(file, ClassPath.SOURCE).findOwnerRoot(file); } @Override public Result getResult(Task task) { if ( result != null ) { return result; } if ( snapshot == null ){ } return new NBMirahParserResult(snapshot, diag); } @Override public void cancel() { } @Override public void addChangeListener(ChangeListener cl) { } @Override public void removeChangeListener(ChangeListener changeListener) { } public static class NBMirahParserResult extends ParserResult { private MirahParseDiagnostics diagnostics; private boolean valid = true; List<Error> errorList = new ArrayList<Error>(); List<Block> blockList = new ArrayList<Block>(); NBMirahParserResult( Snapshot snapshot, MirahParseDiagnostics diagnostics) { super(snapshot); this.diagnostics = diagnostics; } public MirahParseDiagnostics getMirahDiagnostics() { return diagnostics; } @Override public List<? extends Error> getDiagnostics() { return new ArrayList<Error>(); } @Override protected void invalidate() { valid = false; } public List<Error> getErrors() { return errorList; } public void addError(String description, int offset, int length) { errorList.add(new Error(description, offset, length, getSnapshot())); } public List<Block> getBlocks() { return blockList; } public Block addBlock(CharSequence function, int offset, int length, CharSequence extra, ElementKind kind) { Block block = new Block(function, offset, length, extra, kind); blockList.add(block); return block; } public Block addBlock(Block parent, CharSequence function, int offset, int length, CharSequence extra, ElementKind kind){ return parent.addBlock(function, offset, length, extra, kind); } public class Error implements org.netbeans.modules.csl.api.Error { String description; int offset; int length; Snapshot snapshot; public Error(String description, int offset, int length, Snapshot snapshot) { this.description = description; this.offset = offset; this.length = length; this.snapshot = snapshot; } @Override public String getDescription() { return description; } public int getOffset() { return offset; } public int getLength() { return length; } @Override public String getDisplayName() { return description; } @Override public String getKey() { return description; } @Override public FileObject getFile() { return snapshot.getSource().getFileObject(); } @Override public int getStartPosition() { return offset; } @Override public int getEndPosition() { return offset + length; } @Override public boolean isLineError() { return false; } @Override public Severity getSeverity() { return Severity.ERROR; } @Override public Object[] getParameters() { return null; } } public class Block { ElementKind kind; CharSequence function; int offset; int length; CharSequence extra; List<Block> children = new ArrayList<Block>(); public Block(CharSequence function, int offset, int length, CharSequence extra, ElementKind kind) { this.function = function; this.offset = offset; this.length = length; this.extra = extra; this.kind = kind; } public Block addBlock(CharSequence function, int offset, int length, CharSequence extra, ElementKind kind){ Block block = new Block(function, offset, length, extra, kind); children.add(block); return block; } public List<Block> getChildren(){ return Collections.unmodifiableList(children); } public CharSequence getExtra() { return extra; } public CharSequence getDescription() { return function; } public int getOffset() { return offset; } public int getLength() { return length; } public ElementKind getKind(){ return kind; } } } public static class MirahParseDiagnostics extends SimpleDiagnostics { static class SyntaxError { SyntaxError(Diagnostic.Kind k, String pos, String msg) { kind = k; position = pos; message = msg; } Diagnostic.Kind kind; String position; String message; } private List<SyntaxError> errors = new ArrayList<SyntaxError>(); public MirahParseDiagnostics() { super(false); } @Override public int errorCount() { return super.errorCount(); } @Override public void log(Diagnostic.Kind kind, String position, String message) { super.log(kind, position, message); if (!"ERROR_TO_PREVENT_COMPILING".equals(message)) { errors.add(new SyntaxError(kind, position, message)); } } public List<SyntaxError> getErrors() { return errors; } } public static class DocumentDebugger implements DebuggerInterface { public static class PositionType { public int startPos, endPos; public ResolvedType type; public Node node; public String toString() { if (type == null) { return "[" + startPos + "," + endPos + "]"; } else { return "[" + type.name() + " " + startPos + "," + endPos + "]"; } } } final private TreeSet<PositionType> leftEdges; final private TreeSet<PositionType> rightEdges; final private HashMap<Node, ResolvedType> resolvedTypes = new HashMap<Node, ResolvedType>(); public Mirahc compiler; public DocumentDebugger() { leftEdges = new TreeSet<PositionType>( new Comparator<PositionType>() { @Override public int compare(PositionType o1, PositionType o2) { if (o1.startPos < o2.startPos) { return -1; } else if (o2.startPos < o1.startPos) { return 1; } else if (o1.endPos < o2.endPos) { return -1; } else if (o2.endPos < o1.endPos) { return 1; } else { return 0; } } }); rightEdges = new TreeSet<PositionType>( new Comparator<PositionType>() { @Override public int compare(PositionType o1, PositionType o2) { if (o1.endPos < o2.endPos) { return -1; } else if (o2.endPos < o1.endPos) { return 1; } else if (o1.startPos < o2.startPos) { return -1; } else if (o2.startPos < o1.startPos) { return 1; } else { return 0; } } }); } public PositionType findNearestPositionOccurringAfter(int pos) { PositionType t = new PositionType(); t.startPos = pos; return leftEdges.ceiling(t); } public PositionType findNearestPositionOccuringBefore(int pos) { PositionType t = new PositionType(); t.endPos = pos; return rightEdges.lower(t); } public ResolvedType getType(Node node) { return resolvedTypes.get(node); } public SortedSet<PositionType> findPositionsWithRightEdgeInRange( int start, int end) { PositionType p1 = new PositionType(); p1.endPos = start; p1.startPos = 0; PositionType p2 = new PositionType(); p2.endPos = end; p2.startPos = end; SortedSet<PositionType> o1 = rightEdges.subSet(p1, p2); return o1; } public int countNodes() { return rightEdges.size(); } public Node firstNode() { return leftEdges.first().node; } @Override public void parsedNode(Node node) { } @Override public void enterNode(Context cntxt, Node node, boolean bln) { } @Override public void exitNode(Context cntxt, final Node node, TypeFuture tf) { tf.onUpdate(new TypeListener() { @Override public void updated(TypeFuture tf, ResolvedType rt) { if (!tf.isResolved()) { return; } if (node.position() == null) { return; } PositionType t = new PositionType(); t.startPos = node.position().startChar(); t.endPos = node.position().endChar(); t.type = rt; t.node = node; if (leftEdges.contains(t)) { leftEdges.remove(t); } if (rightEdges.contains(t)) { rightEdges.remove(t); } leftEdges.add(t); rightEdges.add(t); resolvedTypes.put(node, rt); } }); } @Override public void inferenceError(Context cntxt, Node node, TypeFuture tf) { } } private static String nodeToString(Node n) { if (n == null || n.position() == null) { if (n != null) { return "" + n; } return ""; } StringBuilder sb = new StringBuilder(); sb.append("Node ").append(n) .append(n.position().startLine()) .append(".") .append(n.position().startColumn()) .append(":") .append(n.position().startChar()) .append("-") .append(n.position().endLine()) .append(".") .append(n.position().endColumn()) .append(":") .append(n.position().endChar()) .append(" # "); return sb.toString(); } private void createJar(File source, String sourceRoot, File jarFile) throws IOException { FileOutputStream fos = null; JarOutputStream jos = null; try { fos = new FileOutputStream(jarFile); jos = new JarOutputStream(fos); jos.setLevel(0); addToJar(source, sourceRoot, jos); } finally { try { if ( jos != null ) jos.close(); } catch ( Throwable t ){} try { if ( fos != null ) fos.close(); } catch ( Throwable t){} } } private void addToJar(File source, String sourceRoot, JarOutputStream jos) throws IOException { if ( source.getName().endsWith(".class")){ String fileName = formatEntry(source, sourceRoot, false); System.out.println("Adding file "+fileName+" to jar ("+source+")"); ZipEntry entry = new ZipEntry(fileName); jos.putNextEntry(entry); InputStream fis = null; try { fis = new FileInputStream(source); byte[] buf = new byte[4096]; int len; while ( (len = fis.read(buf)) != -1 ){ System.out.println("Writing "+len+" bytes"); jos.write(buf, 0, len); } jos.closeEntry(); } finally { try { if ( fis != null ){ fis.close(); } } catch ( Exception ex){} } } else if ( source.isDirectory() ){ String dirName = formatEntry(source, sourceRoot, true); System.out.println("Adding "+dirName+" to jar"); //ZipEntry entry = new ZipEntry(dirName); //jos.putNextEntry(entry); for ( File child : source.listFiles()){ addToJar(child, sourceRoot, jos); } //jos.closeEntry(); } } private String formatEntry(File f, String sourceRoot, boolean directory){ if ( directory ){ String name = f.getPath().substring(sourceRoot.length()); name = name.replace("\\", "/"); if ( !name.endsWith("/")){ name += "/"; } if ( name.startsWith("/")){ name = name.substring(1); } return name; } else { String name = f.getPath().substring(sourceRoot.length()); name = name.replace("\\", "/"); if ( name.startsWith("/")){ name = name.substring(1); } return name; } } }