/*******************************************************************************
* Copyright (c) 2008 Pierre-Antoine Grégoire.
* 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:
* Pierre-Antoine Grégoire - initial API and implementation
*******************************************************************************/
package org.org.eclipse.core.utils.jdt.tools;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
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.SubProgressMonitor;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.org.eclipse.core.utils.jdt.tools.ClasspathEntryDefinition.EntryType;
public class JavaProjectClasspathHelper {
public static class JavaProjectInteractionException extends RuntimeException {
private static final long serialVersionUID = -3299504145509021907L;
public JavaProjectInteractionException() {
super();
}
public JavaProjectInteractionException(String message) {
super(message);
}
public JavaProjectInteractionException(String message, Throwable cause) {
super(message, cause);
}
public JavaProjectInteractionException(Throwable cause) {
super(cause);
}
}
/**
* This helper methods replaces a variable name with another in all matching entries of a project. If the target classpath variable doesn't exist, it is created on the fly.
*
* @param originalName
* @param targetName
* @param javaProject
* @param monitor
*/
public static void changeClasspathVariable(String originalName, String targetName, IJavaProject javaProject, IProgressMonitor monitor) {
try {
if (javaProject == null) {
throw new JavaProjectInteractionException("Project should not be null.");
}
if (JavaCore.getClasspathVariable(targetName) == null) {
JavaCore.setClasspathVariable(targetName, JavaCore.getClasspathVariable(originalName), monitor);
}
IClasspathEntry[] classpathEntries = getRawClasspath(javaProject);
List<IClasspathEntry> targetClasspathEntries = new ArrayList<IClasspathEntry>();
for (IClasspathEntry classpathEntry : classpathEntries) {
if (!JavaProjectClasspathHelper.isVariableEntry(classpathEntry)) {
targetClasspathEntries.add(classpathEntry);
} else {
IPath sourceAttachmentPath = classpathEntry.getSourceAttachmentPath();
IClasspathAttribute javadocClasspathAttribute = null;
for (IClasspathAttribute classpathAttribute : classpathEntry.getExtraAttributes()) {
if (classpathAttribute.getName().equals(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME)) {
javadocClasspathAttribute = classpathAttribute;
}
}
IPath javadocPath = null;
if (javadocClasspathAttribute != null && javadocClasspathAttribute.getValue() != null) {
javadocPath = JavaProjectClasspathHelper.pathFromJavadocAttributeValue(javadocClasspathAttribute);
}
ClasspathEntryDefinition classpathEntryWrapper = new ClasspathEntryDefinition(classpathEntry.getPath(), sourceAttachmentPath, javadocPath, EntryType.VARIABLE);
targetClasspathEntries.add(ClasspathHelper.createVariableEntryWithDifferentVariableName(classpathEntryWrapper, targetName, monitor));
}
}
JavaProjectClasspathHelper.updateRawClasspath(javaProject, targetClasspathEntries, new SubProgressMonitor(monitor, 1));
} catch (Exception e) {
throw new JavaProjectInteractionException(e);
}
}
/**
* This method allows to retrieve the raw classpath of a given java project.
*
* @param javaProject
* @return
* @throws JavaProjectInteractionException
*/
public static IClasspathEntry[] getRawClasspath(IJavaProject javaProject) throws JavaProjectInteractionException {
IClasspathEntry[] rawClassPath = null;
try {
rawClassPath = javaProject.getRawClasspath();
} catch (JavaModelException e) {
throw new JavaProjectInteractionException("Impossible to get the raw classpath of project \"" + javaProject.getProject().getName() + "\" : " + e.getMessage(), e);
}
return rawClassPath;
}
public static List<IClasspathEntry> getRawClasspathAsList(IJavaProject javaProject) {
List<IClasspathEntry> result = new LinkedList<IClasspathEntry>();
IClasspathEntry[] rawClassPath = getRawClasspath(javaProject);
for (IClasspathEntry classpathEntry : rawClassPath) {
result.add(classpathEntry);
}
return result;
}
public static void updateRawClasspath(IJavaProject javaProject, List<IClasspathEntry> classpath, IProgressMonitor monitor) throws JavaModelException {
monitor.subTask("Updating the classpath of project " + javaProject.getElementName() + ".");
javaProject.setRawClasspath((IClasspathEntry[]) classpath.toArray(new IClasspathEntry[0]), new NullProgressMonitor());
monitor.worked(1);
}
/**
* This method update a project's classpath with new entries.
*
* @param javaProject
* @param formerEntries
* @param newEntries
* @param monitor
*/
public static void updateRawClasspath(IJavaProject javaProject, List<IClasspathEntry> formerEntries, List<IClasspathEntry> newEntries, IProgressMonitor monitor) {
List<String> errorMessages = new LinkedList<String>();
try {
List<IClasspathEntry> allEntries = new LinkedList<IClasspathEntry>(formerEntries);
allEntries.addAll(newEntries);
JavaProjectClasspathHelper.updateRawClasspath(javaProject, allEntries, monitor);
} catch (JavaModelException e) {
// IF AN EXCEPTION OCCURS, LET's NAIL DOWN THE ENTRY CAUSING IT
List<IClasspathEntry> addedEntries = new LinkedList<IClasspathEntry>(formerEntries);
for (IClasspathEntry classpathEntry : newEntries) {
addedEntries.add(classpathEntry);
try {
JavaProjectClasspathHelper.updateRawClasspath(javaProject, addedEntries, monitor);
} catch (JavaModelException e2) {
addedEntries.remove(classpathEntry);
// IF ERROR IS A NAME COLLISION, THIS MEANS THAT ENTRY ALREADY EXISTS...BUT WAS NOT PROPERLY RESOLVED PRIOR TO THIS OPERATION
if (!(e2.getJavaModelStatus().getCode() == IJavaModelStatusConstants.NAME_COLLISION)) {
errorMessages.add("entry \"" + classpathEntry + "\" : " + e.getMessage());
}
}
}
}
if (errorMessages.size() > 0) {
final String CRLF = "\n";
StringBuilder stringBuilder = new StringBuilder("Errors occured while updating classpath:" + CRLF);
for (String errorMessage : errorMessages) {
stringBuilder.append("\t" + errorMessage + CRLF);
}
throw new JavaProjectInteractionException(stringBuilder.toString());
}
}
private static boolean isVariableEntry(IClasspathEntry classpathEntry) {
return classpathEntry.getEntryKind() == IClasspathEntry.CPE_VARIABLE;
}
private static IPath pathFromJavadocAttributeValue(IClasspathAttribute javadocClasspathAttribute) {
IPath path = null;
if (javadocClasspathAttribute != null && javadocClasspathAttribute.getValue() != null) {
Pattern pattern = Pattern.compile("jar:(.*)!/");
Matcher matcher = pattern.matcher(javadocClasspathAttribute.getValue());
matcher.find();
path = Path.fromPortableString(matcher.group(1));
}
return path;
}
public static IClasspathEntry[] listClasspathEntriesWithPathMatching(IJavaProject project, Pattern pattern, IProgressMonitor monitor) {
List<IClasspathEntry> result = new ArrayList<IClasspathEntry>();
for (IClasspathEntry classpathEntry : getRawClasspath(project)) {
Matcher matcher = pattern.matcher(classpathEntry.getPath().toPortableString());
if (matcher.matches()) {
result.add(classpathEntry);
}
}
return result.toArray(new IClasspathEntry[0]);
}
public static IClasspathEntry[] listClasspathEntriesWithPathNotMatching(IJavaProject project, Pattern pattern, IProgressMonitor monitor) {
List<IClasspathEntry> result = new ArrayList<IClasspathEntry>();
for (IClasspathEntry classpathEntry : getRawClasspath(project)) {
Matcher matcher = pattern.matcher(classpathEntry.getPath().toPortableString());
if (!matcher.matches()) {
result.add(classpathEntry);
}
}
return result.toArray(new IClasspathEntry[0]);
}
public static void removeClasspathEntriesWithPathMatching(IJavaProject project, Pattern pattern, IProgressMonitor monitor) {
IClasspathEntry[] notMatchingClasspathEntries = listClasspathEntriesWithPathNotMatching(project, pattern, monitor);
try {
updateRawClasspath(project, Arrays.asList(notMatchingClasspathEntries), monitor);
} catch (JavaModelException e) {
throw new JavaProjectInteractionException(e);
}
}
}