/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG 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:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.model;
import java.text.MessageFormat;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Abstract base class for all model entities that can have extensions.
* The most prominent (and currently only) representative of an extensible entity is
* {@link org.eclipse.skalli.services.projects.Project}.
*/
public abstract class ExtensibleEntityBase extends ExtensionEntityBase {
private ExtensionsMap extensions;
private HashSet<String> inheritedExtensions;
/**
* Retrieves the model extension matching the given extension class.
* If the extension is inherited and this entity has a parent entity,
* then the corresponding extension of the parent entity is returned, if any.
*
* @param <T> type of a model extension derived from <code>ExtensionEntityBase</code>.
* @param extensionClass the class of the model extension to retrieve.
*/
public <T extends ExtensionEntityBase> T getExtension(Class<T> extensionClass) {
T extension = null;
if (inheritedExtensions != null && inheritedExtensions.contains(extensionClass.getName())) {
EntityBase parent = getParentEntity();
if (parent instanceof ExtensibleEntityBase) {
extension = ((ExtensibleEntityBase) parent).getExtension(extensionClass);
}
}
else if (extensions != null) {
extension = extensions.getExtension(extensionClass);
}
return extension;
}
/**
* Retrieves model extensions that implement a given additional interface.
* Iterates through all extensions and checks with {@link Class#isAssignableFrom(Class)}
* whether the extension is compatible with the given argument.
*
* @param implementedInterface the interface to search for.
* @return a set of extensions that implement the given interface, or an
* empty set.
*/
public <T> Set<T> getExtensionsImplementing(Class<T> implementedInterface) {
HashSet<T> result = new HashSet<T>();
for (ExtensionEntityBase ext: getAllExtensions()) {
if (implementedInterface.isAssignableFrom(ext.getClass())) {
result.add(implementedInterface.cast(ext));
}
}
return result;
}
/**
* Retrieves the model extensions that implements a given additional interface.
*
* @param implementedInterface the interface to search for.
* @return the extension that implements the given interface, or <code>null</code>
* if there is no such extension.
*
* @throws IllegalStateException if more than one extension implements the
* given interface.
*/
public <T> T getExtensionImplementing(Class<T> implementedInterface) {
Set<T> implementigExtensions = getExtensionsImplementing(implementedInterface);
if (implementigExtensions.size() > 1) {
throw new IllegalStateException(MessageFormat.format("Entity has multiple extensions implementing {0}",
implementedInterface.getName()));
}
return implementigExtensions.isEmpty() ? null : implementigExtensions.iterator().next();
}
/**
* Retrieves all assigned model extensions.
*
* @return an unmodifiable but sorted set of all extensions (see {@link ExtensionsComparator}).
*/
public SortedSet<ExtensionEntityBase> getAllExtensions() {
if (extensions == null) {
return new TreeSet<ExtensionEntityBase>(new ExtensionsComparator());
}
return extensions.getAllExtensions();
}
/**
* Adds the given extension instance to this extensible entity.
*
* Calls {@link ExtensionEntityBase#setExtensibleEntity(ExtensibleEntityBase)
* ExtensionEntityBase#setExtensibleEntity(this)} on the model extension.
* If the model extension corresponding to the given extension instance is inherited,
* inheritance is switched off.
*
* @param <T> type of a model extension derived from <code>ExtensionEntityBase</code>.
* @param extension the model extension to add.
*/
public <T extends ExtensionEntityBase> void addExtension(T extension) {
removeInherited(extension);
add(extension);
}
/**
* Removes the model extension matching the given extension class.
* Calls {@link ExtensionEntityBase#setExtensibleEntity(ExtensibleEntityBase)
* ExtensionEntityBase#setExtensibleEntity(null)} on the removed model extension.
* If the model extension corresponding to the given extension instance is inherited,
* inheritance is switched off.
*
* @param <T> type of a model extension derived from <code>ExtensionEntityBase</code>.
* @param extensionClass the class of the model extension to remove.
*
* @return the model extension that has been removed from this extensible entity,
* or <code>null</code> if no such model extension was assigned to this extensible entity.
*/
public <T extends ExtensionEntityBase> T removeExtension(Class<T> extensionClass) {
removeInherited(extensionClass);
return remove(extensionClass);
}
/**
* Determines whether the given model extension should be inherited from a parent entity.
*
* An instance of the given extension currently associated with this entity will be removed
* even if the entity currently has no parent entity. In that case, {@link #getExtension(Class)}
* will return <code>null</code> unless a parent entity with a suitable extension instance
* is assigned. If there is a parent entity, {@link #getExtension(Class)} will retrieve the
* extension instance from the parent entity or an entity further up the parent hierarchy
* if the direct parent has no instance of the extension registered. Note that switching off
* inheritance for a given model extension does not automatically create an extension instance.
* A new instance must be added explicitly with {@link #addExtension(ExtensionEntityBase)}.
*
* @param <T> type of a model extension derived from <code>ExtensionEntityBase</code>.
* @param extensionClass the class of the model extension to inherit.
* @param inherit if <code>true</code> the model extension is inherited.
*
* @return the model extension that has been removed from this extensible entity,
* or <code>null</code> if no such model extension was assigned to this extensible entity.
*/
public <T extends ExtensionEntityBase> T setInherited(Class<T> extensionClass, boolean inherit) {
T extension = null;
if (inherit) {
extension = remove(extensionClass);
addInherited(extensionClass);
}
else {
removeInherited(extensionClass);
}
return extension;
}
/**
* Checks whether the given model extension is inherited from a parent entity.
* Note that this method may return <code>true</code> even if this entity
* currently has no parent entity. In that case {@link #getExtension(Class)}
* will return <code>null</code> unless a parent entity with a suitable extension
* instance is assigned.
*
* @param <T> type of a model extension derived from <code>ExtensionEntityBase</code>.
* @param extensionClass the class of the model extension to check.
* @return <code>true</code>, if the extension is inherited.
*/
public <T extends ExtensionEntityBase> boolean isInherited(Class<T> extensionClass) {
return inheritedExtensions != null ? inheritedExtensions.contains(extensionClass.getName()) : false;
}
private <T extends ExtensionEntityBase> void add(T extension) {
if (extension != null) {
if (extensions == null) {
extensions = new ExtensionsMap();
}
extension.setExtensibleEntity(this);
extensions.putExtension(extension);
}
}
private <T extends ExtensionEntityBase> T remove(Class<T> extensionClass) {
T extension = null;
if (extensionClass != null && extensions != null) {
extension = extensions.removeExtension(extensionClass);
if (extensions.isEmpty()) {
extensions = null;
}
if (extension != null) {
extension.setExtensibleEntity(null);
}
}
return extension;
}
private <T extends ExtensionEntityBase> void addInherited(Class<T> extensionClass) {
if (extensionClass != null) {
if (inheritedExtensions == null) {
inheritedExtensions = new HashSet<String>();
}
inheritedExtensions.add(extensionClass.getName());
}
}
private <T extends ExtensionEntityBase> void removeInherited(T extension) {
if (extension != null) {
removeInherited(extension.getClass());
}
}
private <T extends ExtensionEntityBase> void removeInherited(Class<T> extensionClass) {
if (extensionClass != null && inheritedExtensions != null) {
inheritedExtensions.remove(extensionClass.getName());
if (inheritedExtensions.isEmpty()) {
inheritedExtensions = null;
}
}
}
}