| /******************************************************************************* |
| * Copyright (c) 2001, 2006 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.jem.internal.beaninfo.core; |
| /* |
| |
| |
| */ |
| |
| |
| import java.util.*; |
| import java.util.Map.Entry; |
| import java.util.logging.Level; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.jdt.core.IClasspathContainer; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| |
| import org.eclipse.jem.internal.beaninfo.adapters.BeaninfoNature; |
| import org.eclipse.jem.internal.proxy.core.*; |
| import org.eclipse.jem.internal.proxy.core.ContainerPathContributionMapping.ContainerContributionEntry; |
| import org.eclipse.jem.internal.proxy.core.IConfigurationContributionInfo.ContainerPaths; |
| import org.eclipse.jem.java.JavaClass; |
| import org.eclipse.jem.util.logger.proxy.Logger; |
| import org.eclipse.jem.util.logger.proxyrender.EclipseLogger; |
| import org.eclipse.jem.util.plugin.JEMUtilPlugin; |
| |
| |
| /** |
| * The plugin class for the org.eclipse.jem.internal.proxy.core plugin. |
| */ |
| |
| public class BeaninfoPlugin extends Plugin { |
| public static final String PI_BEANINFO_PLUGINID = "org.eclipse.jem.beaninfo"; // Plugin ID, used for QualifiedName. //$NON-NLS-1$ |
| public static final String PI_BEANINFO_OVERRIDES = "overrides"; // ID of the overrides extension point. //$NON-NLS-1$ |
| |
| private static BeaninfoPlugin BEANINFO_PLUGIN = null; |
| |
| public BeaninfoPlugin() { |
| BEANINFO_PLUGIN = this; |
| } |
| |
| /** |
| * Accessor method to get the singleton plugin. |
| */ |
| public static BeaninfoPlugin getPlugin() { |
| return BEANINFO_PLUGIN; |
| } |
| |
| /** |
| * Special Override file name used when need to apply an override to a class that is at the root. |
| * A root is one that doesn't have a super type. These are <code>java.lang.Object</code>, interfaces, and any |
| * undefined classes (i.e. classes that don't actually exist). |
| */ |
| public static final String ROOT = "..ROOT.."; //$NON-NLS-1$ |
| |
| |
| /** |
| * Special override scheme to refer to the current java class. (As in "X:ROOT#//@root"). |
| * @since 1.2.0 |
| */ |
| public static final String ROOT_SCHEMA = "X"; //$NON-NLS-1$ |
| |
| /** |
| * Special override opaque part to refer to the current java class. (As in "X:ROOT#//@root"). |
| * @since 1.2.0 |
| */ |
| public static final String ROOT_OPAQUE = "ROOT"; //$NON-NLS-1$ |
| |
| /** |
| * The extension used on any override file when you pass in a path through the method that takes a string. |
| */ |
| public static final String OVERRIDE_EXTENSION = "override"; //$NON-NLS-1$ |
| |
| |
| /* |
| * Map of open natures. This map is needed because on shutdown of beaninfo plugin we need |
| * to shutdown the natures. If we don't do that there is a slight possibility of an error |
| * because proxy plugin will shutdown and this can cause a callback into beaninfo which has |
| * already been shutdown. It calls back through the registry listener that BeaninfoNature |
| * added to the registry to notify that the registry is being shutdown. |
| * |
| * Also BeanInfoCacheController needs to know so that it can tell it the project is closing or |
| * being deleted or that it needs to be cleared due to a clear request. |
| */ |
| private Map openNatures; |
| |
| private ContainerPathContributionMapping beaninfoEntryContributionsMapping; |
| private ContainerPathContributionMapping contributorContributionsMapping; |
| private Map pluginToBeaninfoEntryContributions; |
| private Map pluginToContributors; |
| |
| /* |
| * Override contributions from extension point. |
| * ocFragments: Array of fragments paths. When a match is found for a path, the index |
| * is the index into the ocContainerIds and ocPluginIds array for the contributions. |
| * ocContainerIds: The first dimension is the index of the fragment that the list of OverrideContributions is for. |
| * The second dimension is the array of contributions for that fragment, one per container id. |
| * ocPluginIds: The first dimension is the index of the fragment that the list of OverrideContributions is for. |
| * The second dimension is the array of contributions for that fragment, one per plugin id. |
| * |
| * If a particular fragment doesn't have any entries of container and/or plugin, then EMPTY_OC is used for that |
| * entry so that we don't need to check for null. |
| * |
| * How this is used is for a particular path requested, the ocFragments will be searched for the fragments that |
| * are appropriate, then the index of the entry is used to walk through the OC[] array returned from the ocContainerIds |
| * or ocPluginIds. Each contribution would be checked to see if the container id/plugin id is in the visible classpath (through |
| * the info data stored in the persistent property). If it is, then the overrides from that contribution will be used. |
| */ |
| private IPath ocFragments[]; |
| private OverrideContribution[][] ocContainerIds; |
| private OverrideContribution[][] ocPluginIds; |
| |
| private static final OverrideContribution[] EMPTY_OC = new OverrideContribution[0]; // Used for an empty contribution list for a fragment. |
| |
| public synchronized BeaninfoEntry[] getContainerIdBeanInfos(String containerID, String[] containerPaths) { |
| if (beaninfoEntryContributionsMapping == null) |
| processBeanInfoContributionExtensionPoint(); |
| return (BeaninfoEntry[]) beaninfoEntryContributionsMapping.getContributors(containerID, containerPaths); |
| } |
| |
| public synchronized BeaninfoEntry[] getPluginBeanInfos(String pluginid) { |
| if (pluginToBeaninfoEntryContributions == null) |
| processBeanInfoContributionExtensionPoint(); |
| return (BeaninfoEntry[]) pluginToBeaninfoEntryContributions.get(pluginid); |
| } |
| |
| public synchronized IConfigurationElement[] getPluginContributors(String pluginid) { |
| if (pluginToContributors == null) |
| processBeanInfoContributionExtensionPoint(); |
| return (IConfigurationElement[]) pluginToContributors.get(pluginid); |
| } |
| |
| public synchronized IConfigurationElement[] getContainerIdContributors(String containerID, String[] containerPaths) { |
| if (contributorContributionsMapping == null) |
| processBeanInfoContributionExtensionPoint(); |
| return (IConfigurationElement[]) contributorContributionsMapping.getContributors(containerID, containerPaths); |
| } |
| |
| public static final String PI_BEANINFO_CONTRIBUTION_EXTENSION_POINT = PI_BEANINFO_PLUGINID+".registrations"; //$NON-NLS-1$ |
| public static final String PI_REGISTRATION = "registration"; //$NON-NLS-1$ |
| public static final String PI_BEANINFO = "beaninfo"; //$NON-NLS-1$ |
| public static final String PI_OVERRIDE = "override"; //$NON-NLS-1$ |
| public static final String PI_CONTRIBUTOR = "contributor"; //$NON-NLS-1$ |
| public static final String PI_PACKAGE = "package"; //$NON-NLS-1$ |
| public static final String PI_PATH = "path"; //$NON-NLS-1$ |
| |
| protected synchronized void processBeanInfoContributionExtensionPoint() { |
| ContributorExtensionPointInfo info = ProxyPlugin.processContributionExtensionPoint(PI_BEANINFO_CONTRIBUTION_EXTENSION_POINT); |
| ConfigurationElementReader reader = new ConfigurationElementReader(); |
| // Process the container IDs first. We can't use the info directly because the actual configuration elements of interest are |
| // sub-elements of the info. The info contains the container path that we need. |
| beaninfoEntryContributionsMapping = new ContainerPathContributionMapping(BeaninfoEntry.class); |
| contributorContributionsMapping = new ContainerPathContributionMapping(IConfigurationElement.class); |
| |
| Map fragmentsToIds = new HashMap(); |
| for (Iterator iter = info.containerPathContributions.containerIdToContributions.entrySet().iterator(); iter.hasNext();) { |
| Map.Entry entry= (Map.Entry) iter.next(); |
| String containerid = (String) entry.getKey(); |
| ContainerContributionEntry[] contribElements = (ContainerContributionEntry[]) entry.getValue(); |
| for (int i = 0; i < contribElements.length; i++) { |
| ContainerContributionEntry contribElement = contribElements[i]; |
| IConfigurationElement element = (IConfigurationElement) contribElement.getContribution(); |
| if (PI_REGISTRATION.equals(element.getName())) { |
| IConfigurationElement[] children = element.getChildren(); |
| for (int j = 0; j < children.length; j++) { |
| IConfigurationElement child = children[j]; |
| if (PI_BEANINFO.equals(child.getName())) { |
| // This is a beaninfo entry |
| BeaninfoEntry be = BeaninfoEntry.readEntry(reader, child, null); |
| if (be != null) |
| beaninfoEntryContributionsMapping.addContribution(containerid, contribElement.getContainerPathPattern(), be); |
| } else if (PI_OVERRIDE.equals(child.getName())) { |
| addOverrideEntry(fragmentsToIds, true, containerid, contribElement.getContainerPathPattern(), child); |
| } |
| } |
| } else if (PI_CONTRIBUTOR.equals(element.getName())) { |
| contributorContributionsMapping.addContribution(containerid, contribElement.getContainerPathPattern(), element); |
| } |
| } |
| } |
| |
| beaninfoEntryContributionsMapping.finalizeMapping(); |
| contributorContributionsMapping.finalizeMapping(); |
| |
| // Now process the plugin IDs. |
| pluginToBeaninfoEntryContributions = new HashMap(info.pluginToContributions.size()); |
| for (Iterator iter = info.pluginToContributions.entrySet().iterator(); iter.hasNext();) { |
| Map.Entry entry= (Map.Entry) iter.next(); |
| String pluginId = (String) entry.getKey(); |
| IConfigurationElement[] configElements = (IConfigurationElement[]) entry.getValue(); |
| for (int i = 0; i < configElements.length; i++) { |
| IConfigurationElement element = configElements[i]; |
| if (PI_REGISTRATION.equals(element.getName())) { |
| IConfigurationElement[] children = element.getChildren(); |
| for (int j = 0; j < children.length; j++) { |
| IConfigurationElement child = children[j]; |
| if (PI_BEANINFO.equals(child.getName())) { |
| // This is a beaninfo entry |
| BeaninfoEntry be = BeaninfoEntry.readEntry(reader, child, null); |
| if (be != null) |
| addEntry(pluginToBeaninfoEntryContributions, pluginId, be); |
| } else if (PI_OVERRIDE.equals(child.getName())) { |
| addOverrideEntry(fragmentsToIds, false, pluginId, null, child); |
| } |
| } |
| } else if (PI_CONTRIBUTOR.equals(element.getName())) { |
| if (pluginToContributors == null) |
| pluginToContributors = new HashMap(5); // These are rare, don't create until necessary. |
| addEntry(pluginToContributors, pluginId, element); |
| } |
| } |
| } |
| |
| // Now go through and turn all of the contribution lists into arrays. |
| for (Iterator iter = pluginToBeaninfoEntryContributions.entrySet().iterator(); iter.hasNext();) { |
| Map.Entry entry = (Map.Entry) iter.next(); |
| entry.setValue(((List) entry.getValue()).toArray(new BeaninfoEntry[((List) entry.getValue()).size()])); |
| } |
| |
| if (pluginToContributors == null) |
| pluginToContributors = Collections.EMPTY_MAP; // Since we don't have any. |
| else { |
| for (Iterator iter = pluginToContributors.entrySet().iterator(); iter.hasNext();) { |
| Map.Entry entry = (Map.Entry) iter.next(); |
| entry.setValue(((List) entry.getValue()).toArray(new IConfigurationElement[((List) entry.getValue()).size()])); |
| } |
| } |
| |
| // Now handle the entire list of fragments. |
| ocFragments = new IPath[fragmentsToIds.size()]; |
| ocContainerIds = new OverrideContribution[ocFragments.length][]; |
| ocPluginIds = new OverrideContribution[ocFragments.length][]; |
| Iterator iter; |
| int fragIndex; |
| for (iter = fragmentsToIds.entrySet().iterator(), fragIndex=0; iter.hasNext(); fragIndex++) { |
| Map.Entry mapEntry = (Map.Entry) iter.next(); |
| ocFragments[fragIndex] = (IPath) mapEntry.getKey(); |
| Map[] mapValue = (Map[]) mapEntry.getValue(); |
| if (mapValue[0] == null) |
| ocContainerIds[fragIndex] = EMPTY_OC; |
| else { |
| Map containers = mapValue[0]; |
| List ocContributions = new ArrayList(); |
| for (Iterator ocIterator = containers.entrySet().iterator(); ocIterator.hasNext();) { |
| Map.Entry containerEntry = (Map.Entry) ocIterator.next(); |
| String containerID = (String) containerEntry.getKey(); |
| for (Iterator patternIterator = ((Map) containerEntry.getValue()).entrySet().iterator(); patternIterator.hasNext();) { |
| Map.Entry patternEntry = (Entry) patternIterator.next(); |
| OverrideContribution oc = new OverrideContribution(); |
| oc.id = containerID; |
| oc.pattern = (Pattern) patternEntry.getKey(); |
| List[] ocLists = (List[]) patternEntry.getValue(); |
| oc.pluginIds = (String[]) ocLists[0].toArray(new String[ocLists[0].size()]); |
| oc.paths = (String[]) ocLists[1].toArray(new String[ocLists[1].size()]); |
| ocContributions.add(oc); |
| } |
| } |
| ocContainerIds[fragIndex] = (OverrideContribution[]) ocContributions.toArray(new OverrideContribution[ocContributions.size()]); |
| } |
| if (mapValue[1] == null) |
| ocPluginIds[fragIndex] = EMPTY_OC; |
| else { |
| Map plugins = mapValue[1]; |
| OverrideContribution[] ocContribution = ocPluginIds[fragIndex] = new OverrideContribution[plugins.size()]; |
| int ocIndex; |
| Iterator ocIterator; |
| for (ocIterator = plugins.entrySet().iterator(), ocIndex=0; ocIterator.hasNext(); ocIndex++) { |
| Map.Entry pluginEntry = (Map.Entry) ocIterator.next(); |
| OverrideContribution oc = ocContribution[ocIndex] = new OverrideContribution(); |
| oc.id = (String) pluginEntry.getKey(); |
| List[] ocLists = (List[]) pluginEntry.getValue(); |
| oc.pluginIds = (String[]) ocLists[0].toArray(new String[ocLists[0].size()]); |
| oc.paths = (String[]) ocLists[1].toArray(new String[ocLists[1].size()]); |
| } |
| } |
| } |
| } |
| |
| /* |
| * Add an entry to the map. If the key doesn't exist, create an entry as an array. Then add the entry to array. |
| */ |
| private void addEntry(Map map, Object key, Object entry) { |
| List mapEntry = (List) map.get(key); |
| if (mapEntry == null) { |
| mapEntry = new ArrayList(1); |
| map.put(key, mapEntry); |
| } |
| mapEntry.add(entry); |
| } |
| |
| /* |
| * Add an entry to the map. |
| * id is the container path pattern/plugin id. |
| * |
| * The structure of the map is: |
| * key: fragment name |
| * value: Map[2], where [0] is for container id, and [1] is for plugin ids. |
| * Map[x]: |
| * key: container/plugin id |
| * value: Map(pattern->List(FinalOverride)) for container, of FinalOverride for plugin. |
| * |
| * FinalOverride: List[2], where [0] is list of plugin ids for the override, and [1] is list of paths for the override files relative to that plugin id. |
| * |
| * After all done these maps/list will be boiled down to the arrays that will be used for lookup. |
| */ |
| private void addOverrideEntry(Map map, boolean container, Object id, Pattern pattern, IConfigurationElement entry) { |
| |
| String packageName = entry.getAttribute(PI_PACKAGE); |
| String plugin = null; |
| String pathString = entry.getAttribute(PI_PATH); |
| IPath fragment = null; |
| if (packageName != null && packageName.length() > 0 && pathString != null && pathString.length() > 0) { |
| fragment = new Path(packageName.replace('.', '/')); |
| if (pathString.charAt(pathString.length()-1) != '/') |
| pathString += '/'; |
| if (pathString.charAt(0) != '/') |
| plugin = entry.getDeclaringExtension().getContributor().getName(); |
| else { |
| if (pathString.length() > 4) { |
| int pend = pathString.indexOf('/', 1); |
| if (pend == -1 || pend >= pathString.length()-1) |
| return; // invalid |
| plugin = pathString.substring(1, pend); |
| pathString = pathString.substring(pend+1); |
| } else |
| return; // invalid |
| } |
| } |
| if (pathString.length() < 2) |
| return; // invalid |
| |
| Map[] mapEntry = (Map[]) map.get(fragment); |
| if (mapEntry == null) { |
| mapEntry = new HashMap[2]; |
| map.put(fragment, mapEntry); |
| } |
| List[] idEntry; |
| if (container) { |
| if (mapEntry[0] == null) |
| mapEntry[0] = new HashMap(2); |
| |
| Map patternMap = (Map) mapEntry[0].get(id); |
| if (patternMap == null) |
| mapEntry[0].put(id, patternMap = new HashMap()); |
| |
| idEntry = (List[]) patternMap.get(pattern); |
| if (idEntry == null) { |
| patternMap.put(pattern, idEntry = new List[] { new ArrayList(1), new ArrayList(1)}); |
| } |
| } else { |
| if (mapEntry[1] == null) |
| mapEntry[1] = new HashMap(2); |
| |
| idEntry = (List[]) mapEntry[1].get(id); |
| if (idEntry == null) { |
| mapEntry[1].put(id, idEntry = new List[] { new ArrayList(1), new ArrayList(1)}); |
| } |
| } |
| idEntry[0].add(plugin); |
| idEntry[1].add(pathString); |
| |
| |
| } |
| |
| /* |
| * This is an list of overrides that are available as a contribution for a specific fragment. |
| * <ul> |
| * <li>The id of this contribution. Either container (Pattern) or plugin id depending on which list it was in.. |
| * <li>The plugins array lists the plugin ids for all of the paths in this contribution. |
| * <li>The paths array lists the folder path under that corresponding plugin from "pluginIds". |
| * </ul> |
| * <p> |
| * |
| * @since 1.0.0 |
| */ |
| private static class OverrideContribution { |
| public String id; |
| public Pattern pattern; // Used only for containers. |
| public String[] pluginIds; |
| public String[] paths; |
| } |
| |
| /** |
| * The runnable is to used to apply override. |
| * <p> |
| * This will be called in sequence for each override path found. It is send in on the apply overrides call. This |
| * interface implementation is private. |
| * <p> |
| * Clients (implementers of the IBeanInfoContributor) will be passed in the subinterface <code>IContributorOverrideRunnable</code> which |
| * inherits from this interface. |
| * <p> |
| * This interface is not intended to be implemented by clients. |
| * |
| * @since 1.0.0 |
| * @see BeaninfoPlugin#applyOverrides(IProject, String, String, ResourceSet, IOverrideRunnable) |
| */ |
| public interface IOverrideRunnable { |
| /** |
| * This will be called with the directory path to use. It will be called over and over for every |
| * override path found for a package. The path will be complete, including trailing '/'. |
| * It will be in a URI format for a directory. The overriderunnable implementation will then append the filename call (i.e. classbeingintrospected.override) to get a complete path. |
| * <p> |
| * Clients (IBeanInfoContributor implementers) can call this to apply a specific override file to the current |
| * class being processed. |
| * |
| * @param overridePath the path will be complete, including trailing '/'. It will be in a URI format for a directory. The override file name (classname.override) will be appended to this and retrieved and applied. |
| * |
| * @since 1.0.0 |
| */ |
| public void run(String overridePath); |
| |
| /** |
| * This will be called with the actual resource to use. This will be called by special contributors that want |
| * a special explicit override resource to be used. |
| * <p> |
| * Contributors should use the ResourceSet that was passed into them. This is so that anything java class that |
| * the override resource points to will be found. |
| * <p> |
| * This resource will be automatically removed by BeanInfo after being applied. It must not be left around because |
| * in the process of being applied it will be modified, so it could not be reused. |
| * |
| * @param overrideResource the resource to apply to the current class. NOTE: This resource WILL be removed from |
| * the resource set it is in automatically by this call. It won't be left around because the action of apply |
| * will actually modify the resource. |
| * |
| * @since 1.0.0 |
| */ |
| public void run(Resource overrideRes); |
| } |
| |
| /** |
| * IBeanInfoContributor runnable to use to apply overrides. |
| * <p> |
| * An implementation of this will be passed in to IBeanInfoContributor's so that they can call back to apply the overrides. They |
| * should call the appropriate run method once for each override to be applied. The run can be called more than once from each IBeanInfoContributor. |
| * <p> |
| * It inherits from <code>IOverrideRunnable</code>, so see that for more methods to call. |
| * <p> |
| * This interface is not intended to be implemented by clients. |
| * |
| * @see BeaninfoPlugin.IOverrideRunnable for more methods that can be called. |
| * @since 1.0.0 |
| */ |
| public interface IContributorOverrideRunnable extends IOverrideRunnable { |
| |
| /** |
| * Tests if path has already been contributed once for the current class. |
| * <p> |
| * This can be called by the IBeanInfoContributor for overrides to test if the path (same path as for the IOverrideRunnable.run(String) method) |
| * has already been contributed once for this class. It can be used to save time. However it is not necessary because |
| * BeanInfo will not permit it to be contributed more than once for a class. |
| * |
| * @param path |
| * @return <code>true</code> if used already. |
| * |
| * @see IOverrideRunnable#run(String) |
| * @since 1.0.0 |
| */ |
| public boolean pathContributed(String path); |
| |
| /** |
| * Tests if the URI has already been contributed once for the current class. |
| * <p> |
| * This can be called by an IBeanInfoContributor for overrides to see if the URI (same path as the URI from the IOverrideRunnable.run(Resource) method) |
| * has already been contributed once for this class. It can be used to save time. However, not necessary because |
| * BeanInfo will not permit the URI to be contributed more than once for a class. |
| * |
| * @param resourceURI |
| * @return <code>true</code> if used already. |
| * |
| * @see IOverrideRunnable#run(Resource) |
| * @since 1.0.0 |
| */ |
| public boolean resourceContributed(URI resourceURI); |
| } |
| |
| private static final String[] NO_PATHS = new String[0]; |
| |
| /** |
| * Return just the contributed override paths (through the BeanInfo registrations). Does not include any paths that are contributed from |
| * IBeanInfoContributor's. This is used by the BeanInfoClassAdapter to load the overrides files into one cache file so that it can |
| * be done at one time the next time it is needed. |
| * |
| * @param project |
| * @param packageName |
| * @return array of path strings to the override. The files may not exist, they is just possible overrides. |
| * |
| * @since 1.1.0 |
| */ |
| public String[] getOverridePaths(IProject project, String packageName) { |
| final IPath packagePath = new Path(packageName.replace('.', '/')+'/'); |
| List overridePaths = new ArrayList(); |
| try { |
| IConfigurationContributionInfo info = (IConfigurationContributionInfo) project.getSessionProperty(BeaninfoNature.CONFIG_INFO_SESSION_KEY); |
| if (info == null) { |
| // It hasn't been created yet, so we need to create our own internal version here. |
| info = ProxyLaunchSupport.createDefaultConfigurationContributionInfo(JavaCore.create(project)); |
| BeaninfoNature.computeBeanInfoConfigInfo(info); |
| } |
| synchronized (this) { |
| if (ocFragments == null) |
| processBeanInfoContributionExtensionPoint(); // We haven't processed them yet. |
| } |
| |
| // Cache of tested patterns. (Pattern->Boolean). If a pattern has been tested against all visible container paths we don't need to test the |
| // pattern again if it is found again. (Note: This works for efficiency because ProxyPlugin uses the same pattern instance for |
| // same pattern found while processing one extension point). The value is true if the pattern matches a visible container path. |
| Map testedPatterns = new HashMap(); |
| for (int fragmentIndex = 0; fragmentIndex < ocFragments.length; fragmentIndex++) { |
| if (ocFragments[fragmentIndex].isPrefixOf(packagePath)) { |
| String leftOver = null; // The left over portion of the package. This will be set first time needed. |
| OverrideContribution[] cntrContributions = ocContainerIds[fragmentIndex]; |
| for (int ocindex = 0; ocindex < cntrContributions.length; ocindex++) { |
| OverrideContribution contribution = cntrContributions[ocindex]; |
| Boolean tested = (Boolean) testedPatterns.get(contribution.pattern); |
| if (tested == null) { |
| tested = Boolean.FALSE; |
| ContainerPaths containerPaths = (ContainerPaths) info.getContainerIds().get(contribution.id); |
| if (containerPaths != null) { |
| String[] visible = containerPaths.getVisibleContainerPaths(); |
| for (int i = 0; i < visible.length; i++) { |
| if (contribution.pattern.matcher(visible[i]).matches()) { |
| tested = Boolean.TRUE; |
| break; |
| } |
| } |
| } |
| testedPatterns.put(contribution.pattern, tested); |
| } |
| |
| if (tested.booleanValue()) { |
| for (int cindex = 0; cindex < contribution.pluginIds.length; cindex++) { |
| // Because of URIConverters and normalization in org.eclipse.jem.util stuff, we |
| // need to have plugin uri's in the form "platform:/plugin/pluginname". |
| // Bundle's don't return this format. They return bundle:/stuff |
| // So we will simple create it of the platform:/plugin format. |
| // To save time, we will first see if we have the bundle. |
| Bundle bundle = Platform.getBundle(contribution.pluginIds[cindex]); |
| if (bundle != null) { |
| if (leftOver == null) |
| leftOver = getLeftOver(ocFragments[fragmentIndex], packagePath); |
| overridePaths.add(JEMUtilPlugin.PLATFORM_PROTOCOL+":/"+JEMUtilPlugin.PLATFORM_PLUGIN+'/'+bundle.getSymbolicName()+'/'+contribution.paths[cindex]+leftOver); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| |
| OverrideContribution[] pluginContributions = ocPluginIds[fragmentIndex]; |
| for (int ocindex = 0; ocindex < pluginContributions.length; ocindex++) { |
| OverrideContribution contribution = pluginContributions[ocindex]; |
| Boolean visible = (Boolean) info.getPluginIds().get(contribution.id); |
| if (visible != null && visible.booleanValue()) { |
| for (int cindex = 0; cindex < contribution.pluginIds.length; cindex++) { |
| Bundle bundle = Platform.getBundle(contribution.pluginIds[cindex]); |
| if (bundle != null) { |
| if (leftOver == null) |
| leftOver = getLeftOver(ocFragments[fragmentIndex], packagePath); |
| overridePaths.add(JEMUtilPlugin.PLATFORM_PROTOCOL+":/"+JEMUtilPlugin.PLATFORM_PLUGIN+'/'+bundle.getSymbolicName()+'/'+contribution.paths[cindex]+leftOver); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| } |
| } |
| } catch (CoreException e) { |
| getLogger().log(e, Level.INFO); |
| } |
| return overridePaths.isEmpty() ? NO_PATHS : (String[]) overridePaths.toArray(new String[overridePaths.size()]); |
| } |
| |
| /** |
| * Apply the runnable to all of the override paths that are applicable to the |
| * given package name. It will run through the explicit contributors and the IContainers that implement IBeanInfoContributor. |
| * <p> |
| * The package name uses '.' to delineate the fragments of the name, |
| * i.e. use "<code>java.lang</code>" as a package name. |
| * <p> |
| * Note: This is not meant to be called by clients. It is public only because an internal class in another package needs to call it. |
| * TODO This should be package-protected. Later the other class will be moved into this package. |
| * |
| * @param project the project to run against. |
| * @param packageName |
| * @param className class name of the class that is being overridden. |
| * @param javaClass the java class the overrides will be applied to. |
| * @param resource set that contributors can use to temporarily load dynamic override files. |
| * @param runnable use this runnable to actually apply overrides. |
| * |
| * @since 1.0.0 |
| */ |
| public void applyOverrides(final IProject project, String packageName, final String className, final JavaClass javaClass, final ResourceSet rset, final IOverrideRunnable runnable) { |
| final IPath packagePath = new Path(packageName.replace('.', '/')+'/'); |
| try { |
| IConfigurationContributionInfo info = (IConfigurationContributionInfo) project.getSessionProperty(BeaninfoNature.CONFIG_INFO_SESSION_KEY); |
| if (info == null) { |
| // It hasn't been created yet, so we need to create our own internal version here. |
| info = ProxyLaunchSupport.createDefaultConfigurationContributionInfo(JavaCore.create(project)); |
| BeaninfoNature.computeBeanInfoConfigInfo(info); |
| } |
| final IBeanInfoContributor[] explicitContributors = (IBeanInfoContributor[]) project.getSessionProperty(BeaninfoNature.BEANINFO_CONTRIBUTORS_SESSION_KEY); |
| synchronized (this) { |
| if (ocFragments == null) |
| processBeanInfoContributionExtensionPoint(); // We haven't processed them yet. |
| } |
| |
| final Set usedPaths = new HashSet(10); // Set of used paths. So that the contributors don't supply a path already used. This could cause problems if they did. |
| final IContributorOverrideRunnable contribRunnable = new IContributorOverrideRunnable() { |
| public void run(String overridePath) { |
| if (!usedPaths.contains(overridePath)) { |
| usedPaths.add(overridePath); |
| runnable.run(overridePath); |
| } |
| } |
| |
| public void run(Resource overrideRes) { |
| if (!usedPaths.contains(overrideRes.getURI())) { |
| usedPaths.add(overrideRes.getURI()); |
| try { |
| runnable.run(overrideRes); |
| } finally { |
| overrideRes.getResourceSet().getResources().remove(overrideRes); |
| } |
| } |
| } |
| |
| public boolean pathContributed(String path) { |
| return usedPaths.contains(path); |
| } |
| |
| public boolean resourceContributed(URI resourceURI) { |
| return usedPaths.contains(resourceURI); |
| } |
| }; |
| |
| // Run through the containers that implement IBeanInfoContributor. |
| for (Iterator iter = info.getContainers().entrySet().iterator(); iter.hasNext();) { |
| Map.Entry mapEntry = (Map.Entry) iter.next(); |
| final IClasspathContainer container = (IClasspathContainer) mapEntry.getKey(); |
| if (container instanceof IBeanInfoContributor && ((Boolean) mapEntry.getValue()).booleanValue()) { |
| SafeRunner.run(new ISafeRunnable() { |
| public void handleException(Throwable exception) { |
| // Standard run logs to .log |
| } |
| public void run() throws Exception { |
| ((IBeanInfoContributor) container).runOverrides(packagePath, className, javaClass, rset, contribRunnable); |
| } |
| }); |
| } |
| } |
| |
| // Run through the explicit contributors. |
| for (int i=0; i<explicitContributors.length; i++) { |
| final int ii = i; |
| SafeRunner.run(new ISafeRunnable() { |
| public void handleException(Throwable exception) { |
| // Standard run logs to .log |
| } |
| public void run() throws Exception { |
| explicitContributors[ii].runOverrides(packagePath, className, javaClass, rset, contribRunnable); |
| } |
| }); |
| } |
| } catch (CoreException e) { |
| getLogger().log(e, Level.INFO); |
| } |
| } |
| |
| private String getLeftOver(IPath fragment, IPath packagePath) { |
| return packagePath.removeFirstSegments(fragment.segmentCount()).toString(); |
| } |
| |
| private Logger logger; |
| public Logger getLogger() { |
| if (logger == null) |
| logger = EclipseLogger.getEclipseLogger(this); |
| return logger; |
| } |
| |
| /** |
| * Add that a BeanInfo nature is active. This is used to tell it to shutdown when beaninfo shuts down. |
| * TODO <package-protected> because only BeanInfoNature should call it. (public for now but when we make |
| * BeanInfoNature an API it will be moved into the same package as BeanInfoPlugin). |
| * |
| * @param nature |
| * |
| * @since 1.0.0 |
| */ |
| public void addBeanInfoNature(BeaninfoNature nature) { |
| if (openNatures == null) |
| openNatures = new HashMap(); |
| openNatures.put(nature.getProject(), nature); |
| } |
| |
| /** |
| * Mark that a BeanInfo nature is not active. This is used to tell it to shutdown when beaninfo shuts down. |
| * TODO <package-protected> because only BeanInfoNature should call it. (public for now but when we make |
| * BeanInfoNature an API it will be moved into the same package as BeanInfoPlugin). |
| * |
| * @param nature |
| * |
| * @since 1.0.0 |
| */ |
| public void removeBeanInfoNature(BeaninfoNature nature) { |
| if (openNatures != null) |
| openNatures.remove(nature.getProject()); |
| } |
| |
| /** |
| * Return the registered nature, if any, for the project. This will not cause the |
| * nature to be created. |
| * <p> |
| * <package-protected> because only BeanInfoCacheController should access it. |
| * @param project |
| * @return nature for project or <code>null</code> if not registered. |
| * |
| * @since 1.1.0 |
| */ |
| BeaninfoNature getNature(IProject project) { |
| return (BeaninfoNature) openNatures.get(project); |
| } |
| |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.runtime.Plugin#stop(org.osgi.framework.BundleContext) |
| */ |
| public void stop(BundleContext context) throws Exception { |
| if (openNatures != null && !openNatures.isEmpty()) { |
| for (Iterator natureItr = openNatures.values().iterator(); natureItr.hasNext();) { |
| BeaninfoNature nature = (BeaninfoNature) natureItr.next(); |
| nature.shutdown(); |
| } |
| } |
| |
| super.stop(context); |
| } |
| } |
| |
| |