blob: f772d94c26b3e8979feefc03cfb182cb044b85a3 [file] [log] [blame]
/*******************************************************************************
* 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);
}
}