All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cdc.util.enums.AbstractListDynamicEnum Maven / Gradle / Ivy

package cdc.util.enums;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import cdc.util.lang.Checks;
import cdc.util.lang.FailureReaction;
import cdc.util.lang.ImplementationException;
import cdc.util.lang.NotFoundException;
import cdc.util.lang.Operators;
import cdc.util.lang.UnexpectedValueException;

/**
 * Dynamic enumeration of independent values.
 * 

* Each value has a unique name.
* This implementation supports these features: *

    *
  • {@link DagFeature#LOCKING} *
  • {@link DagFeature#CREATION} till locked. *
  • {@link DagFeature#REMOVAL} if allowed at creation time and till locked. *
  • {@link DagFeature#CONTENT_CHANGE} if allowed at creation time and till locked. *
*

* A typical implementation should look like this: *

{@code
 * public final class Foo extends AbstractListDynamicEnum {
 *     public static final Support SUPPORT = support(Foo.class, Foo::new, Feature.RENAMING, ...);
 *
 *     protected Foo(String value) {
 *         super(value);
 *     }
 * }
 * }
* * @author Damien Carbonne * @param The dynamic enum concrete type. * */ public abstract class AbstractListDynamicEnum> implements ListDynamicEnum, Comparable { private String name; protected AbstractListDynamicEnum(String name) { Checks.isNotNullOrEmpty(name, "name"); this.name = name; } protected final void setName(String name) { this.name = name; } @Override public final String getName() { return name; } @Override public String getQName() { return getName(); } @Override public String toString() { return getName(); } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return this == obj; } @Override public int compareTo(V o) { return name.compareTo(o.getName()); } /** * Support interface describing standard methods expected for a dynamic enum. * * @author Damien Carbonne * * @param The enum type. */ public static interface Support> extends DynamicEnumSupport { // Nothing to add } /** * Interface used to create new instances of a dynamic enum. * * @author Damien Carbonne * * @param The dynamic enum type. */ @FunctionalInterface public static interface Creator> { public V create(String name); } @FunctionalInterface public static interface Modifier> { public void setName(V value, String name); } public static > Support support(Class cls, Predicate nameValidator, Creator creator, Modifier modifier, DagFeature... features) { Checks.isNotNull(cls, "cls"); Checks.isNotNull(nameValidator, "nameValidator"); Checks.isNotNull(creator, "creator"); return new SupportImpl<>(cls, nameValidator, creator, modifier, features); } /** * Creates a support instance. * * @param The dynamic enum type. * @param cls The dynamic enum class. * @param nameValidator The predicate to check names validity. * @param creator The dynamic enum factory * @param features The features to enable. * @return A new instance of Support for {@code } */ protected static > Support support(Class cls, Predicate nameValidator, Creator creator, DagFeature... features) { Checks.isNotNull(cls, "cls"); Checks.isNotNull(nameValidator, "nameValidator"); Checks.isNotNull(creator, "creator"); final Modifier modifier = (value, name) -> value.setName(name); return support(cls, nameValidator, creator, modifier, features); } protected static > Support support(Class cls, Creator creator, DagFeature... features) { Checks.isNotNull(cls, "cls"); Checks.isNotNull(creator, "creator"); return support(cls, AbstractDynamicEnumSupport.DEFAULT_NAME_VALIDATOR, creator, features); } /** * Support Implementation. * * @author Damien Carbonne * * @param The dynamic enum type. */ private static final class SupportImpl> extends AbstractDynamicEnumSupport implements Support { /** Ordered list of valid values. */ private final List validValues = new ArrayList<>(); /** Map from names to values. */ private final Map nameToValue = new HashMap<>(); private final Creator creator; private final Modifier modifier; private static final Predicate POSSIBLE_FEATURES = e -> { switch (e) { case CREATION: case LOCKING: case REMOVAL: case CONTENT_CHANGE: return true; case REPARENTING: return false; default: throw new UnexpectedValueException(e); } }; public SupportImpl(Class cls, Predicate nameValidator, Creator creator, Modifier modifier, DagFeature... features) { super(cls, nameValidator, Checks.areAccepted(POSSIBLE_FEATURES, "features", features)); this.creator = creator; this.modifier = modifier; if (modifier == null) { for (final DagFeature feature : features) { if (feature == DagFeature.CONTENT_CHANGE) { throw new IllegalArgumentException("Unexpected feature " + feature); } } } } @Override protected boolean isContained(V value) { return validValues.contains(value); } @Override public List getValues() { return Collections.unmodifiableList(validValues); } @Override public List getRoots() { return getValues(); } @Override public List getChildren(V value) { Checks.isNotNull(value, "value"); return Collections.emptyList(); } @Override public List getParents(V value) { Checks.isNotNull(value, "value"); return Collections.emptyList(); } @Override public String getName(V value) { return value == null ? null : value.getName(); } @Override public String getQName(V value) { return getName(value); } @Override public V valueOf(String qname, FailureReaction reaction) { return NotFoundException.onResult(nameToValue.get(qname), EnumType.unknownQName(qname), logger, reaction, null); } @Override public V findOrCreate(String qname) { V value = nameToValue.get(qname); if (value == null) { checkIsUnlocked(); checkNameIsValid(qname); checkIsSupported(DagFeature.CREATION); value = creator.create(qname); if (!qname.equals(value.getName())) { throw new ImplementationException("Unexpected name: '" + value.getName() + "'"); } nameToValue.put(qname, value); validValues.add(value); fire(value, DagEventType.CREATED); } return value; } @Override public void remove(V value) { checkIsValid(value); checkIsUnlocked(); checkIsSupported(DagFeature.REMOVAL); validValues.remove(value); nameToValue.remove(value.getName()); fire(value, DagEventType.REMOVED); } @Override public void setName(V value, String name) { checkIsValid(value); checkIsUnlocked(); checkIsSupported(DagFeature.CONTENT_CHANGE); checkNameIsValid(name); checkHasNoSiblingNamed(value, name); if (!value.getName().equals(name)) { nameToValue.remove(value.getName()); modifier.setName(value, name); nameToValue.put(name, value); fire(value, DagEventType.CONTENT_CHANGED); } } @Override public boolean isValid(V value) { return value != null && nameToValue.containsKey(value.getName()); } @Override public boolean areEqual(V left, V right) { return Operators.equals(left, right); } @Override public boolean isStrictlyOver(V left, V right) { return false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy