/* * Copyright (c) 1998-2017 by Richard A. Wilkes. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, version 2.0. If a copy of the MPL was not distributed with * this file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This Source Code Form is "Incompatible With Secondary Licenses", as * defined by the Mozilla Public License, version 2.0. */ package com.trollworks.gcs.template; import com.trollworks.gcs.advantage.Advantage; import com.trollworks.gcs.advantage.AdvantageList; import com.trollworks.gcs.app.GCSImages; import com.trollworks.gcs.common.DataFile; import com.trollworks.gcs.common.LoadState; import com.trollworks.gcs.equipment.Equipment; import com.trollworks.gcs.equipment.EquipmentList; import com.trollworks.gcs.notes.Note; import com.trollworks.gcs.notes.NoteList; import com.trollworks.gcs.skill.Skill; import com.trollworks.gcs.skill.SkillList; import com.trollworks.gcs.skill.Technique; import com.trollworks.gcs.spell.Spell; import com.trollworks.gcs.spell.SpellList; import com.trollworks.gcs.widgets.outline.ListRow; import com.trollworks.toolkit.collections.FilteredIterator; import com.trollworks.toolkit.io.xml.XMLNodeType; import com.trollworks.toolkit.io.xml.XMLReader; import com.trollworks.toolkit.io.xml.XMLWriter; import com.trollworks.toolkit.ui.image.StdImageSet; import com.trollworks.toolkit.ui.widget.outline.OutlineModel; import com.trollworks.toolkit.ui.widget.outline.Row; import com.trollworks.toolkit.ui.widget.outline.RowIterator; import com.trollworks.toolkit.utility.FileType; import com.trollworks.toolkit.utility.text.Text; import java.io.File; import java.io.IOException; import java.util.Iterator; /** A template. */ public class Template extends DataFile { /** The extension for templates. */ public static final String EXTENSION = "gct"; //$NON-NLS-1$ private static final int CURRENT_VERSION = 2; private static final String TAG_ROOT = "template"; //$NON-NLS-1$ private static final String TAG_OLD_NOTES = "notes"; //$NON-NLS-1$ /** The prefix for all template IDs. */ public static final String TEMPLATE_PREFIX = "gct."; //$NON-NLS-1$ /** * The prefix used to indicate a point value is requested from {@link #getValueForID(String)}. */ public static final String POINTS_PREFIX = TEMPLATE_PREFIX + "points."; //$NON-NLS-1$ /** The field ID for point total changes. */ public static final String ID_TOTAL_POINTS = POINTS_PREFIX + "Total"; //$NON-NLS-1$ /** The field ID for advantage point summary changes. */ public static final String ID_ADVANTAGE_POINTS = POINTS_PREFIX + "Advantages"; //$NON-NLS-1$ /** The field ID for disadvantage point summary changes. */ public static final String ID_DISADVANTAGE_POINTS = POINTS_PREFIX + "Disadvantages"; //$NON-NLS-1$ /** The field ID for quirk point summary changes. */ public static final String ID_QUIRK_POINTS = POINTS_PREFIX + "Quirks"; //$NON-NLS-1$ /** The field ID for skill point summary changes. */ public static final String ID_SKILL_POINTS = POINTS_PREFIX + "Skills"; //$NON-NLS-1$ /** The field ID for spell point summary changes. */ public static final String ID_SPELL_POINTS = POINTS_PREFIX + "Spells"; //$NON-NLS-1$ private OutlineModel mAdvantages; private OutlineModel mSkills; private OutlineModel mSpells; private OutlineModel mEquipment; private OutlineModel mNotes; private boolean mNeedAdvantagesPointCalculation; private boolean mNeedSkillPointCalculation; private boolean mNeedSpellPointCalculation; private int mCachedAdvantagePoints; private int mCachedDisadvantagePoints; private int mCachedQuirkPoints; private int mCachedSkillPoints; private int mCachedSpellPoints; /** Creates a new character with only default values set. */ public Template() { super(); mAdvantages = new OutlineModel(); mSkills = new OutlineModel(); mSpells = new OutlineModel(); mEquipment = new OutlineModel(); mNotes = new OutlineModel(); } /** * Creates a new character from the specified file. * * @param file The file to load the data from. * @throws IOException if the data cannot be read or the file doesn't contain a valid character * sheet. */ public Template(File file) throws IOException { this(); load(file); } @Override public FileType getFileType() { return FileType.getByExtension(EXTENSION); } @Override public StdImageSet getFileIcons() { return GCSImages.getTemplateDocumentIcons(); } @Override protected final void loadSelf(XMLReader reader, LoadState state) throws IOException { String marker = reader.getMarker(); do { if (reader.next() == XMLNodeType.START_TAG) { String name = reader.getName(); if (AdvantageList.TAG_ROOT.equals(name)) { loadAdvantageList(reader, state); } else if (SkillList.TAG_ROOT.equals(name)) { loadSkillList(reader, state); } else if (SpellList.TAG_ROOT.equals(name)) { loadSpellList(reader, state); } else if (EquipmentList.TAG_ROOT.equals(name)) { loadEquipmentList(reader, state); } else if (NoteList.TAG_ROOT.equals(name)) { loadNotesList(reader, state); } else if (TAG_OLD_NOTES.equals(name)) { Note note = new Note(this, false); note.setDescription(Text.standardizeLineEndings(reader.readText())); mNotes.addRow(note, false); } else { reader.skipTag(name); } } } while (reader.withinMarker(marker)); calculateAdvantagePoints(); calculateSkillPoints(); calculateSpellPoints(); } private void loadAdvantageList(XMLReader reader, LoadState state) throws IOException { String marker = reader.getMarker(); do { if (reader.next() == XMLNodeType.START_TAG) { String name = reader.getName(); if (Advantage.TAG_ADVANTAGE.equals(name) || Advantage.TAG_ADVANTAGE_CONTAINER.equals(name)) { mAdvantages.addRow(new Advantage(this, reader, state), true); } else { reader.skipTag(name); } } } while (reader.withinMarker(marker)); } private void loadSkillList(XMLReader reader, LoadState state) throws IOException { String marker = reader.getMarker(); do { if (reader.next() == XMLNodeType.START_TAG) { String name = reader.getName(); if (Skill.TAG_SKILL.equals(name) || Skill.TAG_SKILL_CONTAINER.equals(name)) { mSkills.addRow(new Skill(this, reader, state), true); } else if (Technique.TAG_TECHNIQUE.equals(name)) { mSkills.addRow(new Technique(this, reader, state), true); } else { reader.skipTag(name); } } } while (reader.withinMarker(marker)); } private void loadSpellList(XMLReader reader, LoadState state) throws IOException { String marker = reader.getMarker(); do { if (reader.next() == XMLNodeType.START_TAG) { String name = reader.getName(); if (Spell.TAG_SPELL.equals(name) || Spell.TAG_SPELL_CONTAINER.equals(name)) { mSpells.addRow(new Spell(this, reader, state), true); } else { reader.skipTag(name); } } } while (reader.withinMarker(marker)); } private void loadEquipmentList(XMLReader reader, LoadState state) throws IOException { String marker = reader.getMarker(); do { if (reader.next() == XMLNodeType.START_TAG) { String name = reader.getName(); if (Equipment.TAG_EQUIPMENT.equals(name) || Equipment.TAG_EQUIPMENT_CONTAINER.equals(name)) { mEquipment.addRow(new Equipment(this, reader, state), true); } else { reader.skipTag(name); } } } while (reader.withinMarker(marker)); } private void loadNotesList(XMLReader reader, LoadState state) throws IOException { String marker = reader.getMarker(); do { if (reader.next() == XMLNodeType.START_TAG) { String name = reader.getName(); if (Note.TAG_NOTE.equals(name) || Note.TAG_NOTE_CONTAINER.equals(name)) { mNotes.addRow(new Note(this, reader, state), true); } else { reader.skipTag(name); } } } while (reader.withinMarker(marker)); } @Override public int getXMLTagVersion() { return CURRENT_VERSION; } @Override public String getXMLTagName() { return TAG_ROOT; } @Override protected void saveSelf(XMLWriter out) { Iterator<Row> iterator; if (mAdvantages.getRowCount() > 0) { out.startSimpleTagEOL(AdvantageList.TAG_ROOT); for (iterator = mAdvantages.getTopLevelRows().iterator(); iterator.hasNext();) { ((Advantage) iterator.next()).save(out, false); } out.endTagEOL(AdvantageList.TAG_ROOT, true); } if (mSkills.getRowCount() > 0) { out.startSimpleTagEOL(SkillList.TAG_ROOT); for (iterator = mSkills.getTopLevelRows().iterator(); iterator.hasNext();) { ((ListRow) iterator.next()).save(out, false); } out.endTagEOL(SkillList.TAG_ROOT, true); } if (mSpells.getRowCount() > 0) { out.startSimpleTagEOL(SpellList.TAG_ROOT); for (iterator = mSpells.getTopLevelRows().iterator(); iterator.hasNext();) { ((Spell) iterator.next()).save(out, false); } out.endTagEOL(SpellList.TAG_ROOT, true); } if (mEquipment.getRowCount() > 0) { out.startSimpleTagEOL(EquipmentList.TAG_ROOT); for (iterator = mEquipment.getTopLevelRows().iterator(); iterator.hasNext();) { ((Equipment) iterator.next()).save(out, false); } out.endTagEOL(EquipmentList.TAG_ROOT, true); } if (mNotes.getRowCount() > 0) { out.startSimpleTagEOL(NoteList.TAG_ROOT); for (iterator = mNotes.getTopLevelRows().iterator(); iterator.hasNext();) { ((Note) iterator.next()).save(out, false); } out.endTagEOL(NoteList.TAG_ROOT, true); } } /** * @param id The field ID to retrieve the data for. * @return The value of the specified field ID, or <code>null</code> if the field ID is invalid. */ public Object getValueForID(String id) { if (ID_ADVANTAGE_POINTS.equals(id)) { return new Integer(getAdvantagePoints()); } else if (ID_DISADVANTAGE_POINTS.equals(id)) { return new Integer(getDisadvantagePoints()); } else if (ID_QUIRK_POINTS.equals(id)) { return new Integer(getQuirkPoints()); } else if (ID_SKILL_POINTS.equals(id)) { return new Integer(getSkillPoints()); } else if (ID_SPELL_POINTS.equals(id)) { return new Integer(getSpellPoints()); } return null; } @Override protected void startNotifyAtBatchLevelZero() { mNeedAdvantagesPointCalculation = false; mNeedSkillPointCalculation = false; mNeedSpellPointCalculation = false; } @Override public void notify(String type, Object data) { super.notify(type, data); if (Advantage.ID_POINTS.equals(type) || Advantage.ID_ROUND_COST_DOWN.equals(type) || Advantage.ID_LEVELS.equals(type) || Advantage.ID_LIST_CHANGED.equals(type)) { mNeedAdvantagesPointCalculation = true; } if (Skill.ID_POINTS.equals(type) || Skill.ID_LIST_CHANGED.equals(type)) { mNeedSkillPointCalculation = true; } if (Spell.ID_POINTS.equals(type) || Spell.ID_LIST_CHANGED.equals(type)) { mNeedSpellPointCalculation = true; } } @Override protected void endNotifyAtBatchLevelOne() { if (mNeedAdvantagesPointCalculation) { calculateAdvantagePoints(); notify(ID_ADVANTAGE_POINTS, new Integer(getAdvantagePoints())); notify(ID_DISADVANTAGE_POINTS, new Integer(getDisadvantagePoints())); notify(ID_QUIRK_POINTS, new Integer(getQuirkPoints())); } if (mNeedSkillPointCalculation) { calculateSkillPoints(); notify(ID_SKILL_POINTS, new Integer(getSkillPoints())); } if (mNeedSpellPointCalculation) { calculateSpellPoints(); notify(ID_SPELL_POINTS, new Integer(getSpellPoints())); } if (mNeedAdvantagesPointCalculation || mNeedSkillPointCalculation || mNeedSpellPointCalculation) { notify(ID_TOTAL_POINTS, new Integer(getTotalPoints())); } } private int getTotalPoints() { return getAdvantagePoints() + getDisadvantagePoints() + getQuirkPoints() + getSkillPoints() + getSpellPoints(); } /** @return The number of points spent on advantages. */ public int getAdvantagePoints() { return mCachedAdvantagePoints; } /** @return The number of points spent on disadvantages. */ public int getDisadvantagePoints() { return mCachedDisadvantagePoints; } /** @return The number of points spent on quirks. */ public int getQuirkPoints() { return mCachedQuirkPoints; } private void calculateAdvantagePoints() { mCachedAdvantagePoints = 0; mCachedDisadvantagePoints = 0; mCachedQuirkPoints = 0; for (Advantage advantage : getAdvantagesIterator()) { if (!advantage.canHaveChildren()) { int pts = advantage.getAdjustedPoints(); if (pts > 0) { mCachedAdvantagePoints += pts; } else if (pts < -1) { mCachedDisadvantagePoints += pts; } else if (pts == -1) { mCachedQuirkPoints--; } } } } /** @return The number of points spent on skills. */ public int getSkillPoints() { return mCachedSkillPoints; } private void calculateSkillPoints() { mCachedSkillPoints = 0; for (Skill skill : getSkillsIterable()) { if (!skill.canHaveChildren()) { mCachedSkillPoints += skill.getPoints(); } } } /** @return The number of points spent on spells. */ public int getSpellPoints() { return mCachedSpellPoints; } private void calculateSpellPoints() { mCachedSpellPoints = 0; for (Spell spell : getSpellsIterator()) { if (!spell.canHaveChildren()) { mCachedSpellPoints += spell.getPoints(); } } } /** @return The outline model for the advantages. */ public OutlineModel getAdvantagesModel() { return mAdvantages; } /** @return A recursive iterator over the advantages. */ public RowIterator<Advantage> getAdvantagesIterator() { return new RowIterator<>(mAdvantages); } /** @return The outline model for the skills. */ public OutlineModel getSkillsModel() { return mSkills; } /** @return A recursive iterable for the template's skills. */ public Iterable<Skill> getSkillsIterable() { return new FilteredIterator<>(new RowIterator<ListRow>(mSkills), Skill.class); } /** @return The outline model for the spells. */ public OutlineModel getSpellsModel() { return mSpells; } /** @return A recursive iterator over the spells. */ public RowIterator<Spell> getSpellsIterator() { return new RowIterator<>(mSpells); } /** @return The outline model for the equipment. */ public OutlineModel getEquipmentModel() { return mEquipment; } /** @return A recursive iterator over the equipment. */ public RowIterator<Equipment> getEquipmentIterator() { return new RowIterator<>(mEquipment); } /** @return The outline model for the notes. */ public OutlineModel getNotesModel() { return mNotes; } /** @return A recursive iterator over the notes. */ public RowIterator<Note> getNoteIterator() { return new RowIterator<>(mNotes); } }