Java tutorial
/* * 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.IOException; import java.util.List; import java.util.Map; import java.util.Vector; import net.rim.ejde.external.sourceMapper.SourceMapperAccess; import net.rim.ejde.internal.core.ContextManager; import net.rim.ejde.internal.core.IConstants; import net.rim.ejde.internal.core.IRIMMarker; import net.rim.ejde.internal.model.BasicBlackBerryProperties.PreprocessorTag; import net.rim.ejde.internal.model.BlackBerryProject; import net.rim.ejde.internal.model.BlackBerryProperties; import net.rim.ejde.internal.model.preferences.PreprocessorPreferences; import net.rim.ejde.internal.util.EnvVarUtils; import net.rim.ejde.internal.util.ImportUtils; 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.ejde.internal.util.VMUtils; import net.rim.ide.RIA; import net.rim.ide.Workspace; import net.rim.tools.javapp.delegate.IOutputFileCallbackDelegate; import net.rim.tools.javapp.delegate.IPreprocessingListenerDelegate; import net.rim.tools.javapp.delegate.JavaPPDelegate; 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.core.runtime.QualifiedName; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jface.text.BadLocationException; import org.eclipse.osgi.util.NLS; /** * An incremental builder implementation that provides preprocess support as defined for the Antenna Ant tools.<br/> */ public class PreprocessingBuilder extends IncrementalProjectBuilder { public static final String BUILDER_ID = "net.rim.ejde.internal.builder.BlackBerryPreprocessBuilder"; //$NON-NLS-1$ public static final String PREPROCESSED_FILE_FOLDER_NAME = "project_preprocessed_folder"; //$NON-NLS-1$ private static final Logger _log = Logger.getLogger(PreprocessingBuilder.class); final static public QualifiedName NOT_BUILD_BY_JAVA_BUILDER_FLAG_QUALIFIED_NAME = new QualifiedName( ContextManager.PLUGIN_ID, "NotBuiltByJavaBuilders"); //$NON-NLS-1$ private JavaPPDelegate _javaPP = new JavaPPDelegate(); private IFolder _preprocessedFolder; /** * Construct a new builder instance. */ public PreprocessingBuilder() { super(); } /** * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, * org.eclipse.core.runtime.IProgressMonitor) */ protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { _log.trace("Entering PreprocessingBuilder build();kind=" + kind); //$NON-NLS-1$ IProject project = getProject(); _preprocessedFolder = createPreprocessedFolder(getProject(), new NullProgressMonitor()); // fixed MKS516466, after importing an existing project, the .locale_interface and .preprocessed folder are not marked as // derived. we should mark them as derived. if (!_preprocessedFolder.isDerived()) { try { _preprocessedFolder.setDerived(true); } catch (CoreException e) { _log.error(e); } } IFolder localeInterfaceFolderRoot = project.getProject() .getFolder(ImportUtils.getImportPref(ResourceBuilder.LOCALE_INTERFACES_FOLDER_NAME)); if (localeInterfaceFolderRoot.exists() && !localeInterfaceFolderRoot.isDerived()) { try { localeInterfaceFolderRoot.setDerived(true); } catch (CoreException e) { _log.error(e); } } 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) { // if is is a full build, we build all java files ResourceDeltaVisitor resourceVisitor = new ResourceDeltaVisitor(monitor); project.accept(resourceVisitor); } else { // if it is not a full build, we only build changed java files if (delta != null) { ResourceDeltaVisitor deltaVisitor = new ResourceDeltaVisitor(monitor); delta.accept(deltaVisitor); } } _preprocessedFolder.refreshLocal(IResource.DEPTH_INFINITE, monitor); _log.trace("Leaving PreprocessingBuilder build()"); //$NON-NLS-1$ return null; } /** * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor) */ protected void clean(IProgressMonitor monitor) throws CoreException { removePreprocessingMarkers(getProject(), IResource.DEPTH_INFINITE); IFolder preprocessedFolder = getProject() .getFolder(ImportUtils.getImportPref(PREPROCESSED_FILE_FOLDER_NAME)); if (!preprocessedFolder.exists()) return; IResource[] members = preprocessedFolder.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$ } } /** * Return an appropriate output file for the specified resource. This file is not guaranteed to exist. * * @param resource * @return */ public static IFile getOutputFile(IResource resource) { IContainer sourceFolder = PackageUtils.getSrcFolder(resource); if (sourceFolder == null) { return (IFile) resource; } IPath sourceFolderPath = sourceFolder.getProjectRelativePath(); IPath resourcePath = resource.getProjectRelativePath(); IPath firstSegment = resourcePath.removeLastSegments(resourcePath.segmentCount() - 1); IPath projectRelativePath = null; if (firstSegment.toString().equals(ImportUtils.getImportPref(PREPROCESSED_FILE_FOLDER_NAME))) return (IFile) resource; projectRelativePath = new Path(ImportUtils.getImportPref(PREPROCESSED_FILE_FOLDER_NAME)) .append(resource.getProjectRelativePath().removeFirstSegments(sourceFolderPath.segmentCount())); return resource.getProject().getFile(projectRelativePath); } void deletePreprocessedFile(IFile file, IProgressMonitor monitor) throws CoreException { IFile preprocessedFile = getOutputFile(file); // eclipse resource might not be refreshed, we need double check the filesystem if (preprocessedFile.exists()) preprocessedFile.delete(true, monitor); } /** * Clears previous preprocessor markers from the specified resource. * * @param resource * @param depth * * @throws CoreException */ private void removePreprocessingMarkers(IResource resource, int depth) throws CoreException { ResourceBuilderUtils.cleanProblemMarkers(resource, new String[] { IRIMMarker.PREPROCESSING_PROBLEM_MARKER }, depth); } /** * Mark the given <code>resource</code> as need to be built by java compiler. * * @param resource * @param needBuild * <code>true</code> mark the given <code>resource</code> as need build, otherwise, mark the given * <code>resource</code> as not need build. */ static synchronized public void setShouldBuiltByJavaBuilder(IResource resource, boolean needBuild) { try { _log.trace("File " + resource.getName() + " is marked as" //$NON-NLS-1$ //$NON-NLS-2$ + (needBuild ? " need to be built by java compiler." //$NON-NLS-1$ : " not need to be built by java compiler.")); //$NON-NLS-1$ String newBuildFlag = needBuild ? "true" : "false"; String oldBuildFlag = resource.getPersistentProperty(NOT_BUILD_BY_JAVA_BUILDER_FLAG_QUALIFIED_NAME); if ((oldBuildFlag == null) || !oldBuildFlag.equals(newBuildFlag)) { resource.setPersistentProperty(NOT_BUILD_BY_JAVA_BUILDER_FLAG_QUALIFIED_NAME, newBuildFlag); } } catch (CoreException e) { _log.error(e); } } /** * Check if the given <code>resource</code> needs to be built. * * @param file * @return */ static synchronized public boolean shouldBuiltByJavaBuilder(IResource resource) { try { String value = resource.getPersistentProperty(NOT_BUILD_BY_JAVA_BUILDER_FLAG_QUALIFIED_NAME); if ((value == null) || value.equals("true")) { return true; } } catch (CoreException e) { _log.error(e); return true; } return false; } /** * Preprocess the specified java resource. * * @param resource * @param symbols * @param monitor * @throws CoreException */ private void preprocessResource(final IResource resource, IProgressMonitor monitor) throws CoreException { // remove the old markers and preprocessed file removePreprocessingMarkers(resource, IResource.DEPTH_ONE); deletePreprocessedFile((IFile) resource, monitor); // BlackBerryProperties properties = ContextManager.PLUGIN.getBBProperties(getProject().getName(), false); if (properties == null) { _log.error("Could not find the correspond BlackBerry properties."); return; } // get defined directives Vector<String> defines = getDefines(new BlackBerryProject(JavaCore.create(getProject()), properties), true); // if there is no directive defined, we do not do preprocessing if (defines == null || defines.size() == 0) return; // check preprocess hook if (!SourceMapperAccess.isHookCodeInstalled() && PreprocessorPreferences.getPopForPreprocessHookMissing()) { _log.error("Preprocessing hook was not installed."); //$NON-NLS-1$ ProjectUtils.setPreprocessorHook(); return; } else { _log.trace("Preprocessing file : " + resource.getLocation()); //$NON-NLS-1$ } // remove the fake preprocess derive defines.remove(Workspace.getDefineOptNull()); // get java file File javaFile = resource.getLocation().toFile(); Vector<File> javaFiles = new Vector<File>(); javaFiles.add(javaFile); IPreprocessingListenerDelegate listener = new PreprocessingListener(resource); IOutputFileCallbackDelegate callback = new OutputFileCallback(); _javaPP.setPreprocessingListener(listener); _javaPP.setOutputFileCallback(callback); try { _javaPP.preProcess(javaFiles, defines, _preprocessedFolder.getLocation().toFile()); resource.touch(monitor); } catch (IOException e) { // Is handled by PreprocessingListener } catch (Exception e) { try { ResourceBuilderUtils.createProblemMarker(resource, IRIMMarker.PREPROCESSING_PROBLEM_MARKER, e.getMessage(), -1, IMarker.SEVERITY_ERROR); } catch (CoreException e1) { _log.error(e1); } // if we got any exception, delete the proprocessed file. _log.debug(e.getMessage(), e); deletePreprocessedFile((IFile) resource, monitor); } setShouldBuiltByJavaBuilder(resource, javaFiles.size() != 0); } /** * Get all preprocess defines: JRE leve, workspace level and project level. * * @param BBProject * @param ignoreInActive * @return */ static public Vector<String> getDefines(BlackBerryProject BBProject, boolean ignoreInActive) { List<PreprocessorTag> workspaceDefines = PreprocessorPreferences.getPreprocessDefines(); PreprocessorTag[] projectDefines = BBProject.getProperties()._compile.getPreprocessorDefines(); Vector<String> newDefines = new Vector<String>(); for (PreprocessorTag ppDefine : workspaceDefines) { if (!ignoreInActive || ppDefine.isActive()) { newDefines.add(EnvVarUtils.replaceRIAEnvVars(ppDefine.getPreprocessorDefine())); } } for (int i = 0; i < projectDefines.length; i++) { if (!ignoreInActive || projectDefines[i].isActive()) { newDefines.add(EnvVarUtils.replaceRIAEnvVars(projectDefines[i].getPreprocessorDefine())); } } String cpDefine = VMUtils.getJREDirective(BBProject); if (!StringUtils.isBlank(cpDefine)) { newDefines.add(cpDefine); } return newDefines; } private IFolder createPreprocessedFolder(IProject project, IProgressMonitor monitor) throws CoreException { IContainer sourceContainer = project.getFolder(ImportUtils.getImportPref(PREPROCESSED_FILE_FOLDER_NAME)); // refresh the project to reveal the .preprocessed folder sourceContainer.refreshLocal(IResource.DEPTH_ZERO, monitor); if (!sourceContainer.exists()) { // if the .BlackBerryPreprocessed folder does not exist, create it ((IFolder) sourceContainer).create(IResource.DERIVED, true, monitor); } return (IFolder) sourceContainer; } /** * 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.PREPROCESSING_PROBLEM_MARKER, message, lineNumber, severity); } catch (Exception e) { _log.error(e); } } protected class PreprocessingListener implements IPreprocessingListenerDelegate { IResource _resource; public PreprocessingListener(IResource resource) { _resource = resource; } public void setResource(IResource resource) { _resource = resource; } public void error(String message, int lineNumber) { createResourceMarker(_resource, message, lineNumber, IMarker.SEVERITY_ERROR); } public void warning(String message, int lineNumber) { createResourceMarker(_resource, message, lineNumber, IMarker.SEVERITY_WARNING); } }; protected class OutputFileCallback implements IOutputFileCallbackDelegate { public File getOutputFile(File parent, File javaFile) { String packageName = RIA.getPackage(javaFile); if (packageName != null && !packageName.trim().equals(IConstants.EMPTY_STRING)) { IPath parentPath = new Path(parent.getPath()); IPath packagePath = new Path(packageName.replace('.', '/')); IFolder packageFolder = _preprocessedFolder.getFolder(packagePath); if (!packageFolder.exists()) try { ImportUtils.createFolders(getProject(), packageFolder.getProjectRelativePath(), IResource.DERIVED); } catch (CoreException e) { _log.error(e); } parentPath = parentPath.append(packagePath); File parentDir = parentPath.toFile(); return new File(parentDir, javaFile.getName()); } return new File(parent, javaFile.getName()); } } /** * Delta visitor for visiting changed resource files for resource builder. */ private class ResourceDeltaVisitor extends BasicBuilderResourceDeltaVisitor { public ResourceDeltaVisitor(IProgressMonitor monitor) { super(monitor); } @Override protected void removeResource(IResource resource, IProgressMonitor monitor) throws CoreException { if (!(resource instanceof IFile)) return; deletePreprocessedFile((IFile) resource, monitor); } @Override protected void buildResource(IResource resource, IProgressMonitor monitor) throws CoreException { preprocessResource(resource, monitor); } /** * Checks if the given <code>resource</code> is a java file which needs to be proprocessed. * * @param resource * @return */ protected boolean needBuild(IResource resource) { if (resource == null) { return false; } if (!resource.exists()) { return false; } // check if it's an IFile if (!(resource instanceof IFile)) return false; // check if it's a java file if (!PackageUtils.hasJavaExtension(resource.getName())) { return false; } // we do not process a resource which is not on the classpath if (!JavaCore.create(resource.getProject()).isOnClasspath(resource)) { try { deletePreprocessedFile((IFile) resource, new NullProgressMonitor()); } catch (CoreException e) { _log.error(e.getMessage()); } return false; } return true; } } }