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;
public class RegistryObjectManager implements IObjectManager {
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;
static final float DEFAULT_LOADFACTOR = 0.75f;
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;
private HashtableOfStringAndInt extensionPoints;
private ReferenceMap cache;
private OffsetTable fileOffsets = null;
private int nextId = 1;
private final KeyedHashSet newContributions;
private Object formerContributions;
private HashMap<String, RegistryContributor> contributors;
private HashMap<String, RegistryContributor> removedContributors;
private KeyedHashSet namespacesIndex;
private Object orphanExtensions;
private final KeyedHashSet heldObjects = new KeyedHashSet();
private boolean isDirty = false;
private boolean fromCache = false;
private final ExtensionRegistry registry;
public static final String PROP_NO_REGISTRY_FLUSHING = "eclipse.noRegistryFlushing";
public RegistryObjectManager(ExtensionRegistry registry) {
extensionPoints = new HashtableOfStringAndInt();
if ("true".equalsIgnoreCase(RegistryProperties.getProperty(PROP_NO_REGISTRY_FLUSHING))) {
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;
}
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()) {
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) {
removeContribution(Id);
newContributions.add(existingContribution);
} else
existingContribution = newContributions.getByKey(Id);
if (existingContribution != null)
((Contribution) existingContribution).mergeContribution(contribution);
else
newContributions.add(contribution);
updateNamespaceIndex(contribution, true);
}
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);
}
private void updateNamespaceIndex(Contribution contribution, boolean added) {
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);
}
}
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;
}
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 :
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 :
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();
}
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) {
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 {
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) {
int[] newOrphanExtensions = new int[existingOrphanExtensions.length + 1];
System.arraycopy(existingOrphanExtensions, 0, newOrphanExtensions, 0, existingOrphanExtensions.length);
newOrphanExtensions[existingOrphanExtensions.length] = extension;
orphans.put(extensionPoint, newOrphanExtensions);
} else {
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;
}
Map<String, int[]> getOrphanExtensions() {
return getOrphans();
}
int getNextId() {
return nextId;
}
HashtableOfStringAndInt getExtensionPoints() {
return extensionPoints;
}
KeyedHashSet[] getContributions() {
return new KeyedHashSet[] {newContributions, getFormerContributions()};
}
HashMap<String, RegistryContributor> getContributors() {
if (contributors == null) {
if (fromCache == false)
contributors = new HashMap<>();
else
contributors = registry.getTableReader().loadContributors();
}
return contributors;
}
synchronized IContributor[] () {
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;
if (removedContributors != null)
return removedContributors.get(id);
return null;
}
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;
}
private RegistryIndexElement getNamespaceIndex(String namespaceName) {
RegistryIndexElement indexElement = (RegistryIndexElement) getNamespacesIndex().getByKey(namespaceName);
if (indexElement == null) {
indexElement = new RegistryIndexElement(namespaceName);
namespacesIndex.add(indexElement);
}
return indexElement;
}
synchronized Map<Integer, RegistryObject> getAssociatedObjects(String contributionId) {
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;
}
synchronized void addAssociatedObjects(Map<Integer, RegistryObject> map, RegistryObject registryObject) {
collectChildren(registryObject, 0, map);
}
synchronized void addNavigableObjects(Map<Integer, RegistryObject> associatedObjects) {
Map<Integer, RegistryObject> result = new HashMap<>();
for (RegistryObject object : associatedObjects.values()) {
if (object instanceof Extension) {
ExtensionPoint extPoint = getExtensionPointObject(((Extension) object).getExtensionPointIdentifier());
if (extPoint == null)
continue;
Integer extPointIndex = Integer.valueOf(extPoint.getKeyHashCode());
if (!associatedObjects.containsKey(extPointIndex))
result.put(Integer.valueOf(extPoint.getKeyHashCode()), extPoint);
int[] extensions = extPoint.getRawChildren();
for (int j = 0; j < extensions.length; j++) {
Extension tmp = (Extension) basicGetObject(extensions[j], RegistryObjectManager.EXTENSION);
if (tmp == null)
continue;
Integer extensionIndex = Integer.valueOf(extensions[j]);
if (!associatedObjects.containsKey(extensionIndex)) {
result.put(extensionIndex, tmp);
collectChildren(tmp, 0, result);
}
}
} else if (object instanceof ExtensionPoint) {
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)
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) {
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() {
}
public ExtensionRegistry getRegistry() {
return registry;
}
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);
}
synchronized public ExtensionHandle[] getExtensionsFromNamespace(String namespaceName) {
RegistryIndexElement indexElement = getNamespaceIndex(namespaceName);
int[] namespaceExtensions = indexElement.getExtensions();
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);
return (ExtensionHandle[]) getHandles(ids, RegistryObjectManager.EXTENSION);
}
public ExtensionPointHandle[] getExtensionPointsFromContributor(String contributorId) {
int[] ids = getExtensionPointsFrom(contributorId);
return (ExtensionPointHandle[]) getHandles(ids, RegistryObjectManager.EXTENSION_POINT);
}
}