Copyright (c) 2004, 2018 IBM Corporation and others. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 which accompanies this distribution, and is available at https://www.eclipse.org/legal/epl-2.0/ SPDX-License-Identifier: EPL-2.0 Contributors: IBM Corporation - initial API and implementation
/******************************************************************************* * Copyright (c) 2004, 2018 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/
package org.eclipse.core.internal.registry; import java.lang.ref.SoftReference; import java.util.*; import org.eclipse.core.runtime.IContributor; import org.eclipse.core.runtime.InvalidRegistryObjectException; import org.eclipse.core.runtime.spi.RegistryContributor;
This class manage all the object from the registry but does not deal with their dependencies. It serves the objects which are either directly obtained from memory or read from a cache. It also returns handles for objects.
/** * This class manage all the object from the registry but does not deal with their dependencies. * It serves the objects which are either directly obtained from memory or read from a cache. * It also returns handles for objects. */
public class RegistryObjectManager implements IObjectManager { //Constants used to get the objects and their handles static public final byte CONFIGURATION_ELEMENT = 1; static public final byte EXTENSION = 2; static public final byte EXTENSION_POINT = 3; static public final byte THIRDLEVEL_CONFIGURATION_ELEMENT = 4; static final int CACHE_INITIAL_SIZE = 512; //This value has been picked because it is the minimal size required to startup an RCP app. (FYI, eclipse requires 3 growths). static final float DEFAULT_LOADFACTOR = 0.75f; //This is the default factor used in reference map. static final int[] EMPTY_INT_ARRAY = new int[0]; static final String[] EMPTY_STRING_ARRAY = new String[0]; static final ExtensionHandle[] EMPTY_EXTENSIONS_ARRAY = new ExtensionHandle[0]; static int UNKNOWN = -1; // key: extensionPointName, value: object id private HashtableOfStringAndInt extensionPoints; //This is loaded on startup. Then entries can be added when loading a new plugin from the xml. // key: object id, value: an object private ReferenceMap cache; //Entries are added by getter. The structure is not thread safe. //key: int, value: int private OffsetTable fileOffsets = null; //This is read once on startup when loading from the cache. Entries are never added here. They are only removed to prevent "removed" objects to be reloaded. private int nextId = 1; //This is only used to get the next number available. //Those two data structures are only used when the addition or the removal of a plugin occurs. //They are used to keep track on a contributor basis of the extension being added or removed private final KeyedHashSet newContributions; //represents the contributers added during this session. private Object formerContributions; //represents the contributers encountered in previous sessions. This is loaded lazily. private HashMap<String, RegistryContributor> contributors; // key: contributor ID; value: contributor name private HashMap<String, RegistryContributor> removedContributors; // key: contributor ID; value: contributor name private KeyedHashSet namespacesIndex; // registry elements (extension & extensionpoints) indexed by namespaces // Map key: extensionPointFullyQualifiedName, value int[] of orphan extensions. // The orphan access does not need to be synchronized because the it is protected by the lock in extension registry. private Object orphanExtensions; private final KeyedHashSet heldObjects = new KeyedHashSet(); //strong reference to the objects that must be hold on to //Indicate if objects have been removed or added from the table. This only needs to be set in a couple of places (addNamespace and removeNamespace) private boolean isDirty = false; private boolean fromCache = false; private final ExtensionRegistry registry; // TODO this option is not used // OSGI system properties. Copied from EclipseStarter public static final String PROP_NO_REGISTRY_FLUSHING = "eclipse.noRegistryFlushing"; //$NON-NLS-1$ public RegistryObjectManager(ExtensionRegistry registry) { extensionPoints = new HashtableOfStringAndInt(); if ("true".equalsIgnoreCase(RegistryProperties.getProperty(PROP_NO_REGISTRY_FLUSHING))) { //$NON-NLS-1$ cache = new ReferenceMap(ReferenceMap.HARD, CACHE_INITIAL_SIZE, DEFAULT_LOADFACTOR); } else { cache = new ReferenceMap(ReferenceMap.SOFT, CACHE_INITIAL_SIZE, DEFAULT_LOADFACTOR); } newContributions = new KeyedHashSet(); this.registry = registry; }
Initialize the object manager. Return true if the initialization succeeded, false otherwise
/** * Initialize the object manager. Return true if the initialization succeeded, false otherwise */
synchronized boolean init(long timeStamp) { TableReader reader = registry.getTableReader(); Object[] results = reader.loadTables(timeStamp); if (results == null) { return false; } fileOffsets = (OffsetTable) results[0]; extensionPoints = (HashtableOfStringAndInt) results[1]; nextId = ((Integer) results[2]).intValue(); fromCache = true; if (!registry.useLazyCacheLoading()) { //TODO Here we could grow all the tables to the right size (ReferenceMap) reader.setHoldObjects(true); markOrphansHasDirty(getOrphans()); fromCache = reader.readAllCache(this); formerContributions = getFormerContributions(); } return fromCache; } synchronized void addContribution(Contribution contribution) { isDirty = true; Object Id = contribution.getKey(); KeyedElement existingContribution = getFormerContributions().getByKey(Id); if (existingContribution != null) { // move it from former to new contributions removeContribution(Id); newContributions.add(existingContribution); } else existingContribution = newContributions.getByKey(Id); if (existingContribution != null) // merge ((Contribution) existingContribution).mergeContribution(contribution); else newContributions.add(contribution); updateNamespaceIndex(contribution, true); } // TODO make ExtensionPoint, Extension provide namespace in a same way (move it to the RegistryObject?) // See if all the registryObjects have the same namespace. If not, return null. // Also can return null if empty array is passed in or objects are of an unexpected type private String findCommonNamespaceIdentifier(RegistryObject[] registryObjects) { String namespaceName = null; for (RegistryObject currentObject : registryObjects) { String tmp = null; if (currentObject instanceof ExtensionPoint) tmp = ((ExtensionPoint) currentObject).getNamespace(); else if (currentObject instanceof Extension) tmp = ((Extension) currentObject).getNamespaceIdentifier(); if (namespaceName == null) { namespaceName = tmp; continue; } if (!namespaceName.equals(tmp)) { return null; } } return namespaceName; } synchronized void removeExtensionPointFromNamespaceIndex(int extensionPoint, String namespaceName) { RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); indexElement.updateExtensionPoint(extensionPoint, false); } synchronized void removeExtensionFromNamespaceIndex(int extensions, String namespaceName) { RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); indexElement.updateExtension(extensions, false); } // Called from a synchronized method private void updateNamespaceIndex(Contribution contribution, boolean added) { // if all extension points are from the same namespace combine them in one block and add them all together int[] contribExtensionPoints = contribution.getExtensionPoints(); RegistryObject[] extensionPointObjects = getObjects(contribExtensionPoints, EXTENSION_POINT); String commonExptsNamespace = null; if (contribExtensionPoints.length > 1) commonExptsNamespace = findCommonNamespaceIdentifier(extensionPointObjects); if (commonExptsNamespace != null) { RegistryIndexElement indexElement = getNamespaceIndex(commonExptsNamespace); indexElement.updateExtensionPoints(contribExtensionPoints, added); } else { for (int i = 0; i < contribExtensionPoints.length; i++) { String namespaceName = ((ExtensionPoint) extensionPointObjects[i]).getNamespace(); RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); indexElement.updateExtensionPoint(contribExtensionPoints[i], added); } } // if all extensions are from the same namespace combine them in one block and add them all together int[] contrExtensions = contribution.getExtensions(); RegistryObject[] extensionObjects = getObjects(contrExtensions, EXTENSION); String commonExtNamespace = null; if (contrExtensions.length > 1) commonExtNamespace = findCommonNamespaceIdentifier(extensionObjects); if (commonExtNamespace != null) { RegistryIndexElement indexElement = getNamespaceIndex(commonExtNamespace); indexElement.updateExtensions(contrExtensions, added); } else { for (int i = 0; i < contrExtensions.length; i++) { String namespaceName = ((Extension) extensionObjects[i]).getNamespaceIdentifier(); RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); indexElement.updateExtension(contrExtensions[i], added); } } } synchronized int[] getExtensionPointsFrom(String id) { KeyedElement tmp = newContributions.getByKey(id); if (tmp == null) tmp = getFormerContributions().getByKey(id); if (tmp == null) return EMPTY_INT_ARRAY; return ((Contribution) tmp).getExtensionPoints(); } synchronized boolean hasContribution(String id) { Object result = newContributions.getByKey(id); if (result == null) result = getFormerContributions().getByKey(id); return result != null; } private KeyedHashSet getFormerContributions() { KeyedHashSet result; if (fromCache == false) return new KeyedHashSet(0); if (formerContributions == null || (result = ((KeyedHashSet) ((formerContributions instanceof SoftReference) ? ((SoftReference<?>) formerContributions).get() : formerContributions))) == null) { result = registry.getTableReader().loadContributions(); formerContributions = new SoftReference<>(result); } return result; } synchronized public void add(RegistryObject registryObject, boolean hold) { if (registryObject.getObjectId() == UNKNOWN) { int id = nextId++; registryObject.setObjectId(id); } cache.put(registryObject.getObjectId(), registryObject); if (hold) hold(registryObject); } private void remove(RegistryObject registryObject, boolean release) { cache.remove(registryObject.getObjectId()); if (release) release(registryObject); } synchronized void remove(int id, boolean release) { RegistryObject toRemove = (RegistryObject) cache.get(id); if (fileOffsets != null) fileOffsets.removeKey(id); if (toRemove != null) remove(toRemove, release); } private void hold(RegistryObject toHold) { heldObjects.add(toHold); } private void release(RegistryObject toRelease) { heldObjects.remove(toRelease); } @Override public synchronized Object getObject(int id, byte type) { return basicGetObject(id, type); } private Object basicGetObject(int id, byte type) { Object result = cache.get(id); if (result != null) return result; if (fromCache) result = load(id, type); if (result == null) throw new InvalidRegistryObjectException(); cache.put(id, result); return result; } // The current impementation of this method assumes that we don't cache dynamic // extension. In this case all extensions not yet loaded (i.e. not in the memory cache) // are "not dynamic" and we actually check memory objects to see if they are dynamic. // // If we decide to allow caching of dynamic objects, the implementation // of this method would have to retrieved the object from disk and check // its "dynamic" status. The problem is that id alone is not enough to get the object // from the disk; object type is needed as well. public boolean shouldPersist(int id) { Object result = cache.get(id); if (result != null) return ((RegistryObject) result).shouldPersist(); return true; } @Override public synchronized RegistryObject[] getObjects(int[] values, byte type) { if (values.length == 0) { switch (type) { case EXTENSION_POINT : return ExtensionPoint.EMPTY_ARRAY; case EXTENSION : return Extension.EMPTY_ARRAY; case CONFIGURATION_ELEMENT : case THIRDLEVEL_CONFIGURATION_ELEMENT : return ConfigurationElement.EMPTY_ARRAY; } } RegistryObject[] results = null; switch (type) { case EXTENSION_POINT : results = new ExtensionPoint[values.length]; break; case EXTENSION : results = new Extension[values.length]; break; case CONFIGURATION_ELEMENT : case THIRDLEVEL_CONFIGURATION_ELEMENT : results = new ConfigurationElement[values.length]; break; } for (int i = 0; i < values.length; i++) { results[i] = (RegistryObject) basicGetObject(values[i], type); } return results; } synchronized ExtensionPoint getExtensionPointObject(String xptUniqueId) { int id; if ((id = extensionPoints.get(xptUniqueId)) == HashtableOfStringAndInt.MISSING_ELEMENT) return null; return (ExtensionPoint) getObject(id, EXTENSION_POINT); } @Override public Handle getHandle(int id, byte type) { switch (type) { case EXTENSION_POINT : return new ExtensionPointHandle(this, id); case EXTENSION : return new ExtensionHandle(this, id); case CONFIGURATION_ELEMENT : return new ConfigurationElementHandle(this, id); case THIRDLEVEL_CONFIGURATION_ELEMENT : default : //avoid compiler error, type should always be known return new ThirdLevelConfigurationElementHandle(this, id); } } @Override public Handle[] getHandles(int[] ids, byte type) { Handle[] results = null; int nbrId = ids.length; switch (type) { case EXTENSION_POINT : if (nbrId == 0) return ExtensionPointHandle.EMPTY_ARRAY; results = new ExtensionPointHandle[nbrId]; for (int i = 0; i < nbrId; i++) { results[i] = new ExtensionPointHandle(this, ids[i]); } break; case EXTENSION : if (nbrId == 0) return ExtensionHandle.EMPTY_ARRAY; results = new ExtensionHandle[nbrId]; for (int i = 0; i < nbrId; i++) { results[i] = new ExtensionHandle(this, ids[i]); } break; case CONFIGURATION_ELEMENT : if (nbrId == 0) return ConfigurationElementHandle.EMPTY_ARRAY; results = new ConfigurationElementHandle[nbrId]; for (int i = 0; i < nbrId; i++) { results[i] = new ConfigurationElementHandle(this, ids[i]); } break; case THIRDLEVEL_CONFIGURATION_ELEMENT : if (nbrId == 0) return ConfigurationElementHandle.EMPTY_ARRAY; results = new ThirdLevelConfigurationElementHandle[nbrId]; for (int i = 0; i < nbrId; i++) { results[i] = new ThirdLevelConfigurationElementHandle(this, ids[i]); } break; } return results; } synchronized ExtensionPointHandle[] getExtensionPointsHandles() { return (ExtensionPointHandle[]) getHandles(extensionPoints.getValues(), EXTENSION_POINT); } synchronized ExtensionPointHandle getExtensionPointHandle(String xptUniqueId) { int id = extensionPoints.get(xptUniqueId); if (id == HashtableOfStringAndInt.MISSING_ELEMENT) return null; return (ExtensionPointHandle) getHandle(id, EXTENSION_POINT); } private Object load(int id, byte type) { TableReader reader = registry.getTableReader(); if (fileOffsets == null) return null; int offset = fileOffsets.get(id); if (offset == Integer.MIN_VALUE) return null; switch (type) { case CONFIGURATION_ELEMENT : return reader.loadConfigurationElement(offset); case THIRDLEVEL_CONFIGURATION_ELEMENT : return reader.loadThirdLevelConfigurationElements(offset, this); case EXTENSION : return reader.loadExtension(offset); case EXTENSION_POINT : default : //avoid compile errors. type must always be known return reader.loadExtensionPointTree(offset, this); } } synchronized int[] getExtensionsFrom(String contributorId) { KeyedElement tmp = newContributions.getByKey(contributorId); if (tmp == null) tmp = getFormerContributions().getByKey(contributorId); if (tmp == null) return EMPTY_INT_ARRAY; return ((Contribution) tmp).getExtensions(); } synchronized boolean addExtensionPoint(ExtensionPoint currentExtPoint, boolean hold) { String uniqueId = currentExtPoint.getUniqueIdentifier(); if (extensionPoints.get(uniqueId) != HashtableOfStringAndInt.MISSING_ELEMENT) return false; add(currentExtPoint, hold); extensionPoints.put(uniqueId, currentExtPoint.getObjectId()); return true; } synchronized void removeExtensionPoint(String extensionPointId) { int pointId = extensionPoints.removeKey(extensionPointId); if (pointId == HashtableOfStringAndInt.MISSING_ELEMENT) return; remove(pointId, true); } public boolean isDirty() { return isDirty; } public void markDirty() { isDirty = true; } synchronized void removeContribution(Object contributorId) { boolean removed = newContributions.removeByKey(contributorId); if (removed == false) { removed = getFormerContributions().removeByKey(contributorId); if (removed) formerContributions = getFormerContributions(); //This forces the removed namespace to stay around, so we do not forget about removed namespaces } if (removed) { isDirty = true; return; } } @SuppressWarnings("unchecked") private Map<String, int[]> getOrphans() { Object result; if (orphanExtensions == null && !fromCache) { result = new HashMap<>(); orphanExtensions = result; } else if (orphanExtensions == null || (result = ((orphanExtensions instanceof SoftReference) ? ((SoftReference<?>) orphanExtensions).get() : orphanExtensions)) == null) { result = registry.getTableReader().loadOrphans(); orphanExtensions = new SoftReference<>(result); } return (HashMap<String, int[]>) result; } void addOrphans(String extensionPoint, int[] extensions) { Map<String, int[]> orphans = getOrphans(); int[] existingOrphanExtensions = orphans.get(extensionPoint); if (existingOrphanExtensions != null) { // just add int[] newOrphanExtensions = new int[existingOrphanExtensions.length + extensions.length]; System.arraycopy(existingOrphanExtensions, 0, newOrphanExtensions, 0, existingOrphanExtensions.length); System.arraycopy(extensions, 0, newOrphanExtensions, existingOrphanExtensions.length, extensions.length); orphans.put(extensionPoint, newOrphanExtensions); } else { // otherwise this is the first one orphans.put(extensionPoint, extensions); } markOrphansHasDirty(orphans); } void markOrphansHasDirty(Map<String, int[]> orphans) { orphanExtensions = orphans; } void addOrphan(String extensionPoint, int extension) { Map<String, int[]> orphans = getOrphans(); int[] existingOrphanExtensions = orphans.get(extensionPoint); if (existingOrphanExtensions != null) { // just add int[] newOrphanExtensions = new int[existingOrphanExtensions.length + 1]; System.arraycopy(existingOrphanExtensions, 0, newOrphanExtensions, 0, existingOrphanExtensions.length); newOrphanExtensions[existingOrphanExtensions.length] = extension; orphans.put(extensionPoint, newOrphanExtensions); } else { // otherwise this is the first one orphans.put(extensionPoint, new int[] {extension}); } markOrphansHasDirty(orphans); } int[] removeOrphans(String extensionPoint) { Map<String, int[]> orphans = getOrphans(); int[] existingOrphanExtensions = orphans.remove(extensionPoint); if (existingOrphanExtensions != null) { markOrphansHasDirty(orphans); } return existingOrphanExtensions; } void removeOrphan(String extensionPoint, int extension) { Map<String, int[]> orphans = getOrphans(); int[] existingOrphanExtensions = orphans.get(extensionPoint); if (existingOrphanExtensions == null) return; markOrphansHasDirty(orphans); int newSize = existingOrphanExtensions.length - 1; if (newSize == 0) { orphans.remove(extensionPoint); return; } int[] newOrphanExtensions = new int[existingOrphanExtensions.length - 1]; for (int i = 0, j = 0; i < existingOrphanExtensions.length; i++) if (extension != existingOrphanExtensions[i]) newOrphanExtensions[j++] = existingOrphanExtensions[i]; orphans.put(extensionPoint, newOrphanExtensions); return; } //This method is only used by the writer to reach in Map<String, int[]> getOrphanExtensions() { return getOrphans(); } // This method is only used by the writer to reach in int getNextId() { return nextId; } // This method is only used by the writer to reach in HashtableOfStringAndInt getExtensionPoints() { return extensionPoints; } // This method is only used by the writer to reach in KeyedHashSet[] getContributions() { return new KeyedHashSet[] {newContributions, getFormerContributions()}; } // This method is used internally and by the writer to reach in. Notice that it doesn't // return contributors marked as removed. HashMap<String, RegistryContributor> getContributors() { if (contributors == null) { if (fromCache == false) contributors = new HashMap<>(); else contributors = registry.getTableReader().loadContributors(); } return contributors; } synchronized IContributor[] getContributorsSync() { Collection<RegistryContributor> contributorValues = getContributors().values(); return contributorValues.toArray(new IContributor[contributorValues.size()]); } synchronized RegistryContributor getContributor(String id) { RegistryContributor contributor = getContributors().get(id); if (contributor != null) return contributor; // check if we have it among removed contributors - potentially // notification of removals might be processed after the contributor // marked as removed: if (removedContributors != null) return removedContributors.get(id); return null; } // only adds a contributor if it is not already present in the table synchronized void addContributor(RegistryContributor newContributor) { String key = newContributor.getActualId(); if (!getContributors().containsKey(key)) { isDirty = true; if (removedContributors != null) removedContributors.remove(key); getContributors().put(key, newContributor); } } synchronized void removeContributor(String id) { isDirty = true; RegistryContributor removed = getContributors().remove(id); if (removed != null) { if (removedContributors == null) removedContributors = new HashMap<>(); removedContributors.put(id, removed); } } KeyedHashSet getNamespacesIndex() { if (namespacesIndex == null) { if (fromCache == false) namespacesIndex = new KeyedHashSet(0); else namespacesIndex = registry.getTableReader().loadNamespaces(); } return namespacesIndex; } // Find or create required index element private RegistryIndexElement getNamespaceIndex(String namespaceName) { RegistryIndexElement indexElement = (RegistryIndexElement) getNamespacesIndex().getByKey(namespaceName); if (indexElement == null) { indexElement = new RegistryIndexElement(namespaceName); namespacesIndex.add(indexElement); } return indexElement; }
Collect all the objects that are removed by this operation and store them in a IObjectManager so that they can be accessed from the appropriate deltas but not from the registry.
/** * Collect all the objects that are removed by this operation and store * them in a IObjectManager so that they can be accessed from the appropriate * deltas but not from the registry. */
synchronized Map<Integer, RegistryObject> getAssociatedObjects(String contributionId) { //Collect all the objects associated with this contribution int[] xpts = getExtensionPointsFrom(contributionId); int[] exts = getExtensionsFrom(contributionId); Map<Integer, RegistryObject> actualObjects = new HashMap<>(xpts.length + exts.length); for (int i = 0; i < exts.length; i++) { Extension tmp = (Extension) basicGetObject(exts[i], RegistryObjectManager.EXTENSION); actualObjects.put(Integer.valueOf(exts[i]), tmp); collectChildren(tmp, 0, actualObjects); } for (int i = 0; i < xpts.length; i++) { ExtensionPoint xpt = (ExtensionPoint) basicGetObject(xpts[i], RegistryObjectManager.EXTENSION_POINT); actualObjects.put(Integer.valueOf(xpts[i]), xpt); } return actualObjects; }
Adds elements to be removed along with the registry object.
/** * Adds elements to be removed along with the registry object. */
synchronized void addAssociatedObjects(Map<Integer, RegistryObject> map, RegistryObject registryObject) { collectChildren(registryObject, 0, map); }
Add to the set of the objects all extensions and extension points that could be navigated to from the objects in the set.
/** * Add to the set of the objects all extensions and extension points that * could be navigated to from the objects in the set. */
synchronized void addNavigableObjects(Map<Integer, RegistryObject> associatedObjects) { Map<Integer, RegistryObject> result = new HashMap<>(); for (RegistryObject object : associatedObjects.values()) { if (object instanceof Extension) { // add extension point ExtensionPoint extPoint = getExtensionPointObject(((Extension) object).getExtensionPointIdentifier()); if (extPoint == null) // already removed? continue; Integer extPointIndex = Integer.valueOf(extPoint.getKeyHashCode()); if (!associatedObjects.containsKey(extPointIndex)) result.put(Integer.valueOf(extPoint.getKeyHashCode()), extPoint); // add all extensions for the extension point int[] extensions = extPoint.getRawChildren(); for (int j = 0; j < extensions.length; j++) { Extension tmp = (Extension) basicGetObject(extensions[j], RegistryObjectManager.EXTENSION); if (tmp == null) // already removed continue; Integer extensionIndex = Integer.valueOf(extensions[j]); if (!associatedObjects.containsKey(extensionIndex)) { result.put(extensionIndex, tmp); collectChildren(tmp, 0, result); } } } else if (object instanceof ExtensionPoint) { // by now extensions of this extension point have been marked as orphans Map<String, int[]> orphans = getOrphans(); String name = ((ExtensionPoint) object).getUniqueIdentifier(); int[] extensions = orphans.get(name); if (extensions != null) { for (int j = 0; j < extensions.length; j++) { Extension tmp = (Extension) basicGetObject(extensions[j], RegistryObjectManager.EXTENSION); if (tmp == null) // already removed continue; Integer extensionIndex = Integer.valueOf(extensions[j]); if (!associatedObjects.containsKey(extensionIndex)) { result.put(extensionIndex, tmp); collectChildren(tmp, 0, result); } } } } } associatedObjects.putAll(result); } synchronized void removeObjects(Map<?, ?> associatedObjects) { //Remove the objects from the main object manager so they can no longer be accessed. Collection<?> allValues = associatedObjects.values(); for (Iterator<?> iter = allValues.iterator(); iter.hasNext();) { RegistryObject toRemove = (RegistryObject) iter.next(); remove((toRemove).getObjectId(), true); if (toRemove instanceof ExtensionPoint) removeExtensionPoint(((ExtensionPoint) toRemove).getUniqueIdentifier()); } } IObjectManager createDelegatingObjectManager(Map<?, ?> object) { return new TemporaryObjectManager(object, this); } private void collectChildren(RegistryObject ce, int level, Map<Integer, RegistryObject> collector) { ConfigurationElement[] children = (ConfigurationElement[]) getObjects(ce.getRawChildren(), level == 0 || ce.noExtraData() ? RegistryObjectManager.CONFIGURATION_ELEMENT : RegistryObjectManager.THIRDLEVEL_CONFIGURATION_ELEMENT); for (ConfigurationElement child : children) { collector.put(Integer.valueOf(child.getObjectId()), child); collectChildren(child, level + 1, collector); } } @Override public void close() { //do nothing. } public ExtensionRegistry getRegistry() { return registry; } // Called from a synchronized method only private boolean unlinkChildFromContributions(KeyedElement[] contributions, int id) { for (KeyedElement contribution : contributions) { Contribution candidate = (Contribution) contribution; if (candidate == null) continue; if (candidate.hasChild(id)) { candidate.unlinkChild(id); if (candidate.isEmpty()) removeContribution(candidate.getContributorId()); return true; } } return false; } synchronized boolean unlinkChildFromContributions(int id) { if (unlinkChildFromContributions(newContributions.elements, id)) return true; return unlinkChildFromContributions(getFormerContributions().elements, id); } synchronized public ExtensionPointHandle[] getExtensionPointsFromNamespace(String namespaceName) { RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); int[] namespaceExtensionPoints = indexElement.getExtensionPoints(); return (ExtensionPointHandle[]) getHandles(namespaceExtensionPoints, EXTENSION_POINT); } // This method filters out extensions with no extension point synchronized public ExtensionHandle[] getExtensionsFromNamespace(String namespaceName) { RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); int[] namespaceExtensions = indexElement.getExtensions(); // filter extensions with no extension point (orphan extensions) List<Handle> tmp = new ArrayList<>(); Extension[] exts = (Extension[]) getObjects(namespaceExtensions, EXTENSION); for (Extension ext : exts) { if (getExtensionPointObject(ext.getExtensionPointIdentifier()) != null) { tmp.add(getHandle(ext.getObjectId(), EXTENSION)); } } if (tmp.size() == 0) return EMPTY_EXTENSIONS_ARRAY; ExtensionHandle[] result = new ExtensionHandle[tmp.size()]; return tmp.toArray(result); } public ExtensionHandle[] getExtensionsFromContributor(String contributorId) { int[] ids = getExtensionsFrom(contributorId); // never null return (ExtensionHandle[]) getHandles(ids, RegistryObjectManager.EXTENSION); } public ExtensionPointHandle[] getExtensionPointsFromContributor(String contributorId) { int[] ids = getExtensionPointsFrom(contributorId); // never null return (ExtensionPointHandle[]) getHandles(ids, RegistryObjectManager.EXTENSION_POINT); } }