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