blob: fd30f1325b93165a019ca390c73b01e42ac6b762 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 BSI Business Systems Integration AG.
* 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:
* BSI Business Systems Integration AG - initial API and implementation
******************************************************************************/
package org.eclipse.scout.rt.shared.extension;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.eclipse.scout.commons.ClassIdentifier;
import org.eclipse.scout.commons.CollectionUtility;
import org.eclipse.scout.commons.CompareUtility;
import org.eclipse.scout.commons.TypeCastUtility;
import org.eclipse.scout.commons.annotations.Extends;
import org.eclipse.scout.commons.annotations.IOrdered;
import org.eclipse.scout.commons.annotations.Order;
import org.eclipse.scout.commons.logger.IScoutLogger;
import org.eclipse.scout.commons.logger.ScoutLogManager;
import org.eclipse.scout.service.AbstractService;
import org.eclipse.scout.service.SERVICES;
public class ExtensionRegistry extends AbstractService implements IInternalExtensionRegistry {
private static final IScoutLogger LOG = ScoutLogManager.getLogger(ExtensionRegistry.class);
private final Map<ClassIdentifier, List<ExtensionRegistryItem>> m_extensionItemMap;
private final Map<ClassIdentifier, List<ExtensionRegistryItem>> m_contributionItemMap;
private final Map<ClassIdentifier, List<ExtensionRegistryMoveItem>> m_modelMoveItemMap;
private final ThreadLocal<ExtensionStack> m_extensionStack;
private final ThreadLocal<ScopeStack> m_scopeStack;
private final AtomicLong m_registrationOrder;
private final ReadWriteLock m_readWriteLock;
private ExtensionScope<ExtensionRegistryItem> m_globalExtensionScope;
private ExtensionScope<ExtensionRegistryItem> m_globalContributionScope;
private ExtensionScope<ExtensionRegistryMoveItem> m_globalModelMoveItemScope;
public ExtensionRegistry() {
m_readWriteLock = new ReentrantReadWriteLock(/*not fair*/);
m_extensionItemMap = new HashMap<ClassIdentifier, List<ExtensionRegistryItem>>();
m_contributionItemMap = new HashMap<ClassIdentifier, List<ExtensionRegistryItem>>();
m_modelMoveItemMap = new HashMap<ClassIdentifier, List<ExtensionRegistryMoveItem>>();
m_extensionStack = new ThreadLocal<ExtensionStack>();
m_scopeStack = new ThreadLocal<ScopeStack>();
m_registrationOrder = new AtomicLong();
m_globalContributionScope = createGlobalScope(m_contributionItemMap, true);
m_globalExtensionScope = createGlobalScope(m_extensionItemMap, true);
m_globalModelMoveItemScope = createGlobalScope(m_modelMoveItemMap, false);
}
@Override
public void register(Class<?> extensionClass) {
register(extensionClass, (ClassIdentifier) null);
}
@Override
public void register(Class<?> extensionClass, Class<?> ownerClass) {
register(extensionClass, ownerClass, null);
}
@Override
public void register(Class<?> extensionClass, Class<?> ownerClass, Double order) {
if (extensionClass == null) {
throw new IllegalExtensionException("Extension class must not be null.");
}
register(extensionClass, ownerClass == null ? null : new ClassIdentifier(ownerClass), order);
}
@Override
public void register(Class<?> extensionClass, ClassIdentifier ownerClassIdentifier) {
register(extensionClass, ownerClassIdentifier, null);
}
@Override
public void register(Class<?> extensionClass, ClassIdentifier ownerClassIdentifier, Double order) {
if (extensionClass.getEnclosingClass() != null && !Modifier.isStatic(extensionClass.getModifiers())) {
throw new IllegalExtensionException("Extension class [" + extensionClass.getName() + "] is a non-static inner class.");
}
m_readWriteLock.writeLock().lock();
try {
autoRegisterInternal(extensionClass, null, ownerClassIdentifier, null, order);
}
finally {
m_readWriteLock.writeLock().unlock();
}
}
protected Extends getExtendsAnnotation(Class<?> c) {
Extends result = null;
Class<?> currentClass = c;
while (result == null && currentClass != null) {
result = currentClass.getAnnotation(Extends.class);
currentClass = currentClass.getSuperclass();
}
return result;
}
protected void autoRegisterInternal(Class<?> extensionClass, Class<?> declaringClass, ClassIdentifier ownerClassIdentifier, ClassIdentifier ownerClassIdentifierFromDeclaring, Double order) {
if (extensionClass == null) {
throw new IllegalExtensionException("Extension or contribution cannot be null.");
}
boolean isExtension = IExtension.class.isAssignableFrom(extensionClass);
Class<?> extensionGeneric = null;
if (isExtension) {
extensionGeneric = TypeCastUtility.getGenericsParameterClass(extensionClass, IExtension.class);
}
if (ownerClassIdentifier == null) {
// no owner passed: detect!
if (isExtension) {
// 1. try type parameter of extension
ownerClassIdentifier = new ClassIdentifier(extensionGeneric);
}
if (ownerClassIdentifier == null) {
// 2. try @Extends annotation
Extends extendsAnnotation = getExtendsAnnotation(extensionClass);
if (extendsAnnotation != null) {
int pathToContainerLength = extendsAnnotation.pathToContainer().length;
if (pathToContainerLength > 0) {
Class[] segments = new Class[pathToContainerLength + 1];
System.arraycopy(extendsAnnotation.pathToContainer(), 0, segments, 0, pathToContainerLength);
segments[pathToContainerLength] = extendsAnnotation.value();
ownerClassIdentifier = new ClassIdentifier(segments);
}
else {
ownerClassIdentifier = new ClassIdentifier(extendsAnnotation.value());
}
}
if (ownerClassIdentifier == null) {
// 3. inherit owner from declaring type
ownerClassIdentifier = ownerClassIdentifierFromDeclaring;
}
}
}
if (Modifier.isStatic(extensionClass.getModifiers())) {
declaringClass = null; // not required
}
registerInternal(ownerClassIdentifier, declaringClass, extensionClass, order, extensionGeneric, isExtension);
if (isExtension) {
// only step in if it is an "unmanaged" extension.
// managed scout objects (e.g. AbstractCompositeField) create their inner classes itself, therefore inner classes must not be registered
List<Class<?>> innerExtensionClasses = collectInnerExtensionClasses(extensionClass);
for (Class<?> innerExtensionClass : innerExtensionClasses) {
autoRegisterInternal(innerExtensionClass, extensionClass, null, ownerClassIdentifier, null);
}
}
}
/**
* @param extensionClass
* @return
*/
protected List<Class<?>> collectInnerExtensionClasses(Class<?> extensionClass) {
Class<?>[] innerClasses = extensionClass.getClasses();
List<Class<?>> result = new ArrayList<Class<?>>(innerClasses.length);
for (Class<?> innerClass : innerClasses) {
if (!Modifier.isAbstract(innerClass.getModifiers())) {
result.add(innerClass);
}
}
return result;
}
protected void validateRegister(ClassIdentifier ownerClassIdentifier, Class<?> extensionClass, Double modelOrder, Class<?> extensionGeneric, boolean isExtension) {
if (ownerClassIdentifier == null) {
throw new IllegalExtensionException("No owner information available for [" + extensionClass.getName() + "]. Either add an [@" + Extends.class.getSimpleName()
+ "] annotation, move it into a [" + IExtension.class.getName() + "] class or register explicitly on the [" + IExtensionRegistry.class.getName() + "] service.");
}
Class<?> ownerClass = ownerClassIdentifier.getLastSegment();
// Check if an owner is available
if (ownerClass == null) {
throw new IllegalExtensionException("No owner information available for [" + extensionClass.getName() + "]. Either add an [@" + Extends.class.getSimpleName()
+ "] annotation, move it into a [" + IExtension.class.getName() + "] class or register explicitly on the [" + IExtensionRegistry.class.getName() + "] service.");
}
if (isExtension) {
// Check if extension owner is compatible with extension generic
if (!ownerClass.equals(extensionGeneric)) {
if (extensionGeneric == null) {
LOG.warn("Could not parse owner generic of extension [" + extensionClass.getName() + "].");
}
else if (!extensionGeneric.isAssignableFrom(ownerClass)) {
throw new IllegalExtensionException("Owner [" + ownerClass.getName() + "] is not compatible with the generic [" + extensionGeneric.getName() + "] of extension [" + extensionClass.getName() + "].");
}
}
}
else {
boolean isContributionOwner = IContributionOwner.class.isAssignableFrom(ownerClass);
if (!isContributionOwner) {
throw new IllegalExtensionException("The owner [" + ownerClass.getName() + "] of contribution [" + extensionClass.getName() + "] does not implement [" + IContributionOwner.class.getName() + "].");
}
// contributions: check container
if (!isValidContribution(extensionClass, ownerClass)) {
throw new IllegalExtensionException("Contribution [" + extensionClass.getName() + "] is not supported for owner [" + ownerClass.getName() + "].");
}
}
// Warn if there is an order information for a non ordered object.
boolean isOrdered = IOrdered.class.isAssignableFrom(extensionClass);
if (!isOrdered && (modelOrder != null || isOrderAnnotationPresentInSuperClasses(extensionClass))) {
LOG.warn("Order information not valid for extension [" + extensionClass.getName() + "]. This extension is not an [" + IOrdered.class.getName() + "] object.");
}
}
protected boolean isValidContribution(Class<?> contribution, Class<?> container) {
for (IExtensionRegistrationValidatorService validator : SERVICES.getServices(IExtensionRegistrationValidatorService.class)) {
if (validator.isValidContribution(contribution, container)) {
return true;
}
}
return false;
}
protected boolean isOrderAnnotationPresentInSuperClasses(Class<?> clazz) {
Class<?> cls = clazz;
while (cls != null) {
if (cls.isAnnotationPresent(Order.class)) {
return true;
}
cls = cls.getSuperclass();
}
return false;
}
protected void registerInternal(ClassIdentifier ownerClassIdentifier, Class<?> declaringClass, Class<?> extensionClass, Double modelOrder, Class<?> extensionGeneric, boolean isExtension) {
validateRegister(ownerClassIdentifier, extensionClass, modelOrder, extensionGeneric, isExtension);
Map<ClassIdentifier, List<ExtensionRegistryItem>> map = getInsertionMap(isExtension);
List<ExtensionRegistryItem> extensions = map.get(ownerClassIdentifier);
if (extensions == null) {
extensions = new LinkedList<ExtensionRegistryItem>();
map.put(ownerClassIdentifier, extensions);
}
ExtensionRegistryItem item = new ExtensionRegistryItem(ownerClassIdentifier, declaringClass, extensionClass, modelOrder, m_registrationOrder.incrementAndGet());
extensions.add(item);
// recalculate global scope
if (isExtension) {
m_globalExtensionScope = createGlobalScope(map, true);
}
else {
m_globalContributionScope = createGlobalScope(map, true);
}
}
@Override
public void registerMoveToRoot(Class<? extends IOrdered> modelClass, Double newOrder) {
if (modelClass == null) {
throw new IllegalExtensionException("modelClass class must not be null.");
}
registerMoveToRoot(new ClassIdentifier(modelClass), newOrder);
}
@Override
public void registerMoveToRoot(ClassIdentifier modelClassIdentifer, Double newOrder) {
registerMove(modelClassIdentifer, newOrder, IMoveModelObjectToRootMarker.class);
}
@Override
public void registerMove(Class<? extends IOrdered> modelClass, double newOrder) {
if (modelClass == null) {
throw new IllegalExtensionException("modelClass class must not be null.");
}
registerMove(new ClassIdentifier(modelClass), newOrder);
}
@Override
public void registerMove(ClassIdentifier modelClassIdentifier, double newOrder) {
registerMove(modelClassIdentifier, newOrder, (ClassIdentifier) null);
}
@Override
public void registerMove(Class<? extends IOrdered> modelClass, Double newOrder, Class<? extends IOrdered> newContainerClass) {
if (modelClass == null) {
throw new IllegalExtensionException("modelClass class must not be null.");
}
registerMove(new ClassIdentifier(modelClass), newOrder, newContainerClass);
}
@Override
public void registerMove(ClassIdentifier modelClassIdentifer, Double newOrder, Class<? extends IOrdered> newContainerClass) {
registerMove(modelClassIdentifer, newOrder, newContainerClass == null ? null : new ClassIdentifier(newContainerClass));
}
@Override
public void registerMove(ClassIdentifier modelClassIdentifer, Double newOrder, ClassIdentifier newContainerClassIdentifier) {
validateRegisterMove(modelClassIdentifer, newOrder, newContainerClassIdentifier);
m_readWriteLock.writeLock().lock();
try {
Map<ClassIdentifier, List<ExtensionRegistryMoveItem>> modelMoveItemMap = getModelMoveItemMap();
List<ExtensionRegistryMoveItem> moveItems = modelMoveItemMap.get(modelClassIdentifer);
if (moveItems == null) {
moveItems = new LinkedList<ExtensionRegistryMoveItem>();
modelMoveItemMap.put(modelClassIdentifer, moveItems);
}
ExtensionRegistryMoveItem item = new ExtensionRegistryMoveItem(modelClassIdentifer, newContainerClassIdentifier, newOrder, m_registrationOrder.incrementAndGet());
moveItems.add(item);
m_globalModelMoveItemScope = createGlobalScope(getModelMoveItemMap(), false);
}
finally {
m_readWriteLock.writeLock().unlock();
}
}
protected void validateRegisterMove(ClassIdentifier modelClassIdentifier, Double newOrder, ClassIdentifier newContainerClassIdentifier) {
if (modelClassIdentifier == null) {
throw new IllegalExtensionException("modelClassIdentifier class must not be null.");
}
Class<?> lastSegment = modelClassIdentifier.getLastSegment();
if (!IOrdered.class.isAssignableFrom(lastSegment)) {
throw new IllegalExtensionException("modelClassIdentifier's last segment is not an " + IOrdered.class.getSimpleName() + ".");
}
@SuppressWarnings("unchecked")
Class<? extends IOrdered> modelClass = (Class<? extends IOrdered>) lastSegment;
if (modelClass == null) {
throw new IllegalExtensionException("modelClass class must not be null.");
}
if (newOrder == null && newContainerClassIdentifier == null) {
throw new IllegalExtensionException("At least a new order or a new container must be specified.");
}
if (newContainerClassIdentifier != null) {
Class<?> newContainerClass = newContainerClassIdentifier.getLastSegment();
if (modelClass == newContainerClass) {
throw new IllegalExtensionException("model class cannot be moved into itself.");
}
if (!IOrdered.class.isAssignableFrom(newContainerClass)) {
throw new IllegalExtensionException("new container class must be instanceof " + IOrdered.class.getSimpleName() + ".");
}
@SuppressWarnings("unchecked")
Class<? extends IOrdered> newOrderedContainerClass = (Class<? extends IOrdered>) newContainerClass;
if (!isValidMove(modelClass, newOrderedContainerClass)) {
String newOrderStr = null;
if (newOrder == null) {
newOrderStr = "null";
}
else {
newOrderStr = newOrder.toString();
}
throw new IllegalExtensionException("Move of element [" + modelClass.getName() + "] is not supported for order [" + newOrderStr + "] and container [" + newContainerClass.getName() + "].");
}
}
}
protected boolean isValidMove(Class<? extends IOrdered> modelClass, Class<? extends IOrdered> newContainerClass) {
for (IExtensionRegistrationValidatorService validator : SERVICES.getServices(IExtensionRegistrationValidatorService.class)) {
if (validator.isValidMove(modelClass, newContainerClass)) {
return true;
}
}
return false;
}
@Override
public boolean deregisterMove(Class<? extends IOrdered> modelClass, double newOrder) {
return deregisterMove(modelClass, newOrder, null);
}
@Override
public boolean deregisterMove(Class<? extends IOrdered> modelClass, Double newOrder, Class<? extends IOrdered> newContainerClass) {
if (modelClass == null) {
throw new IllegalExtensionException("modelClass class must not be null.");
}
return deregisterMove(new ClassIdentifier(modelClass), newOrder, newContainerClass);
}
@Override
public boolean deregisterMove(ClassIdentifier modelClassIdentifier, Double newOrder, Class<? extends IOrdered> newContainerClass) {
return deregisterMove(modelClassIdentifier, newOrder, newContainerClass == null ? null : new ClassIdentifier(newContainerClass));
}
@Override
public boolean deregisterMove(ClassIdentifier modelClassIdentifier, Double newOrder, ClassIdentifier newContainerClassIdentifier) {
if (newOrder == null && newContainerClassIdentifier == null) {
throw new IllegalExtensionException("At least a new order or a new container must be specified.");
}
m_readWriteLock.writeLock().lock();
try {
boolean changed = false;
Map<ClassIdentifier, List<ExtensionRegistryMoveItem>> modelMoveItemMap = getModelMoveItemMap();
List<ExtensionRegistryMoveItem> moveItems = modelMoveItemMap.get(modelClassIdentifier);
if (CollectionUtility.hasElements(moveItems)) {
Iterator<ExtensionRegistryMoveItem> iterator = moveItems.iterator();
while (iterator.hasNext()) {
ExtensionRegistryMoveItem moveItem = iterator.next();
if (CompareUtility.equals(moveItem.getNewModelOrder(), newOrder) && CompareUtility.equals(moveItem.getNewModelContainerClassIdentifier(), newContainerClassIdentifier)) {
iterator.remove();
changed = true;
}
}
if (moveItems.isEmpty()) {
modelMoveItemMap.remove(modelClassIdentifier);
changed = true;
}
}
if (changed) {
m_globalModelMoveItemScope = createGlobalScope(getModelMoveItemMap(), false);
}
return changed;
}
finally {
m_readWriteLock.writeLock().unlock();
}
}
protected Map<ClassIdentifier, List<ExtensionRegistryItem>> getInsertionMap(boolean isExtension) {
if (isExtension) {
return getExtensionMap();
}
return getContributionMap();
}
protected Map<ClassIdentifier, List<ExtensionRegistryItem>> getExtensionMap() {
return m_extensionItemMap;
}
protected Map<ClassIdentifier, List<ExtensionRegistryItem>> getContributionMap() {
return m_contributionItemMap;
}
protected Map<ClassIdentifier, List<ExtensionRegistryMoveItem>> getModelMoveItemMap() {
return m_modelMoveItemMap;
}
/**
* Returns top-level extensions for the given ownerClass, but not their nested extension classes.
*
* @param ownerClass
* @return
*/
protected Set<ExtensionRegistryItem> getModelExtensionItemsFor(Class<?> ownerClass) {
ScopeStack scopeStack = m_scopeStack.get();
ExtensionScope<ExtensionRegistryItem> extensionScope = null;
if (scopeStack != null) {
extensionScope = scopeStack.getExtensionScope();
}
if (extensionScope == null) {
extensionScope = m_globalExtensionScope;
}
return extensionScope.getRegistryItems(ownerClass);
}
protected Set<ExtensionRegistryItem> getModelContributionItemsFor(Class<?> ownerClass) {
ScopeStack scopeStack = m_scopeStack.get();
ExtensionScope<ExtensionRegistryItem> contributionScope = null;
if (scopeStack != null) {
contributionScope = scopeStack.getContributionScope();
}
if (contributionScope == null) {
contributionScope = m_globalContributionScope;
}
return contributionScope.getRegistryItems(ownerClass);
}
protected Set<ExtensionRegistryMoveItem> getModelMoveItemsFor(Class<?> modelClass, Iterator<?> parentModelObjectIterator) {
if (m_globalModelMoveItemScope == null) {
return null;
}
Set<ScopeItem> scopeItems = m_globalModelMoveItemScope.filterScopeItems(modelClass, parentModelObjectIterator);
return m_globalModelMoveItemScope.resolveRegistryItems(scopeItems);
}
@Override
public boolean deregister(Class<?> extensionOrContributionClass) {
if (extensionOrContributionClass == null) {
throw new IllegalExtensionException("extensionOrContributionClass cannot be null.");
}
m_readWriteLock.writeLock().lock();
try {
boolean changed = false;
boolean isExtension = IExtension.class.isAssignableFrom(extensionOrContributionClass);
Map<ClassIdentifier, List<ExtensionRegistryItem>> mapToRemoveFrom = getInsertionMap(isExtension);
Iterator<List<ExtensionRegistryItem>> iterator = mapToRemoveFrom.values().iterator();
while (iterator.hasNext()) {
List<ExtensionRegistryItem> items = iterator.next();
Iterator<ExtensionRegistryItem> itemIterator = items.iterator();
while (itemIterator.hasNext()) {
ExtensionRegistryItem item = itemIterator.next();
Class<?> extensionClass = item.getExtensionClass();
if (extensionOrContributionClass.equals(extensionClass)) {
itemIterator.remove();
changed = true;
}
}
if (items.isEmpty()) {
iterator.remove();
changed = true;
}
}
if (isExtension) {
// only step in if it is an "unmanaged" extension.
List<Class<?>> innerExtensionClasses = collectInnerExtensionClasses(extensionOrContributionClass);
for (Class<?> innerExtensionClass : innerExtensionClasses) {
boolean innerChanged = deregister(innerExtensionClass);
if (innerChanged) {
changed = true;
}
}
}
// clear scopes if needed
if (changed) {
if (isExtension) {
m_globalExtensionScope = createGlobalScope(getExtensionMap(), true);
}
else {
m_globalContributionScope = createGlobalScope(getContributionMap(), true);
}
}
return changed;
}
finally {
m_readWriteLock.writeLock().unlock();
}
}
@Override
public <T extends IExtension<?>> List<T> createExtensionsFor(Object owner) {
if (owner == null) {
throw new IllegalArgumentException("owner must not be null.");
}
Set<ExtensionRegistryItem> extensionItems = null;
m_readWriteLock.readLock().lock();
try {
extensionItems = getModelExtensionItemsFor(owner.getClass());
}
finally {
m_readWriteLock.readLock().unlock();
}
LinkedList<T> extensions = new LinkedList<T>();
if (CollectionUtility.isEmpty(extensionItems)) {
return extensions;
}
ExtensionStack extensionStack = m_extensionStack.get();
for (ExtensionRegistryItem extensionItem : extensionItems) {
T ext = extensionItem.createInstance(owner, extensionStack);
extensions.addFirst(ext);
}
return extensions;
}
@Override
public <T> List<T> createContributionsFor(Object container, Class<T> filterType) {
if (container == null) {
throw new IllegalArgumentException("container must not be null.");
}
Set<ExtensionRegistryItem> contributionItems = null;
m_readWriteLock.readLock().lock();
try {
contributionItems = getModelContributionItemsFor(container.getClass());
}
finally {
m_readWriteLock.readLock().unlock();
}
LinkedList<T> contributions = new LinkedList<T>();
if (CollectionUtility.isEmpty(contributionItems)) {
return contributions;
}
ExtensionStack extensionStack = m_extensionStack.get();
for (ExtensionRegistryItem item : contributionItems) {
if (filterType == null || filterType.isAssignableFrom(item.getExtensionClass())) {
contributions.addFirst(item.<T> createInstance(container, extensionStack));
}
}
return contributions;
}
@Override
public Set<Class<?>> getContributionsFor(Class<?> container) {
if (container == null) {
throw new IllegalArgumentException("container must not be null.");
}
Set<ExtensionRegistryItem> contributionItems = getModelContributionItemsFor(container);
Set<Class<?>> result = new LinkedHashSet<Class<?>>(contributionItems.size());
for (ExtensionRegistryItem item : contributionItems) {
result.add(item.getExtensionClass());
}
return result;
}
@Override
public <T> MoveDescriptor<T> createModelMoveDescriptorFor(T modelObject, Iterator<?> parentModelObjectIterator) {
if (modelObject == null) {
throw new IllegalExtensionException("modelObject must not be null.");
}
Set<ExtensionRegistryMoveItem> moveItems = null;
m_readWriteLock.readLock().lock();
try {
moveItems = getModelMoveItemsFor(modelObject.getClass(), parentModelObjectIterator);
}
finally {
m_readWriteLock.readLock().unlock();
}
if (CollectionUtility.isEmpty(moveItems)) {
return null;
}
// collapse multiple move items into one move descriptor
ClassIdentifier newContainer = null;
Double newOrder = null;
for (ExtensionRegistryMoveItem item : moveItems) {
if (item.getNewModelContainerClassIdentifier() != null) {
newContainer = item.getNewModelContainerClassIdentifier();
}
if (item.getNewModelOrder() != null) {
newOrder = item.getNewModelOrder();
}
}
return new MoveDescriptor<T>(modelObject, newContainer, newOrder);
}
protected <T extends AbstractExtensionRegistryItem> ExtensionScope<T> createGlobalScope(Map<ClassIdentifier, List<T>> registryItems, boolean topDownStrategy) {
Map<ClassIdentifier, List<T>> clonedRegistryItems = new HashMap<ClassIdentifier, List<T>>(registryItems.size());
for (Entry<ClassIdentifier, List<T>> entry : registryItems.entrySet()) {
clonedRegistryItems.put(entry.getKey(), new LinkedList<T>(entry.getValue()));
}
return new ExtensionScope<T>(clonedRegistryItems, topDownStrategy);
}
@Override
public void pushExtensions(List<? extends IExtension<?>> extensions) {
ExtensionStack extensionStack = m_extensionStack.get();
if (extensionStack == null) {
extensionStack = new ExtensionStack();
m_extensionStack.set(extensionStack);
}
extensionStack.pushExtensions(extensions);
}
@Override
public void popExtensions(List<? extends IExtension<?>> extensions) {
ExtensionStack extensionStack = m_extensionStack.get();
if (extensionStack == null) {
throw new IllegalStateException("popExtensions from empty stack");
}
extensionStack.popExtensions(extensions);
if (extensionStack.isEmpty()) {
m_extensionStack.remove();
}
}
@Override
public void pushScope(Class<?> scopeClass) {
ScopeStack scopeStack = m_scopeStack.get();
if (scopeStack == null) {
scopeStack = new ScopeStack(m_globalContributionScope, m_globalExtensionScope);
m_scopeStack.set(scopeStack);
}
scopeStack.pushScope(scopeClass);
}
@Override
public void popScope() {
ScopeStack scopeStack = m_scopeStack.get();
if (scopeStack == null) {
throw new IllegalStateException("popScope from empty stack");
}
scopeStack.popScope();
if (scopeStack.isEmpty()) {
m_scopeStack.remove();
}
}
}