/******************************************************************************* * 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; } }