/******************************************************************************* * Copyright (c) 2003, 2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jst.j2ee.internal.plugin; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; 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.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jem.workbench.utility.JemProjectUtilities; /** * An example incremental project builder that copies additional class files from a library package * fragment root folder into a Java project's output directory. * * General parameters: * <ul> * <li>The project should be a Java project.</li> * <li>The class files are in the "imported_classes" folder of the project.</li> * <li>This builder should run <b>after </b> the Java builder.</li> * <li>Full build should copy class files from a secondary library folder into the output folder * maintaining package hierarchy; existing class files must never be overwritten.</li> * <li>Only *.class files should be copied (not other resource files).</li> * <li>Incremental build and auto-build should will perform the copy when there is an add/change in * the "imported_classes" folder.</li> * <li>Changing the project's output folder should be handled.</li> * </ul> * Note: the builder is not currently invoking the Minimize helper, it is relying on the copy to not * replace existing class files, and the build path order to ensure that compiled classes override * imported ones. * * @deprecated This class is only used for backwards compatibility */ public class LibCopyBuilder extends IncrementalProjectBuilder { /** * Internal debug tracing. */ static boolean DEBUG = false; /** * Builder id of this incremental project builder. */ public static final String BUILDER_ID = J2EEPlugin.LIBCOPY_BUILDER_ID; /** * The path where we expect to find the .class files to be copied. */ public static final String IMPORTED_CLASSES_PATH = "imported_classes"; //$NON-NLS-1$ /** * The path of the output folder that we last copied class files into, or <code>null</code> if * this builder has not built this project before. */ // private IPath lastOutputPath = null; private List sourceContainers; // private boolean needOutputRefresh; /** * Creates a new instance of the library copying builder. * <p> * All incremental project builders are required to have a public 0-argument constructor. * </p> */ public LibCopyBuilder() { super(); } /** * * The <code>LibCopyBuilder</code> implementation of this * <code>IncrementalProjectBuilder</code> method copies additional class files into the output * folder. * <p> * [Issue: the implementation should report progress.] * </p> * <p> * [Issue: the implementation should probably use a workspace runnable.] * </p> */ @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { // this builder is unnecessary in WTP 1.5 return null; // sourceContainers = null; // needOutputRefresh = false; // if (DEBUG) { // System.out.println(BUILDER_ID + J2EEPluginResourceHandler.__Start_build_project_INFO_ + getProject().getName()); // } // // boolean builderOrderOK = checkBuilderOrdering(); // // if (DEBUG && !builderOrderOK) { // System.out.println(BUILDER_ID + J2EEPluginResourceHandler.__Bad_builder_order_for_project_INFO_ + getProject().getName()); // } // // IFolder[] classFolders = getClassesFolders(); // if (classFolders.length == 0) { // // no files to copy // if (DEBUG) // System.out.println(BUILDER_ID + J2EEPluginResourceHandler.__No_imported_classes_folder__quitting_INFO_); // return null; // } // // IJavaProject jproject = JavaCore.create(getProject()); // if (jproject == null) { // // not a java project (anymore?) // return null; // } // // IPath outputPath = jproject.getOutputLocation(); // IFolder outputFolder = getProject().getParent().getFolder(outputPath); // if (outputPath.equals(lastOutputPath)) { // if (kind == INCREMENTAL_BUILD || kind == AUTO_BUILD) { // processDelta(getDelta(getProject()), outputFolder, monitor, classFolders); // refreshOutputIfNecessary(outputFolder); // return null; // } // } // // if (DEBUG) { // System.out.println(BUILDER_ID + J2EEPluginResourceHandler.__Full_first_build_INFO_); // } // copyAllClassFolders(monitor, classFolders, outputFolder); // lastOutputPath = outputPath; // refreshOutputIfNecessary(outputFolder); // return null; } /** * */ // private void refreshOutputIfNecessary(IFolder outputFolder) throws CoreException { // if (needOutputRefresh && outputFolder != null && outputFolder.exists()) // outputFolder.refreshLocal(IResource.DEPTH_INFINITE, null); // } private void copyAllClassFolders(IProgressMonitor monitor, IFolder[] classFolders, IFolder outputFolder) throws CoreException { for (int i = 0; i < classFolders.length; i++) { if (!classFolders[i].equals(outputFolder)) copyClassFiles(classFolders[i], outputFolder, monitor); } } /** * Process an incremental build delta. * * @return <code>true</code> if the delta requires a copy * @param dest * the destination folder; may or may not exist * @param monitor * the progress monitor, or <code>null</code> if none * @exception CoreException * if something goes wrong */ protected void processDelta(IResourceDelta delta, final IFolder outputFolder, final IProgressMonitor monitor, final IFolder[] classesFolders) { if (DEBUG) { System.out.println(BUILDER_ID + J2EEPluginResourceHandler.__Considering_delta_INFO_ + delta); } IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() { private List copiedClassFolders = new ArrayList(classesFolders.length); public boolean visit(IResourceDelta subdelta) throws CoreException { IResource resource = subdelta.getResource(); if (resource.getType() == IResource.FILE) { IFolder classesFolder = retrieveClassesFolder(resource, classesFolders); if (classesFolder != null && !copiedClassFolders.contains(classesFolder)) { int kind = subdelta.getKind(); switch (kind) { case IResourceDelta.ADDED : case IResourceDelta.CHANGED : if (DEBUG) { System.out.println(BUILDER_ID + J2EEPluginResourceHandler.__Delta_build_INFO_ + subdelta); } copyClassFiles(classesFolder, outputFolder, monitor); break; case IResourceDelta.REMOVED : deleteCorrespondingFile((IFile) resource, classesFolder, outputFolder, monitor); break; case IResourceDelta.ADDED_PHANTOM : break; case IResourceDelta.REMOVED_PHANTOM : break; } } } else if (resource.getType() == IResource.FOLDER && resource.equals(outputFolder)) { copyAllClassFolders(null, classesFolders, outputFolder); return false; } return true; } }; if (delta != null) { try { delta.accept(visitor); } catch (CoreException e) { // should not happen } } } /** * @param file * @param classesFolder * @param outputFolder * @param monitor */ protected void deleteCorrespondingFile(IFile file, IFolder classesFolder, IFolder outputFolder, IProgressMonitor monitor) throws CoreException { IPath path = file.getFullPath(); int segCount = classesFolder.getFullPath().segmentCount(); path = path.removeFirstSegments(segCount); IFile javaFile = findCorrespondingJavaFile(path); if (javaFile != null && javaFile.exists()) return; //There is nothing to do because the file in the output location is from the // java compilation not the copy. IFile outFile = outputFolder.getFile(path); if (outFile.exists()) outFile.delete(true, false, monitor); } /** * Method retrieveClassesFolder. * * @param resource * @return IFolder */ protected IFolder retrieveClassesFolder(IResource resource, IFolder[] classesFolders) { for (int i = 0; i < classesFolders.length; i++) { if (classesFolders[i].getName().equals(resource.getProjectRelativePath().segment(0))) return classesFolders[i]; } return null; } /** * Checks whether this builder is configured to run <b>after </b> the Java builder. * * @return <code>true</code> if the builder order is correct, and <code>false</code> * otherwise * @exception CoreException * if something goes wrong */ // private boolean checkBuilderOrdering() throws CoreException { // determine relative builder position from project's buildspec // ICommand[] cs = getProject().getDescription().getBuildSpec(); // int myIndex = -1; // int javaBuilderIndex = -1; // for (int i = 0; i < cs.length; i++) { // if (cs[i].getBuilderName().equals(JavaCore.BUILDER_ID)) { // javaBuilderIndex = i; // } else if (cs[i].getBuilderName().equals(BUILDER_ID)) { // myIndex = i; // } // } // return myIndex > javaBuilderIndex; // } /** * Copies class files from the given source folder to the given destination folder. The * destination folder will be created if required, but only if at least one class file is * copied. * * @param source * the source folder; must exist * @param dest * the destination folder; may or may not exist * @param monitor * the progress monitor, or <code>null</code> if none * @exception CoreException * if something goes wrong */ private void copyClassFiles(IFolder source, final IFolder dest, final IProgressMonitor monitor) throws CoreException { if (DEBUG) { System.out.println(BUILDER_ID + ": Begin copying class files from " + source.getFullPath() + " to " + dest.getFullPath()); //$NON-NLS-1$ //$NON-NLS-2$ } final int sourcePathLength = source.getFullPath().segmentCount(); class Visitor implements IResourceVisitor { public boolean visit(IResource res) throws CoreException { if (res.getType() == IResource.FILE) { IFile file = (IFile) res; // compute relative path from source folder to this file IPath filePath = file.getFullPath(); IPath dpath = filePath.removeFirstSegments(sourcePathLength); IFile targetFile = dest.getFile(dpath); copyFile(file, targetFile, dpath, monitor); } return true; } } try { source.accept(new Visitor()); } catch (CoreException e) { // should not happen } } /** * Copies the given file to the given destination file. Does nothing if the destination file * already exists. * * @param source * the source file; must exist * @param dest * the destination file; may or may not exist; never overwritten * @param monitor * the progress monitor, or <code>null</code> if none * @exception CoreException * if something goes wrong */ private void copyFile(IFile source, IFile dest, IPath fileRelativePath, IProgressMonitor monitor) throws CoreException { if (pruneForJavaSource(source, fileRelativePath, monitor)) return; //no copy necessary. File sourceFile = null, destFile = null; if (source.exists()) sourceFile = source.getLocation().toFile(); if (dest.exists()) destFile = dest.getLocation().toFile(); if (destFile != null && sourceFile != null) { if (DEBUG) System.out.println(BUILDER_ID + ": " + dest.getFullPath() + " already exists."); //$NON-NLS-1$ //$NON-NLS-2$ if (destFile.lastModified() == sourceFile.lastModified()) return; dest.setContents(source.getContents(false), true, false, monitor); //we have to force // b/c set the mod // stamp makes it // think it is out of // synch. synchronizeModificationStamps(sourceFile, destFile); return; } if (DEBUG) { System.out.println(BUILDER_ID + ": Creating " + dest.getFullPath()); //$NON-NLS-1$ } IContainer parent = dest.getParent(); if (parent.getType() == IResource.FOLDER) { mkdirs((IFolder) parent, monitor); } dest.create(source.getContents(false), false, monitor); destFile = dest.getLocation().toFile(); synchronizeModificationStamps(sourceFile, destFile); dest.setDerived(true); } /** * Return true if a corresponding .java file is found. Remove the .class file from the * imported_classes folder (i.e., delete the source file). * * @param source * @param monitor * @return */ private boolean pruneForJavaSource(IFile classFile, IPath fileRelativePath, IProgressMonitor monitor) throws CoreException { if (classFile.exists()) { IFile javaFile = findCorrespondingJavaFile(fileRelativePath); if (javaFile != null && javaFile.exists()) { ResourcesPlugin.getWorkspace().validateEdit(new IFile[]{javaFile}, null); classFile.delete(true, false, monitor); return true; } } return false; } /** * @param classFilePath * @return */ private IFile findCorrespondingJavaFile(IPath classFilePath) { IPath javaPath = convertToJavaPath(classFilePath); List sourceFolders = getSourceContainers(); IContainer cont; IFile javaFile; for (int i = 0; i < sourceFolders.size(); i++) { cont = (IContainer) sourceFolders.get(i); javaFile = cont.getFile(javaPath); if (javaFile.exists()) return javaFile; } return null; } private List getSourceContainers() { if (sourceContainers == null) sourceContainers = JemProjectUtilities.getSourceContainers(getProject()); return sourceContainers; } /** * @param classFile * @return */ private IPath convertToJavaPath(IPath classFilePath) { IPath javaPath = classFilePath.removeFileExtension(); //handle inner classes...look for outermost java file String fileName = classFilePath.lastSegment(); int innerIndex = fileName.indexOf('$'); if (innerIndex > -1) { javaPath = javaPath.removeLastSegments(1); javaPath = javaPath.append(fileName.substring(0, innerIndex)); } javaPath = javaPath.addFileExtension("java"); //$NON-NLS-1$ return javaPath; } /** * @param source * @param dest */ private void synchronizeModificationStamps(File sourceFile, File destFile) { if (destFile != null && sourceFile != null) { destFile.setLastModified(sourceFile.lastModified()); // needOutputRefresh = true; } } /** * Creates the given folder, and its containing folders, if required. Does nothing if the given * folder already exists. * * @param folder * the folder to create * @param monitor * the progress monitor, or <code>null</code> if none * @exception CoreException * if something goes wrong */ private void mkdirs(IFolder folder, IProgressMonitor monitor) throws CoreException { if (folder.exists()) { return; } IContainer parent = folder.getParent(); if (!parent.exists() && parent.getType() == IResource.FOLDER) { mkdirs((IFolder) parent, monitor); } folder.create(false, true, monitor); } // private IFolder[] getClassesFolders() { // IProject project = getProject(); // IJavaProject javaProj = JemProjectUtilities.getJavaProject(project); // if (javaProj == null) // return new IFolder[0]; // List result = null; // IClasspathEntry[] entries; // try { // entries = javaProj.getResolvedClasspath(true); // } catch (JavaModelException e) { // return new IFolder[0]; // } // for (int i = 0; i < entries.length; i++) { // IClasspathEntry entry = entries[i]; // if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { // IPath path = entry.getPath(); // IResource res = project.getWorkspace().getRoot().findMember(path); // if (res != null && res.isAccessible() && res.getType() == IResource.FOLDER && res.getProject().equals(project)) { // if (result == null) // result = new ArrayList(1); // result.add(res); // } // } // } // if (result == null) // return new IFolder[0]; // return (IFolder[]) result.toArray(new IFolder[result.size()]); // } }