1 /******************************************************************************* 2 * Copyright (c) 2004, 2018 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.core.internal.registry; 15 16 import java.lang.ref.SoftReference; 17 import java.util.*; 18 import org.eclipse.core.runtime.IContributor; 19 import org.eclipse.core.runtime.InvalidRegistryObjectException; 20 import org.eclipse.core.runtime.spi.RegistryContributor; 21 22 /** 23 * This class manage all the object from the registry but does not deal with their dependencies. 24 * It serves the objects which are either directly obtained from memory or read from a cache. 25 * It also returns handles for objects. 26 */ 27 public class RegistryObjectManager implements IObjectManager { 28 //Constants used to get the objects and their handles 29 static public final byte CONFIGURATION_ELEMENT = 1; 30 static public final byte EXTENSION = 2; 31 static public final byte EXTENSION_POINT = 3; 32 static public final byte THIRDLEVEL_CONFIGURATION_ELEMENT = 4; 33 34 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). 35 static final float DEFAULT_LOADFACTOR = 0.75f; //This is the default factor used in reference map. 36 37 static final int[] EMPTY_INT_ARRAY = new int[0]; 38 static final String[] EMPTY_STRING_ARRAY = new String[0]; 39 40 static final ExtensionHandle[] EMPTY_EXTENSIONS_ARRAY = new ExtensionHandle[0]; 41 42 static int UNKNOWN = -1; 43 44 // key: extensionPointName, value: object id 45 private HashtableOfStringAndInt extensionPoints; //This is loaded on startup. Then entries can be added when loading a new plugin from the xml. 46 // key: object id, value: an object 47 private ReferenceMap cache; //Entries are added by getter. The structure is not thread safe. 48 //key: int, value: int 49 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. 50 51 private int nextId = 1; //This is only used to get the next number available. 52 53 //Those two data structures are only used when the addition or the removal of a plugin occurs. 54 //They are used to keep track on a contributor basis of the extension being added or removed 55 private final KeyedHashSet newContributions; //represents the contributers added during this session. 56 private Object formerContributions; //represents the contributers encountered in previous sessions. This is loaded lazily. 57 58 private HashMap<String, RegistryContributor> contributors; // key: contributor ID; value: contributor name 59 private HashMap<String, RegistryContributor> removedContributors; // key: contributor ID; value: contributor name 60 private KeyedHashSet namespacesIndex; // registry elements (extension & extensionpoints) indexed by namespaces 61 62 // Map key: extensionPointFullyQualifiedName, value int[] of orphan extensions. 63 // The orphan access does not need to be synchronized because the it is protected by the lock in extension registry. 64 private Object orphanExtensions; 65 66 private final KeyedHashSet heldObjects = new KeyedHashSet(); //strong reference to the objects that must be hold on to 67 68 //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) 69 private boolean isDirty = false; 70 71 private boolean fromCache = false; 72 73 private final ExtensionRegistry registry; 74 75 // TODO this option is not used 76 // OSGI system properties. Copied from EclipseStarter 77 public static final String PROP_NO_REGISTRY_FLUSHING = "eclipse.noRegistryFlushing"; //$NON-NLS-1$ 78 RegistryObjectManager(ExtensionRegistry registry)79 public RegistryObjectManager(ExtensionRegistry registry) { 80 extensionPoints = new HashtableOfStringAndInt(); 81 if ("true".equalsIgnoreCase(RegistryProperties.getProperty(PROP_NO_REGISTRY_FLUSHING))) { //$NON-NLS-1$ 82 cache = new ReferenceMap(ReferenceMap.HARD, CACHE_INITIAL_SIZE, DEFAULT_LOADFACTOR); 83 } else { 84 cache = new ReferenceMap(ReferenceMap.SOFT, CACHE_INITIAL_SIZE, DEFAULT_LOADFACTOR); 85 } 86 newContributions = new KeyedHashSet(); 87 88 this.registry = registry; 89 } 90 91 /** 92 * Initialize the object manager. Return true if the initialization succeeded, false otherwise 93 */ init(long timeStamp)94 synchronized boolean init(long timeStamp) { 95 TableReader reader = registry.getTableReader(); 96 Object[] results = reader.loadTables(timeStamp); 97 if (results == null) { 98 return false; 99 } 100 fileOffsets = (OffsetTable) results[0]; 101 extensionPoints = (HashtableOfStringAndInt) results[1]; 102 nextId = ((Integer) results[2]).intValue(); 103 fromCache = true; 104 105 if (!registry.useLazyCacheLoading()) { 106 //TODO Here we could grow all the tables to the right size (ReferenceMap) 107 reader.setHoldObjects(true); 108 markOrphansHasDirty(getOrphans()); 109 fromCache = reader.readAllCache(this); 110 formerContributions = getFormerContributions(); 111 } 112 return fromCache; 113 } 114 addContribution(Contribution contribution)115 synchronized void addContribution(Contribution contribution) { 116 isDirty = true; 117 Object Id = contribution.getKey(); 118 119 KeyedElement existingContribution = getFormerContributions().getByKey(Id); 120 if (existingContribution != null) { // move it from former to new contributions 121 removeContribution(Id); 122 newContributions.add(existingContribution); 123 } else 124 existingContribution = newContributions.getByKey(Id); 125 126 if (existingContribution != null) // merge 127 ((Contribution) existingContribution).mergeContribution(contribution); 128 else 129 newContributions.add(contribution); 130 131 updateNamespaceIndex(contribution, true); 132 } 133 134 // TODO make ExtensionPoint, Extension provide namespace in a same way (move it to the RegistryObject?) 135 // See if all the registryObjects have the same namespace. If not, return null. 136 // Also can return null if empty array is passed in or objects are of an unexpected type findCommonNamespaceIdentifier(RegistryObject[] registryObjects)137 private String findCommonNamespaceIdentifier(RegistryObject[] registryObjects) { 138 String namespaceName = null; 139 for (RegistryObject currentObject : registryObjects) { 140 String tmp = null; 141 if (currentObject instanceof ExtensionPoint) 142 tmp = ((ExtensionPoint) currentObject).getNamespace(); 143 else if (currentObject instanceof Extension) 144 tmp = ((Extension) currentObject).getNamespaceIdentifier(); 145 146 if (namespaceName == null) { 147 namespaceName = tmp; 148 continue; 149 } 150 if (!namespaceName.equals(tmp)) { 151 return null; 152 } 153 } 154 return namespaceName; 155 } 156 removeExtensionPointFromNamespaceIndex(int extensionPoint, String namespaceName)157 synchronized void removeExtensionPointFromNamespaceIndex(int extensionPoint, String namespaceName) { 158 RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); 159 indexElement.updateExtensionPoint(extensionPoint, false); 160 } 161 removeExtensionFromNamespaceIndex(int extensions, String namespaceName)162 synchronized void removeExtensionFromNamespaceIndex(int extensions, String namespaceName) { 163 RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); 164 indexElement.updateExtension(extensions, false); 165 } 166 167 // Called from a synchronized method updateNamespaceIndex(Contribution contribution, boolean added)168 private void updateNamespaceIndex(Contribution contribution, boolean added) { 169 // if all extension points are from the same namespace combine them in one block and add them all together 170 int[] contribExtensionPoints = contribution.getExtensionPoints(); 171 RegistryObject[] extensionPointObjects = getObjects(contribExtensionPoints, EXTENSION_POINT); 172 String commonExptsNamespace = null; 173 if (contribExtensionPoints.length > 1) 174 commonExptsNamespace = findCommonNamespaceIdentifier(extensionPointObjects); 175 if (commonExptsNamespace != null) { 176 RegistryIndexElement indexElement = getNamespaceIndex(commonExptsNamespace); 177 indexElement.updateExtensionPoints(contribExtensionPoints, added); 178 } else { 179 for (int i = 0; i < contribExtensionPoints.length; i++) { 180 String namespaceName = ((ExtensionPoint) extensionPointObjects[i]).getNamespace(); 181 RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); 182 indexElement.updateExtensionPoint(contribExtensionPoints[i], added); 183 } 184 } 185 186 // if all extensions are from the same namespace combine them in one block and add them all together 187 int[] contrExtensions = contribution.getExtensions(); 188 RegistryObject[] extensionObjects = getObjects(contrExtensions, EXTENSION); 189 String commonExtNamespace = null; 190 if (contrExtensions.length > 1) 191 commonExtNamespace = findCommonNamespaceIdentifier(extensionObjects); 192 if (commonExtNamespace != null) { 193 RegistryIndexElement indexElement = getNamespaceIndex(commonExtNamespace); 194 indexElement.updateExtensions(contrExtensions, added); 195 } else { 196 for (int i = 0; i < contrExtensions.length; i++) { 197 String namespaceName = ((Extension) extensionObjects[i]).getNamespaceIdentifier(); 198 RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); 199 indexElement.updateExtension(contrExtensions[i], added); 200 } 201 } 202 } 203 getExtensionPointsFrom(String id)204 synchronized int[] getExtensionPointsFrom(String id) { 205 KeyedElement tmp = newContributions.getByKey(id); 206 if (tmp == null) 207 tmp = getFormerContributions().getByKey(id); 208 if (tmp == null) 209 return EMPTY_INT_ARRAY; 210 return ((Contribution) tmp).getExtensionPoints(); 211 } 212 hasContribution(String id)213 synchronized boolean hasContribution(String id) { 214 Object result = newContributions.getByKey(id); 215 if (result == null) 216 result = getFormerContributions().getByKey(id); 217 return result != null; 218 } 219 getFormerContributions()220 private KeyedHashSet getFormerContributions() { 221 KeyedHashSet result; 222 if (fromCache == false) 223 return new KeyedHashSet(0); 224 225 if (formerContributions == null || (result = ((KeyedHashSet) ((formerContributions instanceof SoftReference) ? ((SoftReference<?>) formerContributions).get() : formerContributions))) == null) { 226 result = registry.getTableReader().loadContributions(); 227 formerContributions = new SoftReference<>(result); 228 } 229 return result; 230 } 231 add(RegistryObject registryObject, boolean hold)232 synchronized public void add(RegistryObject registryObject, boolean hold) { 233 if (registryObject.getObjectId() == UNKNOWN) { 234 int id = nextId++; 235 registryObject.setObjectId(id); 236 } 237 cache.put(registryObject.getObjectId(), registryObject); 238 if (hold) 239 hold(registryObject); 240 } 241 remove(RegistryObject registryObject, boolean release)242 private void remove(RegistryObject registryObject, boolean release) { 243 cache.remove(registryObject.getObjectId()); 244 if (release) 245 release(registryObject); 246 } 247 remove(int id, boolean release)248 synchronized void remove(int id, boolean release) { 249 RegistryObject toRemove = (RegistryObject) cache.get(id); 250 if (fileOffsets != null) 251 fileOffsets.removeKey(id); 252 if (toRemove != null) 253 remove(toRemove, release); 254 } 255 hold(RegistryObject toHold)256 private void hold(RegistryObject toHold) { 257 heldObjects.add(toHold); 258 } 259 release(RegistryObject toRelease)260 private void release(RegistryObject toRelease) { 261 heldObjects.remove(toRelease); 262 } 263 264 @Override getObject(int id, byte type)265 public synchronized Object getObject(int id, byte type) { 266 return basicGetObject(id, type); 267 } 268 basicGetObject(int id, byte type)269 private Object basicGetObject(int id, byte type) { 270 Object result = cache.get(id); 271 if (result != null) 272 return result; 273 if (fromCache) 274 result = load(id, type); 275 if (result == null) 276 throw new InvalidRegistryObjectException(); 277 cache.put(id, result); 278 return result; 279 } 280 281 // The current impementation of this method assumes that we don't cache dynamic 282 // extension. In this case all extensions not yet loaded (i.e. not in the memory cache) 283 // are "not dynamic" and we actually check memory objects to see if they are dynamic. 284 // 285 // If we decide to allow caching of dynamic objects, the implementation 286 // of this method would have to retrieved the object from disk and check 287 // its "dynamic" status. The problem is that id alone is not enough to get the object 288 // from the disk; object type is needed as well. shouldPersist(int id)289 public boolean shouldPersist(int id) { 290 Object result = cache.get(id); 291 if (result != null) 292 return ((RegistryObject) result).shouldPersist(); 293 return true; 294 } 295 296 @Override getObjects(int[] values, byte type)297 public synchronized RegistryObject[] getObjects(int[] values, byte type) { 298 if (values.length == 0) { 299 switch (type) { 300 case EXTENSION_POINT : 301 return ExtensionPoint.EMPTY_ARRAY; 302 case EXTENSION : 303 return Extension.EMPTY_ARRAY; 304 case CONFIGURATION_ELEMENT : 305 case THIRDLEVEL_CONFIGURATION_ELEMENT : 306 return ConfigurationElement.EMPTY_ARRAY; 307 } 308 } 309 310 RegistryObject[] results = null; 311 switch (type) { 312 case EXTENSION_POINT : 313 results = new ExtensionPoint[values.length]; 314 break; 315 case EXTENSION : 316 results = new Extension[values.length]; 317 break; 318 case CONFIGURATION_ELEMENT : 319 case THIRDLEVEL_CONFIGURATION_ELEMENT : 320 results = new ConfigurationElement[values.length]; 321 break; 322 } 323 for (int i = 0; i < values.length; i++) { 324 results[i] = (RegistryObject) basicGetObject(values[i], type); 325 } 326 return results; 327 } 328 getExtensionPointObject(String xptUniqueId)329 synchronized ExtensionPoint getExtensionPointObject(String xptUniqueId) { 330 int id; 331 if ((id = extensionPoints.get(xptUniqueId)) == HashtableOfStringAndInt.MISSING_ELEMENT) 332 return null; 333 return (ExtensionPoint) getObject(id, EXTENSION_POINT); 334 } 335 336 @Override getHandle(int id, byte type)337 public Handle getHandle(int id, byte type) { 338 switch (type) { 339 case EXTENSION_POINT : 340 return new ExtensionPointHandle(this, id); 341 342 case EXTENSION : 343 return new ExtensionHandle(this, id); 344 345 case CONFIGURATION_ELEMENT : 346 return new ConfigurationElementHandle(this, id); 347 348 case THIRDLEVEL_CONFIGURATION_ELEMENT : 349 default : //avoid compiler error, type should always be known 350 return new ThirdLevelConfigurationElementHandle(this, id); 351 } 352 } 353 354 @Override getHandles(int[] ids, byte type)355 public Handle[] getHandles(int[] ids, byte type) { 356 Handle[] results = null; 357 int nbrId = ids.length; 358 switch (type) { 359 case EXTENSION_POINT : 360 if (nbrId == 0) 361 return ExtensionPointHandle.EMPTY_ARRAY; 362 results = new ExtensionPointHandle[nbrId]; 363 for (int i = 0; i < nbrId; i++) { 364 results[i] = new ExtensionPointHandle(this, ids[i]); 365 } 366 break; 367 368 case EXTENSION : 369 if (nbrId == 0) 370 return ExtensionHandle.EMPTY_ARRAY; 371 results = new ExtensionHandle[nbrId]; 372 for (int i = 0; i < nbrId; i++) { 373 results[i] = new ExtensionHandle(this, ids[i]); 374 } 375 break; 376 377 case CONFIGURATION_ELEMENT : 378 if (nbrId == 0) 379 return ConfigurationElementHandle.EMPTY_ARRAY; 380 results = new ConfigurationElementHandle[nbrId]; 381 for (int i = 0; i < nbrId; i++) { 382 results[i] = new ConfigurationElementHandle(this, ids[i]); 383 } 384 break; 385 386 case THIRDLEVEL_CONFIGURATION_ELEMENT : 387 if (nbrId == 0) 388 return ConfigurationElementHandle.EMPTY_ARRAY; 389 results = new ThirdLevelConfigurationElementHandle[nbrId]; 390 for (int i = 0; i < nbrId; i++) { 391 results[i] = new ThirdLevelConfigurationElementHandle(this, ids[i]); 392 } 393 break; 394 } 395 return results; 396 } 397 getExtensionPointsHandles()398 synchronized ExtensionPointHandle[] getExtensionPointsHandles() { 399 return (ExtensionPointHandle[]) getHandles(extensionPoints.getValues(), EXTENSION_POINT); 400 } 401 getExtensionPointHandle(String xptUniqueId)402 synchronized ExtensionPointHandle getExtensionPointHandle(String xptUniqueId) { 403 int id = extensionPoints.get(xptUniqueId); 404 if (id == HashtableOfStringAndInt.MISSING_ELEMENT) 405 return null; 406 return (ExtensionPointHandle) getHandle(id, EXTENSION_POINT); 407 } 408 load(int id, byte type)409 private Object load(int id, byte type) { 410 TableReader reader = registry.getTableReader(); 411 if (fileOffsets == null) 412 return null; 413 int offset = fileOffsets.get(id); 414 if (offset == Integer.MIN_VALUE) 415 return null; 416 switch (type) { 417 case CONFIGURATION_ELEMENT : 418 return reader.loadConfigurationElement(offset); 419 420 case THIRDLEVEL_CONFIGURATION_ELEMENT : 421 return reader.loadThirdLevelConfigurationElements(offset, this); 422 423 case EXTENSION : 424 return reader.loadExtension(offset); 425 426 case EXTENSION_POINT : 427 default : //avoid compile errors. type must always be known 428 return reader.loadExtensionPointTree(offset, this); 429 } 430 } 431 getExtensionsFrom(String contributorId)432 synchronized int[] getExtensionsFrom(String contributorId) { 433 KeyedElement tmp = newContributions.getByKey(contributorId); 434 if (tmp == null) 435 tmp = getFormerContributions().getByKey(contributorId); 436 if (tmp == null) 437 return EMPTY_INT_ARRAY; 438 return ((Contribution) tmp).getExtensions(); 439 } 440 addExtensionPoint(ExtensionPoint currentExtPoint, boolean hold)441 synchronized boolean addExtensionPoint(ExtensionPoint currentExtPoint, boolean hold) { 442 String uniqueId = currentExtPoint.getUniqueIdentifier(); 443 if (extensionPoints.get(uniqueId) != HashtableOfStringAndInt.MISSING_ELEMENT) 444 return false; 445 add(currentExtPoint, hold); 446 extensionPoints.put(uniqueId, currentExtPoint.getObjectId()); 447 return true; 448 } 449 removeExtensionPoint(String extensionPointId)450 synchronized void removeExtensionPoint(String extensionPointId) { 451 int pointId = extensionPoints.removeKey(extensionPointId); 452 if (pointId == HashtableOfStringAndInt.MISSING_ELEMENT) 453 return; 454 remove(pointId, true); 455 } 456 isDirty()457 public boolean isDirty() { 458 return isDirty; 459 } 460 markDirty()461 public void markDirty() { 462 isDirty = true; 463 } 464 removeContribution(Object contributorId)465 synchronized void removeContribution(Object contributorId) { 466 boolean removed = newContributions.removeByKey(contributorId); 467 if (removed == false) { 468 removed = getFormerContributions().removeByKey(contributorId); 469 if (removed) 470 formerContributions = getFormerContributions(); //This forces the removed namespace to stay around, so we do not forget about removed namespaces 471 } 472 473 if (removed) { 474 isDirty = true; 475 return; 476 } 477 478 } 479 480 @SuppressWarnings("unchecked") getOrphans()481 private Map<String, int[]> getOrphans() { 482 Object result; 483 if (orphanExtensions == null && !fromCache) { 484 result = new HashMap<>(); 485 orphanExtensions = result; 486 } else if (orphanExtensions == null || (result = ((orphanExtensions instanceof SoftReference) ? ((SoftReference<?>) orphanExtensions).get() : orphanExtensions)) == null) { 487 result = registry.getTableReader().loadOrphans(); 488 orphanExtensions = new SoftReference<>(result); 489 } 490 return (HashMap<String, int[]>) result; 491 } 492 addOrphans(String extensionPoint, int[] extensions)493 void addOrphans(String extensionPoint, int[] extensions) { 494 Map<String, int[]> orphans = getOrphans(); 495 int[] existingOrphanExtensions = orphans.get(extensionPoint); 496 497 if (existingOrphanExtensions != null) { 498 // just add 499 int[] newOrphanExtensions = new int[existingOrphanExtensions.length + extensions.length]; 500 System.arraycopy(existingOrphanExtensions, 0, newOrphanExtensions, 0, existingOrphanExtensions.length); 501 System.arraycopy(extensions, 0, newOrphanExtensions, existingOrphanExtensions.length, extensions.length); 502 orphans.put(extensionPoint, newOrphanExtensions); 503 } else { 504 // otherwise this is the first one 505 orphans.put(extensionPoint, extensions); 506 } 507 markOrphansHasDirty(orphans); 508 } 509 markOrphansHasDirty(Map<String, int[]> orphans)510 void markOrphansHasDirty(Map<String, int[]> orphans) { 511 orphanExtensions = orphans; 512 } 513 addOrphan(String extensionPoint, int extension)514 void addOrphan(String extensionPoint, int extension) { 515 Map<String, int[]> orphans = getOrphans(); 516 int[] existingOrphanExtensions = orphans.get(extensionPoint); 517 518 if (existingOrphanExtensions != null) { 519 // just add 520 int[] newOrphanExtensions = new int[existingOrphanExtensions.length + 1]; 521 System.arraycopy(existingOrphanExtensions, 0, newOrphanExtensions, 0, existingOrphanExtensions.length); 522 newOrphanExtensions[existingOrphanExtensions.length] = extension; 523 orphans.put(extensionPoint, newOrphanExtensions); 524 } else { 525 // otherwise this is the first one 526 orphans.put(extensionPoint, new int[] {extension}); 527 } 528 markOrphansHasDirty(orphans); 529 } 530 removeOrphans(String extensionPoint)531 int[] removeOrphans(String extensionPoint) { 532 Map<String, int[]> orphans = getOrphans(); 533 int[] existingOrphanExtensions = orphans.remove(extensionPoint); 534 if (existingOrphanExtensions != null) { 535 markOrphansHasDirty(orphans); 536 } 537 return existingOrphanExtensions; 538 } 539 removeOrphan(String extensionPoint, int extension)540 void removeOrphan(String extensionPoint, int extension) { 541 Map<String, int[]> orphans = getOrphans(); 542 int[] existingOrphanExtensions = orphans.get(extensionPoint); 543 544 if (existingOrphanExtensions == null) 545 return; 546 547 markOrphansHasDirty(orphans); 548 int newSize = existingOrphanExtensions.length - 1; 549 if (newSize == 0) { 550 orphans.remove(extensionPoint); 551 return; 552 } 553 554 int[] newOrphanExtensions = new int[existingOrphanExtensions.length - 1]; 555 for (int i = 0, j = 0; i < existingOrphanExtensions.length; i++) 556 if (extension != existingOrphanExtensions[i]) 557 newOrphanExtensions[j++] = existingOrphanExtensions[i]; 558 559 orphans.put(extensionPoint, newOrphanExtensions); 560 return; 561 } 562 563 //This method is only used by the writer to reach in getOrphanExtensions()564 Map<String, int[]> getOrphanExtensions() { 565 return getOrphans(); 566 } 567 568 // This method is only used by the writer to reach in getNextId()569 int getNextId() { 570 return nextId; 571 } 572 573 // This method is only used by the writer to reach in getExtensionPoints()574 HashtableOfStringAndInt getExtensionPoints() { 575 return extensionPoints; 576 } 577 578 // This method is only used by the writer to reach in getContributions()579 KeyedHashSet[] getContributions() { 580 return new KeyedHashSet[] {newContributions, getFormerContributions()}; 581 } 582 583 // This method is used internally and by the writer to reach in. Notice that it doesn't 584 // return contributors marked as removed. getContributors()585 HashMap<String, RegistryContributor> getContributors() { 586 if (contributors == null) { 587 if (fromCache == false) 588 contributors = new HashMap<>(); 589 else 590 contributors = registry.getTableReader().loadContributors(); 591 } 592 return contributors; 593 } 594 getContributorsSync()595 synchronized IContributor[] getContributorsSync() { 596 Collection<RegistryContributor> contributorValues = getContributors().values(); 597 return contributorValues.toArray(new IContributor[contributorValues.size()]); 598 } 599 getContributor(String id)600 synchronized RegistryContributor getContributor(String id) { 601 RegistryContributor contributor = getContributors().get(id); 602 if (contributor != null) 603 return contributor; 604 // check if we have it among removed contributors - potentially 605 // notification of removals might be processed after the contributor 606 // marked as removed: 607 if (removedContributors != null) 608 return removedContributors.get(id); 609 return null; 610 } 611 612 // only adds a contributor if it is not already present in the table addContributor(RegistryContributor newContributor)613 synchronized void addContributor(RegistryContributor newContributor) { 614 String key = newContributor.getActualId(); 615 if (!getContributors().containsKey(key)) { 616 isDirty = true; 617 if (removedContributors != null) 618 removedContributors.remove(key); 619 getContributors().put(key, newContributor); 620 } 621 } 622 removeContributor(String id)623 synchronized void removeContributor(String id) { 624 isDirty = true; 625 RegistryContributor removed = getContributors().remove(id); 626 if (removed != null) { 627 if (removedContributors == null) 628 removedContributors = new HashMap<>(); 629 removedContributors.put(id, removed); 630 } 631 } 632 getNamespacesIndex()633 KeyedHashSet getNamespacesIndex() { 634 if (namespacesIndex == null) { 635 if (fromCache == false) 636 namespacesIndex = new KeyedHashSet(0); 637 else 638 namespacesIndex = registry.getTableReader().loadNamespaces(); 639 } 640 return namespacesIndex; 641 } 642 643 // Find or create required index element getNamespaceIndex(String namespaceName)644 private RegistryIndexElement getNamespaceIndex(String namespaceName) { 645 RegistryIndexElement indexElement = (RegistryIndexElement) getNamespacesIndex().getByKey(namespaceName); 646 if (indexElement == null) { 647 indexElement = new RegistryIndexElement(namespaceName); 648 namespacesIndex.add(indexElement); 649 } 650 return indexElement; 651 } 652 653 /** 654 * Collect all the objects that are removed by this operation and store 655 * them in a IObjectManager so that they can be accessed from the appropriate 656 * deltas but not from the registry. 657 */ getAssociatedObjects(String contributionId)658 synchronized Map<Integer, RegistryObject> getAssociatedObjects(String contributionId) { 659 //Collect all the objects associated with this contribution 660 int[] xpts = getExtensionPointsFrom(contributionId); 661 int[] exts = getExtensionsFrom(contributionId); 662 Map<Integer, RegistryObject> actualObjects = new HashMap<>(xpts.length + exts.length); 663 for (int ext : exts) { 664 Extension tmp = (Extension) basicGetObject(ext, RegistryObjectManager.EXTENSION); 665 actualObjects.put(Integer.valueOf(ext), tmp); 666 collectChildren(tmp, 0, actualObjects); 667 } 668 for (int xpt2 : xpts) { 669 ExtensionPoint xpt = (ExtensionPoint) basicGetObject(xpt2, RegistryObjectManager.EXTENSION_POINT); 670 actualObjects.put(Integer.valueOf(xpt2), xpt); 671 } 672 673 return actualObjects; 674 } 675 676 /** 677 * Adds elements to be removed along with the registry object. 678 */ addAssociatedObjects(Map<Integer, RegistryObject> map, RegistryObject registryObject)679 synchronized void addAssociatedObjects(Map<Integer, RegistryObject> map, RegistryObject registryObject) { 680 collectChildren(registryObject, 0, map); 681 } 682 683 /** 684 * Add to the set of the objects all extensions and extension points that 685 * could be navigated to from the objects in the set. 686 */ addNavigableObjects(Map<Integer, RegistryObject> associatedObjects)687 synchronized void addNavigableObjects(Map<Integer, RegistryObject> associatedObjects) { 688 Map<Integer, RegistryObject> result = new HashMap<>(); 689 for (RegistryObject object : associatedObjects.values()) { 690 if (object instanceof Extension) { 691 // add extension point 692 ExtensionPoint extPoint = getExtensionPointObject(((Extension) object).getExtensionPointIdentifier()); 693 if (extPoint == null) // already removed? 694 continue; 695 696 Integer extPointIndex = Integer.valueOf(extPoint.getKeyHashCode()); 697 if (!associatedObjects.containsKey(extPointIndex)) 698 result.put(Integer.valueOf(extPoint.getKeyHashCode()), extPoint); 699 700 // add all extensions for the extension point 701 for (int childId : extPoint.getRawChildren()) { 702 Extension tmp = (Extension) basicGetObject(childId, RegistryObjectManager.EXTENSION); 703 if (tmp == null) // already removed 704 continue; 705 Integer extensionIndex = Integer.valueOf(childId); 706 if (!associatedObjects.containsKey(extensionIndex)) { 707 result.put(extensionIndex, tmp); 708 collectChildren(tmp, 0, result); 709 } 710 } 711 } else if (object instanceof ExtensionPoint) { 712 // by now extensions of this extension point have been marked as orphans 713 Map<String, int[]> orphans = getOrphans(); 714 String name = ((ExtensionPoint) object).getUniqueIdentifier(); 715 int[] extensions = orphans.get(name); 716 if (extensions != null) { 717 for (int orphanId : extensions) { 718 Extension tmp = (Extension) basicGetObject(orphanId, RegistryObjectManager.EXTENSION); 719 if (tmp == null) // already removed 720 continue; 721 Integer extensionIndex = Integer.valueOf(orphanId); 722 if (!associatedObjects.containsKey(extensionIndex)) { 723 result.put(extensionIndex, tmp); 724 collectChildren(tmp, 0, result); 725 } 726 } 727 } 728 } 729 } 730 associatedObjects.putAll(result); 731 } 732 removeObjects(Map<?, ?> associatedObjects)733 synchronized void removeObjects(Map<?, ?> associatedObjects) { 734 //Remove the objects from the main object manager so they can no longer be accessed. 735 for (Object registryObject : associatedObjects.values()) { 736 RegistryObject toRemove = (RegistryObject) registryObject; 737 remove((toRemove).getObjectId(), true); 738 if (toRemove instanceof ExtensionPoint) 739 removeExtensionPoint(((ExtensionPoint) toRemove).getUniqueIdentifier()); 740 } 741 } 742 createDelegatingObjectManager(Map<?, ?> object)743 IObjectManager createDelegatingObjectManager(Map<?, ?> object) { 744 return new TemporaryObjectManager(object, this); 745 } 746 collectChildren(RegistryObject ce, int level, Map<Integer, RegistryObject> collector)747 private void collectChildren(RegistryObject ce, int level, Map<Integer, RegistryObject> collector) { 748 ConfigurationElement[] children = (ConfigurationElement[]) getObjects(ce.getRawChildren(), level == 0 || ce.noExtraData() ? RegistryObjectManager.CONFIGURATION_ELEMENT : RegistryObjectManager.THIRDLEVEL_CONFIGURATION_ELEMENT); 749 for (ConfigurationElement child : children) { 750 collector.put(Integer.valueOf(child.getObjectId()), child); 751 collectChildren(child, level + 1, collector); 752 } 753 } 754 755 @Override close()756 public void close() { 757 //do nothing. 758 } 759 getRegistry()760 public ExtensionRegistry getRegistry() { 761 return registry; 762 } 763 764 // Called from a synchronized method only unlinkChildFromContributions(KeyedElement[] contributions, int id)765 private boolean unlinkChildFromContributions(KeyedElement[] contributions, int id) { 766 for (KeyedElement contribution : contributions) { 767 Contribution candidate = (Contribution) contribution; 768 if (candidate == null) 769 continue; 770 if (candidate.hasChild(id)) { 771 candidate.unlinkChild(id); 772 if (candidate.isEmpty()) 773 removeContribution(candidate.getContributorId()); 774 return true; 775 } 776 } 777 return false; 778 } 779 unlinkChildFromContributions(int id)780 synchronized boolean unlinkChildFromContributions(int id) { 781 if (unlinkChildFromContributions(newContributions.elements, id)) 782 return true; 783 return unlinkChildFromContributions(getFormerContributions().elements, id); 784 } 785 getExtensionPointsFromNamespace(String namespaceName)786 synchronized public ExtensionPointHandle[] getExtensionPointsFromNamespace(String namespaceName) { 787 RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); 788 int[] namespaceExtensionPoints = indexElement.getExtensionPoints(); 789 return (ExtensionPointHandle[]) getHandles(namespaceExtensionPoints, EXTENSION_POINT); 790 } 791 792 // This method filters out extensions with no extension point getExtensionsFromNamespace(String namespaceName)793 synchronized public ExtensionHandle[] getExtensionsFromNamespace(String namespaceName) { 794 RegistryIndexElement indexElement = getNamespaceIndex(namespaceName); 795 int[] namespaceExtensions = indexElement.getExtensions(); 796 797 // filter extensions with no extension point (orphan extensions) 798 List<Handle> tmp = new ArrayList<>(); 799 Extension[] exts = (Extension[]) getObjects(namespaceExtensions, EXTENSION); 800 for (Extension ext : exts) { 801 if (getExtensionPointObject(ext.getExtensionPointIdentifier()) != null) { 802 tmp.add(getHandle(ext.getObjectId(), EXTENSION)); 803 } 804 } 805 if (tmp.size() == 0) 806 return EMPTY_EXTENSIONS_ARRAY; 807 ExtensionHandle[] result = new ExtensionHandle[tmp.size()]; 808 return tmp.toArray(result); 809 } 810 getExtensionsFromContributor(String contributorId)811 public ExtensionHandle[] getExtensionsFromContributor(String contributorId) { 812 int[] ids = getExtensionsFrom(contributorId); // never null 813 return (ExtensionHandle[]) getHandles(ids, RegistryObjectManager.EXTENSION); 814 } 815 getExtensionPointsFromContributor(String contributorId)816 public ExtensionPointHandle[] getExtensionPointsFromContributor(String contributorId) { 817 int[] ids = getExtensionPointsFrom(contributorId); // never null 818 return (ExtensionPointHandle[]) getHandles(ids, RegistryObjectManager.EXTENSION_POINT); 819 } 820 } 821