net.rim.ejde.internal.builders.ResourceBuilder.java Source code

Java tutorial

Introduction

Here is the source code for net.rim.ejde.internal.builders.ResourceBuilder.java

Source

/*
* Copyright (c) 2010-2012 Research In Motion Limited. All rights reserved.
*
* This program and the accompanying materials are made available
* under the terms of the Eclipse Public License, Version 1.0,
* which accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*
*/
package net.rim.ejde.internal.builders;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Map;
import java.util.Vector;

import net.rim.ejde.internal.core.IConstants;
import net.rim.ejde.internal.core.IRIMMarker;
import net.rim.ejde.internal.util.ImportUtils;
import net.rim.ejde.internal.util.Messages;
import net.rim.ejde.internal.util.PackageUtils;
import net.rim.ejde.internal.util.ProjectUtils;
import net.rim.ejde.internal.util.ResourceBuilderUtils;
import net.rim.ejde.internal.util.StatusFactory;
import net.rim.sdk.CompilerException;
import net.rim.sdk.rc.ConvertUtil;
import net.rim.sdk.rc.ResourceCompiler;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.eclipse.core.resources.IContainer;
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.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.widgets.Display;

/**
 * This class is used to create interface java files for resource files.
 */
public class ResourceBuilder extends IncrementalProjectBuilder {
    static private Logger log = Logger.getLogger(ResourceBuilder.class);
    public static final String RESOURCE_BUILD_TMP_FOLDER_HEAD = "resourceBuilder_rc_";
    public static final String BUILDER_ID = "net.rim.ejde.internal.builder.BlackBerryResourcesBuilder"; //$NON-NLS-1$
    public static final String LOCALE_INTERFACES_FOLDER_NAME = "project_locale_interfaces_folder"; //$NON-NLS-1$
    static private String _tmpOutputFolder;
    private Hashtable<String, String> _resourceBuilderOptions = null;
    private Vector<IFile> _filesNeedToReBuild = new Vector<IFile>();

    /**
     * (non-javadoc)
     *
     * @see IncrementalProjectBuilder#build(int, Map, IProgressMonitor)
     */
    protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
        log.trace("Entering ResourcesBuilder build();kind=" + kind); //$NON-NLS-1$
        IProject project = getProject();
        IResourceDelta delta = getDelta(getProject());
        IResourceDelta classpathFile = null;
        if (delta != null) {
            classpathFile = delta.findMember(new Path(IConstants.CLASSPATH_FILE_NAME));
        }
        if (kind == IncrementalProjectBuilder.FULL_BUILD || classpathFile != null) {
            ResourceDeltaVisitor resourceVisitor = new ResourceDeltaVisitor(monitor);
            project.accept(resourceVisitor);
        } else {
            _filesNeedToReBuild = getFilesNeedRebuild();
            if (delta != null) {
                ResourceDeltaVisitor deltaVisitor = new ResourceDeltaVisitor(monitor);
                delta.accept(deltaVisitor);
            }
            if (_filesNeedToReBuild.size() > 0) {
                for (IFile file : _filesNeedToReBuild) {
                    compile(file, monitor);
                }
            }
        }
        log.trace("Leaving ResourcesBuilder build()"); //$NON-NLS-1$
        return null;
    }

    static public void cleanTmpDir() {
        // synchronizlly delete the tmp folder and files
        Display.getDefault().syncExec(new Runnable() {

            @Override
            public void run() {
                cleanupTempSubdir(_tmpOutputFolder);
            }

        });
    }

    /**
     * Get rrh/rrc files which need to be rebuilt.
     *
     * @return
     */
    private Vector<IFile> getFilesNeedRebuild() {
        Vector<IFile> vector = new Vector<IFile>();
        IMarker[] markers;
        try {
            markers = getProject().findMarkers(IRIMMarker.RESOURCE_BUILD_PROBLEM_MARKER, true,
                    IResource.DEPTH_INFINITE);
            for (int i = 0; i < markers.length; i++) {
                IResource resource = markers[i].getResource();
                if (resource instanceof IFile && (PackageUtils.hasRRCExtension(resource.getName())
                        || PackageUtils.hasRRHExtension(resource.getName()))) {
                    vector.add((IFile) resource);
                }
            }
        } catch (CoreException e) {
            log.error(e.getMessage());
        }
        return vector;
    }

    protected void clean(IProgressMonitor monitor) throws CoreException {
        removeResourceBuildMarkers(getProject(), IResource.DEPTH_INFINITE);
        IFolder tmpFolder = getProject()
                .getFolder(ImportUtils.getImportPref(ResourceBuilder.LOCALE_INTERFACES_FOLDER_NAME));
        if (!tmpFolder.exists())
            return;
        IResource[] members = tmpFolder.members();
        for (int i = 0; i < members.length; i++) {
            if (members[i] instanceof IFile) {
                members[i].delete(true, monitor);
            } else if (members[i] instanceof IFolder)
                ((IFolder) members[i]).delete(true, false, monitor);
            else
                throw new CoreException(StatusFactory.createErrorStatus(NLS.bind("", members[i].getName()))); //$NON-NLS-1$
        }
    }

    /**
     * Compiles given <code>iFile</code>. <code>iFIle</code> should be either of rrc or rrh file.
     *
     * @param resource
     * @param monitor
     * @throws CoreException
     */
    void compile(IResource iFile, IProgressMonitor monitor) throws CoreException {
        log.trace("ResourcesBuilder compile(IResource); Resource: " //$NON-NLS-1$
                + iFile.getLocation().toOSString());
        // remove the old markers
        removeResourceBuildMarkers(iFile, IResource.DEPTH_ONE);
        File resourceFile = null;
        // get the absolute path of the file
        resourceFile = ResourceBuilderUtils.getFile(iFile);
        if (resourceFile == null)
            log.error(NLS.bind("", //$NON-NLS-1$
                    iFile.getLocationURI()));

        // get the parent directory of the resource file
        Vector<String> fileList = new Vector<String>();
        try {
            ResourceCompiler.compile(resourceFile.getPath(), null, getResourceBuilderOptions(), fileList);
        } catch (CompilerException e) {
            log.error(
                    NLS.bind(Messages.RIMResourcesBuilder_COMPILE_FILE_ERROR_MSG, new String[] { e.getMessage() }));
            createResourceMarker(iFile,
                    NLS.bind(Messages.RIMResourcesBuilder_COMPILE_FILE_ERROR_MSG, new String[] { e.getMessage() }),
                    0, IMarker.SEVERITY_ERROR);
            return;
        } catch (IOException e) {
            log.error(
                    NLS.bind(Messages.RIMResourcesBuilder_COMPILE_FILE_ERROR_MSG, new String[] { e.getMessage() }));
            createResourceMarker(iFile,
                    NLS.bind(Messages.RIMResourcesBuilder_COMPILE_FILE_ERROR_MSG, new String[] { e.getMessage() }),
                    0, IMarker.SEVERITY_ERROR);
            return;
        }
        // parse the result file list
        for (String string : fileList) {
            File processedFile = new File(string);
            IFolder tmpParentFolder = getParentFolder(iFile.getRawLocation().toFile(), processedFile,
                    iFile.getProject(), monitor);
            // get the name of the compiled resource file
            String fileName = processedFile.getName();
            // get the IFile handle of the compiled resource file
            IFile file = tmpParentFolder.getFile(fileName);
            if (file.exists())
                file.delete(IResource.DERIVED | IResource.FORCE, monitor);
            FileInputStream fileInputStream = null;
            try {
                fileInputStream = new FileInputStream(processedFile);
                file.create(fileInputStream, IResource.DERIVED | IResource.FORCE, monitor);
            } catch (FileNotFoundException e) {
                log.error(e);
                continue;
            } finally {
                try {
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                } catch (IOException e) {
                    log.error(e.getMessage());
                }
            }
        }
    }

    /**
     * Gets the parent IFolder of the given <code>compildedFile</code>.
     * <p>
     * If the <code>compiledFile</code> is a java file, we return the folder which is .locale_interfaces + package;
     * </p>
     * <p>
     * If the <code>compiledFile</code> is a crb file, we return the folder which is .locale_interfaces because crb files are
     * supposed to be put in the root of the jar file.
     *
     * @param compiledFile
     * @param originalFile
     * @param project
     * @param monitor
     * @return
     * @throws CoreException
     */
    private IFolder getParentFolder(File compiledFile, File originalFile, IProject project,
            IProgressMonitor monitor) throws CoreException {
        // get the resource builder output root folder
        IFolder localeInterfaceFolderRoot = project
                .getFolder(ImportUtils.getImportPref(ResourceBuilder.LOCALE_INTERFACES_FOLDER_NAME));
        // create the .locale_interface folder if it does not exist
        ResourceBuilderUtils.createResourcesOutputRoot(project, monitor);
        if (PackageUtils.hasRRCExtension(originalFile.getName())) {
            return localeInterfaceFolderRoot;
        }
        // get the package info of the file
        String packageName = null;
        packageName = PackageUtils.getFilePackageString(originalFile, null);
        IFolder localeInterfaceParentFolder;
        if (StringUtils.isBlank(packageName))
            localeInterfaceParentFolder = localeInterfaceFolderRoot;
        else {
            IPath parentFolderPath = new Path(packageName);
            localeInterfaceParentFolder = localeInterfaceFolderRoot.getFolder(parentFolderPath);
        }
        if (!localeInterfaceParentFolder.exists())
            ImportUtils.createFolders(project, localeInterfaceParentFolder.getProjectRelativePath(),
                    IResource.DERIVED);
        if (!localeInterfaceParentFolder.exists()) {
            log.error(NLS.bind(Messages.RIMResourcesBuilder_ResourceInterfaceFolderMissingMessage,
                    localeInterfaceParentFolder.getProjectRelativePath()));
            return null;
        }
        return localeInterfaceParentFolder;
    }

    /**
     * Create a new marker in the specified resource.
     *
     * @param resource
     * @param message
     * @param lineNumber
     * @param severity
     * @throws CoreException
     * @throws BadLocationException
     */
    void createResourceMarker(IResource resource, String message, int lineNumber, int severity) {
        try {
            ResourceBuilderUtils.createProblemMarker(resource, IRIMMarker.RESOURCE_BUILD_PROBLEM_MARKER, message,
                    lineNumber, severity);
        } catch (CoreException e) {
            log.error(e);
        }
    }

    /**
     * Clears previous preprocessor markers from the specified resource.
     *
     * @param resource
     * @param depth
     *
     * @throws CoreException
     */
    void removeResourceBuildMarkers(IResource resource, int depth) throws CoreException {
        ResourceBuilderUtils.cleanProblemMarkers(resource,
                new String[] { IRIMMarker.RESOURCE_BUILD_PROBLEM_MARKER }, depth);
    }

    private static int removeFiles(File dir) throws IOException {
        int count = 0;
        String[] inDir = dir.list();
        if (inDir != null) {
            for (int j = 0; j < inDir.length; ++j) {
                File f = new File(dir.getPath(), inDir[j]);
                if (f.isDirectory()) {
                    count += removeFiles(f);
                } else {
                    count += (int) f.length();
                    if (!f.delete()) {
                        throw new IOException();
                    }
                }
            }
        }
        if (!dir.delete()) {
            throw new IOException();
        }
        return count;
    }

    private Hashtable<String, String> getResourceBuilderOptions() {
        if (_resourceBuilderOptions == null) {
            _resourceBuilderOptions = new Hashtable<String, String>();
            _resourceBuilderOptions.put(ResourceCompiler.OPT_OUTPUT_FOLDER, getTempOutputFolder());
            // For BB OS older than 6.0.0, UTF-16BE doesn't work well in localizing strings for Asian simulators based on that OS
        }
        String vmver = ProjectUtils.getVMVersionForProject(getProject());
        _resourceBuilderOptions.put(ResourceCompiler.OPT_USE_UTF8,
                vmver.compareTo("6.0.0") < 0 ? ResourceCompiler.OPT_TRUE : ResourceCompiler.OPT_FALSE);
        return _resourceBuilderOptions;
    }

    static private String getTempOutputFolder() {
        if (StringUtils.isBlank(_tmpOutputFolder)) {
            _tmpOutputFolder = getTemporaryWorkingDir();
        }
        return _tmpOutputFolder;
    }

    private static synchronized String getTemporaryWorkingDir() {
        String tmpDir = System.getProperty("java.io.tmpdir");
        if (tmpDir == null) {
            // Shouldn't happen
            tmpDir = ".";
        }

        File subDir = null;

        for (int retry = 0; retry < 10; retry++) {
            subDir = new File(tmpDir,
                    RESOURCE_BUILD_TMP_FOLDER_HEAD + System.currentTimeMillis() + ConvertUtil.ext_dir);
            if (!subDir.exists() && subDir.mkdir()) {
                return subDir.toString();
            }
        }

        throw new RuntimeException("unable to create temporary subdirectory: " + subDir.getPath());
    }

    private static void cleanupTempSubdir(String name) {
        if (name == null)
            return;
        try {
            File dir = new File(name);
            String[] inDir = dir.list();
            if (inDir != null) {
                for (int i = 0; i < inDir.length; ++i) {
                    File subDir = new File(dir, inDir[i]);
                    if (subDir.isDirectory()) {
                        removeFiles(subDir);
                    } else {
                        subDir.delete();
                    }
                }
            }
            dir.delete();
        } catch (IOException ioe) {
            log.error(ioe);
        }
    }

    void removeResourceInterface(IFile rrhFile) throws CoreException {
        if (rrhFile == null)
            return;
        // get the resource builder output root folder
        IProject project = rrhFile.getProject();
        IFolder tmpRoot = project
                .getFolder(ImportUtils.getImportPref(ResourceBuilder.LOCALE_INTERFACES_FOLDER_NAME));
        IPath interfacePath = getPackagePath(rrhFile);
        String rrhFileName = rrhFile.getName();
        String interfaceFileName = rrhFileName.substring(0, rrhFileName.length() - 4) + "Resource.java"; //$NON-NLS-1$
        interfacePath = interfacePath.append(interfaceFileName);
        IFile iFile = tmpRoot.getFile(interfacePath);
        if (iFile.exists()) {
            iFile.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
            iFile.delete(true, new NullProgressMonitor());
        }
    }

    /**
     * Get the package path of the given <code>file</code>, e.g. net/rim/api The given <code>file</code> should be a file in a
     * source folder.
     *
     * @param file
     * @return
     */
    IPath getPackagePath(IFile file) {
        IContainer sourceFolder = PackageUtils.getSrcFolder(file);
        IPath sourceFolderPath = sourceFolder.getProjectRelativePath();
        IPath filePath = file.getProjectRelativePath();
        IPath packagePath = filePath.removeFirstSegments(sourceFolderPath.segmentCount());
        return packagePath.removeLastSegments(1);
    }

    void removeCorrespondingCRBFile(IFile rrcFile) throws CoreException {
        if (rrcFile == null)
            return;

        IFile crbFile = getCorrespondingCRBFile(rrcFile);

        if (crbFile != null && crbFile.exists()) {
            crbFile.delete(true, new NullProgressMonitor());
        }
    }

    IFile getCorrespondingCRBFile(IFile rrcFile) {
        if (rrcFile == null)
            return null;
        // get the resource builder output root folder
        IProject project = rrcFile.getProject();
        IFolder tmpRoot = project
                .getFolder(ImportUtils.getImportPref(ResourceBuilder.LOCALE_INTERFACES_FOLDER_NAME));
        IPath packagePath = getPackagePath(rrcFile);
        String fileName = rrcFile.getName().replace(IConstants.RRC_FILE_EXTENSION_WITH_DOT,
                IConstants.EMPTY_STRING);
        String packageID = PackageUtils.convertPkgStringToID(packagePath.toString());
        packageID = packageID.replace(File.separator, IConstants.DOT_MARK);
        String crbFileName = PackageUtils.getCRBFileName(fileName, packageID, false);
        IFile crbFile = tmpRoot.getFile(crbFileName);
        String crbAltFileName;
        if (!crbFile.exists()) {
            if (!(crbAltFileName = PackageUtils.getCRBFileName(fileName, packageID, true)).equals(crbFileName)) {
                crbFile = tmpRoot.getFile(crbAltFileName);
                if (!crbFile.exists()) {
                    return null;
                }
            } else {
                return null;
            }
        }
        return crbFile;
    }

    /**
     * Checks if the given <code>file</code> is in the right package structure.
     *
     * @param file
     * @return
     */
    private boolean hasValidPackage(IFile file) {
        try {
            String packageID = PackageUtils.getFilePackageString(file.getLocation().toFile(), null);
            IPath packagePath = new Path(packageID);
            IPath currentPackagePath = getPackagePath(file);
            if (currentPackagePath.equals(packagePath)) {
                return true;
            }
            String message = Messages.ResourceBuilder_WRONG_PACKAGE_MSG;
            createResourceMarker(file, NLS.bind(message, file.getName()), -1, IMarker.SEVERITY_ERROR);
            return false;
        } catch (CoreException e) {
            log.error(e.getMessage());
            createResourceMarker(file, e.getMessage(), -1, IMarker.SEVERITY_ERROR);
            return false;
        }
    }

    /**
     * Delta visitor for visiting changed resource files for preprocessing.
     */
    private class ResourceDeltaVisitor extends BasicBuilderResourceDeltaVisitor {

        public ResourceDeltaVisitor(IProgressMonitor monitor) {
            super(monitor);
        }

        protected void buildResource(IResource resource, IProgressMonitor monitor) throws CoreException {
            if (PackageUtils.hasRRHExtension(resource.getName())) {
                removeResourceInterface((IFile) resource);
            } else {
                removeCorrespondingCRBFile((IFile) resource);
            }
            compile(resource, monitor);
            // remove the file from the need rebuild file list
            if (_filesNeedToReBuild != null) {
                _filesNeedToReBuild.remove(resource);
            }
        }

        @Override
        protected void removeResource(IResource resource, IProgressMonitor monitor) throws CoreException {
            if (PackageUtils.hasRRHExtension(resource.getName())) {
                removeResourceInterface((IFile) resource);
            } else if (PackageUtils.hasRRCExtension(resource.getName())) {
                removeCorrespondingCRBFile((IFile) resource);
            }
            // remove the file from the need rebuild file list
            if (_filesNeedToReBuild != null) {
                _filesNeedToReBuild.remove(resource);
            }
        }

        @Override
        protected boolean needBuild(IResource resource) {
            if (resource == null) {
                return false;
            }
            if (!resource.exists()) {
                return false;
            }
            if (!PackageUtils.hasRRHExtension(resource.getName())
                    && !PackageUtils.hasRRCExtension(resource.getName())) {
                return false;
            }
            if (!hasValidPackage((IFile) resource)) {
                return false;
            }
            // we do not process a resource which is not on the classpath
            if (!JavaCore.create(resource.getProject()).isOnClasspath(resource)) {
                try {
                    if (PackageUtils.hasRRHExtension(resource.getName())) {
                        removeResourceInterface((IFile) resource);
                    } else {
                        removeCorrespondingCRBFile((IFile) resource);
                    }

                } catch (CoreException e) {
                    log.error("needBuild Error: " + e.getMessage());
                }
                return false;
            }
            return true;
        }
    }
}