| /******************************************************************************* |
| * Copyright (c) 2004, 2012 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.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. |
| */ |
| 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 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 contributors; // key: contributor ID; value: contributor name |
| private HashMap 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 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 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 |
| */ |
| 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 (int i = 0; i < registryObjects.length; i++) { |
| RegistryObject currentObject = registryObjects[i]; |
| 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; |
| } |
| |
| } |
| |
| private Map getOrphans() { |
| Object result = orphanExtensions; |
| 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) result; |
| } |
| |
| void addOrphans(String extensionPoint, int[] extensions) { |
| Map orphans = getOrphans(); |
| int[] existingOrphanExtensions = (int[]) 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 orphans) { |
| orphanExtensions = orphans; |
| } |
| |
| void addOrphan(String extensionPoint, int extension) { |
| Map orphans = getOrphans(); |
| int[] existingOrphanExtensions = (int[]) 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 orphans = getOrphans(); |
| int[] existingOrphanExtensions = (int[]) orphans.remove(extensionPoint); |
| if (existingOrphanExtensions != null) { |
| markOrphansHasDirty(orphans); |
| } |
| return existingOrphanExtensions; |
| } |
| |
| void removeOrphan(String extensionPoint, int extension) { |
| Map orphans = getOrphans(); |
| int[] existingOrphanExtensions = (int[]) 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 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 getContributors() { |
| if (contributors == null) { |
| if (fromCache == false) |
| contributors = new HashMap(); |
| else |
| contributors = registry.getTableReader().loadContributors(); |
| } |
| return contributors; |
| } |
| |
| synchronized IContributor[] getContributorsSync() { |
| Collection contributorValues = getContributors().values(); |
| return (IContributor[]) contributorValues.toArray(new IContributor[contributorValues.size()]); |
| } |
| |
| synchronized RegistryContributor getContributor(String id) { |
| RegistryContributor contributor = (RegistryContributor) 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 (RegistryContributor) 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 = (RegistryContributor) 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. |
| */ |
| synchronized Map getAssociatedObjects(String contributionId) { |
| //Collect all the objects associated with this contribution |
| int[] xpts = getExtensionPointsFrom(contributionId); |
| int[] exts = getExtensionsFrom(contributionId); |
| Map 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(new Integer(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(new Integer(xpts[i]), xpt); |
| } |
| |
| return actualObjects; |
| } |
| |
| /** |
| * Adds elements to be removed along with the registry object. |
| */ |
| synchronized void addAssociatedObjects(Map 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. |
| */ |
| synchronized void addNavigableObjects(Map associatedObjects) { |
| Map result = new HashMap(); |
| for (Iterator iter = associatedObjects.values().iterator(); iter.hasNext();) { |
| RegistryObject object = (RegistryObject) iter.next(); |
| if (object instanceof Extension) { |
| // add extension point |
| ExtensionPoint extPoint = getExtensionPointObject(((Extension) object).getExtensionPointIdentifier()); |
| if (extPoint == null) // already removed? |
| continue; |
| |
| Integer extPointIndex = new Integer(extPoint.getKeyHashCode()); |
| if (!associatedObjects.containsKey(extPointIndex)) |
| result.put(new Integer(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 = new Integer(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 orphans = getOrphans(); |
| String name = ((ExtensionPoint) object).getUniqueIdentifier(); |
| int[] extensions = (int[]) 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 = new Integer(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 collector) { |
| ConfigurationElement[] children = (ConfigurationElement[]) getObjects(ce.getRawChildren(), level == 0 || ce.noExtraData() ? RegistryObjectManager.CONFIGURATION_ELEMENT : RegistryObjectManager.THIRDLEVEL_CONFIGURATION_ELEMENT); |
| for (int j = 0; j < children.length; j++) { |
| collector.put(new Integer(children[j].getObjectId()), children[j]); |
| collectChildren(children[j], 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 (int i = 0; i < contributions.length; i++) { |
| Contribution candidate = (Contribution) contributions[i]; |
| 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 tmp = new ArrayList(); |
| Extension[] exts = (Extension[]) getObjects(namespaceExtensions, EXTENSION); |
| for (int i = 0; i < exts.length; i++) { |
| if (getExtensionPointObject(exts[i].getExtensionPointIdentifier()) != null) |
| tmp.add(getHandle(exts[i].getObjectId(), EXTENSION)); |
| } |
| if (tmp.size() == 0) |
| return EMPTY_EXTENSIONS_ARRAY; |
| ExtensionHandle[] result = new ExtensionHandle[tmp.size()]; |
| return (ExtensionHandle[]) 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); |
| } |
| } |