package org.eclipse.jdt.internal.launching;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IAccessRule;
import org.eclipse.jdt.core.IClasspathAttribute;
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.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstallChangedListener;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;
import org.eclipse.jdt.launching.PropertyChangeEvent;
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
import org.eclipse.osgi.util.NLS;
public class JREContainer implements IClasspathContainer {
private IVMInstall fVMInstall = null;
private IPath fPath = null;
private IJavaProject fProject = null;
private static Map<IVMInstall, IClasspathEntry[]> fgClasspathEntries = new HashMap<>(10);
private static IAccessRule[] EMPTY_RULES = new IAccessRule[0];
private static Map<RuleKey, RuleEntry> fgClasspathEntriesWithRules = new HashMap<>(10);
static class RuleKey {
private String fEnvironmentId = null;
private IVMInstall fInstall = null;
public RuleKey(IVMInstall install, String environmentId) {
fInstall = install;
fEnvironmentId = environmentId;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof RuleKey) {
RuleKey key = (RuleKey) obj;
return fEnvironmentId.equals(key.fEnvironmentId) && fInstall.equals(key.fInstall);
}
return false;
}
@Override
public int hashCode() {
return fEnvironmentId.hashCode() + fInstall.hashCode();
}
}
static class RuleEntry {
private IAccessRule[][] fRules = null;
private IClasspathEntry[] fEntries = null;
public RuleEntry(IAccessRule[][] rules, IClasspathEntry[] entries) {
fRules = rules;
fEntries = entries;
}
public IClasspathEntry[] getClasspathEntries() {
return fEntries;
}
@Override
public boolean equals(Object obj) {
IAccessRule[][] rules = null;
if(obj instanceof RuleEntry) {
rules = ((RuleEntry)obj).fRules;
}
if(obj instanceof IAccessRule[][]) {
rules = (IAccessRule[][]) obj;
}
if (fRules == rules) {
return true;
}
if(rules != null) {
if (fRules.length == rules.length) {
for (int i = 0; i < fRules.length; i++) {
if (!rulesEqual(fRules[i], rules[i])){
return false;
}
}
return true;
}
}
return false;
}
private static boolean rulesEqual(IAccessRule[] a, IAccessRule[] b){
if (a == b){
return true;
}
if (a.length != b.length){
return false;
}
for (int j = 0; j < a.length; j++) {
if (!a[j].equals(b[j])) {
return false;
}
}
return true;
}
}
static {
IVMInstallChangedListener listener = new IVMInstallChangedListener() {
@Override
public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current) {}
@Override
public void vmAdded(IVMInstall newVm) {}
@Override
public void vmChanged(PropertyChangeEvent event) {
if (event.getSource() != null) {
fgClasspathEntries.remove(event.getSource());
removeRuleEntry(event.getSource());
}
}
@Override
public void vmRemoved(IVMInstall removedVm) {
fgClasspathEntries.remove(removedVm);
removeRuleEntry(removedVm);
}
private void removeRuleEntry(Object obj) {
if(obj instanceof IVMInstall) {
IVMInstall install = (IVMInstall) obj;
RuleKey key = null;
ArrayList<RuleKey> list = new ArrayList<>();
for(Iterator<RuleKey> iter = fgClasspathEntriesWithRules.keySet().iterator(); iter.hasNext();) {
key = iter.next();
if(key.fInstall.equals(install)) {
list.add(key);
}
}
for(int i = 0; i < list.size(); i++) {
fgClasspathEntriesWithRules.remove(list.get(i));
}
}
}
};
JavaRuntime.addVMInstallChangedListener(listener);
}
private static IClasspathEntry[] getClasspathEntries(IVMInstall vm, IPath containerPath, IJavaProject project) {
String id = JavaRuntime.getExecutionEnvironmentId(containerPath);
IClasspathEntry[] entries = null;
if (id == null) {
entries = fgClasspathEntries.get(vm);
if (entries == null) {
entries = computeClasspathEntries(vm, project, id);
fgClasspathEntries.put(vm, entries);
}
} else {
if (LaunchingPlugin.DEBUG_JRE_CONTAINER) {
LaunchingPlugin.trace("\tEE:\t" + id);
}
entries = computeClasspathEntries(vm, project, id);
}
return entries;
}
private static IClasspathEntry[] computeClasspathEntries(IVMInstall vm, IJavaProject project, String environmentId) {
LibraryLocation[] libs = vm.getLibraryLocations();
boolean overrideJavaDoc = false;
if (libs == null) {
libs = JavaRuntime.getLibraryLocations(vm);
overrideJavaDoc = true;
}
IAccessRule[][] rules = null;
if (environmentId != null) {
IExecutionEnvironment environment = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment(environmentId);
if (environment != null) {
rules = environment.getAccessRules(vm, libs, project);
}
}
RuleKey key = null;
if (vm != null && rules != null && environmentId != null) {
key = new RuleKey(vm, environmentId);
RuleEntry entry = fgClasspathEntriesWithRules.get(key);
if(entry != null && entry.equals(rules)) {
return entry.getClasspathEntries();
}
}
List<IClasspathEntry> entries = new ArrayList<>(libs.length);
for (int i = 0; i < libs.length; i++) {
if (!libs[i].getSystemLibraryPath().isEmpty()) {
IPath sourcePath = libs[i].getSystemLibrarySourcePath();
if (sourcePath.isEmpty()) {
sourcePath = null;
}
IPath rootPath = libs[i].getPackageRootPath();
if (rootPath.isEmpty()) {
rootPath = null;
}
IClasspathAttribute[] attributes = JREContainer.buildClasspathAttributes(vm, libs[i], overrideJavaDoc);
IAccessRule[] libRules = null;
if (rules != null) {
libRules = rules[i];
} else {
libRules = EMPTY_RULES;
}
entries.add(JavaCore.newLibraryEntry(libs[i].getSystemLibraryPath(), sourcePath, rootPath, libRules, attributes, false));
}
}
IClasspathEntry[] cpEntries = entries.toArray(new IClasspathEntry[entries.size()]);
if (key != null && rules != null) {
fgClasspathEntriesWithRules.put(key, new RuleEntry(rules, cpEntries));
}
return cpEntries;
}
private static IClasspathAttribute[] buildClasspathAttributes(final IVMInstall vm, final LibraryLocation lib, final boolean overrideJavaDoc) {
List<IClasspathAttribute> classpathAttributes = new LinkedList<>();
URL javadocLocation = lib.getJavadocLocation();
if (overrideJavaDoc && javadocLocation == null) {
javadocLocation = vm.getJavadocLocation();
}
if(javadocLocation != null) {
IClasspathAttribute javadocCPAttribute = JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, javadocLocation.toExternalForm());
classpathAttributes.add(javadocCPAttribute);
}
URL indexLocation = lib.getIndexLocation();
if(indexLocation != null) {
IClasspathAttribute indexCPLocation = JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, indexLocation.toExternalForm());
classpathAttributes.add(indexCPLocation);
}
IPath annotationsPath = lib.getExternalAnnotationsPath();
if (null != annotationsPath && !annotationsPath.isEmpty()) {
IClasspathAttribute xAnnLocation = JavaCore.newClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, annotationsPath.toPortableString());
classpathAttributes.add(xAnnLocation);
}
return classpathAttributes.toArray(new IClasspathAttribute[classpathAttributes.size()]);
}
public JREContainer(IVMInstall vm, IPath path, IJavaProject project) {
fVMInstall = vm;
fPath = path;
fProject = project;
}
@Override
public IClasspathEntry[] getClasspathEntries() {
if (LaunchingPlugin.DEBUG_JRE_CONTAINER) {
LaunchingPlugin.trace("<JRE_CONTAINER> getClasspathEntries() " + this.toString());
LaunchingPlugin.trace("\tJRE:\t" + fVMInstall.getName());
LaunchingPlugin.trace("\tPath:\t" + getPath().toString());
LaunchingPlugin.trace("\tProj:\t" + fProject.getProject().getName());
}
IClasspathEntry[] entries = getClasspathEntries(fVMInstall, getPath(), fProject);
if (LaunchingPlugin.DEBUG_JRE_CONTAINER) {
LaunchingPlugin.trace("\tResolved " + entries.length + " entries:");
}
return entries;
}
@Override
public String getDescription() {
String environmentId = JavaRuntime.getExecutionEnvironmentId(getPath());
String tag = null;
if (environmentId == null) {
tag = fVMInstall.getName();
} else {
tag = environmentId;
}
return NLS.bind(LaunchingMessages.JREContainer_JRE_System_Library_1, new String[]{tag});
}
@Override
public int getKind() {
return IClasspathContainer.K_DEFAULT_SYSTEM;
}
@Override
public IPath getPath() {
return fPath;
}
}