/* * Copyright 2005 (C) Tom Parker <thpr@users.sourceforge.net> * * This library 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 library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on June 18, 2005. * * Current Ver: $Revision: 513 $ */ package pcgen.cdom.enumeration; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.List; import java.util.Map; import pcgen.base.formula.Formula; import pcgen.base.lang.UnreachableError; import pcgen.base.util.CaseInsensitiveMap; import pcgen.cdom.base.CDOMObject; import pcgen.cdom.reference.CDOMSingleRef; import pcgen.core.AbilityCategory; /** * @author Tom Parker <thpr@users.sourceforge.net> * * This is a Typesafe enumeration of legal Characteristics of an Association. It * is designed to act as an index to a specific Objects within an item that * forms associations. * * @see pcgen.cdom.base.AssociatedObject * * AssociationKeys are designed to store items in an AssociatedObject in a * type-safe fashion. Note that it is possible to use the AssociationKey to cast * the object to the type of object stored by the AssociationKey. (This assists * with Generics) * * @param <T> * The class of object stored by this AssociationKey. */ public final class AssociationKey<T> { /* * These items are used by the Load (Context) System to identify the owning * CDOMObject and the TOKEN that processed to load the item into the * Context. */ public static final AssociationKey<CDOMObject> OWNER = new AssociationKey<>(); public static final AssociationKey<String> TOKEN = new AssociationKey<>(); /* * End Load (Context) items */ /* * These items are used by Tokens to store relationship information to specific items. */ public static final AssociationKey<SkillCost> SKILL_COST = new AssociationKey<>(); public static final AssociationKey<Integer> SPELL_LEVEL = new AssociationKey<>(); public static final AssociationKey<Boolean> KNOWN = new AssociationKey<>(); public static final AssociationKey<List<String>> ASSOC_CHOICES = new AssociationKey<>(); public static final AssociationKey<Nature> NATURE = new AssociationKey<>(); public static final AssociationKey<CDOMSingleRef<AbilityCategory>> CATEGORY = new AssociationKey<>(); public static final AssociationKey<String> CASTER_LEVEL = new AssociationKey<>(); public static final AssociationKey<Formula> TIMES_PER_UNIT = new AssociationKey<>(); public static final AssociationKey<String> TIME_UNIT = new AssociationKey<>(); public static final AssociationKey<String> SPELLBOOK = new AssociationKey<>(); public static final AssociationKey<String> DC_FORMULA = new AssociationKey<>(); /* * End token items */ /* * The following items are Associations used for Player Characters and thus * fall into the domain of CODE-1908 (to have the information stored in the * associations of these keys moved into facets) */ /* * Note: SPECIALTY is best done after SubClassFacet is made type safe. * Making SubClassFacet type safe is gated by CODE-1928 */ public static final AssociationKey<String> SPECIALTY = new AssociationKey<>(); /* * End Player Character items related to CODE-1908 */ private static CaseInsensitiveMap<AssociationKey<?>> map = null; private AssociationKey() { // Only allow instantiation here } public T cast(Object obj) { return (T) obj; } public static <OT> AssociationKey<OT> getKeyFor(Class<OT> assocClass, String assocName) { if (map == null) { buildMap(); } /* * CONSIDER This is actually not type safe, there is a case of asking * for a String a second time with a different Class that ObjectKey * currently does not handle. Two solutions: One, store this in a * Two-Key map and allow a String to map to more than one ObjectKey * given different output types (considered confusing) or Two, store the * Class and validate that with a an error message if a different class * is requested. */ AssociationKey<OT> key = (AssociationKey<OT>) map.get(assocName); if (key == null) { key = new AssociationKey<>(); map.put(assocName, key); } return key; } private static void buildMap() { map = new CaseInsensitiveMap<>(); Field[] fields = AssociationKey.class.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { int mod = fields[i].getModifiers(); if (Modifier.isStatic(mod) && Modifier.isFinal(mod) && Modifier.isPublic(mod)) { try { Object obj = fields[i].get(null); if (obj instanceof AssociationKey) { map.put(fields[i].getName(), (AssociationKey<?>) obj); } } catch (IllegalArgumentException | IllegalAccessException e) { throw new UnreachableError(e); } } } } @Override public String toString() { if (map == null) { buildMap(); } /* * CONSIDER Should this find a way to do a Two-Way Map or something to * that effect? */ for (Map.Entry<?, AssociationKey<?>> me : map.entrySet()) { if (me.getValue() == this) { return me.getKey().toString(); } } // Error return ""; } }