summaryrefslogtreecommitdiff
path: root/eclipse/java/Slice2javaPlugin/src/com/zeroc/slice2javaplugin/builder/Slice2JavaBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/java/Slice2javaPlugin/src/com/zeroc/slice2javaplugin/builder/Slice2JavaBuilder.java')
-rw-r--r--eclipse/java/Slice2javaPlugin/src/com/zeroc/slice2javaplugin/builder/Slice2JavaBuilder.java1282
1 files changed, 1282 insertions, 0 deletions
diff --git a/eclipse/java/Slice2javaPlugin/src/com/zeroc/slice2javaplugin/builder/Slice2JavaBuilder.java b/eclipse/java/Slice2javaPlugin/src/com/zeroc/slice2javaplugin/builder/Slice2JavaBuilder.java
new file mode 100644
index 00000000000..d65596deb95
--- /dev/null
+++ b/eclipse/java/Slice2javaPlugin/src/com/zeroc/slice2javaplugin/builder/Slice2JavaBuilder.java
@@ -0,0 +1,1282 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
+//
+// This plug-in is provided to you under the terms and conditions
+// of the Eclipse Public License Version 1.0 ("EPL"). A copy of
+// the EPL is available at http://www.eclipse.org/legal/epl-v10.html.
+//
+// **********************************************************************
+
+package com.zeroc.slice2javaplugin.builder;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.resources.IResourceDeltaVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.console.ConsolePlugin;
+import org.eclipse.ui.console.IConsole;
+import org.eclipse.ui.console.IConsoleManager;
+import org.eclipse.ui.console.MessageConsole;
+import org.eclipse.ui.console.MessageConsoleStream;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.zeroc.slice2javaplugin.Activator;
+import com.zeroc.slice2javaplugin.internal.Configuration;
+import com.zeroc.slice2javaplugin.internal.Dependencies;
+
+public class Slice2JavaBuilder extends IncrementalProjectBuilder
+{
+ public static final String BUILDER_ID = "com.zeroc.Slice2JavaPlugin.Slice2JavaBuilder";
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.core.internal.events.InternalBuilder#build(int,
+ * java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ protected IProject[] build(int kind, @SuppressWarnings("rawtypes")Map args, IProgressMonitor monitor)
+ throws CoreException
+ {
+ long start = System.currentTimeMillis();
+
+ IResourceDelta delta = getDelta(getProject());
+ BuildState state = new BuildState(getProject(), delta, monitor);
+ state.dependencies.read();
+
+ try
+ {
+ if(kind == FULL_BUILD)
+ {
+ fullBuild(state, monitor);
+ }
+ else
+ {
+ if(delta == null)
+ {
+ fullBuild(state, monitor);
+ }
+ else
+ {
+ incrementalBuild(state, monitor);
+ }
+ }
+ }
+ finally
+ {
+ long end = System.currentTimeMillis();
+ if(state.out != null)
+ {
+ state.out.println("Build complete. Elapsed time: " + (end - start) / 1000 + "s.");
+ }
+ state.dependencies.write();
+ }
+ return null;
+ }
+
+ protected void clean(IProgressMonitor monitor)
+ throws CoreException
+ {
+ BuildState state = new BuildState(getProject(), null, monitor);
+
+ // Don't read the existing dependencies. That will have the
+ // effect of trashing them.
+
+ try
+ {
+ // Now, clean the generated sub-directory.
+ Set<IFile> files = new HashSet<IFile>();
+ getResources(files, state.generated.members());
+
+ for(IFile file : files)
+ {
+ // Don't delete "." files (such as .gitignore).
+ if(!file.getName().startsWith("."))
+ {
+ file.delete(true, false, monitor);
+ }
+ }
+ }
+ finally
+ {
+ state.dependencies.write();
+ }
+ }
+
+ static class StreamReaderThread extends Thread
+ {
+ public StreamReaderThread(InputStream in, StringBuffer out)
+ {
+ _in = new BufferedReader(new InputStreamReader(in), 1024);
+ _out = out;
+ }
+
+ public void run()
+ {
+ try
+ {
+ char[] buf = new char[1024];
+ while(true)
+ {
+ int read = _in.read(buf);
+ if(read == -1)
+ {
+ break;
+ }
+ _out.append(buf, 0, read);
+ }
+ }
+ catch(Exception e)
+ {
+ }
+ finally
+ {
+ try
+ {
+ _in.close();
+ }
+ catch(IOException e1)
+ {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+ private StringBuffer _out;
+ private BufferedReader _in;
+ }
+
+ static class BuildState
+ {
+ BuildState(IProject project, IResourceDelta delta, IProgressMonitor monitor) throws CoreException
+ {
+ config = new Configuration(project);
+
+ if(config.getConsole())
+ {
+ initializeConsole();
+ out = _consoleout;
+ err = _consoleerr;
+ }
+
+ generated = project.getFolder(config.getGeneratedDir());
+ if(!generated.exists())
+ {
+ generated.create(false, true, monitor);
+ }
+
+ _sourceLocations = new HashSet<IFolder>();
+ for(Iterator<String> p = config.getSliceSourceDirs().iterator(); p.hasNext();)
+ {
+ _sourceLocations.add(project.getFolder(p.next()));
+ }
+
+ project.accept(new IResourceVisitor()
+ {
+ public boolean visit(IResource resource)
+ throws CoreException
+ {
+ if(resource instanceof IFile)
+ {
+ IFile file = (IFile) resource;
+ if(filter(file))
+ {
+ _resources.add((IFile) resource);
+ }
+ }
+ return true;
+ }
+ });
+
+ if(delta != null)
+ {
+ delta.accept(new IResourceDeltaVisitor()
+ {
+ public boolean visit(IResourceDelta delta)
+ throws CoreException
+ {
+ IResource resource = delta.getResource();
+ if(resource instanceof IFile)
+ {
+ IFile file = (IFile) resource;
+ if(filter(file))
+ {
+ switch (delta.getKind())
+ {
+ case IResourceDelta.ADDED:
+ case IResourceDelta.CHANGED:
+ _deltaCandidates.add(file);
+ break;
+ case IResourceDelta.REMOVED:
+ _removed.add(file);
+ break;
+ }
+ }
+ }
+ return true;
+ }
+ });
+ }
+
+ dependencies = new Dependencies(project, _resources, err);
+ }
+
+ public Set<IFile> deltas()
+ {
+ return _deltaCandidates;
+ }
+
+ public List<IFile> removed()
+ {
+ return _removed;
+ }
+
+ public Set<IFile> resources()
+ {
+ return _resources;
+ }
+
+ public boolean filter(IFile file)
+ {
+ String ext = file.getFileExtension();
+ if(ext != null && ext.equals("ice"))
+ {
+ //
+ // The parent may not be an IFolder (e.g., it could be a Project).
+ //
+ if(file.getParent() instanceof IFolder)
+ {
+ IFolder folder = (IFolder)file.getParent();
+ if(_sourceLocations.contains(folder))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ synchronized static private void initializeConsole()
+ {
+ if(_consoleout == null)
+ {
+ MessageConsole console = new MessageConsole("slice2java", null);
+ IConsole[] ics = new IConsole[1];
+ ics[0] = console;
+ IConsoleManager csmg = ConsolePlugin.getDefault().getConsoleManager();
+ csmg.addConsoles(ics);
+ csmg.showConsoleView(console);
+
+ _consoleout = console.newMessageStream();
+ _consoleerr = console.newMessageStream();
+
+ final Display display = PlatformUI.getWorkbench().getDisplay();
+ display.syncExec(new Runnable() {
+ public void run() {
+ _consoleerr.setColor(display.getSystemColor(SWT.COLOR_RED));
+ }
+ });
+ }
+ }
+
+ Configuration config;
+ Dependencies dependencies;
+ IFolder generated;
+ private Set<IFolder> _sourceLocations;
+
+ private Set<IFile> _resources = new HashSet<IFile>();
+ private Set<IFile> _deltaCandidates = new HashSet<IFile>();
+ private List<IFile> _removed = new ArrayList<IFile>();
+
+ private MessageConsoleStream out = null;
+ private MessageConsoleStream err = null;
+
+ static private MessageConsoleStream _consoleout = null;
+ static private MessageConsoleStream _consoleerr = null;
+ }
+
+ private int build(BuildState state, Set<IFile> files, boolean depend, StringBuffer out, StringBuffer err)
+ throws CoreException
+ {
+ // Clear the output buffer.
+ out.setLength(0);
+ if(err != null)
+ {
+ err.setLength(0);
+ }
+
+ List<String> cmd = new LinkedList<String>();
+ String translator = state.config.getTranslator();
+ if(translator == null)
+ {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Cannot locate slice2java translator: please fix Ice install location", null));
+ }
+
+ cmd.add(translator);
+ if(depend)
+ {
+ cmd.add("--depend-xml");
+ }
+ else
+ {
+ cmd.add("--output-dir=" + state.generated.getProjectRelativePath().toString());
+ cmd.add("--list-generated");
+ }
+ List<String> cmdBase = new LinkedList<String>();
+ cmdBase.addAll(cmd);
+
+ cmd.addAll(state.config.getCommandLine());
+
+ Set<IFile> resourcesWithArguments = new HashSet<IFile>();
+
+ boolean allHasOptions = true;
+ for(Iterator<IFile> p = files.iterator(); p.hasNext();)
+ {
+ IFile f = p.next();
+ if(!Configuration.resourceHasOptions(f))
+ {
+ allHasOptions = false;
+ cmd.add(f.getLocation().toOSString());
+ }
+ else
+ {
+ resourcesWithArguments.add(f);
+ }
+ }
+
+ ProcessBuilder builder;
+ IPath rootLocation = getProject().getLocation();
+ Map<String, String> env;
+ int status = 0;
+
+ if(!allHasOptions)
+ {
+ builder = new ProcessBuilder(cmd);
+ if(err == null)
+ {
+ builder.redirectErrorStream(true);
+ }
+
+ builder.directory(rootLocation.toFile());
+ env = builder.environment();
+ Configuration.setupSharedLibraryPath(env);
+
+ status = runSliceCompiler(builder, state, out, err);
+ }
+
+ for(Iterator<IFile> p = resourcesWithArguments.iterator(); p.hasNext();)
+ {
+ IFile f = p.next();
+ cmd = new LinkedList<String>();
+ cmd.addAll(cmdBase);
+ cmd.addAll(state.config.getCommandLine(f));
+
+ cmd.add(f.getLocation().toOSString());
+
+ builder = new ProcessBuilder(cmd);
+ if(err == null)
+ {
+ builder.redirectErrorStream(true);
+ }
+ builder.directory(rootLocation.toFile());
+ env = builder.environment();
+ Configuration.setupSharedLibraryPath(env);
+
+ status = runSliceCompiler(builder, state, out, err);
+ }
+
+ return status;
+ }
+
+ private int
+ runSliceCompiler(ProcessBuilder builder, BuildState state, StringBuffer out, StringBuffer err)
+ throws CoreException
+ {
+ try
+ {
+
+
+ if(state.out != null)
+ {
+ for(Iterator<String> p = builder.command().iterator(); p.hasNext();)
+ {
+ state.out.print(p.next());
+ state.out.print(" ");
+ }
+ state.out.println("");
+ }
+
+ Process proc = builder.start();
+
+ StreamReaderThread outThread = new StreamReaderThread(proc.getInputStream(), out);
+ outThread.start();
+ StreamReaderThread errThread = null;
+ if(err != null)
+ {
+ errThread = new StreamReaderThread(proc.getErrorStream(), err);
+ errThread.start();
+ }
+
+ int status = proc.waitFor();
+
+ outThread.join();
+ if(errThread != null)
+ {
+ errThread.join();
+ }
+
+ if(status != 0 && state.err != null)
+ {
+ state.err.println("slice2java status: " + status);
+ }
+
+ return status;
+ }
+ catch(Exception e)
+ {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.toString(), null));
+ }
+ }
+
+ private void
+ createMarker(BuildState state, IFile source, IPath filename, int line, String msg)
+ throws CoreException
+ {
+ // Process the error.
+ IPath dir = getProject().getLocation();
+
+ IFile file = null;
+ if(filename != null && dir.isPrefixOf(filename))
+ {
+ // Locate the file within the project.
+ file = getProject().getFile(filename.removeFirstSegments(dir.segmentCount()));
+
+ // If the file is not the current source file, and the file exists in the project
+ // then it must already contain a marker, so don't place another.
+ if(!file.equals(source) && state.filter(file))
+ {
+ return;
+ }
+ }
+
+ // If the message isn't contained in the source file, then identify the
+ // file:line in the message itself.
+ if(file == null)
+ {
+ if(line != -1)
+ {
+ msg = filename + ":" + line + ": " + msg;
+ }
+ else
+ {
+ msg = filename + ": " + msg;
+ }
+ }
+
+ IMarker marker = source.createMarker(IMarker.PROBLEM);
+ marker.setAttribute(IMarker.MESSAGE, msg);
+ if(msg.toLowerCase().indexOf("warning:") >= 0)
+ {
+ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_WARNING);
+ }
+ else
+ {
+ marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
+ }
+ if(line != -1)
+ {
+ if(file != null && file.equals(source))
+ {
+ marker.setAttribute(IMarker.LINE_NUMBER, line);
+ }
+ else
+ {
+ marker.setAttribute(IMarker.LINE_NUMBER, 1);
+ }
+ }
+ }
+
+ private void createMarkers(BuildState state, IFile source, String output)
+ throws CoreException
+ {
+ output = output.trim();
+ if(output.length() == 0)
+ {
+ return;
+ }
+
+ String[] lines = output.split("\n");
+
+ IPath filename = null;
+ int line = -1;
+ StringBuffer msg = new StringBuffer();
+
+ boolean continuation = false;
+
+ for(int i = 0; i < lines.length; ++i)
+ {
+ if(continuation)
+ {
+ if(lines[i].startsWith(" "))
+ {
+ // Continuation of the previous message.
+ msg.append(lines[i]);
+ continue;
+ }
+ else
+ {
+ // Process the message.
+ createMarker(state, source, filename, line, msg.toString());
+ }
+ }
+
+ // We're on a new message.
+ msg.setLength(0);
+ continuation = false;
+
+ // Ignore.
+ if(lines[i].contains("errors in preprocessor") || lines[i].contains("error in preprocessor"))
+ {
+ continue;
+ }
+
+ //
+ // Parse a line of the form:
+ //
+ // file:[line:] message
+ //
+ int start = 0;
+ int end;
+ // Handle drive letters.
+ if(lines[i].length() > 2 && lines[i].charAt(1) == ':')
+ {
+ end = lines[i].indexOf(':', 2);
+ }
+ else
+ {
+ end = lines[i].indexOf(':');
+ }
+ if(end != -1)
+ {
+ filename = new Path(lines[i].substring(start, end));
+ start = end + 1;
+ end = lines[i].indexOf(':', start);
+ if(end != -1)
+ {
+ try
+ {
+ line = Integer.parseInt(lines[i].substring(start, end));
+ start = end + 1;
+ }
+ catch(NumberFormatException e)
+ {
+ // The message may not have a line number.
+ line = -1;
+ }
+ msg.append(lines[i].substring(start, lines[i].length()));
+
+ continuation = true;
+ continue;
+ }
+ }
+ // Unknown format.
+ createMarker(state, source, null, -1, lines[i]);
+ }
+
+ if(continuation)
+ {
+ createMarker(state, source, filename, line, msg.toString());
+ }
+ }
+
+ private void getResources(Set<IFile> files, IResource[] members)
+ throws CoreException
+ {
+ for(int i = 0; i < members.length; ++i)
+ {
+ if(members[i] instanceof IFile)
+ {
+ files.add((IFile) members[i]);
+ }
+ else if(members[i] instanceof IFolder)
+ {
+ getResources(files, ((IFolder) members[i]).members());
+ }
+ }
+ }
+
+ private void fullBuild(BuildState state, final IProgressMonitor monitor)
+ throws CoreException
+ {
+ clean(monitor);
+ Set<IFile> candidates = state.resources();
+ if(candidates.isEmpty())
+ {
+ return;
+ }
+
+ if(state.out != null)
+ {
+ java.util.Date date = new java.util.Date();
+ state.out.println("Start full build at " + new SimpleDateFormat("HH:mm:ss").format(date));
+
+ state.out.println("Candidate list:");
+ // This is a complete list of Slice files.
+ for(Iterator<IFile> p = candidates.iterator(); p.hasNext();)
+ {
+ state.out.println(" " + p.next().getProjectRelativePath().toString());
+ }
+ state.out.println("Regenerating java source files.");
+ }
+
+ StringBuffer out = new StringBuffer();
+
+ Set<IFile> depends = new HashSet<IFile>();
+
+ // Delete each marker.
+ for(Iterator<IFile> p = candidates.iterator(); p.hasNext();)
+ {
+ IFile file = p.next();
+ file.deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
+ }
+
+ // Do the build.
+ build(state, candidates, false, out, null);
+
+ out = mergeXmls(out, false);
+
+ // Refresh the generated subdirectory prior to processing the
+ // generated files list.
+ state.generated.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+
+ // Parse the output.
+ Slice2JavaGeneratedParser parser = getGeneratedFiles(state, candidates, out);
+ for(Map.Entry<IFile, Slice2JavaGeneratedParser.Entry> entry : parser.output.entrySet())
+ {
+ IFile source = entry.getKey();
+
+ Slice2JavaGeneratedParser.Entry outputEntry = entry.getValue();
+ Set<IFile> newGeneratedJavaFiles = outputEntry.files;
+
+ for(IFile f : newGeneratedJavaFiles)
+ {
+ // Mark the resource as derived.
+ f.setDerived(true, null);
+ }
+
+ if(!outputEntry.error)
+ {
+ depends.add(source);
+ if(state.out != null)
+ {
+ if(newGeneratedJavaFiles.isEmpty())
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": No java files emitted.");
+ }
+ else
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": Emitted:");
+ for(Iterator<IFile> q = newGeneratedJavaFiles.iterator(); q.hasNext();)
+ {
+ state.out.println(" " + q.next().getProjectRelativePath().toString());
+ }
+ }
+ }
+ }
+ else
+ {
+ state.dependencies.errorSliceFiles.add(source);
+ if(state.out != null)
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": Error.");
+ }
+ }
+
+ // Update the set of slice -> java dependencies.
+ state.dependencies.sliceJavaDependencies.put(source, newGeneratedJavaFiles);
+
+ // Create markers for each warning/error.
+ createMarkers(state, source, outputEntry.output);
+ }
+
+ // Update the slice->slice dependencies.
+ // Only update the dependencies for those files with no build problems.
+ if(!depends.isEmpty())
+ {
+ if(state.out != null)
+ {
+ state.out.println("Updating dependencies.");
+ }
+
+ StringBuffer err = new StringBuffer();
+ if(build(state, depends, true, out, err) == 0)
+ {
+ out = mergeXmls(out, true);
+ // Parse the new dependency set.
+ state.dependencies.updateDependencies(out.toString());
+ }
+ else if(state.err != null)
+ {
+ state.err.println("Dependencies not updated due to error.");
+ state.err.println(err.toString());
+ }
+ }
+ }
+
+ private void incrementalBuild(BuildState state, IProgressMonitor monitor)
+ throws CoreException
+ {
+ Set<IFile> candidates = state.deltas();
+ List<IFile> removed = state.removed();
+
+ if(state.out != null)
+ {
+ java.util.Date date = new java.util.Date();
+ state.out.println("Start incremental build at " + new SimpleDateFormat("HH:mm:ss").format(date));
+
+ state.out.println("Candidate list:");
+ // This is a complete list of slice files.
+ for(Iterator<IFile> p = candidates.iterator(); p.hasNext();)
+ {
+ state.out.println(" + " + p.next().getProjectRelativePath().toString());
+ }
+ for(Iterator<IFile> p = removed.iterator(); p.hasNext();)
+ {
+ state.out.println(" - " + p.next().getProjectRelativePath().toString());
+ }
+ }
+
+ // The orphan candidate set.
+ Set<IFile> orphanCandidateSet = new HashSet<IFile>();
+
+ // Go through the removed list, removing the dependencies.
+ for(Iterator<IFile> p = removed.iterator(); p.hasNext();)
+ {
+ IFile f = p.next();
+
+ // Remove the file from the error list, if necessary.
+ if(state.dependencies.errorSliceFiles.contains(f))
+ {
+ state.dependencies.errorSliceFiles.remove(f);
+ }
+
+ Set<IFile> dependents = state.dependencies.sliceSliceDependencies.remove(f);
+ if(dependents != null)
+ {
+ Iterator<IFile> dependentsIterator = dependents.iterator();
+ while(dependentsIterator.hasNext())
+ {
+ IFile dependent = dependentsIterator.next();
+ Set<IFile> files = state.dependencies.reverseSliceSliceDependencies.get(dependent);
+ if(files != null)
+ {
+ files.remove(f);
+ }
+ }
+ }
+
+ Set<IFile> oldJavaFiles = state.dependencies.sliceJavaDependencies.remove(f);
+ if(state.out != null)
+ {
+ if(oldJavaFiles == null || oldJavaFiles.isEmpty())
+ {
+ state.out.println(f.getProjectRelativePath().toString() + ": No orphans.");
+ }
+ else
+ {
+ state.out.println(f.getProjectRelativePath().toString() + ": Orphans:");
+ for(Iterator<IFile> q = oldJavaFiles.iterator(); q.hasNext();)
+ {
+ state.out.println(" " + q.next().getProjectRelativePath().toString());
+ }
+ }
+ }
+
+ if(oldJavaFiles != null)
+ {
+ orphanCandidateSet.addAll(oldJavaFiles);
+ }
+ }
+
+ // Add the removed files to the candidates set
+ // prior to determining additional candidates.
+ candidates.addAll(removed);
+
+ // Add to the candidate set any slice files that are in error. Clear the
+ // error list.
+ candidates.addAll(state.dependencies.errorSliceFiles);
+ state.dependencies.errorSliceFiles.clear();
+
+ Set<IFile> candidatesTmp = new HashSet<IFile>();
+
+ for(Iterator<IFile> p = candidates.iterator(); p.hasNext();)
+ {
+ IFile f = p.next();
+
+ Set<IFile> files = state.dependencies.reverseSliceSliceDependencies.get(f);
+ if(files != null)
+ {
+ for(Iterator<IFile> q = files.iterator(); q.hasNext();)
+ {
+ IFile potentialCandidate = q.next();
+ if(potentialCandidate.exists())
+ {
+ candidatesTmp.add(potentialCandidate);
+ }
+ }
+ }
+
+ // If this is a file in the contained list, then remove the
+ // dependency entry.
+ if(removed.contains(f))
+ {
+ state.dependencies.reverseSliceSliceDependencies.remove(f);
+ }
+ }
+ candidates.addAll(candidatesTmp);
+
+ // Remove all the removed files from the candidates list.
+ candidates.removeAll(removed);
+
+ if(state.out != null)
+ {
+ if(candidates.isEmpty())
+ {
+ state.out.println("No remaining candidates.");
+ }
+ else
+ {
+ state.out.println("Expanded candidate list:");
+ // This is a complete list of slice files.
+ for(Iterator<IFile> p = candidates.iterator(); p.hasNext();)
+ {
+ state.out.println(" " + p.next().getProjectRelativePath().toString());
+ }
+ }
+ }
+
+ StringBuffer out = new StringBuffer();
+
+ // The set of files that we'll generate dependencies for.
+ Set<IFile> depends = new HashSet<IFile>();
+
+ if(!candidates.isEmpty())
+ {
+ if(state.out != null)
+ {
+ state.out.println("Regenerating java source files.");
+ }
+
+ // The complete set of generated java files by this build.
+ Set<IFile> generatedJavaFiles = new HashSet<IFile>();
+
+ // Remove all markers for the candidate list.
+ for(Iterator<IFile> p = candidates.iterator(); p.hasNext();)
+ {
+ IFile file = p.next();
+ file.deleteMarkers(IMarker.PROBLEM, true, IResource.DEPTH_INFINITE);
+ }
+
+ // Do the build.
+ build(state, candidates, false, out, null);
+ out = mergeXmls(out, false);
+
+ // Refresh the generated directory prior to processing the generated
+ // files list.
+ state.generated.refreshLocal(IResource.DEPTH_INFINITE, monitor);
+
+ // Parse the emitted XML file that describes what was produced by
+ // the build.
+ Slice2JavaGeneratedParser parser = getGeneratedFiles(state, candidates, out);
+ for(Map.Entry<IFile, Slice2JavaGeneratedParser.Entry> entry : parser.output.entrySet())
+ {
+ IFile source = entry.getKey();
+
+ Slice2JavaGeneratedParser.Entry outputEntry = entry.getValue();
+
+ Set<IFile> newGeneratedJavaFiles = outputEntry.files;
+ for(IFile f : newGeneratedJavaFiles)
+ {
+ // Mark the resource as derived.
+ f.setDerived(true, null);
+ }
+
+ // If the build of the file didn't result in an error, add to
+ // the dependencies list. Otherwise, add to the error list.
+ if(!outputEntry.error)
+ {
+ depends.add(source);
+ }
+ else
+ {
+ if(state.out != null)
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": Error.");
+ }
+ state.dependencies.errorSliceFiles.add(source);
+ }
+
+ // Compute the set difference between the old set and new set
+ // of generated files. The difference should be added to the
+ // orphan candidate set.
+ Set<IFile> oldJavaFiles = state.dependencies.sliceJavaDependencies.get(source);
+ if(oldJavaFiles != null)
+ {
+ // Compute the set difference.
+ oldJavaFiles.removeAll(newGeneratedJavaFiles);
+ if(state.out != null)
+ {
+ if(oldJavaFiles.isEmpty())
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": No orphans.");
+ }
+ else
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": Orphans:");
+ for(Iterator<IFile> q = oldJavaFiles.iterator(); q.hasNext();)
+ {
+ state.out.println(" " + q.next().getProjectRelativePath().toString());
+ }
+ }
+ }
+ orphanCandidateSet.addAll(oldJavaFiles);
+ }
+
+ // Update the set of slice -> java dependencies.
+ state.dependencies.sliceJavaDependencies.put(source, newGeneratedJavaFiles);
+
+ // If the build resulted in an error, there will be no java source files.
+ if(state.out != null && !outputEntry.error)
+ {
+ if(newGeneratedJavaFiles.isEmpty())
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": No java files emitted.");
+ }
+ else
+ {
+ state.out.println(source.getProjectRelativePath().toString() + ": Emitted:");
+ for(Iterator<IFile> q = newGeneratedJavaFiles.iterator(); q.hasNext();)
+ {
+ state.out.println(" " + q.next().getProjectRelativePath().toString());
+ }
+ }
+ }
+
+ generatedJavaFiles.addAll(newGeneratedJavaFiles);
+
+ // Create markers for each warning/error.
+ createMarkers(state, source, outputEntry.output);
+ }
+
+ // Do a set difference between the orphan candidate set
+ // and the complete set of generated java source files.
+ // Any remaining are complete orphans and should
+ // be removed.
+ orphanCandidateSet.removeAll(generatedJavaFiles);
+ }
+
+ if(state.out != null)
+ {
+ if(orphanCandidateSet.isEmpty())
+ {
+ state.out.println("No orphans from this build.");
+ }
+ else
+ {
+ state.out.println("Orphans from this build:");
+ for(Iterator<IFile> p = orphanCandidateSet.iterator(); p.hasNext();)
+ {
+ state.out.println(" " + p.next().getProjectRelativePath().toString());
+ }
+ }
+ }
+
+ //
+ // Remove orphans.
+ //
+ for(Iterator<IFile> p = orphanCandidateSet.iterator(); p.hasNext();)
+ {
+ p.next().delete(true, false, monitor);
+ }
+
+ // The dependencies of any files without build errors should be updated.
+ if(!depends.isEmpty())
+ {
+ if(state.out != null)
+ {
+ state.out.println("Updating dependencies.");
+ }
+
+ StringBuffer err = new StringBuffer();
+
+ // We've already added markers for any errors... Only update the
+ // dependencies if no problems resulted in the build.
+ if(build(state, depends, true, out, err) == 0)
+ {
+ out = mergeXmls(out, true);
+ // Parse the new dependency set.
+ state.dependencies.updateDependencies(out.toString());
+ }
+ else if(state.err != null)
+ {
+ state.err.println("Dependencies not updated due to error.");
+ state.err.println(err.toString());
+ }
+ }
+ }
+
+ //
+ // This method merge the XML produced by multiple Slice translator
+ // invocations in a single XML. If depend argument is true, the input
+ // buffer is treated as a dependencies XML, otherwise is treated as
+ // a generated list XML.
+ //
+ private StringBuffer
+ mergeXmls(StringBuffer input, boolean depend)
+ {
+ //
+ // Merge depend XMLs in a single XML
+ //
+ String v = input.toString();
+ StringTokenizer lines = new StringTokenizer(v, System.getProperty("line.separator"));
+ boolean firstLine = true;
+ boolean firstGenerated = true;
+ StringBuffer out = new StringBuffer();
+ while(lines.hasMoreTokens())
+ {
+ String line = lines.nextToken();
+ if(line.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") &&
+ !firstLine)
+ {
+ continue;
+ }
+
+ if(depend)
+ {
+ if(line.equals("<dependencies>"))
+ {
+ if(firstGenerated)
+ {
+ firstGenerated = false;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else if(line.equals("</dependencies>") && lines.hasMoreTokens())
+ {
+ continue;
+ }
+ }
+ else
+ {
+ if(line.equals("<generated>"))
+ {
+ if(firstGenerated)
+ {
+ firstGenerated = false;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ else if(line.equals("</generated>") && lines.hasMoreTokens())
+ {
+ continue;
+ }
+ }
+
+ out.append(line + "\n");
+ firstLine = false;
+ }
+ return out;
+ }
+
+ private static class Slice2JavaGeneratedParser
+ {
+ static class Entry
+ {
+ boolean error; // Did the build result in an error.
+ String output; // Any warnings/errors from the build.
+ Set<IFile> files; // The set of java source files associated with the source file.
+ }
+ Map<IFile, Entry> output = new HashMap<IFile, Entry>(); // Map of source files to build entry.
+
+ private IFolder _generated;
+ private IPath _generatedPath;
+ // Map of absolute path to project location.
+ private Map<IPath, IFile> _sources = new HashMap<IPath, IFile>();
+
+ Slice2JavaGeneratedParser(IFolder generated, Set<IFile> candidates)
+ {
+ _generated = generated;
+ _generatedPath = generated.getProjectRelativePath();
+ for(IFile f : candidates)
+ {
+ _sources.put(f.getLocation(), f);
+ }
+ }
+
+ private Node findNode(Node n, String qName)
+ throws SAXException
+ {
+ NodeList children = n.getChildNodes();
+ for(int i = 0; i < children.getLength(); ++i)
+ {
+ Node child = children.item(i);
+ if(child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(qName))
+ {
+ return child;
+ }
+ }
+ throw new SAXException("no such node: " + qName);
+ }
+
+ private IFile convert(String fname)
+ {
+ IPath p = new Path(fname); // fname contains "generated/...".
+ int match = p.matchingFirstSegments(_generatedPath);
+ return _generated.getFile(p.removeFirstSegments(match));
+ }
+
+ public Set<IFile> visitSource(Node source) throws SAXException
+ {
+ Set<IFile> files = new HashSet<IFile>();
+ NodeList sourceNodes = source.getChildNodes();
+ for(int j = 0; j < sourceNodes.getLength(); ++j)
+ {
+ if(sourceNodes.item(j).getNodeType() == Node.ELEMENT_NODE && sourceNodes.item(j).getNodeName().equals("file"))
+ {
+ Element file = (Element)sourceNodes.item(j);
+ String name = file.getAttribute("name");
+ if(name.length() == 0)
+ {
+ throw new SAXException("empty name attribute");
+ }
+ files.add(convert(name));
+ }
+ }
+ return files;
+ }
+
+ private String getText(Node n) throws SAXException
+ {
+ NodeList children = n.getChildNodes();
+ if(children.getLength() == 1 && children.item(0).getNodeType() == Node.TEXT_NODE)
+ {
+ return children.item(0).getNodeValue();
+ }
+ return "";
+ }
+
+ public void visit(Node doc) throws SAXException
+ {
+ Node n = findNode(doc, "generated");
+ NodeList fileNodes = n.getChildNodes();
+ for(int j = 0; j < fileNodes.getLength(); ++j)
+ {
+ if(fileNodes.item(j).getNodeType() == Node.ELEMENT_NODE && fileNodes.item(j).getNodeName().equals("source"))
+ {
+ Element sourceElement = (Element)fileNodes.item(j);
+ String name = sourceElement.getAttribute("name");
+ if(name.length() == 0)
+ {
+ throw new SAXException("empty name attribute");
+ }
+
+ // The source file
+ IFile source = _sources.get(new Path(name));
+ if(source == null)
+ {
+ throw new SAXException("unknown source file: " + name);
+ }
+
+ Entry e = new Entry();
+ e.error = true;
+ e.output = getText(findNode(sourceElement, "output"));
+
+ String error = sourceElement.getAttribute("error");
+ if(error.equals("true"))
+ {
+ e.error = true;
+ e.files = new HashSet<IFile>();
+ }
+ else
+ {
+ e.error = false;
+ e.files = visitSource(sourceElement);
+ }
+ output.put(source, e);
+ }
+ }
+ }
+ }
+
+ private Slice2JavaGeneratedParser getGeneratedFiles(BuildState state, Set<IFile> candidates, StringBuffer out)
+ throws CoreException
+ {
+ Slice2JavaGeneratedParser parser = new Slice2JavaGeneratedParser(state.generated, candidates);
+ try
+ {
+ InputStream in = new ByteArrayInputStream(out.toString().getBytes());
+ Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new BufferedInputStream(in));
+ parser.visit(doc);
+ }
+ catch(SAXException e)
+ {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "internal error reading the generated output list", e));
+ }
+ catch(ParserConfigurationException e)
+ {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "internal error reading the generated output list", e));
+ }
+ catch(IOException e)
+ {
+ throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID,
+ "internal error reading the generated output list", e));
+ }
+ return parser;
+ }
+}