/*******************************************************************************
* Copyright (c) 2000, 2013 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.che.jdt.core.launching;
import org.eclipse.che.jdt.core.JavaCore;
import org.eclipse.che.jdt.core.launching.environments.IExecutionEnvironment;
import org.eclipse.che.jdt.internal.core.JavaProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
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.osgi.util.NLS;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* JRE Container - resolves a classpath container variable to a JRE
*/
public class JREContainer implements IClasspathContainer {
/**
* Corresponding JRE
*/
private IVMInstallType fVMInstall = null;
/**
* Container path used to resolve to this JRE
*/
private IPath fPath = null;
/**
* The project this container is for
*/
private JavaProject fProject = null;
/**
* Cache of classpath entries per VM install. Cleared when a VM changes.
*/
private static Map<IVMInstallType, IClasspathEntry[]> fgClasspathEntries = new HashMap<>(10);
/**
* Variable to return an empty array of <code>IAccessRule</code>s
*/
private static IAccessRule[] EMPTY_RULES = new IAccessRule[0];
/**
* Map of {IVMInstall -> Map of {{IExeuctionEnvironment, IAccessRule[][]} -> {IClasspathEntry[]}}
*/
private static Map<RuleKey, RuleEntry> fgClasspathEntriesWithRules = new HashMap<RuleKey, RuleEntry>(10);
/**
* A single key entry for the cache of access rules and classpath entries
* A rule key is made up of an <code>IVMInstall</code> and an execution environment id
*
* @since 3.3
*/
static class RuleKey {
private String fEnvironmentId = null;
private IVMInstallType fInstall = null;
/**
* Constructor
*
* @param install
* the VM
* @param environmentId
* the environment
*/
public RuleKey(IVMInstallType install, String environmentId) {
fInstall = install;
fEnvironmentId = environmentId;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@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;
}
/* (non-Javadoc)
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return fEnvironmentId.hashCode() + fInstall.hashCode();
}
}
/**
* Holds an entry for the cache of access rules/classpath entries.
* An entry is made up of an array of classpath entries and the collection of access rules.
*
* @since 3.3
*/
static class RuleEntry {
private IAccessRule[][] fRules = null;
private IClasspathEntry[] fEntries = null;
/**
* Constructor
*
* @param rules
* the rules
* @param entries
* the entries
*/
public RuleEntry(IAccessRule[][] rules, IClasspathEntry[] entries) {
fRules = rules;
fEntries = entries;
}
/**
* Returns the collection of classpath entries for this RuleEntry
*
* @return the cached array of classpath entries
*/
public IClasspathEntry[] getClasspathEntries() {
return fEntries;
}
/* (non-Javadoc)
* @see java.lang.Object#equals(java.lang.Object)
*/
@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;
}
/**
* Checks if the two arrays of rules are equal (same rules in each position in the array)
*
* @param a
* First list of rules to compare, must not be <code>null</code>
* @param b
* Second list of rules to compare, must not be <code>null</code>
* @return <code>true</code> if the arrays are equal, <code>false</code> otherwise
*/
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;
}
}
// /**
// * Add a VM changed listener to clear cached values when a VM changes or is removed
// */
// static {
// IVMInstallChangedListener listener = new IVMInstallChangedListener() {
//
// /* (non-Javadoc)
// * @see org.eclipse.jdt.launching.IVMInstallChangedListener#defaultVMInstallChanged(org.eclipse.jdt.launching.IVMInstall, org
// .eclipse.jdt.launching.IVMInstall)
// */
// public void defaultVMInstallChanged(IVMInstall previous, IVMInstall current) {}
//
// /* (non-Javadoc)
// * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmAdded(org.eclipse.jdt.launching.IVMInstall)
// */
// public void vmAdded(IVMInstall newVm) {}
//
// /* (non-Javadoc)
// * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmChanged(org.eclipse.jdt.launching.PropertyChangeEvent)
// */
// public void vmChanged(PropertyChangeEvent event) {
// if (event.getSource() != null) {
// fgClasspathEntries.remove(event.getSource());
// removeRuleEntry(event.getSource());
// }
// }
//
// /* (non-Javadoc)
// * @see org.eclipse.jdt.launching.IVMInstallChangedListener#vmRemoved(org.eclipse.jdt.launching.IVMInstall)
// */
// public void vmRemoved(IVMInstall removedVm) {
// fgClasspathEntries.remove(removedVm);
// removeRuleEntry(removedVm);
// }
//
// /**
// * Removes all occurrences of the given VM found as part key members in the current
// * cache for classpath entries
// * @param obj an object which should be castable to IVMInstall
// */
// private void removeRuleEntry(Object obj) {
// if(obj instanceof IVMInstall) {
// IVMInstall install = (IVMInstall) obj;
// RuleKey key = null;
// ArrayList<RuleKey> list = new ArrayList<RuleKey>();
// 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);
// }
/**
* Returns the classpath entries associated with the given VM
* in the context of the given path and project.
*
* @param vm the VM
* @param containerPath the container path resolution is for
* @param project project the resolution is for
* @return classpath entries
*/
private static IClasspathEntry[] getClasspathEntries(IVMInstallType vm, IPath containerPath, JavaProject project) {
String id = null; // JavaRuntime.getExecutionEnvironmentId(containerPath);
IClasspathEntry[] entries = null;
if (id == null) {
// cache classpath entries per JRE when not bound to an EE
entries = fgClasspathEntries.get(vm);
if (entries == null) {
entries = computeClasspathEntries(vm, project, id);
fgClasspathEntries.put(vm, entries);
}
} else {
if (Launching.DEBUG_JRE_CONTAINER) {
Launching.log("\tEE:\t" + id); //$NON-NLS-1$
}
// dynamically compute entries when bound to an EE
entries = computeClasspathEntries(vm, project, id);
}
return entries;
}
/**
* Evaluates library locations for a IVMInstall. If no library locations are set on the install, a default
* location is evaluated and checked if it exists.
* @param vm the {@link org.eclipse.che.jdt.core.launching.IVMInstallType} to compute locations for
* @return library locations with paths that exist or are empty
* @since 2.0
*/
public static LibraryLocation[] getLibraryLocations(IVMInstallType vm) {
IPath[] libraryPaths;
IPath[] sourcePaths;
IPath[] sourceRootPaths;
URL[] javadocLocations;
LibraryLocation[] locations= null; //vm.getLibraryLocations();
if (locations == null) {
URL defJavaDocLocation = null; //vm.getJavadocLocation();
File installLocation = vm.detectInstallLocation();
if (installLocation == null) {
return new LibraryLocation[0];
}
LibraryLocation[] dflts= vm.getDefaultLibraryLocations(installLocation);
libraryPaths = new IPath[dflts.length];
sourcePaths = new IPath[dflts.length];
sourceRootPaths = new IPath[dflts.length];
javadocLocations= new URL[dflts.length];
for (int i = 0; i < dflts.length; i++) {
libraryPaths[i]= dflts[i].getSystemLibraryPath();
if (defJavaDocLocation == null) {
javadocLocations[i]= dflts[i].getJavadocLocation();
} else {
javadocLocations[i]= defJavaDocLocation;
}
if (!libraryPaths[i].toFile().isFile()) {
libraryPaths[i]= Path.EMPTY;
}
sourcePaths[i]= dflts[i].getSystemLibrarySourcePath();
if (sourcePaths[i].toFile().isFile()) {
sourceRootPaths[i]= dflts[i].getPackageRootPath();
} else {
sourcePaths[i]= Path.EMPTY;
sourceRootPaths[i]= Path.EMPTY;
}
}
} else {
libraryPaths = new IPath[locations.length];
sourcePaths = new IPath[locations.length];
sourceRootPaths = new IPath[locations.length];
javadocLocations= new URL[locations.length];
for (int i = 0; i < locations.length; i++) {
libraryPaths[i]= locations[i].getSystemLibraryPath();
sourcePaths[i]= locations[i].getSystemLibrarySourcePath();
sourceRootPaths[i]= locations[i].getPackageRootPath();
javadocLocations[i] = locations[i].getJavadocLocation();
}
}
locations = new LibraryLocation[sourcePaths.length];
for (int i = 0; i < sourcePaths.length; i++) {
locations[i] = new LibraryLocation(libraryPaths[i], sourcePaths[i], sourceRootPaths[i], javadocLocations[i]);
}
return locations;
}
/**
* Computes the classpath entries associated with a VM - one entry per library
* in the context of the given path and project.
*
* @param vm the VM
* @param project the project the resolution is for
* @param environmentId execution environment the resolution is for, or <code>null</code>
* @return classpath entries
*/
private static IClasspathEntry[] computeClasspathEntries(IVMInstallType vm, JavaProject project, String environmentId) {
LibraryLocation[] libs = null; //vm.getLibraryLocations();
boolean overrideJavaDoc = false;
if (libs == null) {
libs = getLibraryLocations(vm);
overrideJavaDoc = true;
}
IAccessRule[][] rules = null;
// if (environmentId != null) {
// compute access rules for execution environment
IExecutionEnvironment environment = JavaRuntime.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<IClasspathEntry>(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;
}
// construct the classpath attributes for this library location
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 IVMInstallType vm, final LibraryLocation lib,
final boolean overrideJavaDoc) {
List<IClasspathAttribute> classpathAttributes = new LinkedList<IClasspathAttribute>();
// process the javadoc location
URL javadocLocation = lib.getJavadocLocation();
if (overrideJavaDoc && javadocLocation == null) {
javadocLocation = null; //vm.getJavadocLocation();
}
if (javadocLocation != null) {
IClasspathAttribute javadocCPAttribute =
JavaCore.newClasspathAttribute(IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, javadocLocation.toExternalForm());
classpathAttributes.add(javadocCPAttribute);
}
// process the index location
URL indexLocation = lib.getIndexLocation();
if (indexLocation != null) {
IClasspathAttribute indexCPLocation =
JavaCore.newClasspathAttribute(IClasspathAttribute.INDEX_LOCATION_ATTRIBUTE_NAME, indexLocation.toExternalForm());
classpathAttributes.add(indexCPLocation);
}
return classpathAttributes.toArray(new IClasspathAttribute[classpathAttributes.size()]);
}
/**
* Constructs a JRE classpath container on the given VM install
*
* @param vm VM install - cannot be <code>null</code>
* @param path container path used to resolve this JRE
* @param project the project context
*/
public JREContainer(IVMInstallType vm, IPath path, JavaProject project) {
fVMInstall = vm;
fPath = path;
fProject = project;
}
/**
* @see IClasspathContainer#getClasspathEntries()
*/
public IClasspathEntry[] getClasspathEntries() {
if (Launching.DEBUG_JRE_CONTAINER) {
Launching.log("<JRE_CONTAINER> getClasspathEntries() " + this.toString()); //$NON-NLS-1$
Launching.log("\tJRE:\t" + fVMInstall.getName()); //$NON-NLS-1$
Launching.log("\tPath:\t" + getPath().toString()); //$NON-NLS-1$
Launching.log("\tProj:\t" + fProject.getName()); //$NON-NLS-1$
}
IClasspathEntry[] entries = getClasspathEntries(fVMInstall, getPath(), fProject);
if (Launching.DEBUG_JRE_CONTAINER) {
Launching.log("\tResolved " + entries.length + " entries:"); //$NON-NLS-1$//$NON-NLS-2$
}
return entries;
}
/**
* @see IClasspathContainer#getDescription()
*/
public String getDescription() {
String environmentId = null;// JavaRuntime.getExecutionEnvironmentId(getPath());
String tag = null;
if (environmentId == null) {
tag = fVMInstall.getName();
} else {
tag = environmentId;
}
return NLS.bind("JRE System Library [{0}]", new String[]{tag});
}
/**
* @see IClasspathContainer#getKind()
*/
public int getKind() {
return IClasspathContainer.K_DEFAULT_SYSTEM;
}
/**
* @see IClasspathContainer#getPath()
*/
public IPath getPath() {
return fPath;
}
}