/* * Copyright (c) 2007 Tom Parker <thpr@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ package pcgen.cdom.base; import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import pcgen.base.formula.Formula; import pcgen.base.formula.base.VarScoped; import pcgen.base.lang.StringUtil; import pcgen.base.util.DoubleKeyMapToList; import pcgen.base.util.Indirect; import pcgen.base.util.MapToList; import pcgen.base.util.ObjectContainer; import pcgen.cdom.enumeration.FactKey; import pcgen.cdom.enumeration.FactSetKey; import pcgen.cdom.enumeration.FormulaKey; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.MapKey; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.enumeration.StringKey; import pcgen.cdom.enumeration.VariableKey; import pcgen.cdom.util.FactSetKeyMapToList; import pcgen.cdom.util.ListKeyMapToList; import pcgen.cdom.util.MapKeyMap; import pcgen.core.Description; import pcgen.core.Equipment; import pcgen.core.PlayerCharacter; import pcgen.core.analysis.BonusActivation; import pcgen.core.bonus.BonusObj; public abstract class CDOMObject extends ConcretePrereqObject implements Cloneable, BonusContainer, Loadable, Reducible, VarScoped { private URI sourceURI = null; private String displayName = Constants.EMPTY_STRING; /* * CONSIDER This should be a NumberMap - not Integer, but allow Double as * well, in one HashMap... this will control the size of CDOMObject. */ /** A map to hold items keyed by Integers for the object */ // TODO make this final once clone() is no longer required... private Map<IntegerKey, Integer> integerChar = null; /** A map to hold items keyed by Strings for the object */ // TODO make this final once clone() is no longer required... private Map<StringKey, String> stringChar = null; /** A map to hold items keyed by Strings for the object */ // TODO make this final once clone() is no longer required... private Map<FormulaKey, Formula> formulaChar = null; /** A map to hold items keyed by Strings for the object */ // TODO make this final once clone() is no longer required... private Map<VariableKey, Formula> variableChar = null; /** A map to hold items keyed by Strings for the object */ // TODO make this final once clone() is no longer required... private Map<ObjectKey<?>, Object> objectChar = null; /** A map to hold items keyed by Strings for the object */ // TODO make this final once clone() is no longer required... private Map<FactKey<?>, Object> factChar = null; /** A map to hold items keyed by Strings for the object */ // TODO make this final once clone() is no longer required... private FactSetKeyMapToList factSetChar = null; /** A map of Lists for the object */ // TODO make this final once clone() is no longer required... private ListKeyMapToList listChar = null; /** A map of Maps for the object */ // TODO make this final once clone() is no longer required... private MapKeyMap mapChar = null; // TODO make this final once clone() is no longer required... /* * CONSIDER This is currently order enforcing the reference fetching to * match the integration tests that we perform, and their current behavior. * Not sure if this is really the best solution? */ private DoubleKeyMapToList<CDOMReference<? extends CDOMList<?>>, CDOMReference<?>, AssociatedPrereqObject> cdomListMods = null; public final boolean containsKey(IntegerKey key) { return integerChar != null && integerChar.containsKey(key); } public final Integer get(IntegerKey key) { return integerChar == null ? null : integerChar.get(key); } public final int getSafe(IntegerKey key) { Integer intValue = integerChar == null ? null : integerChar.get(key); return intValue == null ? key.getDefault() : intValue.intValue(); } public final Integer put(IntegerKey key, Integer intValue) { if (integerChar == null) { integerChar = new HashMap<>(); } return integerChar.put(key, intValue); } public final Integer remove(IntegerKey key) { Integer out = integerChar == null ? null : integerChar.remove(key); if (out != null && integerChar.isEmpty()) { integerChar = null; } return out; } public final Set<IntegerKey> getIntegerKeys() { return integerChar == null ? Collections.emptySet() : new HashSet<>(integerChar.keySet()); } public final boolean containsKey(StringKey key) { return stringChar != null && stringChar.containsKey(key); } public final String get(StringKey key) { return stringChar == null ? null : stringChar.get(key); } public final String getSafe(StringKey key) { String str = stringChar == null ? null : stringChar.get(key); return str == null ? "" : str; } public final String put(StringKey key, String value) { if (stringChar == null) { stringChar = new HashMap<>(); } return stringChar.put(key, value); } public final String remove(StringKey key) { String out = stringChar == null ? null : stringChar.remove(key); if (out != null && stringChar.isEmpty()) { stringChar = null; } return out; } public final Set<StringKey> getStringKeys() { return stringChar == null ? Collections.emptySet() : new HashSet<>(stringChar.keySet()); } public final boolean containsKey(FormulaKey key) { return formulaChar != null && formulaChar.containsKey(key); } public final Formula get(FormulaKey key) { return formulaChar == null ? null : formulaChar.get(key); } public final Formula getSafe(FormulaKey key) { Formula formula = get(key); return formula == null ? key.getDefault() : formula; } public final Formula put(FormulaKey key, Formula value) { if (formulaChar == null) { formulaChar = new HashMap<>(); } return formulaChar.put(key, value); } public final Formula remove(FormulaKey key) { Formula out = formulaChar == null ? null : formulaChar.remove(key); if (out != null && formulaChar.isEmpty()) { formulaChar = null; } return out; } public final Set<FormulaKey> getFormulaKeys() { return formulaChar == null ? Collections.emptySet() : new HashSet<>(formulaChar.keySet()); } public final boolean containsKey(VariableKey key) { return variableChar != null && variableChar.containsKey(key); } public final Formula get(VariableKey key) { return variableChar == null ? null : variableChar.get(key); } public final Set<VariableKey> getVariableKeys() { return variableChar == null ? Collections.emptySet() : new HashSet<>(variableChar.keySet()); } public final Formula put(VariableKey key, Formula value) { if (variableChar == null) { variableChar = new HashMap<>(); } return variableChar.put(key, value); } public final Formula remove(VariableKey key) { Formula out = variableChar == null ? null : variableChar.remove(key); if (out != null && variableChar.isEmpty()) { variableChar = null; } return out; } public final void removeAllVariables() { variableChar = null; } public final boolean containsKey(ObjectKey<?> key) { return objectChar != null && objectChar.containsKey(key); } public final <OT> OT get(ObjectKey<OT> key) { return objectChar == null ? null : key.cast(objectChar.get(key)); } public final <OT> OT getSafe(ObjectKey<OT> key) { OT obj = get(key); return obj == null ? key.getDefault() : obj; } public final <OT> OT put(ObjectKey<OT> key, OT value) { if (objectChar == null) { objectChar = new HashMap<>(); } return key.cast(objectChar.put(key, value)); } public final <OT> OT remove(ObjectKey<OT> key) { OT out = objectChar == null ? null : key.cast(objectChar.remove(key)); if (out != null && objectChar.isEmpty()) { objectChar = null; } return out; } public final Set<ObjectKey<?>> getObjectKeys() { return objectChar == null ? Collections.emptySet() : new HashSet<>(objectChar.keySet()); } public final boolean containsKey(FactKey<?> key) { return factChar != null && factChar.containsKey(key); } public final <FT> Indirect<FT> get(FactKey<FT> key) { if (factChar == null) { return null; } return (Indirect<FT>) factChar.get(key); } public final <FT> FT getResolved(FactKey<FT> key) { if (factChar == null) { return null; } Indirect<FT> indirect = (Indirect<FT>) factChar.get(key); if (indirect == null) { return null; } return indirect.get(); } public final <FT> FT put(FactKey<FT> key, Indirect<FT> value) { if (factChar == null) { factChar = new HashMap<>(); } return key.cast(factChar.put(key, value)); } public final <FT> FT remove(FactKey<FT> key) { FT out = factChar == null ? null : key.cast(factChar.remove(key)); if (out != null && factChar.isEmpty()) { factChar = null; } return out; } public final Set<FactKey<?>> getFactKeys() { return factChar == null ? Collections.emptySet() : new HashSet<>(factChar.keySet()); } public final boolean containsSetFor(FactSetKey<?> key) { return factSetChar != null && factSetChar.containsListFor(key); } public final <T> void addToSetFor(FactSetKey<T> key, Indirect<T> element) { if (factSetChar == null) { factSetChar = new FactSetKeyMapToList(); } factSetChar.addToListFor(key, element); } public final <T> void addAllToSetFor(FactSetKey<T> key, Collection<ObjectContainer<T>> elementCollection) { if (factSetChar == null) { factSetChar = new FactSetKeyMapToList(); } factSetChar.addAllToListFor(key, elementCollection); } public final <T> List<Indirect<T>> getSetFor(FactSetKey<T> key) { return factSetChar == null ? null : factSetChar.getListFor(key); } public final <T> List<Indirect<T>> getSafeSetFor(FactSetKey<T> key) { return factSetChar != null && factSetChar.containsListFor(key) ? factSetChar.getListFor(key) : new ArrayList<>(); } public final String getSetAsString(FactSetKey<?> key) { return StringUtil.join(getSetFor(key), ", "); } public final int getSizeOfSetFor(FactSetKey<?> key) { // The javadoc says throw NPE, but the code returns 0, so I also return 0 here return factSetChar == null ? 0 : factSetChar.sizeOfListFor(key); } public final int getSafeSizeOfSetFor(FactSetKey<?> key) { return factSetChar == null ? 0 : factSetChar.containsListFor(key) ? factSetChar.sizeOfListFor(key) : 0; } public final <T> boolean containsInSet(FactSetKey<T> key, ObjectContainer<T> element) { return factSetChar != null && factSetChar.containsInList(key, element); } public final <T> boolean containsAnyInSet(FactSetKey<T> key, Collection<ObjectContainer<T>> elementCollection) { return factSetChar != null && factSetChar.containsAnyInList(key, elementCollection); } public final <T> List<ObjectContainer<T>> removeSetFor(FactSetKey<T> key) { List<ObjectContainer<T>> out = factSetChar == null ? null : factSetChar.removeListFor(key); if (out != null && factSetChar.isEmpty()) { factSetChar = null; } return out; } public final <T> boolean removeFromSetFor(FactSetKey<T> key, Indirect<T> element) { boolean removed = factSetChar != null && factSetChar.removeFromListFor(key, element); if (removed && factSetChar.isEmpty()) { factSetChar = null; } return removed; } public final Set<FactSetKey<?>> getFactSetKeys() { return factSetChar == null ? Collections.emptySet() : factSetChar.getKeySet(); } public final boolean containsListFor(ListKey<?> key) { return listChar != null && listChar.containsListFor(key); } public final <T> void addToListFor(ListKey<T> key, T element) { if (listChar == null) { listChar = new ListKeyMapToList(); } listChar.addToListFor(key, element); } public final <T> void addAllToListFor(ListKey<T> key, Collection<T> elementCollection) { if (listChar == null) { listChar = new ListKeyMapToList(); } listChar.addAllToListFor(key, elementCollection); } /** * Returns a copy of the list of objects stored in this CDOMObject for the * given ListKey. * * No order is guaranteed, and the returned List may contain duplicates. * There is no guarantee that duplicate items are sequential items in the * returned List. * * This method is value-semantic in that no changes are made to the key * passed into the method and ownership of the returned List is transferred * to the class calling this method. * * @param key * The ListKey for which a copy of the list should be returned. * @return A copy of the List contained in this CDOMObject for the given * key; null if the given key is not a ListKey in this CDOMObject. */ public final <T> List<T> getListFor(ListKey<T> key) { return listChar == null ? null : listChar.getListFor(key); } /** * Returns a non-null copy of the list of objects stored in this CDOMObject * for the given ListKey. * * No order is guaranteed, and the returned List may contain duplicates. * There is no guarantee that duplicate items are sequential items in the * returned List. * * This method is value-semantic in that no changes are made to the key * passed into the method and ownership of the returned List is transferred * to the class calling this method. * * @param key * The ListKey for which a copy of the list should be returned. * @return A copy of the List contained in this CDOMObject for the given * key. */ public final <T> List<T> getSafeListFor(ListKey<T> key) { return listChar != null && listChar.containsListFor(key) ? listChar.getListFor(key) : new ArrayList<>(); } /** * Returns a non-null Set of the objects stored in this CDOMObject for the * given ListKey. The List is converted to a Set to ensure that each entry * in the List is only occurs once. * * This is used because the loading system cannot guarantee "Set" behavior * (cannot guarantee uniqueness), and a specific infrastructure for a Set * (vs a List) is considered overkill for the few use cases that require it. * * No order of the objects is guaranteed. * * This method is value-semantic in that no changes are made to the key * passed into the method and ownership of the returned Set is transferred * to the class calling this method. * * @param key * The ListKey for which a Set of the objects stored in this * CDOMObject for the given ListKey should be returned. * @return A Set of objects in the List contained in this CDOMObject for the * given key. */ public final <T extends Comparable<T>> Set<T> getUniqueListFor( ListKey<T> key) { if (listChar == null) { return new HashSet<>(); } List<T> list = listChar.getListFor(key); if (list == null) { return new HashSet<>(); } return new LinkedHashSet<>(list); } public final String getListAsString(ListKey<?> key) { return StringUtil.join(getListFor(key), ", "); } public final int getSizeOfListFor(ListKey<?> key) { // The javadoc says throw NPE, but the code returns 0, so I also return 0 here return listChar == null ? 0 : listChar.sizeOfListFor(key); } public final int getSafeSizeOfListFor(ListKey<?> key) { return listChar == null ? 0 : listChar.containsListFor(key) ? listChar.sizeOfListFor(key) : 0; } public final <T> boolean containsInList(ListKey<T> key, T element) { return listChar != null && listChar.containsInList(key, element); } public final <T> boolean containsAnyInList(ListKey<T> key, Collection<T> elementCollection) { return listChar != null && listChar.containsAnyInList(key, elementCollection); } public final <T> T getElementInList(ListKey<T> key, int index) { return listChar == null ? null : listChar.getElementInList(key, index); } public final <T> List<T> removeListFor(ListKey<T> key) { List<T> out = listChar == null ? null : listChar.removeListFor(key); if (out != null && listChar.isEmpty()) { listChar = null; } return out; } public final <T> boolean removeFromListFor(ListKey<T> key, T element) { boolean removed = listChar != null && listChar.removeFromListFor(key, element); if (removed && listChar.isEmpty()) { listChar = null; } return removed; } public final Set<ListKey<?>> getListKeys() { return listChar == null ? Collections.emptySet() : listChar.getKeySet(); } // ===== MapKeyMap Methods ===== /** * Add a value to the map of maps. * * @param mapKey The MapKey we are adding an entry to * @param key The key to assign against * @param value The value to be stored. */ public final <K, V> V addToMapFor(MapKey<K, V> mapKey, K key, V value) { if (mapChar == null) { mapChar = new MapKeyMap(); } return mapChar.addToMapFor(mapKey, key, value); } /** * Remove a value from the map of maps. * * @param mapKey The MapKey we are removing an entry from * @param key The key to eject */ public final <K, V> void removeFromMapFor(MapKey<K, V> mapKey, K key) { if (mapChar != null) { boolean removed = mapChar.removeFromMapFor(mapKey, key); if (removed && mapChar.isEmpty()) { mapChar = null; } } } /** * Remove a map from the map of maps. * * @param mapKey The MapKey we are removing */ public final <K, V> void removeMapFor(MapKey<K, V> mapKey) { if (mapChar != null) { Map<K, V> removed = mapChar.removeMapFor(mapKey); if (removed != null && mapChar.isEmpty()) { mapChar = null; } } } /** * Retrieve the map of keys and values for the MapKey. * * @param mapKey The MapKey we are retrieving * @return The map of keys and values. */ public final <K, V> Map<K, V> getMapFor(MapKey<K, V> mapKey) { // The javadoc for getMapFor() says that it returns null, but the implementation does NOT // This caused an NPE in AspectToken.parseNonEmptyToken because it assumed a non-null map return mapChar == null ? Collections.emptyMap() : mapChar.getMapFor(mapKey); } /** * Retrieve the set of keys for the MapKey. * * @param mapKey The MapKey we are retrieving * @return The set of keys. */ public final <K, V> Set<K> getKeysFor(MapKey<K, V> mapKey) { return mapChar == null ? Collections.emptySet() : mapChar.getKeysFor(mapKey); } /** * Get the value for the given MapKey and secondary key. If there is * not a mapping for the given keys, null is returned. * * @param mapKey * The MapKey for retrieving the given value * @param key2 * The secondary key for retrieving the given value * @return Object The value stored for the given keys */ public final <K, V> V get(MapKey<K, V> mapKey, K key2) { return mapChar == null ? null : mapChar.get(mapKey, key2); } /** * Remove the value associated with the primary and secondary keys * from the map. * * @param mapKey The MapKey of the entry we are removing * @param key2 The secondary key of the entry we are removing * @return true if the key and its associated value were successfully removed * from the map; false otherwise */ public final <K, V> boolean removeFromMap(MapKey<K, V> mapKey, K key2) { boolean removed = mapChar != null && mapChar.removeFromMapFor(mapKey, key2); if (removed && mapChar.isEmpty()) { mapChar = null; } return removed; } /** * Retrieve the set of mapkeys held. * * @return The set of mapkeys. */ public final Set<MapKey<?, ?>> getMapKeys() { return mapChar == null ? Collections.emptySet() : mapChar.getKeySet(); } @Override public String getKeyName() { // FIXME TODO Patched for now to avoid NPEs, but this is wrong String returnKey = this.get(StringKey.KEY_NAME); if (returnKey == null) { returnKey = this.getDisplayName(); //returnKey = this.get(StringKey.NAME); } return returnKey; } public void setKeyName(String key) { put(StringKey.KEY_NAME, key); } public final int getSafeSizeOfMapFor(MapKey<?, ?> mapKey) { return mapChar != null && mapChar.containsMapFor(mapKey) ? mapChar.getKeysFor(mapKey).size() : 0; } @Override public void setName(String name) { setDisplayName(name); } public void setDisplayName(String name) { displayName = name; } public boolean isCDOMEqual(CDOMObject cdo) { if (cdo == this) { return true; } if (!equalsPrereqObject(cdo)) { return false; } if (integerChar == null ? cdo.integerChar != null : !integerChar.equals(cdo.integerChar)) { // System.err.println("CDOM Inequality Integer"); // System.err.println(integerChar + " " + cdo.integerChar); return false; } if (stringChar == null ? cdo.stringChar != null : !stringChar.equals(cdo.stringChar)) { // System.err.println("CDOM Inequality String"); // System.err.println(stringChar + " " + cdo.stringChar); return false; } if (formulaChar == null ? cdo.formulaChar != null : !formulaChar.equals(cdo.formulaChar)) { // System.err.println("CDOM Inequality Formula"); // System.err.println(formulaChar + " " + cdo.formulaChar); return false; } if (variableChar == null ? cdo.variableChar != null : !variableChar.equals(cdo.variableChar)) { // System.err.println("CDOM Inequality Variable"); // System.err.println(variableChar + " " + cdo.variableChar); return false; } if (objectChar == null ? cdo.objectChar != null : !objectChar.equals(cdo.objectChar)) { // System.err.println("CDOM Inequality Object"); // System.err.println(objectChar + " " + cdo.objectChar); return false; } if (factChar == null ? cdo.factChar != null : !factChar.equals(cdo.factChar)) { // System.err.println("CDOM Inequality Object"); // System.err.println(objectChar + " " + cdo.objectChar); return false; } if (listChar == null ? cdo.listChar != null : !listChar.equals(cdo.listChar)) { // System.err.println("CDOM Inequality List"); // System.err.println(listChar + " " + cdo.listChar); // System.err.println(listChar.getKeySet() + " " // + cdo.listChar.getKeySet()); return false; } if (mapChar == null ? cdo.mapChar != null : !mapChar.equals(cdo.mapChar)) { return false; } return cdomListMods == null ? cdo.cdomListMods == null : cdomListMods.equals(cdo.cdomListMods); } public final <T extends CDOMObject> void putToList( CDOMReference<? extends CDOMList<?>> listRef, CDOMReference<T> granted, AssociatedPrereqObject associations) { if (cdomListMods == null) { cdomListMods = new DoubleKeyMapToList<>( HashMap.class, LinkedHashMap.class); } cdomListMods.addToListFor(listRef, granted, associations); } public final <T extends CDOMObject> void removeFromList( CDOMReference<? extends CDOMList<?>> listRef, CDOMReference<T> granted) { if (cdomListMods != null) { List<AssociatedPrereqObject> removed = cdomListMods.removeListFor(listRef, granted); if (removed != null && cdomListMods.isEmpty()) { cdomListMods = null; } } } public final boolean hasListMods( CDOMReference<? extends CDOMList<?>> listRef) { return cdomListMods != null && cdomListMods.containsListFor(listRef); } public final <BT extends CDOMObject, L extends CDOMList<BT>> Collection<CDOMReference<BT>> getListMods( CDOMReference<L> listRef) { if (cdomListMods == null) { return null; } Collection<CDOMReference<BT>> set = (Collection<CDOMReference<BT>>) (Set<?>) cdomListMods .getSecondaryKeySet(listRef); if (set == null || set.isEmpty()) { return null; } return set; } public final <BT extends CDOMObject> Collection<CDOMReference<BT>> getSafeListMods( CDOMReference<? extends CDOMList<BT>> listRef) { Collection<CDOMReference<BT>> set = getListMods(listRef); if (set == null) { return Collections.emptySet(); } return set; } public final Collection<AssociatedPrereqObject> getListAssociations( CDOMReference<? extends CDOMList<?>> listRef, CDOMReference<?> key) { return cdomListMods == null ? null : cdomListMods.getListFor(listRef, key); } /** * @return A list of references to the global lists that this CDOM Object has modified */ public final Collection<CDOMReference<? extends CDOMList<?>>> getModifiedLists() { return cdomListMods == null ? Collections .emptySet() : cdomListMods.getKeySet(); } @Override public final String getLSTformat() { return getKeyName(); } public final void overlayCDOMObject(CDOMObject cdo) { addAllPrerequisites(cdo.getPrerequisiteList()); if (cdo.integerChar != null) { if (integerChar == null) { integerChar = new HashMap<>(); } integerChar.putAll(cdo.integerChar); } if (cdo.stringChar != null) { if (stringChar == null) { stringChar = new HashMap<>(); } stringChar.putAll(cdo.stringChar); } if (cdo.formulaChar != null) { if (formulaChar == null) { formulaChar = new HashMap<>(); } formulaChar.putAll(cdo.formulaChar); } if (cdo.objectChar != null) { if (objectChar == null) { objectChar = new HashMap<>(); } objectChar.putAll(cdo.objectChar); } if (cdo.factChar != null) { if (factChar == null) { factChar = new HashMap<>(); } factChar.putAll(cdo.factChar); } if (cdo.variableChar != null) { if (variableChar == null) { variableChar = new HashMap<>(); } variableChar.putAll(cdo.variableChar); } if (cdo.listChar != null) { if (listChar == null) { listChar = new ListKeyMapToList(); } listChar.addAllLists(cdo.listChar); } if (cdo.factSetChar != null) { if (factSetChar == null) { factSetChar = new FactSetKeyMapToList(); } factSetChar.addAllLists(cdo.factSetChar); } if (cdo.mapChar != null) { if (mapChar == null) { mapChar = new MapKeyMap(); } mapChar.putAll(cdo.mapChar); } if (cdo.cdomListMods != null) { if (cdomListMods == null) { cdomListMods = new DoubleKeyMapToList<>( HashMap.class, LinkedHashMap.class); } cdomListMods.addAll(cdo.cdomListMods); } } @Override public CDOMObject clone() throws CloneNotSupportedException { CDOMObject clone = (CDOMObject) super.clone(); clone.integerChar = integerChar == null ? null : new HashMap<>(integerChar); clone.stringChar = stringChar == null ? null : new HashMap<>(stringChar); clone.formulaChar = formulaChar == null ? null : new HashMap<>(formulaChar); clone.variableChar = variableChar == null ? null : new HashMap<>(variableChar); clone.objectChar = objectChar == null ? null : new HashMap<>(objectChar); clone.factChar = factChar == null ? null : new HashMap<>(factChar); if (listChar != null) { clone.listChar = new ListKeyMapToList(); clone.listChar.addAllLists(listChar); } if (factSetChar != null) { clone.factSetChar = new FactSetKeyMapToList(); clone.factSetChar.addAllLists(factSetChar); } if (mapChar != null) { clone.mapChar = new MapKeyMap(); clone.mapChar.putAll(mapChar); } clone.cdomListMods = cdomListMods == null ? null : cdomListMods.clone(); clone.ownBonuses(clone); return clone; } public void removeAllFromList(CDOMReference<? extends CDOMList<?>> listRef) { if (cdomListMods != null) { MapToList<CDOMReference<?>, AssociatedPrereqObject> removed = cdomListMods.removeListsFor(listRef); if (removed != null && cdomListMods.isEmpty()) { cdomListMods = null; } } } @Override public abstract boolean isType(String type); public <T extends CDOMObject> boolean hasObjectOnList( CDOMReference<? extends CDOMList<T>> list, T element) { if (element == null) { return false; } Collection<CDOMReference<T>> references = getListMods(list); if (references == null) { return false; } for (CDOMReference<T> ref : references) { if (ref.contains(element)) { return true; } } return false; } public ListKey<Description> getDescriptionKey() { return ListKey.DESCRIPTION; } /** * Set's all the BonusObj's to this creator * * Hopefully this is a temporary import - thpr Oct 9, 2008 * @throws CloneNotSupportedException */ public void ownBonuses(Object owner) throws CloneNotSupportedException { List<BonusObj> bonusList = getListFor(ListKey.BONUS); if (bonusList != null) { removeListFor(ListKey.BONUS); for (BonusObj orig : bonusList) { BonusObj bonus = orig.clone(); addToListFor(ListKey.BONUS, bonus); } } } /** * Hopefully this is a temporary import - thpr Oct 11, 2008 * * Return the qualified key, ususally used as the source in a * getVariableValue call. Always returns an empty string, but * may be overridden by subclasses to return a required value. * * @return The qualified name of the object */ public String getQualifiedKey() { return Constants.EMPTY_STRING; } /** * Get the list of bonuses for this object * @param pc the current player character * @return the list of bonuses for this object */ public List<BonusObj> getRawBonusList(PlayerCharacter pc) { List<BonusObj> bonusList = getSafeListFor(ListKey.BONUS); if (pc != null) { bonusList.addAll(pc.getAddedBonusList(this)); bonusList.addAll(pc.getSaveableBonusList(this)); } return bonusList; } /** * returns all BonusObj's that are "active" * @param pc A PlayerCharacter object. * @return active bonuses */ @Override public List<BonusObj> getActiveBonuses(final PlayerCharacter pc) { final List<BonusObj> aList = new ArrayList<>(); for (BonusObj bonus : getRawBonusList(pc)) { if (pc.isApplied(bonus)) { aList.add(bonus); } } return aList; } public List<BonusObj> getBonusList(PlayerCharacter assocStore) { return getRawBonusList(assocStore); } public List<BonusObj> getBonusList(Equipment e) { return getRawBonusList(null); } /** * Set the source file for this object * @param source */ @Override public final void setSourceURI(URI source) { sourceURI = source; } /** * Get the source file for this object * @return the source file for this object */ @Override public final URI getSourceURI() { return sourceURI; } /** * Get name * @return name */ @Override public final String getDisplayName() { return displayName; } /** * Sets all the BonusObj's to "active" * @param pc */ @Override public void activateBonuses(final PlayerCharacter pc) { BonusActivation.activateBonuses(this, pc); } @Override public boolean isInternal() { return getSafe(ObjectKey.INTERNAL).booleanValue(); } /** * @see pcgen.cdom.base.Reducible#getCDOMObject() */ @Override public CDOMObject getCDOMObject() { return this; } @Override @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract") public String getLocalScopeName() { //I don't have one return null; } @Override @SuppressWarnings("PMD.EmptyMethodInAbstractClassShouldBeAbstract") public VarScoped getVariableParent() { //Fall back to Global return null; } }