com.iw.plugins.spindle.core.builder.TapestryBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.iw.plugins.spindle.core.builder.TapestryBuilder.java

Source

package com.iw.plugins.spindle.core.builder;

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Spindle, an Eclipse Plugin for Tapestry.
 *
 * The Initial Developer of the Original Code is
 * Geoffrey Longman.
 * Portions created by the Initial Developer are Copyright (C) 2001-2005
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * 
 *  glongman@gmail.com
 *
 * ***** END LICENSE BLOCK ***** */

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.tapestry.spec.IComponentSpecification;
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.IWorkspaceRoot;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.ui.IWorkbench;
import org.osgi.framework.Bundle;

import com.iw.plugins.spindle.core.TapestryCore;
import com.iw.plugins.spindle.core.TapestryProject;
import com.iw.plugins.spindle.core.resources.ClasspathRootLocation;
import com.iw.plugins.spindle.core.resources.ContextRootLocation;
import com.iw.plugins.spindle.core.resources.IResourceWorkspaceLocation;
import com.iw.plugins.spindle.core.resources.templates.TemplateFinder;
import com.iw.plugins.spindle.core.source.IProblemCollector;
import com.iw.plugins.spindle.core.util.Markers;

/**
 * The Tapestry Builder, kicks off full and incremental builds.
 * 
 * @author glongman@gmail.com
 */
public class TapestryBuilder extends IncrementalProjectBuilder {

    private final Bundle systemBundle = Platform.getBundle("org.eclipse.osgi");

    private static ThreadLocal PACKAGE_CACHE;

    private static ThreadLocal TYPE_CACHE;

    private static ThreadLocal STORAGE_CACHE;

    static {
        PACKAGE_CACHE = new ThreadLocal();
        TYPE_CACHE = new ThreadLocal();
        STORAGE_CACHE = new ThreadLocal();
    }

    public static Map getPackageCache() {
        return (Map) PACKAGE_CACHE.get();
    }

    public static Map getTypeCache() {
        return (Map) TYPE_CACHE.get();
    }

    public static Map getStorageCache() {
        return (Map) STORAGE_CACHE.get();
    }

    public static final String STRING_KEY = "builder-";

    public static final String APPLICATION_EXTENSION = "application";

    public static final String COMPONENT_EXTENSION = "jwc";

    public static final String PAGE_EXTENSION = "page";

    public static final String TEMPLATE_EXTENSION = "html";

    public static final String SCRIPT_EXTENSION = "script";

    public static final String LIBRARY_EXTENSION = "library";

    public static final String[] KnownExtensions = new String[] { APPLICATION_EXTENSION, COMPONENT_EXTENSION,
            PAGE_EXTENSION, TEMPLATE_EXTENSION,
            // SCRIPT_EXTENSION,
            LIBRARY_EXTENSION };

    public static final String APP_SPEC_PATH_PARAM = "org.apache.tapestry.application-specification";

    public static final String ENGINE_CLASS_PARAM = "org.apache.tapestry.engine-class";

    public static boolean DEBUG = false;

    public static long BUILD_START;

    // TODO this is really ugly, but I need this fast.
    public static List fDeferredActions = new ArrayList();

    /**
     * Obtain all the template locations for a component specification
     * 
     * @param specification
     *            the IComponentSpecification we want to find templates for
     * @param collector
     *            an IProblemCollector to collect any problems encountered.
     * @return an array of IResourceWorkspaceLocation - the template locations.
     */
    public static IResourceWorkspaceLocation[] scanForTemplates(IComponentSpecification specification,
            IProblemCollector collector) {
        TemplateFinder finder = new TemplateFinder();

        // IResourceWorkspaceLocation[] locations = new IResourceWorkspaceLocation[0];
        // TemplateFinder finder = new TemplateFinder();
        try {
            return finder.getTemplates(specification, collector);
        } catch (CoreException e) {
            TapestryCore.log(e);
        }
        return new IResourceWorkspaceLocation[] {};
    }

    public static State readState(DataInputStream in) throws IOException {
        return State.read(in);
    }

    public static void writeState(Object state, DataOutputStream out) throws IOException {
        ((State) state).write(out);
    }

    IProject fCurrentProject;

    IJavaProject fJavaProject;

    TapestryProject fTapestryProject;

    IWorkspaceRoot fWorkspaceRoot;

    ContextRootLocation fContextRoot;

    ClasspathRootLocation fClasspathRoot;

    IProject[] fInterestingProjects;

    int fProjectType;

    boolean fValidateWebXML;

    private IBuild fBuild;

    BuildNotifier fNotifier;

    IClasspathEntry[] fClasspath;

    /**
     * Constructor for TapestryBuilder.
     */
    public TapestryBuilder() {
        super();
    }

    /*
     * (non-Javadoc)
     * 
     * @see org.eclipse.core.internal.events.InternalBuilder#build(int, java.util.Map,
     *      org.eclipse.core.runtime.IProgressMonitor)
     */
    protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {

        if (DEBUG)
            System.out.println("***************************************************************");
        if (systemBundle.getState() == Bundle.STOPPING)
            throw new OperationCanceledException();

        fInterestingProjects = new IProject[] {};

        PACKAGE_CACHE.set(new HashMap());
        TYPE_CACHE.set(new HashMap());
        STORAGE_CACHE.set(new HashMap());

        IWorkbench workbench = TapestryCore.getDefault().getWorkbench();
        if (workbench.isClosing())
            return fInterestingProjects;
        fCurrentProject = getProject();
        if (fCurrentProject == null || !fCurrentProject.isAccessible())
            return new IProject[0];

        BUILD_START = System.currentTimeMillis();
        if (DEBUG)
            System.out.println("\nStarting build of " + fCurrentProject.getName() + " @ "
                    + new Date(System.currentTimeMillis()));
        this.fNotifier = new BuildNotifier(monitor, fCurrentProject);
        fNotifier.begin();
        boolean ok = false;
        try {
            fNotifier.checkCancel();
            initializeBuilder();

            if (isWorthBuilding()) {
                fNotifier.checkCancel();
                if (kind == FULL_BUILD) {
                    buildAll();
                } else {
                    buildIncremental();
                }
                System.out.println("about to do deferred at:" + (System.currentTimeMillis() - BUILD_START));
                for (Iterator iter = fDeferredActions.iterator(); iter.hasNext();) {
                    IBuildAction action = (IBuildAction) iter.next();
                    action.run();
                }
                ok = true;
                System.out.println("finished build proper:" + (System.currentTimeMillis() - BUILD_START));
            }
        } catch (CoreException e) {
            ErrorDialog.openError(TapestryCore.getDefault().getWorkbench().getActiveWorkbenchWindow().getShell(),
                    TapestryCore.getString("build-failed-core-title"),
                    TapestryCore.getString("build-failed-core-message"), e.getStatus());
        } catch (BrokenWebXMLException e) {
            Markers.addBuildBrokenProblemMarkerToResource(getProject(), e.getMessage());
        } catch (BuilderException e) {
            Markers.removeProblemsForProject(fCurrentProject);
            Markers.removeInterestingProjectMarkers(fCurrentProject, fWorkspaceRoot, IResource.DEPTH_ONE);
            Markers.addBuildBrokenProblemMarkerToResource(getProject(), e.getMessage());
        } catch (NullPointerException e) {
            TapestryCore.log(e);
            throw e;
        } catch (RuntimeException e) {
            TapestryCore.log(e);
            throw e;
        } finally {
            PACKAGE_CACHE.set(null);
            TYPE_CACHE.set(null);
            STORAGE_CACHE.set(null);
            if (!ok)
                // If the build failed, clear the previously built state,
                // forcing a full
                // build next time.
                clearLastState();
            fNotifier.done();
            long stop = System.currentTimeMillis();
            if (DEBUG)
                System.out.println("Finished build of " + fCurrentProject.getName() + " @ " + new Date(stop));
            System.out.println("elapsed (ms) = " + (stop - BUILD_START));
            fDeferredActions.clear();
            cleanup();
            TapestryCore.buildOccurred();
        }

        return fInterestingProjects;
    }

    /**
     * Method cleanup.
     */
    private void cleanup() {
        if (fBuild != null)
            fBuild.cleanUp();

        fClasspath = null;
    }

    public static IProject[] getBuildInterestingProjects(IJavaProject jproject, IWorkspaceRoot root) {
        if (jproject == null || root == null)
            return new IProject[0];

        ArrayList projects = new ArrayList();
        try {
            IClasspathEntry[] entries = ((JavaProject) jproject).getExpandedClasspath();
            for (int i = 0, length = entries.length; i < length; i++) {
                IClasspathEntry entry = JavaCore.getResolvedClasspathEntry(entries[i]);
                if (entry != null) {
                    IPath path = entry.getPath();
                    IProject p = null;
                    if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
                        IProject workspaceProject = root.getProject(path.lastSegment());
                        if (workspaceProject.hasNature(JavaCore.NATURE_ID))
                            p = workspaceProject;

                    }
                    if (p != null && !projects.contains(p))
                        projects.add(p);

                }
            }
        } catch (CoreException e) {
            return new IProject[0];
        }
        IProject[] result = new IProject[projects.size()];
        projects.toArray(result);
        return result;
    }

    State getLastState(IProject project) {
        return (State) TapestryArtifactManager.getTapestryArtifactManager().getLastBuildState(project, false);
    }

    /**
     * Method clearLastState.
     */
    private void clearLastState() {
        TapestryArtifactManager.getTapestryArtifactManager().setLastBuildState(fCurrentProject, null);
    }

    /**
     * Method buildAll.
     */
    private void buildAll() throws BuilderException, CoreException {
        if (TapestryBuilder.DEBUG)
            System.out.println("FULL Tapestry build");

        fNotifier.subTask(TapestryCore.getString(TapestryBuilder.STRING_KEY + "full-build-starting"));

        Markers.removeProblemsForProject(fCurrentProject);
        Markers.removeInterestingProjectMarkers(fCurrentProject, fWorkspaceRoot, IResource.DEPTH_ONE);

        fBuild = new FullBuild(this);

        fBuild.build();

        Markers.addInterestingProjectMarkers(fCurrentProject, fInterestingProjects);

    }

    protected void buildIncremental() throws BuilderException, CoreException {
        System.out.println("buildIncremental() time so far = " + (BUILD_START - System.currentTimeMillis()));

        IIncrementalBuild incBuild = null;

        ArrayList allDeltas = new ArrayList();
        allDeltas.add(getDelta(fTapestryProject.getProject()));

        for (int i = 0; i < fInterestingProjects.length; i++) {
            IResourceDelta delta = getDelta(fInterestingProjects[i]);
            if (delta != null)
                allDeltas.add(delta);
        }

        incBuild = new IncrementalProjectBuild(this,
                (IResourceDelta[]) allDeltas.toArray(new IResourceDelta[allDeltas.size()]));

        if (incBuild.canIncrementalBuild()) {
            if (!incBuild.needsIncrementalBuild())
                return;

            fBuild = incBuild;
            if (TapestryBuilder.DEBUG)
                System.out.println("Incremental Tapestry build");

            fNotifier.subTask(TapestryCore.getString(TapestryBuilder.STRING_KEY + "incremental-build-starting"));

            System.out.println("about to call incBuild.build(); = " + (System.currentTimeMillis() - BUILD_START));
            incBuild.build();
            System.out.println("finished incBuild.build(); = " + (System.currentTimeMillis() - BUILD_START));
        } else {
            buildAll();
        }
    }

    /**
     * Method isWorthBuilding.
     * 
     * @return boolean
     */
    private boolean isWorthBuilding() {

        if (fJavaProject == null)
            throw new BuilderException(TapestryCore.getString(STRING_KEY + "non-java-projects"));

        try {
            fClasspath = fJavaProject.getResolvedClasspath(true);
        } catch (JavaModelException e3) {
            throw new BuilderException(TapestryCore.getString(STRING_KEY + "classpath-not-determined"));
        }

        try {
            IResource resource = fJavaProject.getUnderlyingResource();
            IMarker[] jprojectMarkers = new IMarker[0];
            if (resource != null && resource.exists())
                jprojectMarkers = resource.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false,
                        IResource.DEPTH_ZERO);
            if (jprojectMarkers.length > 0)
                throw new BuilderException(TapestryCore.getString(STRING_KEY + "java-builder-failed"));

        } catch (CoreException e) {
            // assume there are no Java builder problems
        }

        try {
            IPath outputPath = fJavaProject.getOutputLocation();
            IPath projectPath = fJavaProject.getPath();

            if (projectPath.equals(outputPath))
                throw new BuilderException(TapestryCore.getString(STRING_KEY + "abort-invalid-output-location",
                        outputPath.toString()));

        } catch (JavaModelException e1) {
            throw new BuilderException(TapestryCore.getString(STRING_KEY + "abort-no-output-location"));
        }

        if (fTapestryProject == null)
            throw new BuilderException("could not obtain the TapestryProject instance!");

        if (fClasspathRoot == null)
            throw new BuilderException("could not obtain the ClasspathRoot!");

        if (getType(TapestryCore.getString(STRING_KEY + "applicationServletClassname")) == null)
            throw new BuilderException(TapestryCore.getString(STRING_KEY + "tapestry-jar-missing"));

        if (fContextRoot == null)
            throw new BuilderException(TapestryCore.getString(STRING_KEY + "missing-context-bad"));

        if (!fContextRoot.exists())
            throw new BuilderException(TapestryCore.getString(STRING_KEY + "missing-context", fContextRoot));

        IResourceWorkspaceLocation webXML = (IResourceWorkspaceLocation) fContextRoot
                .getRelativeLocation("WEB-INF/web.xml");

        if (webXML.getStorage() == null)
            throw new BuilderException(
                    TapestryCore.getString(STRING_KEY + "abort-missing-web-xml", webXML.toString()));

        return true;
    }

    /**
     * Method initializeBuilder.
     */
    private void initializeBuilder() {
        try {
            fJavaProject = (IJavaProject) fCurrentProject.getNature(JavaCore.NATURE_ID);
        } catch (CoreException e) {
            TapestryCore.log(e);
        }

        fWorkspaceRoot = ResourcesPlugin.getWorkspace().getRoot();

        fInterestingProjects = getBuildInterestingProjects(fJavaProject, fWorkspaceRoot);

        try {
            fTapestryProject = (TapestryProject) fCurrentProject.getNature(TapestryCore.NATURE_ID);
            fTapestryProject.clearMetadata();
        } catch (CoreException e) {
            TapestryCore.log(e);
        }

        fContextRoot = fTapestryProject.getWebContextLocation();

        try {
            fClasspathRoot = fTapestryProject.getClasspathRoot();
        } catch (CoreException e1) {
            TapestryCore.log(e1);
        }

        fValidateWebXML = fTapestryProject.isValidatingWebXML();

    }

    protected IType getType(String fullyQualifiedName) {
        Map cache = getTypeCache();

        if (cache != null && cache.containsKey(fullyQualifiedName))
            return (IType) cache.get(fullyQualifiedName);

        IType result = null;
        try {
            result = fJavaProject.findType(fullyQualifiedName);
        } catch (JavaModelException e) {
            // do nothing
        }

        // bug [ spindle-Bugs-1186225 ] Java inner classes trigger error message
        if (result == null && fullyQualifiedName.indexOf("$") > 0)
            try {
                result = fJavaProject.findType(fullyQualifiedName.replaceAll("$", "."));
            } catch (JavaModelException e) {
                // do nothing
            }

        if (result != null)
            cache.put(fullyQualifiedName, result);

        return result;
    }

    protected boolean conflictsWithJavaOutputDirectory(IResource resource) {
        try {
            IPath containerPath = fJavaProject.getOutputLocation();
            return containerPath.isPrefixOf(resource.getFullPath());
        } catch (JavaModelException e) {
            // do nothing
        }
        return false;
    }

}