/************************************************************************************* * Copyright (c) 2008-2011 Red Hat, Inc. 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: * JBoss by Red Hat - Initial implementation. ************************************************************************************/ package org.jboss.tools.common.jdt.core.buildpath; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Map; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.resources.WorkspaceJob; 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.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.osgi.util.NLS; import org.jboss.tools.common.jdt.core.JDTExtActivator; import org.jboss.tools.common.jdt.core.Messages; import org.jboss.tools.common.util.FileUtil; public class MaterializeLibraryJob extends WorkspaceJob { private final IContainer libFolder; private final IJavaProject javaProject; private final Map<IPath, String> jars; private final IClasspathContainer containerToRemove; private final boolean keepSourceAttachments; public MaterializeLibraryJob(IJavaProject javaProject, IClasspathContainer containerToMaterialize, Map<IPath, String> jars, IContainer libFolder, boolean keepSourceAttachments) { super(Messages.Materialize_Library); if (javaProject == null || javaProject.getProject() == null) { throw new IllegalArgumentException("Project must not be null"); } if (containerToMaterialize == null) { throw new IllegalArgumentException("Container to materialize must not be null"); } if (libFolder == null) { throw new IllegalArgumentException("Destination library folder must not be null"); } this.javaProject = javaProject; this.libFolder = libFolder; this.containerToRemove = containerToMaterialize; this.jars = jars; this.keepSourceAttachments = keepSourceAttachments; } @Override public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { final IPath rootPath = ResourcesPlugin.getWorkspace().getRoot().getLocation(); IPath containerToRemovePath = containerToRemove.getPath(); LinkedHashSet<IClasspathEntry> newCpes = null; //Create new Classpath entries if (jars != null && !jars.isEmpty()) { newCpes = copyClasspathEntries(monitor, rootPath); } // Remove the Classpath Library first removeClasspathContainer(javaProject, containerToRemovePath); // Then add the new Classpath entries if (newCpes != null && !newCpes.isEmpty()) { IClasspathEntry[] entries = new IClasspathEntry[newCpes.size()]; newCpes.toArray(entries); addToClasspath(javaProject, entries, monitor); } //Finally execute post processors (needed to remove project nature, for ex.) for (ILibraryMaterializationPostProcessor processor : getPostProcessors()) { processor.execute(javaProject, containerToRemovePath, monitor); } //Refresh project IResource resourceToRefresh = libFolder.getProject(); if (resourceToRefresh ==null) { resourceToRefresh = ResourcesPlugin.getWorkspace().getRoot(); } resourceToRefresh.refreshLocal(IResource.DEPTH_INFINITE, monitor); return Status.OK_STATUS; } private LinkedHashSet<IClasspathEntry> copyClasspathEntries( IProgressMonitor monitor, final IPath rootPath) throws CoreException, JavaModelException { int jarSize = jars.size(); monitor.beginTask(Messages.Materialize_Library, jarSize); if (libFolder instanceof IFolder) { mkdirs((IFolder)libFolder, monitor); } IPath destination = libFolder.getLocation(); LinkedHashSet<IClasspathEntry> newCpes = new LinkedHashSet<IClasspathEntry>(jarSize); IClasspathEntry[] cpEntries = containerToRemove.getClasspathEntries(); for (IClasspathEntry entry : cpEntries) { if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) { IPath sourceFilePath = entry.getPath(); String fileName = jars.get(sourceFilePath); if (fileName == null) { // Jar was not selected continue; } monitor.subTask(fileName); IPath destinationFilePath = destination.append(fileName); try { if ((javaProject.findPackageFragmentRoot(destinationFilePath) == null)// Not already defined && copy(sourceFilePath, destinationFilePath)) { // TODO handle duplicate files -> rename? // FIXME use absolute / relative paths depending on the // location of the destination folder (in project / // elsewhere in the workspace) // FIXME need a more elegant way to get a relative path // with a leading / IPath relativePath = new Path("/").append(libFolder.getFullPath()).append(fileName); //$NON-NLS-1$ IClasspathEntry newEntry = getNewClasspathEntry(entry, relativePath); newCpes.add(newEntry); } } catch (IOException e) { IStatus status = new Status(IStatus.ERROR, JDTExtActivator.PLUGIN_ID, NLS.bind(Messages.MaterializeLibraryJob_error_copying_file, fileName), e); throw new CoreException(status); } } else if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) { newCpes.add(entry); } } return newCpes; } private ILibraryMaterializationPostProcessor[] getPostProcessors() { ILibraryMaterializationPostProcessorFactory ppFactory = JDTExtActivator.getDefault() .getLibraryMaterializationPostProcessorFactory(); ILibraryMaterializationPostProcessor[] postProcessors = ppFactory.getLibraryMaterializationPostProcessors(); return postProcessors; } private IClasspathEntry getNewClasspathEntry(IClasspathEntry entry, IPath destinationFilePath) throws CoreException { try { return JavaCore.newLibraryEntry(destinationFilePath, (keepSourceAttachments)?entry.getSourceAttachmentPath():null, (keepSourceAttachments)?entry.getSourceAttachmentRootPath():null, entry.getAccessRules(), entry.getExtraAttributes(), entry.isExported()); } catch (Exception e) { IStatus status = new Status(IStatus.ERROR, JDTExtActivator.PLUGIN_ID, NLS.bind(Messages.MaterializeLibraryJob_Error_creating_classpath_entry, e.getMessage()), e); throw new CoreException(status); } } private void addToClasspath(IJavaProject javaProject, IClasspathEntry[] newCpes, IProgressMonitor monitor) throws JavaModelException { if (newCpes.length > 0) { IClasspathEntry[] originalClasspathEntries = javaProject.getRawClasspath(); IClasspathEntry[] newClasspath = new IClasspathEntry[originalClasspathEntries.length + newCpes.length]; System.arraycopy(originalClasspathEntries, 0, newClasspath, 0, originalClasspathEntries.length); System.arraycopy(newCpes, 0, newClasspath, originalClasspathEntries.length, newCpes.length); javaProject.setRawClasspath(newClasspath, monitor); } } private boolean copy(IPath sourceFilePath, IPath destinationFilePath) throws IOException { File sourcefile = sourceFilePath.toFile(); if (sourcefile.isFile()) { File destinationFile = destinationFilePath.toFile(); //Overwrite existing file FileUtil.copyFile(sourcefile, destinationFile); if (destinationFile.exists()) { return true; } } return false; } private void removeClasspathContainer(IJavaProject javaProject, IPath containerToRemovePath) throws JavaModelException { if (javaProject != null) { // remove classpatch container from JavaProject ArrayList<IClasspathEntry> newEntries = new ArrayList<IClasspathEntry>(); for (IClasspathEntry entry : javaProject.getRawClasspath()) { if (!containerToRemovePath.equals(entry.getPath())) { newEntries.add(entry); } } javaProject.setRawClasspath(newEntries.toArray(new IClasspathEntry[newEntries.size()]), null); } } private void mkdirs(final IFolder folder, IProgressMonitor monitor) throws CoreException { if (!folder.exists()) { if (folder.getParent() instanceof IFolder) { mkdirs((IFolder) folder.getParent(), monitor); } folder.create(true /* force */, true /* local */, monitor); } else { IContainer x = folder; while (x instanceof IFolder && x.isDerived()) { x.setDerived(false, monitor); x = x.getParent(); } } } }