|
From: David C. <dc...@us...> - 2005-10-02 22:49:09
|
Update of /cvsroot/rubyeclipse/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/builder In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv8784/src/org/rubypeople/rdt/internal/core/builder Modified Files: RubyBuilder.java Added Files: SingleFileCompiler.java IFileFinder.java TaskCompiler.java MarkerManager.java ProjectFileFinder.java IoUtils.java RubyCompiler.java RdtCompiler.java IndexUpdater.java IMarkerManager.java IncrementalFileFinder.java Removed Files: WorkQueue.java Log Message: Refactored RubyBuilder into several smaller classes. Wrote UTs for a few of them. Began work on a SymbolIndex. --- WorkQueue.java DELETED --- --- NEW FILE: IFileFinder.java --- /** * */ package org.rubypeople.rdt.internal.core.builder; import java.util.List; import org.eclipse.core.runtime.CoreException; interface IFileFinder { public List findFiles() throws CoreException; } Index: RubyBuilder.java =================================================================== RCS file: /cvsroot/rubyeclipse/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/builder/RubyBuilder.java,v retrieving revision 1.12 retrieving revision 1.13 diff -C2 -d -r1.12 -r1.13 *** RubyBuilder.java 21 Sep 2005 18:58:37 -0000 1.12 --- RubyBuilder.java 1 Oct 2005 23:01:00 -0000 1.13 *************** *** 1,34 **** - /** - * - */ package org.rubypeople.rdt.internal.core.builder; - import java.io.IOException; - import java.io.InputStream; - import java.io.InputStreamReader; - import java.io.Reader; - import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; - import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; - import org.eclipse.core.resources.IResource; - import org.eclipse.core.resources.IResourceDelta; - import org.eclipse.core.resources.IResourceProxy; - import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; - import org.eclipse.core.runtime.OperationCanceledException; - import org.eclipse.core.runtime.preferences.IEclipsePreferences; - import org.jruby.lexer.yacc.SyntaxException; - import org.rubypeople.rdt.core.IRubyModelMarker; import org.rubypeople.rdt.core.RubyCore; - import org.rubypeople.rdt.internal.core.parser.MarkerUtility; - import org.rubypeople.rdt.internal.core.parser.RdtWarnings; - import org.rubypeople.rdt.internal.core.parser.RubyParser; - import org.rubypeople.rdt.internal.core.parser.TaskParser; /** --- 1,13 ---- *************** *** 38,288 **** public class RubyBuilder extends IncrementalProjectBuilder { ! private static final boolean DEBUG = false; ! private static final int MAX_AT_ONCE = 1000; ! private IProject currentProject; ! private int totalWork = 10000; ! private WorkQueue workQueue; ! private boolean compiledAllAtOnce; ! private int percentPerUnit = 0; ! private int grandTotal = 0; ! private RdtWarnings warnings; ! private RubyParser parser; ! private TaskParser taskParser; ! ! public RubyBuilder() { ! this.workQueue = new WorkQueue(); ! warnings = new RdtWarnings(); ! parser = new RubyParser(warnings); ! IEclipsePreferences preferences = RubyCore.getInstancePreferences(); ! taskParser = new TaskParser(preferences); ! } - /* - * (non-Javadoc) - * - * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, - * java.util.Map, org.eclipse.core.runtime.IProgressMonitor) - */ protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { ! monitor.beginTask("build", totalWork); IProject[] returnProjects = new IProject[0]; ! this.currentProject = getProject(); ! if (currentProject == null || !currentProject.isAccessible()) return returnProjects; ! ! checkCancel(monitor); ! if (kind == INCREMENTAL_BUILD || kind == AUTO_BUILD) { ! if (DEBUG) System.out.println("INCREMENTAL build..."); ! workQueue.clear(); ! IResourceDelta delta = getDelta(currentProject); ! List files = getAffectedFiles(delta.getAffectedChildren()); ! IFile[] fileArray = new IFile[files.size()]; ! System.arraycopy(files.toArray(), 0, fileArray, 0, fileArray.length); ! compile(fileArray, monitor); ! cleanUp(); ! return returnProjects; ! } ! build(monitor); ! cleanUp(); ! // FIXME Get the required projects as in JavaBuilder ! if (DEBUG) System.out.println("Finished build of " + currentProject.getName() //$NON-NLS-1$ ! + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ return returnProjects; } ! private void cleanUp() { ! workQueue.clear(); ! percentPerUnit = 0; ! warnings.clear(); ! taskParser.clear(); ! } ! ! /** ! * Check whether the build has been canceled. ! * ! * @param monitor ! */ ! public void checkCancel(IProgressMonitor monitor) { ! if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); ! } ! ! private List getAffectedFiles(IResourceDelta[] deltas) { ! List files = new ArrayList(); ! for (int i = 0; i < deltas.length; i++) { ! IResourceDelta curDelta = deltas[i]; ! // Skip removals, we don't want to parse those ! if (curDelta.getKind() == IResourceDelta.REMOVED) continue; ! IResource resource = curDelta.getResource(); ! if (resource.getType() == IResource.FOLDER || resource.getType() == IResource.PROJECT) { ! files.addAll(getAffectedFiles(curDelta.getAffectedChildren())); ! continue; ! } ! if (resource.getType() != IResource.FILE) continue; ! // FIXME This uses the Util class to check if teh filename looks like a ruby file, use behavior like RubyFileMatcher ! if (!org.rubypeople.rdt.internal.core.util.Util.isRubyLikeFileName(resource.getName())) continue; ! files.add(resource); ! } ! return files; ! } ! ! private void build(IProgressMonitor monitor) { ! if (DEBUG) System.out.println("FULL build"); //$NON-NLS-1$ ! ! try { ! RubyBuilder.removeProblemsAndTasksFor(currentProject); ! ! ArrayList sourceFiles = new ArrayList(33); ! addAllSourceFiles(sourceFiles); ! ! if (sourceFiles.size() > 0) { ! IFile[] allSourceFiles = new IFile[sourceFiles.size()]; ! sourceFiles.toArray(allSourceFiles); ! workQueue.clear(); ! workQueue.addAll(allSourceFiles); ! compile(allSourceFiles, monitor); ! } ! ! } catch (CoreException e) { ! // throw internalException(e); ! throw new RuntimeException(e); ! } ! } ! ! public static void removeProblemsAndTasksFor(IResource resource) { ! try { ! if (resource != null && resource.exists()) { ! resource.deleteMarkers(IRubyModelMarker.RUBY_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); ! resource.deleteMarkers(IRubyModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE); ! } ! } catch (CoreException e) { ! // assume there were no problems ! } ! } ! ! protected void addAllSourceFiles(final ArrayList sourceFiles) throws CoreException { ! currentProject.accept(new IResourceProxyVisitor() { ! ! public boolean visit(IResourceProxy proxy) throws CoreException { ! IResource resource = null; ! switch (proxy.getType()) { ! case IResource.FILE: ! if (org.rubypeople.rdt.internal.core.util.Util.isRubyLikeFileName(proxy.getName())) { ! if (resource == null) resource = proxy.requestResource(); ! sourceFiles.add(resource); ! } ! return false; ! } ! return true; ! } ! }, IResource.NONE); ! } ! ! /* ! * Compile the given elements, adding more elements to the work queue if ! * they are affected by the changes. ! */ ! protected void compile(IFile[] units, IProgressMonitor monitor) { ! if (units == null) return; ! int unitsLength = units.length; ! grandTotal = unitsLength; ! if (unitsLength == 0) return; ! percentPerUnit = totalWork / unitsLength; ! ! this.compiledAllAtOnce = unitsLength <= MAX_AT_ONCE; ! if (this.compiledAllAtOnce) { ! // do them all now ! doCompile(units, monitor); ! } else { ! int i = 0; ! boolean compilingFirstGroup = true; ! while (i < unitsLength) { ! int doNow = unitsLength < MAX_AT_ONCE ? unitsLength : MAX_AT_ONCE; ! int index = 0; ! IFile[] toCompile = new IFile[doNow]; ! while (i < unitsLength && index < doNow) { ! // Although it needed compiling when this method was called, ! // it may have ! // already been compiled when it was referenced by another ! // unit. ! IFile unit = units[i++]; ! if (compilingFirstGroup || workQueue.isWaiting(unit)) { ! toCompile[index++] = unit; ! } ! } ! if (index < doNow) System.arraycopy(toCompile, 0, toCompile = new IFile[index], 0, index); ! IFile[] additionalUnits = new IFile[unitsLength - i]; ! System.arraycopy(units, i, additionalUnits, 0, additionalUnits.length); ! compilingFirstGroup = false; ! doCompile(toCompile, monitor); ! } ! } ! } ! ! /* ! * Compile the given elements, adding more elements to the work queue if ! * they are affected by the changes. ! */ ! protected void doCompile(IFile[] units, IProgressMonitor monitor) { ! int unitsLength = units.length; ! if (unitsLength == 0) { ! monitor.worked(unitsLength * percentPerUnit); ! return; ! } ! ! // do them all now ! for (int i = 0; i < unitsLength; i++) { ! checkCancel(monitor); ! Reader reader = null; ! try { ! IFile file = units[i]; ! if (DEBUG) System.out.println("About to compile " + file); //$NON-NLS-1$ ! String name = file.getFullPath().makeRelative().toString(); ! monitor.subTask(name + ": (" + i + " of " + grandTotal + ")"); ! ! removeProblemsAndTasksFor(file); ! reader = new InputStreamReader(file.getContents()); ! try { ! parser.parse(units[i].getName(), reader); ! } catch (SyntaxException e) { ! MarkerUtility.createSyntaxError(file, e); ! } ! MarkerUtility.createProblemMarkers(file, warnings.getWarnings()); ! createTasks(file); ! monitor.worked(percentPerUnit); ! } catch (CoreException e) { ! RubyCore.log(e); ! } catch (IOException e) { ! RubyCore.log(e); ! } finally { ! try { ! reader.close(); ! } catch (IOException e) { ! e.printStackTrace(); ! } ! } ! warnings.clear(); ! } ! checkCancel(monitor); ! } ! private void createTasks(IFile file) throws CoreException, IOException { ! InputStream contents = file.getContents(); ! try { ! taskParser.clear(); ! taskParser.parse(new InputStreamReader(contents)); ! MarkerUtility.createTasks(file, taskParser.getTasks()); ! taskParser.clear(); ! } finally { ! closeSilently(contents); ! } ! } ! private void closeSilently(InputStream contents) { ! try { ! contents.close(); ! } catch (IOException e) { ! } ! } ! } --- 17,62 ---- public class RubyBuilder extends IncrementalProjectBuilder { ! private static final int TOTAL_WORK = 10000; ! private IProject currentProject; protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { ! this.currentProject = getProject(); ! monitor.beginTask("build", TOTAL_WORK); IProject[] returnProjects = new IProject[0]; ! if (currentProject == null || !currentProject.isAccessible()) ! return returnProjects; ! ! RubyCore.trace("Started " + buildType(kind) + " build of " + buildDescription()); //$NON-NLS-1$ ! MarkerManager rubyMarkerManager = new MarkerManager(); ! if (!isPartialBuild(kind)) ! rubyMarkerManager.removeProblemsAndTasksFor(currentProject); ! ! List files = createFileFinder(kind).findFiles(); ! doCompile(files, monitor); ! RubyCore.trace("Finished build of " + buildDescription()); //$NON-NLS-1$ return returnProjects; } + private String buildType(int kind) { + return isPartialBuild(kind) ? "Incremental" : "Full"; + } + + private IFileFinder createFileFinder(int kind) { + if (isPartialBuild(kind)) + return new IncrementalFileFinder(getDelta(currentProject)); + return new ProjectFileFinder(currentProject); + } ! private String buildDescription() { ! return currentProject.getName() + " @ " + new Date(System.currentTimeMillis()); ! } ! private boolean isPartialBuild(int kind) { ! return kind == INCREMENTAL_BUILD || kind == AUTO_BUILD; ! } ! protected void doCompile(List files, IProgressMonitor monitor) { ! new RubyCompiler(TOTAL_WORK/files.size()).compile(files, monitor); ! }} --- NEW FILE: SingleFileCompiler.java --- /** * */ package org.rubypeople.rdt.internal.core.builder; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; interface SingleFileCompiler { public void compileFile(IFile file) throws CoreException; } --- NEW FILE: MarkerManager.java --- package org.rubypeople.rdt.internal.core.builder; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.jruby.lexer.yacc.SyntaxException; import org.rubypeople.rdt.core.IRubyModelMarker; import org.rubypeople.rdt.internal.core.parser.MarkerUtility; import org.rubypeople.rdt.internal.core.parser.RdtPosition; import org.rubypeople.rdt.internal.core.parser.Warning; class MarkerManager implements IMarkerManager { public void removeProblemsAndTasksFor(IResource resource) { try { if (resource != null && resource.exists()) { resource.deleteMarkers(IRubyModelMarker.RUBY_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); resource.deleteMarkers(IRubyModelMarker.TASK_MARKER, false, IResource.DEPTH_INFINITE); } } catch (CoreException e) { // assume there were no problems } } public void createSyntaxError(IFile file, SyntaxException e) { MarkerUtility.createSyntaxError(file, e); } public void createTasks(IFile file, List tasks) throws CoreException { MarkerUtility.createTasks(file, tasks); } public void addWarning(IFile file, String message) { addWarning(file,message, 1, 0, 0); } public void addWarning(IFile file, String message, int startLine, int startOffset, int endOffset) { MarkerUtility.createProblemMarker(file, new Warning(new RdtPosition(startLine, startOffset, endOffset), message)); } } --- NEW FILE: TaskCompiler.java --- /** * */ package org.rubypeople.rdt.internal.core.builder; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.internal.core.parser.TaskParser; public final class TaskCompiler implements SingleFileCompiler { private final TaskParser taskParser; private final IMarkerManager markerManager; public TaskCompiler(IMarkerManager markerManager) { this(markerManager, new TaskParser(RubyCore.getInstancePreferences())); } public TaskCompiler(IMarkerManager markerManager, TaskParser taskParser) { this.taskParser = taskParser; this.markerManager = markerManager; } public void compileFile(IFile file) throws CoreException { InputStream contents = null; try { contents = file.getContents(); taskParser.clear(); taskParser.parse(new InputStreamReader(contents)); markerManager.createTasks(file, taskParser.getTasks()); taskParser.clear(); } catch (IOException e) { RubyCore.log(e); } finally { IoUtils.closeQuietly(contents); } } } --- NEW FILE: IMarkerManager.java --- package org.rubypeople.rdt.internal.core.builder; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.jruby.lexer.yacc.SyntaxException; public interface IMarkerManager { public void removeProblemsAndTasksFor(IResource resource); public void createSyntaxError(IFile file, SyntaxException e); public void createTasks(IFile file, List tasks) throws CoreException; public void addWarning(IFile file, String message); public void addWarning(IFile file, String message, int startLine, int startOffset, int endOffset); } --- NEW FILE: ProjectFileFinder.java --- /** * */ package org.rubypeople.rdt.internal.core.builder; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceProxy; import org.eclipse.core.resources.IResourceProxyVisitor; import org.eclipse.core.runtime.CoreException; final class ProjectFileFinder implements IFileFinder { private final IProject project; public ProjectFileFinder(IProject project) { this.project = project; } public List findFiles() throws CoreException { List files = new ArrayList(); addAllSourceFiles(files); return files; } protected void addAllSourceFiles(final List sourceFiles) throws CoreException { project.accept(new IResourceProxyVisitor() { public boolean visit(IResourceProxy proxy) throws CoreException { IResource resource = null; switch (proxy.getType()) { case IResource.FILE: if (org.rubypeople.rdt.internal.core.util.Util.isRubyLikeFileName(proxy.getName())) { if (resource == null) resource = proxy.requestResource(); sourceFiles.add(resource); } return false; } return true; } }, IResource.NONE); } } --- NEW FILE: RdtCompiler.java --- /** * */ package org.rubypeople.rdt.internal.core.builder; import java.io.InputStreamReader; import java.io.Reader; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.jruby.ast.Node; import org.jruby.lexer.yacc.SyntaxException; import org.rubypeople.rdt.internal.core.parser.ImmediateWarnings; import org.rubypeople.rdt.internal.core.parser.RubyParser; public final class RdtCompiler implements SingleFileCompiler { private final IMarkerManager markerManager; private RubyParser parser; private final IndexUpdater indexUpdater; public RdtCompiler(IMarkerManager markerManager) { this(markerManager, new RubyParser(new ImmediateWarnings(markerManager)), new IndexUpdater(null)); } public RdtCompiler(IMarkerManager markerManager, RubyParser parser, IndexUpdater indexUpdater) { this.markerManager = markerManager; this.parser = parser; this.indexUpdater = indexUpdater; } public void compileFile(IFile file) throws CoreException { Reader reader = new InputStreamReader(file.getContents()); try { Node rootNode = parser.parse(file, reader); // indexUpdater.update(file, rootNode); } catch (SyntaxException e) { markerManager.createSyntaxError(file, e); } finally { IoUtils.closeQuietly(reader); } } } --- NEW FILE: RubyCompiler.java --- /** * */ package org.rubypeople.rdt.internal.core.builder; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.rubypeople.rdt.core.RubyCore; class RubyCompiler { private int percentPerUnit = 0; private List compilers = new ArrayList(); private final MarkerManager rubyMarkerManager; public RubyCompiler(int percentPerUnit) { this.percentPerUnit = percentPerUnit; rubyMarkerManager = new MarkerManager(); this.compilers = createSingleFileCompilers(rubyMarkerManager); } private static List createSingleFileCompilers(MarkerManager rubyMarkerManager) { List compilers = new ArrayList(); compilers.add(new RdtCompiler(rubyMarkerManager)); compilers.add(new TaskCompiler(rubyMarkerManager)); return compilers; } public void compile(List units, IProgressMonitor monitor){ int unitsLength = units.size(); if (unitsLength == 0) { monitor.worked(unitsLength * percentPerUnit); return; } // do them all now // TODO figure out what to do with exceptions - add an error for the file? for (int i = 0; i < unitsLength; i++) { try { IFile file = (IFile) units.get(i); monitoredCompile(monitor, i, unitsLength, file); } catch (CoreException e) { RubyCore.log(e); } } } private void monitoredCompile(IProgressMonitor monitor, int fileNumber, int grandTotal, IFile file) throws CoreException { checkCancel(monitor); RubyCore.trace("About to compile " + file); //$NON-NLS-1$ String name = file.getFullPath().makeRelative().toString(); monitor.subTask(name + ": (" + fileNumber + " of " + grandTotal + ")"); rubyMarkerManager.removeProblemsAndTasksFor(file); runCompilers(file); monitor.worked(percentPerUnit); } private void runCompilers(IFile file) throws CoreException { for (Iterator iter = compilers.iterator(); iter.hasNext();) { SingleFileCompiler compiler = (SingleFileCompiler) iter.next(); compiler.compileFile(file); } } private void checkCancel(IProgressMonitor monitor) { if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); } } --- NEW FILE: IoUtils.java --- package org.rubypeople.rdt.internal.core.builder; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import org.rubypeople.rdt.core.RubyCore; public class IoUtils { static void closeQuietly(Reader reader) { try { reader.close(); } catch (IOException e) { RubyCore.log(e); } } static void closeQuietly(InputStream contents) { try { contents.close(); } catch (IOException e) { RubyCore.log(e); } } public static String readAll(Reader reader) throws IOException { StringBuffer result = new StringBuffer(); char[] buffer = new char[1024]; while (true) { int bytesRead = reader.read(buffer); if (bytesRead <= 0) return result.toString(); result.append(buffer, 0, bytesRead); } } public static String readAllQuietly(Reader reader) { try { return readAll(reader); } catch (IOException e) { throw new RuntimeException(); } } } --- NEW FILE: IndexUpdater.java --- package org.rubypeople.rdt.internal.core.builder; import org.eclipse.core.resources.IFile; import org.jruby.ast.ClassNode; import org.jruby.ast.Node; import org.rubypeople.rdt.internal.core.symbols.ClassSymbol; import org.rubypeople.rdt.internal.core.symbols.SymbolIndex; public class IndexUpdater { private final SymbolIndex index; public IndexUpdater(SymbolIndex index) { this.index = index; } public void update(IFile file, Node rootNode) { index.flush(file.getFullPath()); if (rootNode instanceof ClassNode) { ClassNode classNode = (ClassNode) rootNode; index.add(new ClassSymbol(classNode.getClassName()), file, classNode.getPosition()); } } } --- NEW FILE: IncrementalFileFinder.java --- /** * */ package org.rubypeople.rdt.internal.core.builder; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; class IncrementalFileFinder implements IFileFinder { private final IResourceDelta delta; IncrementalFileFinder(IResourceDelta delta) { super(); this.delta = delta; } public List findFiles() { return getAffectedFiles(delta.getAffectedChildren()); } private List getAffectedFiles(IResourceDelta[] deltas) { List files = new ArrayList(); for (int i = 0; i < deltas.length; i++) { IResourceDelta curDelta = deltas[i]; // Skip removals, we don't want to parse those if (curDelta.getKind() == IResourceDelta.REMOVED) continue; IResource resource = curDelta.getResource(); if (isContainer(resource)) { files.addAll(getAffectedFiles(curDelta.getAffectedChildren())); continue; } if (isFile(resource)) continue; // FIXME This uses the Util class to check if teh filename looks like a ruby file, use behavior like RubyFileMatcher if (!isRubyResource(resource)) continue; files.add(resource); } return files; } private boolean isContainer(IResource resource) { return resource.getType() == IResource.FOLDER || resource.getType() == IResource.PROJECT; } private boolean isFile(IResource resource) { return resource.getType() != IResource.FILE; } private boolean isRubyResource(IResource resource) { return org.rubypeople.rdt.internal.core.util.Util.isRubyLikeFileName(resource.getName()); } } |