/* * This software is Copyright 2005,2006,2007,2008 Langdale Consultants. * Langdale Consultants can be contacted at: http://www.langdale.com.au */ package au.com.langdale.cimtoole.builder; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import org.eclipse.core.resources.IFile; 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.IWorkspaceRunnable; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import au.com.langdale.cimtoole.builder.ConsistencyChecks.ProfileChecker; import au.com.langdale.cimtoole.builder.ProfileBuildlets.CopyBuildlet; import au.com.langdale.cimtoole.builder.ProfileBuildlets.LegacyRDFSBuildlet; import au.com.langdale.cimtoole.builder.ProfileBuildlets.ProfileBuildlet; import au.com.langdale.cimtoole.builder.ProfileBuildlets.SimpleOWLBuildlet; import au.com.langdale.cimtoole.builder.ProfileBuildlets.TextBuildlet; import au.com.langdale.cimtoole.builder.ProfileBuildlets.TransformBuildlet; import au.com.langdale.cimtoole.builder.ProfileBuildlets.XSDBuildlet; import au.com.langdale.cimtoole.project.Info; import au.com.langdale.cimtoole.registries.ProfileBuildletRegistry; /** * The builder for CIMTool projects. * * This class divides the build into smaller units of work * performed by <code>Buildlets</code>. */ public class CIMBuilder extends IncrementalProjectBuilder { public static final String BUILDER_ID = "au.com.langdale.cimtoole.CIMBuilder"; private static final String MARKER_TYPE = "au.com.langdale.cimtoole.problem"; /** * @return an array of buildlets that together build a CIMTool project */ public Buildlet[] createBuildlets() { Buildlet[] defaultBuildlets = new Buildlet[] { new SchemaBuildlet(), new ProfileChecker(), new XSDBuildlet(), new TransformBuildlet(null, "xml"), new TransformBuildlet("html", "html"), new TextBuildlet("sql", "sql"), new TextBuildlet("jpa", "java"), new TextBuildlet("scala", "scala"), new SimpleOWLBuildlet("RDF/XML", "simple-flat-owl", false), new SimpleOWLBuildlet("RDF/XML-ABBREV", "simple-owl", false), new LegacyRDFSBuildlet("RDF/XML", "legacy-rdfs", false), new SimpleOWLBuildlet("RDF/XML", "simple-flat-owl-augmented", true), new SimpleOWLBuildlet("RDF/XML-ABBREV", "simple-owl-augmented", true), new LegacyRDFSBuildlet("RDF/XML", "legacy-rdfs-augmented", true), new CopyBuildlet("TURTLE", "ttl"), new ValidationBuildlet(), new SplitValidationBuildlet(), new IncrementalValidationBuildlet(), }; ProfileBuildlet[] registered = ProfileBuildletRegistry.INSTANCE.getBuildlets(); if (registered.length>0){ Buildlet[] combined = new Buildlet[defaultBuildlets.length+registered.length]; System.arraycopy(defaultBuildlets, 0, combined, 0, defaultBuildlets.length); System.arraycopy(registered, 0, combined, defaultBuildlets.length, registered.length); return combined; }else return defaultBuildlets; } /** * Utility to add a problem marker to a file. * @param file: the file to be marked * @param message: the text describing the problem * @param lineNumber: the line to be marked. * @param severity: the severity code (see <code>IMarker</code>). * @throws CoreException */ public static void addMarker(IFile file, String message, int lineNumber, int severity) throws CoreException { IMarker marker = addMarker(file, message, severity); if (lineNumber == -1) lineNumber = 1; marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); } /** * Utility to add a problem marker to a file. * @param file: the file to be marked * @param message: the text describing the problem * @param severity: the severity code (see <code>IMarker</code>). * @throws CoreException */ public static IMarker addMarker(IFile file, String message, int severity) throws CoreException { IMarker marker = file.createMarker(MARKER_TYPE); marker.setAttribute(IMarker.MESSAGE, message); marker.setAttribute(IMarker.SEVERITY, severity); return marker; } /** * Create a SAX error handler that converts each error into a marker. * @param result: the file to which the markers will be attached * @return: the SAX error handler * @throws CoreException */ public static ErrorHandler createErrorHandler(final IFile result) throws CoreException { removeMarkers(result); return new ErrorHandler() { private void log(final IFile result, SAXParseException ex, int severity) throws SAXException { try { addMarker(result, ex.getMessage(), ex.getLineNumber(), severity); } catch (CoreException e) { throw new SAXException(e); } } public void warning(SAXParseException ex) throws SAXException { log(result, ex, IMarker.SEVERITY_WARNING); } public void error(SAXParseException ex) throws SAXException { log(result, ex, IMarker.SEVERITY_ERROR); } public void fatalError(SAXParseException ex) throws SAXException { log(result, ex, IMarker.SEVERITY_ERROR); } }; } /** * Utility to add a problem marker with ERROR severity to a file. * @param file: the file to be marked * @param message: the text describing the problem * @throws CoreException */ public static IMarker addMarker(IFile file, String message) throws CoreException{ return addMarker(file, message, IMarker.SEVERITY_ERROR); } /** * Utility to remove all CIMTool markers from a file. * @param file: the file * @throws CoreException */ public static void removeMarkers(IFile file) throws CoreException { if( file.exists()) file.deleteMarkers(MARKER_TYPE, false, IResource.DEPTH_ZERO); } private static class Worker implements IResourceDeltaVisitor, IResourceVisitor, IWorkspaceRunnable { private boolean cleanup; private boolean rebuild; private Buildlet[] buildlets; private Map work = new LinkedHashMap(); public Worker(Buildlet[] buildlets, boolean cleanup) { this.buildlets = buildlets; this.cleanup = cleanup; } public boolean getRebuild() { return rebuild; } public boolean visit(IResourceDelta delta) throws CoreException { switch (delta.getKind()) { case IResourceDelta.ADDED: case IResourceDelta.REMOVED: collect(delta.getResource()); break; case IResourceDelta.CHANGED: if((delta.getFlags()&(IResourceDelta.CONTENT|IResourceDelta.REPLACED)) != 0) collect(delta.getResource()); break; } return true; } public boolean visit(IResource resource) throws CoreException { collect(resource); return true; } private void collect(IResource resource) throws CoreException { rebuild = rebuild || Info.isSchema(resource); for( int ix = 0; ix < buildlets.length; ix++) { Buildlet buildlet = buildlets[ix]; Iterator outputs = buildlet.getOutputs(resource).iterator(); while( outputs.hasNext()) { IFile output = (IFile) outputs.next(); if(work.remove(output) != null) System.out.println("CIMBuilder: push down in build order: " + output.getName()); // push output down in the build order else System.out.println("CIMBuilder: adding to build: " + output.getName()); work.put(output, buildlet); collect(output); // not efficient since we might encounter an output many times } } } public void run(IProgressMonitor monitor) throws CoreException { Iterator outputs = work.keySet().iterator(); while(outputs.hasNext()) { IFile output = (IFile) outputs.next(); Buildlet buildlet = (Buildlet) work.get(output); System.out.println("CIMBuilder: building: " + output.getName()); buildlet.run(output, cleanup, monitor); } } } @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { Buildlet[] buildlets = createBuildlets(); Worker worker = new Worker(buildlets, false); IProject project = getProject(); if (kind == FULL_BUILD) { project.accept(worker); } else { IResourceDelta delta = getDelta(getProject()); if (delta == null) { project.accept(worker); } else { delta.accept(worker); if( worker.getRebuild()) project.accept(worker); } } worker.run(monitor); return null; } @Override protected void clean(IProgressMonitor monitor) throws CoreException { Worker worker = new Worker(createBuildlets(), true); getProject().accept(worker); worker.run(monitor); } }