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.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; } } }