/* * DataSet.java * Copyright 2010 Connor Petty <cpmeister@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 Apr 27, 2010, 2:45:31 PM */ package pcgen.core; import java.math.BigDecimal; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import pcgen.cdom.base.Category; import pcgen.cdom.base.ChooseInformation; import pcgen.cdom.base.Constants; import pcgen.cdom.enumeration.IntegerKey; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.enumeration.StringKey; import pcgen.cdom.enumeration.Type; import pcgen.core.character.EquipSlot; import pcgen.core.prereq.Prerequisite; import pcgen.facade.core.AbilityCategoryFacade; import pcgen.facade.core.AbilityFacade; import pcgen.facade.core.AlignmentFacade; import pcgen.facade.core.BodyStructureFacade; import pcgen.facade.core.CampaignFacade; import pcgen.facade.core.ClassFacade; import pcgen.facade.core.DataSetFacade; import pcgen.facade.core.DeityFacade; import pcgen.facade.core.EquipmentFacade; import pcgen.facade.core.GameModeFacade; import pcgen.facade.core.GearBuySellFacade; import pcgen.facade.core.KitFacade; import pcgen.facade.core.RaceFacade; import pcgen.facade.core.SizeAdjustmentFacade; import pcgen.facade.core.SkillFacade; import pcgen.facade.core.StatFacade; import pcgen.facade.core.TemplateFacade; import pcgen.facade.core.generator.StatGenerationFacade; import pcgen.facade.util.AbstractMapFacade; import pcgen.facade.util.DefaultListFacade; import pcgen.facade.util.ListFacade; import pcgen.facade.util.MapFacade; import pcgen.core.prereq.PrerequisiteOperator; import pcgen.rules.context.LoadContext; import pcgen.util.enumeration.View; /** * * @author Connor Petty <cpmeister@users.sourceforge.net> */ public class DataSet implements DataSetFacade { private DefaultListFacade<RaceFacade> races; private DefaultListFacade<ClassFacade> classes; private DefaultListFacade<DeityFacade> deities; private DefaultListFacade<SkillFacade> skills; private DefaultListFacade<TemplateFacade> templates; private DefaultListFacade<AlignmentFacade> alignments; private DefaultListFacade<KitFacade> kits; private DefaultListFacade<StatFacade> stats; private AbilityMap abilityMap; // private DefaultListFacade<AbilityCategoryFacade> categories; // private Map<AbilityCategoryFacade, ListFacade<AbilityFacade>> abilityMap; private final LoadContext context; private final GameMode gameMode; private final ListFacade<CampaignFacade> campaigns; private SkillFacade speakLanguageSkill = null; private DefaultListFacade<BodyStructureFacade> bodyStructures; private DefaultListFacade<EquipmentFacade> equipment; private DefaultListFacade<String> xpTableNames; private DefaultListFacade<GearBuySellFacade> gearBuySellSchemes; private DefaultListFacade<String> characterTypes; private DefaultListFacade<SizeAdjustmentFacade> sizes; public DataSet( LoadContext context, GameMode gameMode, ListFacade<CampaignFacade> campaigns) { races = new DefaultListFacade<>(); classes = new DefaultListFacade<>(); deities = new DefaultListFacade<>(); skills = new DefaultListFacade<>(); templates = new DefaultListFacade<>(); alignments = new DefaultListFacade<>(); stats = new DefaultListFacade<>(); // categories = new DefaultListFacade<AbilityCategoryFacade>(); // abilityMap = new HashMap<AbilityCategoryFacade, ListFacade<AbilityFacade>>(); abilityMap = new AbilityMap(); bodyStructures = new DefaultListFacade<>(); equipment = new DefaultListFacade<>(); xpTableNames = new DefaultListFacade<>(); characterTypes = new DefaultListFacade<>(); kits = new DefaultListFacade<>(); sizes = new DefaultListFacade<>(); this.context = context; this.gameMode = gameMode; this.campaigns = campaigns; initLists(); } private void initLists() { List<Race> raceList = new ArrayList<>(context.getReferenceContext().getConstructedCDOMObjects(Race.class)); raceList.sort(new RaceComparator()); for (Race race : raceList) { if (race.getSafe(ObjectKey.VISIBILITY).isVisibleTo(View.VISIBLE_DISPLAY)) { races.addElement(race); } } List<PCClass> classList = new ArrayList<>(context.getReferenceContext().getConstructedCDOMObjects(PCClass.class)); classList.sort(new PCClassComparator()); for (PCClass pcClass : classList) { if (pcClass.getSafe(ObjectKey.VISIBILITY).isVisibleTo(View.VISIBLE_DISPLAY)) { classes.addElement(pcClass); } } for (Skill skill : context.getReferenceContext().getConstructedCDOMObjects(Skill.class)) { if (skill.getSafe(ObjectKey.VISIBILITY).isVisibleTo(View.VISIBLE_DISPLAY)) { skills.addElement(skill); } } for (Deity deity : context.getReferenceContext().getConstructedCDOMObjects(Deity.class)) { deities.addElement(deity); } for (PCTemplate template : context.getReferenceContext().getConstructedCDOMObjects(PCTemplate.class)) { if (template.getSafe(ObjectKey.VISIBILITY).isVisibleTo(View.VISIBLE_DISPLAY)) { templates.addElement(template); } } for (Kit kit : context.getReferenceContext().getConstructedCDOMObjects(Kit.class)) { kits.addElement(kit); } for (PCAlignment alignment : context.getReferenceContext().getOrderSortedCDOMObjects(PCAlignment.class)) { alignments.addElement(alignment); } for (PCStat stat : context.getReferenceContext().getOrderSortedCDOMObjects(PCStat.class)) { stats.addElement(stat); } // List<AbilityCategory> displayOrderCategories = // new ArrayList<AbilityCategory>( // gameMode.getAllAbilityCategories()); // Collections.sort(displayOrderCategories, // new AbilityCategoryComparator()); for (AbilityCategory category : gameMode.getAllAbilityCategories()) { if (category.isVisibleTo(View.VISIBLE_DISPLAY)) { // categories.addElement(category); List<Ability> abList = new ArrayList<>(Globals.getContext().getReferenceContext() .getManufacturer(Ability.class, category) .getAllObjects()); Globals.sortPObjectListByName(abList); DefaultListFacade<AbilityFacade> abilityList = new DefaultListFacade<>(abList); for (Iterator<AbilityFacade> iterator = abilityList.iterator(); iterator .hasNext();) { AbilityFacade facade = iterator.next(); if (facade instanceof Ability) { Ability ability = (Ability) facade; if (!(ability.getSafe(ObjectKey.VISIBILITY).isVisibleTo(View.VISIBLE_DISPLAY))) { iterator.remove(); } } } abilityMap.put(category, abilityList); } } Map<String, BodyStructure> structMap = new HashMap<>(SystemCollections .getUnmodifiableBodyStructureList().size() + 3); for (String name : SystemCollections.getUnmodifiableBodyStructureList()) { // TODO i18n the display name and correct the DataSetTest String displayName = name.substring(0, 1).toUpperCase() + name.substring(1).toLowerCase(); final BodyStructure bodyStructure = new BodyStructure(displayName); bodyStructures.addElement(bodyStructure); structMap.put(name, bodyStructure); } Set<Type> typesWithDesignatedSlots = buildSlottedTypeList(); bodyStructures.addElement(new BodyStructure( Constants.EQUIP_LOCATION_EQUIPPED, true, typesWithDesignatedSlots)); bodyStructures.addElement(new BodyStructure(Constants.EQUIP_LOCATION_CARRIED, true)); bodyStructures.addElement(new BodyStructure(Constants.EQUIP_LOCATION_NOTCARRIED, true)); for (EquipSlot es : SystemCollections.getUnmodifiableEquipSlotList()) { if (structMap.containsKey(es.getBodyStructureName())) { structMap.get(es.getBodyStructureName()).addEquipSlot(es); } } for (Equipment eq : context.getReferenceContext().getConstructedCDOMObjects(Equipment.class)) { if (eq.getSafe(ObjectKey.VISIBILITY).isVisibleTo(View.VISIBLE_DISPLAY)) { equipment.addElement(eq); } } for (String xpTableName : gameMode.getXPTableNames()) { xpTableNames.addElement(xpTableName); } for (String characterType : gameMode.getCharacterTypeList()) { characterTypes.addElement(characterType); } for (SizeAdjustment size : context.getReferenceContext().getSortedList( SizeAdjustment.class, IntegerKey.SIZEORDER)) { sizes.addElement(size); } createGearBuySellSchemes(); } /** * @return */ private Set<Type> buildSlottedTypeList() { Set<Type> typeList = new HashSet<>(); for (EquipSlot es : SystemCollections.getUnmodifiableEquipSlotList()) { for (String typeString : es.getContainType()) { typeList.add(Type.getConstant(typeString)); } } return typeList; } /** * Create the default set of GearBuySellSchemes. * TODO: This should be loaded from the game mode and allow for user additions. */ private void createGearBuySellSchemes() { BigDecimal fullPrice = new BigDecimal(100.0); BigDecimal halfPrice = new BigDecimal(50.0); BigDecimal free = BigDecimal.ZERO; gearBuySellSchemes = new DefaultListFacade<>(); // TODO i18n this gearBuySellSchemes.addElement(new GearBuySellScheme("Market price", fullPrice, halfPrice, fullPrice)); gearBuySellSchemes.addElement(new GearBuySellScheme("Character build", fullPrice, fullPrice, fullPrice)); gearBuySellSchemes.addElement(new GearBuySellScheme("Cashless", free, free, free)); gearBuySellSchemes.addElement(new GearBuySellScheme("Crafting", halfPrice, halfPrice, fullPrice)); } @Override public MapFacade<AbilityCategoryFacade, ListFacade<AbilityFacade>> getAbilities() { return abilityMap; } /* (non-Javadoc) * @see pcgen.core.facade.DataSetFacade#getPrereqAbilities(pcgen.core.facade.AbilityFacade) */ @Override public List<AbilityFacade> getPrereqAbilities(AbilityFacade abilityFacade) { if (abilityFacade == null || !(abilityFacade instanceof Ability)) { return Collections.emptyList(); } Ability ability = (Ability) abilityFacade; List<AbilityFacade> prereqList = new ArrayList<>(); for (Prerequisite prereq : ability.getPrerequisiteList()) { prereqList.addAll(getAbilitiesFromPrereq(prereq, ability.getCDOMCategory())); } return prereqList; } private List<AbilityFacade> getAbilitiesFromPrereq(Prerequisite prereq, Category<Ability> cat) { List<AbilityFacade> prereqList = new ArrayList<>(); // Exclude negated prereqs if (prereq == null || (prereq.getOperator() == PrerequisiteOperator.LT && "1".equals(prereq.getOperand()))) { return prereqList; } if ("FEAT" == prereq.getKind() || "FEAT".equalsIgnoreCase(prereq.getKind()) || "ABILITY" == prereq.getKind() || "ABILITY".equalsIgnoreCase(prereq.getKind())) { Ability ability = Globals.getContext().getReferenceContext() .getManufacturer(Ability.class, cat).getObject( prereq.getKey()); if (ability != null) { prereqList.add(ability); } } for (Prerequisite childPrereq : prereq.getPrerequisites()) { prereqList.addAll(getAbilitiesFromPrereq(childPrereq, cat)); } return prereqList; } @Override public ListFacade<SkillFacade> getSkills() { return skills; } @Override public ListFacade<RaceFacade> getRaces() { return races; } @Override public ListFacade<ClassFacade> getClasses() { return classes; } @Override public ListFacade<DeityFacade> getDeities() { return deities; } @Override public ListFacade<TemplateFacade> getTemplates() { return templates; } @Override public GameModeFacade getGameMode() { return gameMode; } @Override public ListFacade<CampaignFacade> getCampaigns() { return campaigns; } @Override public ListFacade<AlignmentFacade> getAlignments() { return alignments; } @Override public ListFacade<StatFacade> getStats() { return stats; } @Override public ListFacade<StatGenerationFacade> getStatGenerators() { throw new UnsupportedOperationException("Not supported yet."); } @Override public SkillFacade getSpeakLanguageSkill() { if (speakLanguageSkill != null) { return speakLanguageSkill ; } for (SkillFacade aSkillFacade : skills) { Skill aSkill = (Skill) aSkillFacade; ChooseInformation<?> chooseInfo = aSkill.get(ObjectKey.CHOOSE_INFO); if ((chooseInfo != null) && "LANG".equals(chooseInfo.getName())) { speakLanguageSkill = aSkillFacade; } } return speakLanguageSkill; } /* (non-Javadoc) * @see pcgen.core.facade.DataSetFacade#getEquipmentLocations() */ @Override public ListFacade<BodyStructureFacade> getEquipmentLocations() { return bodyStructures; } @Override public ListFacade<EquipmentFacade> getEquipment() { return equipment; } @Override public void addEquipment(EquipmentFacade equip) { equipment.addElement(equip); } @Override public void refreshEquipment() { equipment.updateContents(new ArrayList<EquipmentFacade>(context.getReferenceContext() .getConstructedCDOMObjects(Equipment.class))); } @Override public ListFacade<String> getXPTableNames() { return xpTableNames; } @Override public ListFacade<String> getCharacterTypes() { return characterTypes; } @Override public ListFacade<GearBuySellFacade> getGearBuySellSchemes() { return gearBuySellSchemes; } /** * The Class <code>RaceComparator</code> sorts races so that PC races come * at the top of the list, just after <None Selected>. */ class RaceComparator implements Comparator<Race> { /* (non-Javadoc) * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ @Override public int compare(Race r1, Race r2) { final int BEFORE = -1; final int EQUAL = 0; final int AFTER = 1; if (r1 == r2) { return EQUAL; } final String NONE_SELECTED = "<none selected>"; if (r1.getKeyName().equals(NONE_SELECTED) && !r2.getKeyName().equals(NONE_SELECTED)) { return BEFORE; } if (!r1.getKeyName().equals(NONE_SELECTED) && r2.getKeyName().equals(NONE_SELECTED)) { return AFTER; } final String PC_TYPE = "PC"; if (r1.isType(PC_TYPE) && !r2.isType(PC_TYPE)) { return BEFORE; } if (!r1.isType(PC_TYPE) && r2.isType(PC_TYPE)) { return AFTER; } final String HUMANOID = "Humanoid"; if (r1.isType(HUMANOID) && !r2.isType(HUMANOID)) { return BEFORE; } if (!r1.isType(HUMANOID) && r2.isType(HUMANOID)) { return AFTER; } // Check sort keys String key1 = r1.get(StringKey.SORT_KEY); if (key1 == null) { key1 = r1.getDisplayName(); } String key2 = r2.get(StringKey.SORT_KEY); if (key2 == null) { key2 = r2.getDisplayName(); } final Collator collator = Collator.getInstance(); return collator.compare(key1, key2); } } /** * The Class <code>PCClassComparator</code> sorts classes so that base * classes come at the top of the list. */ class PCClassComparator implements Comparator<PCClass> { /* (non-Javadoc) * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ @Override public int compare(PCClass c1, PCClass c2) { final int BEFORE = -1; final int EQUAL = 0; final int AFTER = 1; if (c1 == c2) { return EQUAL; } final String BASE_TYPE = "BASE.PC"; if (c1.isType(BASE_TYPE) && !c2.isType(BASE_TYPE)) { return BEFORE; } if (!c1.isType(BASE_TYPE) && c2.isType(BASE_TYPE)) { return AFTER; } final String PRESTIGE_TYPE = "Prestige"; if (c1.isType(PRESTIGE_TYPE) && !c2.isType(PRESTIGE_TYPE)) { return BEFORE; } if (!c1.isType(PRESTIGE_TYPE) && c2.isType(PRESTIGE_TYPE)) { return AFTER; } final String NPC_TYPE = "NPC"; if (c1.isType(NPC_TYPE) && !c2.isType(NPC_TYPE)) { return BEFORE; } if (!c1.isType(NPC_TYPE) && c2.isType(NPC_TYPE)) { return AFTER; } // Check sort keys String key1 = c1.get(StringKey.SORT_KEY); if (key1 == null) { key1 = c1.getDisplayName(); } String key2 = c2.get(StringKey.SORT_KEY); if (key2 == null) { key2 = c2.getDisplayName(); } final Collator collator = Collator.getInstance(); return collator.compare(key1, key2); } } class AbilityMap extends AbstractMapFacade<AbilityCategoryFacade, ListFacade<AbilityFacade>> { private TreeMap<AbilityCategoryFacade, ListFacade<AbilityFacade>> map; public AbilityMap() { map = new TreeMap<>(new AbilityCategoryComparator()); } @Override public Set<AbilityCategoryFacade> getKeys() { return Collections.unmodifiableSet(map.keySet()); } @Override public ListFacade<AbilityFacade> getValue(AbilityCategoryFacade key) { return map.get(key); } public void put(AbilityCategoryFacade key, ListFacade<AbilityFacade> value) { map.put(key, value); } } class AbilityCategoryComparator implements Comparator<AbilityCategoryFacade> { @Override public int compare(AbilityCategoryFacade f1, AbilityCategoryFacade f2) { AbilityCategory o1 = (AbilityCategory) f1; AbilityCategory o2 = (AbilityCategory) f2; final int BEFORE = -1; final int EQUAL = 0; final int AFTER = 1; final String ORDER[] = {"FEATS", "RACIAL ABILITIES", "TRAITS", "CLASS ABILITIES"}; if (o1 == o2) { return EQUAL; } String ac1Key = o1.getKeyName(); String ac2Key = o2.getKeyName(); String ac1Display = o1.getDisplayLocation().toString().toUpperCase(); String ac2Display = o2.getDisplayLocation().toString().toUpperCase(); if (ac1Display == null && ac2Display != null) { return AFTER; } if (ac1Display != null && ac2Display == null) { return BEFORE; } if ((ac1Display != null && ac2Display != null) && !ac1Display.equals(ac2Display)) { for (String displayOrder : ORDER) { if (ac1Display.equals(displayOrder)) { return BEFORE; } if (ac2Display.equals(displayOrder)) { return AFTER; } } return ac1Display.compareTo(ac2Display); } if (ac1Key == null && ac2Key != null) { return AFTER; } if (ac1Key != null && ac2Key == null) { return BEFORE; } if ((ac1Key == null && ac2Key == null) || ac1Key.equals(ac2Key)) { return EQUAL; } return ac1Key.compareTo(ac2Key); } } @Override public ListFacade<KitFacade> getKits() { return kits; } @Override public ListFacade<SizeAdjustmentFacade> getSizes() { return sizes; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("DataSet [gameMode="); builder.append(gameMode); builder.append(", campaigns="); builder.append(campaigns); builder.append("]"); return builder.toString(); } }