net.ostis.scpdev.builder.ScRepositoryBuilder.java Source code

Java tutorial

Introduction

Here is the source code for net.ostis.scpdev.builder.ScRepositoryBuilder.java

Source

/*
 * This source file is part of OSTIS (Open Semantic Technology for Intelligent
 * Systems) For the latest info, see http://www.ostis.net
 *
 * Copyright (c) 2010 OSTIS
 *
 * OSTIS is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * OSTIS is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with OSTIS. If not, see <http://www.gnu.org/licenses/>.
 */
package net.ostis.scpdev.builder;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.ostis.scpdev.ScpdevPlugin;
import net.ostis.scpdev.builder.scg.SCgFileBuilder;
import net.ostis.scpdev.core.ScNature;
import net.ostis.scpdev.external.ScCoreModule;
import net.ostis.scpdev.util.ResourceHelper;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;

/**
 * Builder of SC source repository.
 *
 * @author Dmitry Lazurkin
 */
public class ScRepositoryBuilder extends IncrementalProjectBuilder {

    public static final String BUILDER_ID = "net.ostis.scpdev.sc_rbuilder";

    public static final String BUILD_PROBLEM_MARKER = "net.ostis.scpdev.build_problem";

    private static final Log log = LogFactory.getLog(ScRepositoryBuilder.class);

    private List<IElementBuilder> buildQueue;

    private static Map<String, Class<? extends IElementBuilder>> buildersRegistry = new HashMap<String, Class<? extends IElementBuilder>>();

    private static Set<Pattern> skipPatterns = new HashSet<Pattern>();

    private static Set<String> skipExtensions = new HashSet<String>();

    private static Set<Path> specialFolders = new HashSet<Path>();

    static {
        addFileBuilder("scs", SCsFileBuilder.class);
        addFileBuilder("scg", SCgFileBuilder.class);
        addFileBuilder("m4scp", M4ScpFileBuilder.class);

        specialFolders.add(new Path("tmp"));
        specialFolders.add(new Path("proc"));

        skipExtensions.add("gwf");
        skipExtensions.add("scsy");

        skipPatterns.add(Pattern.compile("^\\..+$")); // Starts with dot
    }

    public static synchronized void addFileBuilder(String type, Class<? extends IElementBuilder> builderKlass) {
        if (log.isDebugEnabled())
            log.debug("Register builder '" + builderKlass + "' for extension '" + type + "'");
        buildersRegistry.put(type, builderKlass);
    }

    protected void checkCancel(IProgressMonitor monitor) {
        if (monitor.isCanceled()) {
            forgetLastBuiltState();// not always necessary
            throw new OperationCanceledException();
        }
    }

    private boolean addFileBuilder(IFolder srcroot, IFile source) {
        Class<? extends IElementBuilder> klass = buildersRegistry.get(source.getFileExtension());

        boolean result = false;

        if (klass != null) {
            try {
                Constructor<? extends IElementBuilder> constructor = klass.getConstructor(IFolder.class,
                        IFile.class);
                buildQueue.add(constructor.newInstance(srcroot, source));
                result = true;
            } catch (Exception e) {
                log.error(klass + " haven't constructor thats receive IFolder and IResource", e);
            }
        } else {
            if (skipExtensions.contains(source.getFileExtension()) == false)
                ResourceHelper.addMarker(source, BUILD_PROBLEM_MARKER,
                        "File type isn't handled, but why file in source repository tree?",
                        IMarker.SEVERITY_WARNING);
        }

        return result;
    }

    /**
     * Create META-file in binary folder.
     */
    private void createMetaFile(IFolder binary) throws CoreException {
        IFile meta = binary.getFile("META");

        if (log.isDebugEnabled())
            log.debug("Create META for " + binary);

        String scs2tgf = ScCoreModule.getSCsCompilerPath();

        try {
            Process ps = Runtime.getRuntime()
                    .exec(String.format("\"%s\" -nc - \"%s\"", scs2tgf, meta.getLocation().toOSString()));

            BufferedWriter outStream2 = new BufferedWriter(new OutputStreamWriter(ps.getOutputStream()));
            outStream2.write("\"/info/dirent\" = {META={}");

            // TODO: remove bad solution with filesystem raw working
            File binaryFolder = binary.getRawLocation().toFile();
            for (String n : binaryFolder.list()) {
                outStream2.write(",\"" + n + "\"={}");
            }

            outStream2.write("};");
            outStream2.close();

            if (ps.waitFor() != 0) {
                System.err.println(IOUtils.toString(ps.getErrorStream()));
            }
        } catch (Exception e) {
            String msg = "Error while creating META-file for binary folder " + binary;
            log.error(msg, e);
            throw new CoreException(new Status(IStatus.ERROR, ScpdevPlugin.PLUGIN_ID, msg, e));
        }
    }

    /**
     * @return if resource not for build - true, else - false.
     */
    private boolean isSkipedResource(IResource resource) {
        String name = resource.getName();

        if (skipExtensions.contains(resource.getFileExtension()))
            return true;

        for (Pattern p : skipPatterns) {
            Matcher matcher = p.matcher(name);
            if (matcher.matches())
                return true;
        }

        return false;
    }

    public class CollectForBuildAllVisitor implements IResourceVisitor {

        private final IFolder srcroot;

        public CollectForBuildAllVisitor(IFolder srcroot) {
            this.srcroot = srcroot;
        }

        @Override
        public boolean visit(IResource resource) throws CoreException {
            if (resource.isAccessible()) {
                boolean skipped = isSkipedResource(resource);

                if (skipped)
                    return false;

                if (resource instanceof IFile) {
                    IFile source = (IFile) resource;
                    addFileBuilder(srcroot, source);
                }

                return true;
            } else {
                return false;
            }
        }

    }

    private class CollectForBuildDeltaVisitor implements IResourceDeltaVisitor {

        private final IFolder srcroot;

        private final IFolder binroot;

        public CollectForBuildDeltaVisitor(IFolder srcroot, IFolder binroot) {
            this.srcroot = srcroot;
            this.binroot = binroot;
        }

        @Override
        public boolean visit(IResourceDelta delta) throws CoreException {
            boolean skipped = isSkipedResource(delta.getResource());

            if (log.isDebugEnabled())
                log.debug("Resource is changed : " + delta.getResource());

            if (skipped)
                return false;

            switch (delta.getKind()) {
            case IResourceDelta.ADDED: {
                IResource addedResource = delta.getResource();

                if (addedResource instanceof IFile) {
                    IFile source = (IFile) addedResource;
                    addFileBuilder(srcroot, source);
                }

                break;
            }
            case IResourceDelta.REMOVED: {
                String rawBinaryPath = AbstractElementBuilder.getRawBinaryPath(srcroot, binroot,
                        delta.getResource());
                try {
                    FileUtils.forceDelete(new File(rawBinaryPath));
                } catch (IOException e) {
                    log.error("Source resource " + delta.getResource()
                            + " was deleted. Catch exception while deleting binary resource " + rawBinaryPath, e);
                }
                // TODO: remove all empty parent binary folders
                break;
            }
            case IResourceDelta.CHANGED: {
                IResource changed = delta.getResource();

                if (delta.getFlags() == IResourceDelta.CONTENT) {
                    addFileBuilder(srcroot, (IFile) changed);
                }

                break;
            }
            }

            return true;
        }

    }

    /**
     * If at least one scsy is modified then return true else - false.
     */
    private boolean isIncludesChanged(IFolder binroot, IResourceDelta delta) {
        final long binrootTimeStamp = binroot.getLocalTimeStamp();

        try {
            delta.accept(new IResourceDeltaVisitor() {
                @Override
                public boolean visit(IResourceDelta delta) throws CoreException {
                    IResource resource = delta.getResource();
                    String ext = resource.getFileExtension();

                    if (ext != null && ext.equals("scsy")) {
                        if (delta.getResource().getLocalTimeStamp() >= binrootTimeStamp) {
                            if (log.isDebugEnabled())
                                log.debug("Include " + delta.getResource() + " is changed. Forcing rebuild.");
                            throw new RuntimeException("Include changed, force rebuild");
                        }
                    }

                    return !isSkipedResource(resource);
                }
            });
        } catch (Exception e) {
            return true;
        }

        return false;
    }

    /**
     * Create special folder in binary repository with specified path.
     */
    private void createSpecialFolder(IFolder binroot, IPath path) throws CoreException {
        IFolder folder = binroot.getFolder(path);
        if (folder.exists() == false)
            folder.create(true, true, null);
    }

    /**
     * Create all registred special folders in binary repository.
     */
    private void createSpecialFolders(IFolder binroot) throws CoreException {
        for (Path p : specialFolders)
            createSpecialFolder(binroot, p);
    }

    @SuppressWarnings("rawtypes")
    protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
        buildQueue = new LinkedList<IElementBuilder>();

        ScNature scnature = ScNature.getScNature(getProject());

        IFolder srcroot = scnature.getSourceRoot();
        IFolder binroot = scnature.getBinaryRootAndCreate();

        if (kind == FULL_BUILD) {
            fullBuild(srcroot, binroot, monitor);
        } else {
            IResourceDelta delta = getDelta(getProject());
            if (delta == null || isIncludesChanged(binroot, delta)) {
                fullBuild(srcroot, binroot, monitor);
            } else {
                if (delta.getResource().equals(getProject())) {
                    IResourceDelta srcrootDelta = delta.findMember(srcroot.getProjectRelativePath());
                    if (srcrootDelta != null)
                        incrementalBuild(srcroot, binroot, srcrootDelta, monitor);
                }
            }
        }

        return null;
    }

    @Override
    protected void clean(IProgressMonitor monitor) throws CoreException {
        IFolder binRoot = getProject().getFolder("fs_repo");
        if (binRoot.exists())
            binRoot.delete(true, monitor);
    }

    /**
     * Evaluate all build goals from build queue.
     */
    private void evaluateBuilders(IFolder srcroot, IFolder binroot, final IProgressMonitor monitor)
            throws CoreException {
        try {
            monitor.beginTask("Building source repository", buildQueue.size());

            while (buildQueue.isEmpty() != true) {
                IElementBuilder curBuilder = buildQueue.remove(0);
                monitor.setTaskName("Building " + curBuilder.getResource().toString());

                checkCancel(monitor);

                curBuilder.build(binroot);
                monitor.worked(1);
            }

            binroot.refreshLocal(IResource.DEPTH_INFINITE, monitor);
        } finally {
            monitor.done();
        }
    }

    /**
     * Create META-file for folders in binary repository.
     */
    private class MetaCreatorVisitor implements IResourceVisitor {

        private boolean fullBuild;

        /**
         * @param fullBuild true - if need full rebuild of META-files.
         */
        public MetaCreatorVisitor(boolean fullBuild) {
            this.fullBuild = fullBuild;
        }

        @Override
        public boolean visit(IResource resource) throws CoreException {
            if (resource.isAccessible()) {
                if (resource instanceof IFolder) {
                    IFolder binary = (IFolder) resource;
                    if (fullBuild) {
                        createMetaFile(binary);
                    } else {
                        IFile metaFile = binary.getFile("META");
                        // If META-file doesn't exist or it older then binary
                        // folder, then recreate META-file
                        if (metaFile.exists() == false || binary.getRawLocation().toFile().lastModified() > metaFile
                                .getRawLocation().toFile().lastModified()) {
                            createMetaFile(binary);
                        }
                    }
                }
                return true;
            } else {
                return false;
            }
        }
    }

    protected void fullBuild(IFolder srcroot, IFolder binroot, final IProgressMonitor monitor)
            throws CoreException {
        // Collect all goals for build.
        srcroot.accept(new CollectForBuildAllVisitor(srcroot));
        evaluateBuilders(srcroot, binroot, monitor);
        // Create META-file for all binary folders.
        binroot.accept(new MetaCreatorVisitor(true));
        binroot.refreshLocal(IResource.DEPTH_INFINITE, monitor);
        createSpecialFolders(binroot);
    }

    protected void incrementalBuild(IFolder srcroot, IFolder binroot, IResourceDelta delta,
            IProgressMonitor monitor) throws CoreException {
        // Collect all goals for build.
        delta.accept(new CollectForBuildDeltaVisitor(srcroot, binroot));
        evaluateBuilders(srcroot, binroot, monitor);
        // Create META-file only for changed binary folders.
        binroot.accept(new MetaCreatorVisitor(false));
        binroot.refreshLocal(IResource.DEPTH_INFINITE, monitor);
        createSpecialFolders(binroot);
    }

}