PCGVer2Parser.java

Index Score
pcgen.io
PCGen

View: Reasons, Metrics, Source Code

These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.

MetricDescription
LOCLines of code
BLOCKSNumber of blocks
COMPARISONSNumber of comparison operators
EXEC_COMMENTSComments in executable code
LINESNumber of lines in the source file
CYCLOMATICCyclomatic complexity
OPERATORSNumber of operators
PROGRAM_LENGTHHalstead program length
ELOCEffective lines of code
OPERANDSNumber of operands
EXITSProcedure exits
LINE_COMMENTNumber of line comments
JAVA0145JAVA0145 Tab character used in source file
SIZESize of the file in bytes
LOGICAL_LINESNumber of statements
UNIQUE_OPERANDSNumber of unique operands
PROGRAM_VOCABHalstead program vocabulary
WHITESPACENumber of whitespace lines
RETURNSNumber of return points from functions
COMMENTSComment lines
INTERFACE_COMPLEXITYInterface complexity
BLOCK_COMMENTNumber of block comment lines
FUNCTIONSNumber of function declarations
JAVA0177JAVA0177 Variable declaration missing initializer
PARAMSNumber of formal parameter declarations
JAVA0285JAVA0285 Dereference of potentially null variable
DECL_COMMENTSComments in declarations
LOOPSNumber of loops
JAVA0049JAVA0049 Nested block at depth N (maximum: M)
DOC_COMMENTNumber of javadoc comment lines
JAVA0076JAVA0076 Use of magic number
JAVA0174JAVA0174 Assigned local variable never used
JAVA0034JAVA0034 Missing braces in if statement
JAVA0254JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0009JAVA0009 Protected member in final class
NEST_DEPTHMaximum nesting depth
JAVA0270JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
UNIQUE_OPERATORSNumber of unique operators
JAVA0259JAVA0259 Return of collection/array field
JAVA0130JAVA0130 Non-static method does not use instance fields
JAVA0117JAVA0117 Missing javadoc: method 'method'
JAVA0144JAVA0144 Line exceeds maximum M characters
JAVA0283JAVA0283 Control variable not updated in loop body
PROGRAM_VOLUMEHalstead program volume
JAVA0136JAVA0136 N methods defined in class (maximum: M)
JAVA0126JAVA0126 Method declares unchecked exception in throws
JAVA0110JAVA0110 Incorrect javadoc: no @return tag
JAVA0109JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA0173JAVA0173 Unused method parameter
JAVA0108JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
/* * PCGVer2Parser.java * Copyright 2002 (C) Thomas Behr <ravenlock@gmx.de> * * 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 March 22, 2002, 12:15 AM * * Current Ver: $Revision: 7367 $ * Last Editor: $Author: thpr $ * Last Edited: $Date: 2008-08-05 22:15:01 -0400 (Tue, 05 Aug 2008) $ * */ package pcgen.io; import java.math.BigDecimal; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import pcgen.cdom.base.CDOMObject; import pcgen.cdom.base.ChoiceSet; import pcgen.cdom.base.Constants; import pcgen.cdom.base.TransitionChoice; import pcgen.cdom.content.LevelCommandFactory; import pcgen.cdom.enumeration.ListKey; import pcgen.cdom.enumeration.ObjectKey; import pcgen.cdom.enumeration.StringKey; import pcgen.cdom.list.ClassSpellList; import pcgen.cdom.list.DomainSpellList; import pcgen.core.Ability; import pcgen.core.AbilityCategory; import pcgen.core.AbilityUtilities; import pcgen.core.Campaign; import pcgen.core.CharacterDomain; import pcgen.core.Deity; import pcgen.core.Domain; import pcgen.core.Equipment; import pcgen.core.FeatMultipleChoice; import pcgen.core.GameMode; import pcgen.core.Globals; import pcgen.core.Kit; import pcgen.core.Language; import pcgen.core.NoteItem; import pcgen.core.PCClass; import pcgen.core.PCSpell; import pcgen.core.PCTemplate; import pcgen.core.PObject; import pcgen.core.PlayerCharacter; import pcgen.core.Race; import pcgen.core.SettingsHandler; import pcgen.core.Skill; import pcgen.core.SpecialAbility; import pcgen.core.SubClass; import pcgen.core.SubstitutionClass; import pcgen.core.WeaponProf; import pcgen.core.bonus.Bonus; import pcgen.core.bonus.BonusObj; import pcgen.core.character.CharacterSpell; import pcgen.core.character.EquipSet; import pcgen.core.character.Follower; import pcgen.core.character.SpellBook; import pcgen.core.character.SpellInfo; import pcgen.core.levelability.LevelAbility; import pcgen.core.pclevelinfo.PCLevelInfo; import pcgen.core.spell.Spell; import pcgen.core.utils.CoreUtility; import pcgen.gui.GuiConstants; import pcgen.io.parsers.CharacterDomainParser; import pcgen.persistence.PersistenceLayerException; import pcgen.persistence.PersistenceManager; import pcgen.persistence.lst.PCClassLstToken; import pcgen.persistence.lst.TokenStore; import pcgen.rules.context.AbstractReferenceContext; import pcgen.rules.context.LoadContext; import pcgen.util.Logging; import pcgen.util.PropertyFactory; import pcgen.util.StringPClassUtil; /** * <code>PCGVer2Parser</code> * Parses a line oriented format. * Each line should adhere to the following grammar:<br> * * <i>line</i> := EMPTY | <i>comment</i> | <i>taglist</i> * <i>comment</i> := '#' STRING * <i>taglist</i> := tag ('|' tag)* * <i>tag</i> := simpletag | nestedtag * <i>nestedtag</i> := TAGNAME ':' '[' taglist ']' * <i>simpletag</i> := TAGNAME ':' TAGVALUE * * * @author Thomas Behr 22-03-02 * @version $Revision: 7367 $ */ final class PCGVer2Parser implements PCGParser, IOConstants { /** * DO NOT CHANGE line separator. * Need to keep the Unix line separator to ensure cross-platform portability. * * author: Thomas Behr 2002-11-13 */ private final List<String> warnings = new ArrayList<String>(); private Cache cache; private final List<String> weaponprofs = new ArrayList<String>(); private PlayerCharacter thePC; private final Set<String> seenStats = new HashSet<String>(); // // MAJOR.MINOR.REVISION // private int[] pcgenVersion = {0, 0, 0}; private String pcgenVersionSuffix; private boolean calcFeatPoolAfterLoad = false; private double baseFeatPool = 0.0; private boolean featsPresent = false; /** * Constructor * @param aPC */ PCGVer2Parser(PlayerCharacter aPC) { thePC = aPC; } /** * Selector * * <br>author: Thomas Behr 22-03-02 * * @return a list of warning messages */ public List<String> getWarnings() { return warnings; } /** * parse a String in PCG format * * <br>author: Thomas Behr 20-07-02 * * @param lines the String to parse * @throws PCGParseException */ public void parsePCG(String[] lines) throws PCGParseException { initCache(lines.length); for (int i = 0; i < lines.length; ++i) { if ((lines[i].trim().length() > 0) && !isComment(lines[i])) { cacheLine(lines[i].trim()); } } parseCachedLines(); } /* * ############################################################### * Miscellaneous methods * ############################################################### */ /** * Convenience Method * * <br>author: Thomas Behr 28-04-02 * * @param line * @return true if it is a comment */ private static boolean isComment(String line) { return line.trim().startsWith(TAG_COMMENT); } /* * Given a Source string and Target string, * return a List of BonusObj's */ private List<BonusObj> getBonusFromName(String sName, String tName) { //sName = SPELL=Haste //tName = PC String sourceStr = sName.substring(TAG_TEMPBONUS.length() + 1); String targetStr = tName.substring(TAG_TEMPBONUSTARGET.length() + 1); Object oSource = null; Object oTarget = null; if (sourceStr.startsWith(TAG_FEAT + '=')) { sourceStr = sourceStr.substring(5); oSource = thePC.getFeatKeyed(sourceStr); } else if (sourceStr.startsWith(TAG_SPELL + '=')) { sourceStr = sourceStr.substring(6); //oSource = aPC.getSpellNamed(sourceStr); oSource = Globals.getSpellKeyed(sourceStr); } else if (sourceStr.startsWith(TAG_EQUIPMENT + '=')) { sourceStr = sourceStr.substring(10); oSource = thePC.getEquipmentNamed(sourceStr); } else if (sourceStr.startsWith(TAG_CLASS + '=')) { sourceStr = sourceStr.substring(6); oSource = thePC.getClassKeyed(sourceStr); } else if (sourceStr.startsWith(TAG_TEMPLATE + '=')) { sourceStr = sourceStr.substring(9); oSource = thePC.getTemplateKeyed(sourceStr); } else if (sourceStr.startsWith(TAG_SKILL + '=')) { sourceStr = sourceStr.substring(6); oSource = thePC.getSkillKeyed(sourceStr); } else { // TODO Error message // Hmm, not a supported type } if (oSource != null) { sourceStr = ((PObject) oSource).getKeyName(); } if (targetStr.equals(TAG_PC)) { targetStr = thePC.getName(); } else { oTarget = thePC.getEquipmentNamed(targetStr); targetStr = ((PObject) oTarget).getDisplayName(); } List<BonusObj> aList = thePC.getTempBonusList(sourceStr, targetStr); return aList; } private PCTemplate addKeyedTemplate(final String templateKey) { PCTemplate aPCTemplate = Globals.getContext().ref.silentlyGetConstructedCDOMObject(PCTemplate.class, EntityEncoder.decode(templateKey)); if (aPCTemplate != null) { final int preXP = thePC.getXP(); aPCTemplate = thePC.addTemplate(aPCTemplate); // // XP written to file contains leveladjustment XP. If template modifies XP, then // it will have already been added into total. Need to make sure it is not doubled. // if (thePC.getXP() != preXP) { thePC.setXP(preXP); } } return aPCTemplate; } private void cacheLine(String s) { cache.put(s.substring(0, s.indexOf(':')), s); } private void checkSkillPools() { int skillPoints = 0; for (final PCClass pcClass : thePC.getClassList()) { skillPoints += pcClass.getSkillPool(thePC); } thePC.setSkillPoints(skillPoints); } private void checkStats() throws PCGParseException { if (seenStats.size() != SettingsHandler.getGame().s_ATTRIBSHORT.length) { final String message = PropertyFactory.getFormattedString( "Exceptions.PCGenParser.WrongNumAttributes", //$NON-NLS-1$ seenStats.size(), SettingsHandler.getGame().s_ATTRIBSHORT.length); throw new PCGParseException("parseStatLines", "N/A", message); //$NON-NLS-1$//$NON-NLS-2$ } } /* * ############################################################### * private helper methods * ############################################################### */ private void initCache(int capacity) { cache = new Cache((capacity * 4) / 3); } private void parseAgeLine(String line) { try { thePC .setAge(Integer.parseInt(line.substring(TAG_AGE.length() + 1))); } catch (NumberFormatException nfe) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalAgeLine", //$NON-NLS-1$ line); warnings.add(message); } } private void parseAgeSet(String line) { final StringTokenizer aTok = new StringTokenizer(line, TAG_END, false); int i = 0; aTok.nextToken(); // skip tag while (aTok.hasMoreTokens() && (i < 10)) { thePC.setHasMadeKitSelectionForAgeSet(i++, aTok.nextToken().equals( "1")); //$NON-NLS-1$ } } private void parseAlignmentLine(String line) { final String alignment = line.substring(TAG_ALIGNMENT.length() + 1); final int i = SettingsHandler.getGame().getIndexOfAlignment(alignment); if (i >= 0) { thePC.setAlignment(i, true); return; } final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalAlignment", //$NON-NLS-1$ line); warnings.add(message); } private void parseArmorProfLine(String line) { final StringTokenizer stok = new StringTokenizer(line.substring(TAG_ARMORPROF.length() + 1), TAG_END, false); // should be in the form ARMORPROF:objectype=name:prof:prof:prof:prof:etc. final String objecttype = stok.nextToken(); final String objectname = objecttype.substring(objecttype.indexOf('=') + 1); final List<String> aList = new ArrayList<String>(); while (stok.hasMoreTokens()) { aList.add(stok.nextToken()); } if (objecttype.startsWith(TAG_DEITY)) { if (thePC.getDeity() != null) { thePC.getDeity().addSelectedArmorProfs(aList); } } else if (objecttype.startsWith(TAG_CLASS)) { final PCClass aClass = thePC.getClassKeyed(objectname); if (aClass != null) { aClass.addSelectedArmorProfs(aList); } else { Logging.errorPrintLocalised( "Errors.PCGenParser.ObjectNotFound", //$NON-NLS-1$ line, TAG_CLASS, objectname); } } else if (objecttype.startsWith(TAG_FEAT)) { final Ability aFeat = thePC.getFeatNamed(objectname); if (aFeat != null) { aFeat.addSelectedArmorProfs(aList); } else { Logging.errorPrintLocalised( "Errors.PCGenParser.ObjectNotFound", //$NON-NLS-1$ line, TAG_FEAT, objectname); } } else if (objecttype.startsWith(TAG_SKILL)) { final Skill aSkill = thePC.getSkillKeyed(objectname); if (aSkill != null) { aSkill.addSelectedArmorProfs(aList); } else { Logging.errorPrintLocalised( "Errors.PCGenParser.ObjectNotFound", //$NON-NLS-1$ line, TAG_SKILL, objectname); } } else if (objecttype.startsWith(TAG_DOMAIN)) { final Domain aDomain = thePC.getCharacterDomainKeyed(objectname); if (aDomain != null) { aDomain.addSelectedArmorProfs(aList); } else { Logging.errorPrintLocalised( "Errors.PCGenParser.ObjectNotFound", //$NON-NLS-1$ line, TAG_DOMAIN, objectname); } } else if (objecttype.startsWith(TAG_EQUIPMENT)) { final Equipment eq = thePC.getEquipmentNamed(objectname); if (eq != null) { eq.addSelectedArmorProfs(aList); } else { Logging.errorPrintLocalised( "Errors.PCGenParser.ObjectNotFound", //$NON-NLS-1$ line, TAG_EQUIPMENT, objectname); } } else if (objecttype.startsWith(TAG_TEMPLATE)) { final PCTemplate aTemplate = thePC.getTemplateKeyed(objectname); if (aTemplate != null) { aTemplate.addSelectedArmorProfs(aList); } else { Logging.errorPrintLocalised( "Errors.PCGenParser.ObjectNotFound", //$NON-NLS-1$ line, TAG_TEMPLATE, objectname); } } else { Logging.errorPrintLocalised("Errors.PCGenParser.UnknownObject", //$NON-NLS-1$ line); return; // no object type found } } /** * Auto sort gear (Y/N) * @param line Line of saved data to be processed. **/ private void parseAutoSortGearLine(String line) { thePC.setAutoSortGear(line.endsWith(VALUE_Y)); } /** * Ignore cost for gear (Y/N) * @param line Line of saved data to be processed. **/ private void parseIgnoreCostLine(String line) { thePC.setIgnoreCost(line.endsWith(VALUE_Y)); } /** * Allow debt for gear (Y/N) * @param line Line of saved data to be processed. **/ private void parseAllowDebtLine(String line) { thePC.setAllowDebt(line.endsWith(VALUE_Y)); } /** * Auto resize gear (Y/N) * @param line Line of saved data to be processed. **/ private void parseAutoResizeGearLine(String line) { thePC.setAutoResize(line.endsWith(VALUE_Y)); } /** * # Auto sort skills - transition only, line is no longer saved * @param line **/ private void parseAutoSortSkillsLine(String line) { if (line.endsWith(VALUE_Y)) { thePC .setSkillsOutputOrder(GuiConstants.INFOSKILLS_OUTPUT_BY_NAME_ASC); } else { thePC .setSkillsOutputOrder(GuiConstants.INFOSKILLS_OUTPUT_BY_MANUAL); } } /** * # Auto known spells * @param line **/ private void parseAutoSpellsLine(String line) { thePC.setAutoSpells(line.endsWith(VALUE_Y)); } /** * Process the Use Higher Known Spell Slot line. * @param buffer The buffer to append to. */ private void parseUseHigherKnownSpellSlotsLine(String line) { thePC.setUseHigherKnownSlots(line.endsWith(VALUE_Y)); } /** * Process the Use Higher Prepped Spell Slot line. * @param buffer The buffer to append to. */ private void parseUseHigherPreppedSpellSlotsLine(String line) { thePC.setUseHigherPreppedSlots(line.endsWith(VALUE_Y)); } private void parseBirthdayLine(String line) { thePC.setBirthday(EntityEncoder.decode(line.substring(TAG_BIRTHDAY .length() + 1))); } private void parseBirthplaceLine(String line) { thePC.setBirthplace(EntityEncoder.decode(line.substring(TAG_BIRTHPLACE .length() + 1))); } /** * Does the actual work:<br> * Retrieves cached lines and parses each line. * * Note: May have to change parse order! * * <br>author: Thomas Behr 31-07-02 * * @throws PCGParseException */ private void parseCachedLines() throws PCGParseException { if (cache.containsKey(TAG_GAMEMODE)) { parseGameMode(cache.get(TAG_GAMEMODE).get(0)); } /* * #System Information * CAMPAIGN:CMP - Monkey Book I - Book For Monkeys * CAMPAIGN:CMP - Monkey Book II - Book By Monkeys * ... * * first thing to do is checking campaigns - no matter what! */ if (cache.containsKey(TAG_CAMPAIGN)) { parseCampaignLines(cache.get(TAG_CAMPAIGN)); } /* * VERSION:x.x.x */ if (cache.containsKey(TAG_VERSION)) { parseVersionLine(cache.get(TAG_VERSION).get(0)); } /* * #Character Attributes * STAT:STR=18 * STAT:DEX=18 * STAT:CON=18 * STAT:INT=18 * STAT:WIS=18 * STAT:CHA=18 * ALIGN:LG * RACE:Human */ if (cache.containsKey(TAG_STAT)) { for (final String stat : cache.get(TAG_STAT)) { parseStatLine(stat); } checkStats(); } if (cache.containsKey(TAG_ALIGNMENT)) { parseAlignmentLine(cache.get(TAG_ALIGNMENT).get(0)); } /* * # Kits - Just adds a reference to the character that the template * was picked. */ if (cache.containsKey(TAG_KIT)) { for (final String line : cache.get(TAG_KIT)) { parseKitLine(line); } } if (cache.containsKey(TAG_RACE)) { parseRaceLine(cache.get(TAG_RACE).get(0)); } if (cache.containsKey(TAG_FAVOREDCLASS)) { parseFavoredClassLine(cache.get(TAG_FAVOREDCLASS).get(0)); } /* * #System Information * CAMPAIGNS:>:-delimited list< * VERSION:x.x.x * ROLLMETHOD:xxx * PURCHASEPOINTS:Y or N|TYPE:>living City, Living greyhawk, etc< * UNLIMITEDPOOLCHECKED:Y or N * POOLPOINTS:>numeric value 0-?< * POOLPOINTSAVAIL:>numeric value 0-?< * GAMEMODE:DnD * TABLABEL:0 * AUTOSPELLS:Y or N * LOADCOMPANIONS:Y or N * USETEMPMODS:Y or N * AUTOSORTGEAR:Y or N * SKILLSOUTPUTORDER:0 */ if (cache.containsKey(TAG_POOLPOINTS)) { parsePoolPointsLine(cache.get(TAG_POOLPOINTS).get(0)); } if (cache.containsKey(TAG_POOLPOINTSAVAIL)) { parsePoolPointsLine2(cache.get(TAG_POOLPOINTSAVAIL).get(0)); } if (cache.containsKey(TAG_AUTOSPELLS)) { parseAutoSpellsLine(cache.get(TAG_AUTOSPELLS).get(0)); } if (cache.containsKey(TAG_USEHIGHERKNOWN)) { parseUseHigherKnownSpellSlotsLine(cache.get(TAG_USEHIGHERKNOWN) .get(0)); } if (cache.containsKey(TAG_USEHIGHERPREPPED)) { parseUseHigherPreppedSpellSlotsLine(cache.get(TAG_USEHIGHERPREPPED) .get(0)); } if (cache.containsKey(TAG_LOADCOMPANIONS)) { parseLoadCompanionLine(cache.get(TAG_LOADCOMPANIONS).get(0)); } if (cache.containsKey(TAG_USETEMPMODS)) { parseUseTempModsLine(cache.get(TAG_USETEMPMODS).get(0)); } if (cache.containsKey(TAG_HTMLOUTPUTSHEET)) { parseHTMLOutputSheetLine(cache.get(TAG_HTMLOUTPUTSHEET).get(0)); } if (cache.containsKey(TAG_PDFOUTPUTSHEET)) { parsePDFOutputSheetLine(cache.get(TAG_PDFOUTPUTSHEET).get(0)); } if (cache.containsKey(TAG_AUTOSORTGEAR)) { parseAutoSortGearLine(cache.get(TAG_AUTOSORTGEAR).get(0)); } if (cache.containsKey(TAG_IGNORECOST)) { parseIgnoreCostLine(cache.get(TAG_IGNORECOST).get(0)); } if (cache.containsKey(TAG_ALLOWDEBT)) { parseAllowDebtLine(cache.get(TAG_ALLOWDEBT).get(0)); } if (cache.containsKey(TAG_AUTORESIZEGEAR)) { parseAutoResizeGearLine(cache.get(TAG_AUTORESIZEGEAR).get(0)); } if (cache.containsKey(TAG_AUTOSORTSKILLS)) { parseAutoSortSkillsLine(cache.get(TAG_AUTOSORTSKILLS).get(0)); } if (cache.containsKey(TAG_SKILLSOUTPUTORDER)) { parseSkillsOutputOrderLine(cache.get(TAG_SKILLSOUTPUTORDER).get(0)); } /* * #Character Class(es) * CLASS:Fighter|LEVEL=3 * CLASSABILITIESLEVEL:Fighter=1(>This would only display up to the level the character has already,) * CLASSABILITIESLEVEL:Fighter=2(>with any special abilities not covered by other areas,) * CLASSABILITIESLEVEL:Fighter=3(>such as skills, feats, etc., but would list SA's, and the like<) * CLASS:Wizard|LEVEL=1 * CLASSABILITIESLEVEL:Wizard=1(SA's, MEMORIZE:Y, etc) */ if (cache.containsKey(TAG_CLASS)) { for (String line : cache.get(TAG_CLASS)) { parseClassLine(line); } checkSkillPools(); } final List<PCLevelInfo> pcLevelInfoList = new ArrayList<PCLevelInfo>(thePC.getLevelInfo()); if (cache.containsKey(TAG_CLASSABILITIESLEVEL)) { thePC.getLevelInfo().clear(); for (String line : cache.get(TAG_CLASSABILITIESLEVEL)) { parseClassAbilitiesLevelLine(line, pcLevelInfoList); } } /* * #Character Experience * EXPERIENCE:6000 */ if (cache.containsKey(TAG_EXPERIENCE)) { parseExperienceLine(cache.get(TAG_EXPERIENCE).get(0)); } /* * #Character Templates * TEMPLATESAPPLIED:If any, else this would just have the comment line, and skip to the next */ if (cache.containsKey(TAG_TEMPLATESAPPLIED)) { for (String line : cache.get(TAG_TEMPLATESAPPLIED)) { parseTemplateLine(line); } } if (cache.containsKey(TAG_REGION)) { for (String line : cache.get(TAG_REGION)) { parseRegionLine(line); } } /* * ############################################################### * Character Skills methods * ############################################################### */ /* * #Character Skills * CLASSBOUGHT:Fighter * SKILL:Alchemy|CLASS:N|COST:2|RANK:7 (Should be Obvious what each of these does, I hope ;p) * SKILL:Survival|CLASS:Y|COST:1|SYNERGY:Wilderness Lore=5=2|RANK:10 * CLASSBOUGHT:Wizard * SKILL:Spellcraft|CLASS:Y|COST:1|RANK:7 * * CLASSBOUGHT not supported */ if (cache.containsKey(TAG_SKILL)) { for (final String line : cache.get(TAG_SKILL)) { parseSkillLine(line); } } /* * #Character Languages * LANGUAGE:Chondathan|LANGUAGE:Common|LANGUAGE:Literacy */ if (cache.containsKey(TAG_LANGUAGE)) { for (final String line : cache.get(TAG_LANGUAGE)) { parseLanguageLine(line); } } /* * Anything that is already Pipe Delimited should be in * parenthesis to avoid confusion on PCGen's part * * #Character Feats * FEAT:Alertness|TYPE:General|(BONUS:SKILL|Listen,Spot|2)|DESC:+2 on Listen and Spot checks * FEATPOOL:>number of remaining feats< */ if (cache.containsKey(TAG_FEAT)) { for (final String line : cache.get(TAG_FEAT)) { parseFeatLine(line); } } if (cache.containsKey(TAG_VFEAT)) { for (final String line : cache.get(TAG_VFEAT)) { parseVFeatLine(line); } } if (cache.containsKey(TAG_FEATPOOL)) { for (final String line : cache.get(TAG_FEATPOOL)) { parseFeatPoolLine(line); } } if (cache.containsKey(TAG_ABILITY)) { for (final String line : cache.get(TAG_ABILITY)) { parseAbilityLine(line); } } if (cache.containsKey(TAG_USERPOOL)) { for (final String line : cache.get(TAG_USERPOOL)) { parseUserPoolLine(line); } } /* * Contains information about PC's equipment * Money goes here as well * * #Character Equipment * EQUIPNAME:Longsword|OUTPUTORDER:1|COST:5|WT:5|NOTE:It's very sharp!|>other info< * EQUIPNAME:Backpack|OUTPUTORDER:-1|COST:5|WT:5|NOTE:on my back * EQUIPNAME:Rope (Silk)|OUTPUTORDER:3|COST:5|WT:5 */ if (cache.containsKey(TAG_MONEY)) { for (final String line : cache.get(TAG_MONEY)) { parseMoneyLine(line); } } if (cache.containsKey(TAG_EQUIPNAME)) { for (final String line : cache.get(TAG_EQUIPNAME)) { parseEquipmentLine(line); } } if (cache.containsKey(TAG_EQUIPSET)) { /* * strangely enough this works even if we create a * EquipSet for content whose container EquipSet * has not been created yet * author: Thomas Behr 10-09-02 * * Comment from EquipSet author: * It only works because I've already sorted on output * in PCGVer2Creator * author: Jayme Cox 01-16-03 * */ //Collections.sort(cache.get(TAG_EQUIPSET), new EquipSetLineComparator()); for (final String line : cache.get(TAG_EQUIPSET)) { parseEquipmentSetLine(line); } } /** * CALCEQUIPSET line contains the "working" equipment list **/ if (cache.containsKey(TAG_CALCEQUIPSET)) { for (final String line : cache.get(TAG_CALCEQUIPSET)) { parseCalcEquipSet(line); } } /* * #Character Deity/Domain * DEITY:Yondalla|DEITYDOMAINS:[DOMAIN:Good|DOMAIN:Law|DOMAIN:Protection]|ALIGNALLOW:013|DESC:Halflings, Protection, Fertility|SYMBOL:None|DEITYFAVWEAP:Sword (Short)|DEITYALIGN:ALIGN:LG * DOMAIN:GOOD|DOMAINGRANTS:>list of abilities< * DOMAINSPELLS:GOOD|SPELLLIST:[SPELL:bla|SPELL:blubber|...] */ if (cache.containsKey(TAG_DEITY)) { for (final String line : cache.get(TAG_DEITY)) { parseDeityLine(line); } } if (cache.containsKey(TAG_DOMAIN)) { for (final String line : cache.get(TAG_DOMAIN)) { parseDomainLine(line); } } if (cache.containsKey(TAG_DOMAINSPELLS)) { for (final String line : cache.get(TAG_DOMAINSPELLS)) { parseDomainSpellsLine(line); } } if (cache.containsKey(TAG_SPELLBOOK)) { for (final String line : cache.get(TAG_SPELLBOOK)) { parseSpellBookLines(line); } } /* * This one is what will make spellcasters U G L Y!!! * * #Character Spells Information * CLASS:Wizard|CANCASTPERDAY:2,4(Totals the levels all up + includes attribute bonuses) * SPELLNAME:Blah|SCHOOL:blah|SUBSCHOOL:blah|Etc */ if (cache.containsKey(TAG_SPELLLIST)) { for (final String line : cache.get(TAG_SPELLLIST)) { parseSpellListLines(line); } } if (cache.containsKey(TAG_SPELLNAME)) { for (final String line : cache.get(TAG_SPELLNAME)) { parseSpellLine(line); } sortCharacterSpells(); } /* * #Character Description/Bio/Historys * CHARACTERBIO:any text that's in the BIO field * CHARACTERDESC:any text that's in the BIO field */ if (cache.containsKey(TAG_CHARACTERBIO)) { parseCharacterBioLine(cache.get(TAG_CHARACTERBIO).get(0)); } if (cache.containsKey(TAG_CHARACTERDESC)) { parseCharacterDescLine(cache.get(TAG_CHARACTERDESC).get(0)); } if (cache.containsKey(TAG_CHARACTERCOMP)) { for (final String line : cache.get(TAG_CHARACTERCOMP)) { parseCharacterCompLine(line); } } if (cache.containsKey(TAG_CHARACTERASSET)) { for (final String line : cache.get(TAG_CHARACTERASSET)) { parseCharacterAssetLine(line); } } if (cache.containsKey(TAG_CHARACTERMAGIC)) { for (final String line : cache.get(TAG_CHARACTERMAGIC)) { parseCharacterMagicLine(line); } } /* * #Character Master/Followers * MASTER:Mynex|TYPE:Follower|HITDICE:20|FILE:E$\DnD\dnd-chars\ravenlock.pcg * FOLLOWER:Raven|TYPE:Animal Companion|HITDICE:5|FILE:E$\DnD\dnd-chars\raven.pcg */ if (cache.containsKey(TAG_MASTER)) { for (final String line : cache.get(TAG_MASTER)) { parseMasterLine(line); } } if (cache.containsKey(TAG_FOLLOWER)) { for (final String line : cache.get(TAG_FOLLOWER)) { parseFollowerLine(line); } } /* * #Character Notes Tab */ if (cache.containsKey(TAG_NOTE)) { for (final String line : cache.get(TAG_NOTE)) { parseNoteLine(line); } } /* * #Character Bio * CHARACTERNAME:Code Monkey * TABNAME:Code Monkey the Best Ever No Really! * PLAYERNAME:Jason Monkey * HEIGHT:75 * WEIGHT:198 * AGE:17 * GENDER:text * HANDED:text * SKIN:text * EYECOLOR:text * HAIRCOLOR:text * HAIRSTYLE:text * LOCATION:text * CITY:text * PERSONALITYTRAIT1:text * PERSONALITYTRAIT2:text * SPEECHPATTERN:text * PHOBIAS:text * INTERESTS:text * CATCHPHRASE:text */ if (cache.containsKey(TAG_CHARACTERNAME)) { parseCharacterNameLine(cache.get(TAG_CHARACTERNAME).get(0)); } if (cache.containsKey(TAG_TABNAME)) { parseTabNameLine(cache.get(TAG_TABNAME).get(0)); } if (cache.containsKey(TAG_PLAYERNAME)) { parsePlayerNameLine(cache.get(TAG_PLAYERNAME).get(0)); } if (cache.containsKey(TAG_HEIGHT)) { parseHeightLine(cache.get(TAG_HEIGHT).get(0)); } if (cache.containsKey(TAG_WEIGHT)) { parseWeightLine(cache.get(TAG_WEIGHT).get(0)); } if (cache.containsKey(TAG_AGE)) { parseAgeLine(cache.get(TAG_AGE).get(0)); } if (cache.containsKey(TAG_GENDER)) { parseGenderLine(cache.get(TAG_GENDER).get(0)); } if (cache.containsKey(TAG_HANDED)) { parseHandedLine(cache.get(TAG_HANDED).get(0)); } if (cache.containsKey(TAG_SKINCOLOR)) { parseSkinColorLine(cache.get(TAG_SKINCOLOR).get(0)); } if (cache.containsKey(TAG_EYECOLOR)) { parseEyeColorLine(cache.get(TAG_EYECOLOR).get(0)); } if (cache.containsKey(TAG_HAIRCOLOR)) { parseHairColorLine(cache.get(TAG_HAIRCOLOR).get(0)); } if (cache.containsKey(TAG_HAIRSTYLE)) { parseHairStyleLine(cache.get(TAG_HAIRSTYLE).get(0)); } if (cache.containsKey(TAG_LOCATION)) { parseLocationLine(cache.get(TAG_LOCATION).get(0)); } //this tag is obsolete, but left in for backward-compatibility, replaced by TAG_CITY if (cache.containsKey(TAG_RESIDENCE)) { parseResidenceLine(cache.get(TAG_RESIDENCE).get(0)); } if (cache.containsKey(TAG_CITY)) { parseCityLine(cache.get(TAG_CITY).get(0)); } if (cache.containsKey(TAG_BIRTHDAY)) { parseBirthdayLine(cache.get(TAG_BIRTHDAY).get(0)); } if (cache.containsKey(TAG_BIRTHPLACE)) { parseBirthplaceLine(cache.get(TAG_BIRTHPLACE).get(0)); } if (cache.containsKey(TAG_PERSONALITYTRAIT1)) { for (final String line : cache.get(TAG_PERSONALITYTRAIT1)) { parsePersonalityTrait1Line(line); } } if (cache.containsKey(TAG_PERSONALITYTRAIT2)) { for (final String line : cache.get(TAG_PERSONALITYTRAIT2)) { parsePersonalityTrait2Line(line); } } if (cache.containsKey(TAG_SPEECHPATTERN)) { parseSpeechPatternLine(cache.get(TAG_SPEECHPATTERN).get(0)); } if (cache.containsKey(TAG_PHOBIAS)) { parsePhobiasLine(cache.get(TAG_PHOBIAS).get(0)); } if (cache.containsKey(TAG_INTERESTS)) { parseInterestsLine(cache.get(TAG_INTERESTS).get(0)); } if (cache.containsKey(TAG_CATCHPHRASE)) { parseCatchPhraseLine(cache.get(TAG_CATCHPHRASE).get(0)); } if (cache.containsKey(TAG_PORTRAIT)) { parsePortraitLine(cache.get(TAG_PORTRAIT).get(0)); } /* * #Character Weapon proficiencies */ if (cache.containsKey(TAG_WEAPONPROF)) { for (final String line : cache.get(TAG_WEAPONPROF)) { parseWeaponProficienciesLine(line); } checkWeaponProficiencies(); } if (cache.containsKey(TAG_ARMORPROF)) { for (final String line : cache.get(TAG_ARMORPROF)) { parseArmorProfLine(line); } } /* * # Temporary Bonuses */ if (cache.containsKey(TAG_TEMPBONUS)) { for (final String line : cache.get(TAG_TEMPBONUS)) { parseTempBonusLine(line); } } /* * # EquipSet Temporary bonuses * Must be done after both EquipSet and TempBonuses are parsed */ if (cache.containsKey(TAG_EQSETBONUS)) { for (final String line : cache.get(TAG_EQSETBONUS)) { parseEquipSetTempBonusLine(line); } } if (cache.containsKey(TAG_AGESET)) { for (final String line : cache.get(TAG_AGESET)) { parseAgeSet(line); } } } /* * ############################################################### * System Information methods * ############################################################### */ private void parseCampaignLines(final List<String> lines) throws PCGParseException { final List<Campaign> campaigns = new ArrayList<Campaign>(); if (SettingsHandler.isLoadCampaignsWithPC()) { PCGTokenizer tokens; for (final String line : lines) { try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { /* * Campaigns are critical for characters, * need to stop the load process * * Thomas Behr 14-08-02 */ throw new PCGParseException( "parseCampaignLines", line, pcgpex.getMessage()); //$NON-NLS-1$ } for (PCGElement element : tokens.getElements()) { final Campaign aCampaign = Globals.getCampaignKeyed(element.getText()); if (aCampaign != null && aCampaign.isGameMode(SettingsHandler.getGame() .getName())) { if (!aCampaign.isLoaded()) { campaigns.add(aCampaign); } } } } if (campaigns.size() > 0) { try { //PersistenceObserver observer = new PersistenceObserver(); PersistenceManager pManager = PersistenceManager.getInstance(); //pManager.addObserver( observer ); pManager.loadCampaigns(campaigns); //pManager.deleteObserver( observer ); } catch (PersistenceLayerException e) { throw new PCGParseException( "parseCampaignLines", "N/A", e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$ } if (Globals.getUseGUI()) { pcgen.gui.PCGen_Frame1.getInst().getMainSource() .updateLoadedCampaignsUI(); } } } if (!Globals.displayListsHappy()) { throw new PCGParseException("parseCampaignLines", "N/A", //$NON-NLS-1$ //$NON-NLS-2$ PropertyFactory .getString("Exceptions.PCGenParser.NoCampainInfo")); //$NON-NLS-1$ } } private void parseCatchPhraseLine(final String line) { thePC.setCatchPhrase(EntityEncoder.decode(line .substring(TAG_CATCHPHRASE.length() + 1))); } private void parseCharacterAssetLine(final String line) { thePC.getMiscList().set( 0, EntityEncoder.decode(line .substring(TAG_CHARACTERASSET.length() + 1))); } private void parseCharacterCompLine(final String line) { thePC.getMiscList().set( 1, EntityEncoder .decode(line.substring(TAG_CHARACTERCOMP.length() + 1))); } private void parseCharacterDescLine(final String line) { thePC.setDescription(EntityEncoder.decode(line .substring(TAG_CHARACTERDESC.length() + 1))); } private void parseCharacterMagicLine(final String line) { thePC.getMiscList().set( 2, EntityEncoder.decode(line .substring(TAG_CHARACTERMAGIC.length() + 1))); } /* * ############################################################### * Character Bio methods * ############################################################### */ private void parseCharacterNameLine(final String line) { thePC.setName(EntityEncoder.decode(line.substring(TAG_CHARACTERNAME .length() + 1))); } private void parseCityLine(final String line) { thePC.setResidence(EntityEncoder.decode(line.substring(TAG_CITY .length() + 1))); } private void parseClassAbilitiesLevelLine(final String line, final List<PCLevelInfo> pcLevelInfoList) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalClassAbility" //$NON-NLS-1$ , line, pcgpex.getMessage()); warnings.add(message); return; } int level = -1; PCClass aPCClass = null; String tag; PCGElement element; PCLevelInfo pcl = null; final Iterator<PCGElement> it = tokens.getElements().iterator(); // the first element defines the class key name and level // eg: Cleric=4 if (it.hasNext()) { element = it.next(); final int index = element.getText().indexOf('='); if (index < 0) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidClassLevel", //$NON-NLS-1$ element.getText()); warnings.add(message); return; } final String classKeyName = EntityEncoder.decode(element.getText().substring(0, index)); aPCClass = thePC.getClassKeyed(classKeyName); if (aPCClass == null) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.ClassNotFound", //$NON-NLS-1$ classKeyName); warnings.add(message); return; } try { level = Integer .parseInt(element.getText().substring(index + 1)); } catch (NumberFormatException nfe) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidClassLevel", //$NON-NLS-1$ element.getText()); warnings.add(message); return; } if (level < 1) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidClassLevel", //$NON-NLS-1$ element.getText()); warnings.add(message); return; } for (PCLevelInfo info : pcLevelInfoList) { if (classKeyName.equalsIgnoreCase(info.getClassKeyName()) && level == info.getLevel()) { pcl = info; break; } } if (pcl == null) { pcl = thePC.saveLevelInfo(classKeyName); pcl.setLevel(level); } else { thePC.getLevelInfo().add(pcl); } pcl.setSkillPointsRemaining(0); } String specialAbilityName; SpecialAbility specialAbility; while (it.hasNext()) { element = it.next(); tag = element.getName(); if (TAG_SUBSTITUTIONLEVEL.equals(tag)) { final String substitutionClassKeyName = EntityEncoder.decode(element.getText()); SubstitutionClass aSubstitutionClass = aPCClass .getSubstitutionClassKeyed(substitutionClassKeyName); if (aSubstitutionClass == null) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.ClassNotFound", //$NON-NLS-1$ substitutionClassKeyName); warnings.add(message); return; } aPCClass.setSubstitutionClassKey(substitutionClassKeyName, level); aSubstitutionClass.applyLevelArrayModsToLevel(aPCClass, level, thePC); } else if (TAG_HITPOINTS.equals(tag)) { try { aPCClass.setHitPoint(level - 1, Integer.valueOf(element .getText())); } catch (NumberFormatException nfe) { final String message = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidHP", //$NON-NLS-1$ tag, element.getText()); warnings.add(message); } } else if (TAG_SAVES.equals(tag)) { for (final PCGElement child : element.getChildren()) { final String dString = EntityEncoder.decode(child.getText()); // TODO This doesn't seem to be written in Creator. if (dString.startsWith(TAG_BONUS + TAG_SEPARATOR)) { aPCClass.addBonusList(dString.substring(6)); } aPCClass.addSave(dString); } } else if (TAG_SPECIALTIES.equals(tag)) { for (final PCGElement child : element.getChildren()) { aPCClass .addSpecialty(EntityEncoder.decode(child.getText())); } } else if (TAG_SPECIALABILITIES.equals(tag)) { for (PCGElement child : element.getChildren()) { // TODO - This looks like a bug. Looks like it should // be child.getText(). I am not sure how to test this // though. // specialAbilityName = EntityEncoder.decode(element.getText()); specialAbilityName = EntityEncoder.decode(child.getText()); if (pcgenVersion[0] <= 5 && pcgenVersion[1] <= 5 && pcgenVersion[2] < 6) { if (specialAbilityName.equals("Turn Undead")) //$NON-NLS-1$ { parseFeatLine("FEAT:Turn Undead|TYPE:SPECIAL.TURNUNDEAD|DESC:"); //$NON-NLS-1$ continue; } else if (specialAbilityName.equals("Rebuke Undead")) //$NON-NLS-1$ { parseFeatLine("FEAT:Rebuke Undead|TYPE:SPECIAL.TURNUNDEAD|DESC:"); //$NON-NLS-1$ continue; } } specialAbility = new SpecialAbility(specialAbilityName); // TODO This looks bogus. PCClass should probably be the // same case in both instances below. if (specialAbilityName.endsWith(":-1")) //$NON-NLS-1$ { specialAbilityName = specialAbilityName.substring(0, specialAbilityName.length() - 3); specialAbility = new SpecialAbility(specialAbilityName); specialAbility .setSASource("PCClass|" + aPCClass.getKeyName() + '|' + 0); //$NON-NLS-1$ } else { specialAbility = new SpecialAbility(specialAbilityName); specialAbility .setSASource("PCCLASS|" + aPCClass.getKeyName() + '|' + level); //$NON-NLS-1$ } if (!thePC.hasSpecialAbility(specialAbilityName)) { aPCClass.addToListFor(ListKey.SPECIAL_ABILITY, specialAbility); } if (!aPCClass.containsInList(ListKey.SAVE, specialAbilityName)) { aPCClass.addSave(specialAbilityName); } } } else if (tag.equals(TAG_LEVELABILITY)) { parseLevelAbilityInfo(element, aPCClass, level); } else if (tag.equals(TAG_ADDTOKEN)) { parseAddTokenInfo(element, aPCClass.getClassLevel(level)); } // // abbrev=score // else if (tag.equals(TAG_PRESTAT) || tag.equals(TAG_POSTSTAT)) { boolean isPre = false; if (tag.equals(TAG_PRESTAT)) { isPre = true; } final int idx = element.getText().indexOf('='); if (idx > 0) { final int idxStat = SettingsHandler.getGame().getStatFromAbbrev( element.getText().substring(0, idx)); if (idxStat >= 0) { try { thePC.saveStatIncrease(element.getText().substring( 0, idx), Integer.parseInt(element.getText() .substring(idx + 1)), isPre); } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidStatMod", //$NON-NLS-1$ tag, element.getText()); warnings.add(msg); } } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.UnknownStat", //$NON-NLS-1$ tag, element.getText()); warnings.add(msg); } } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.MissingEquals", //$NON-NLS-1$ tag, element.getText()); warnings.add(msg); } } else if ((pcl != null) && TAG_SKILLPOINTSGAINED.equals(tag)) { pcl.setSkillPointsGained(Integer.parseInt(element.getText())); } else if ((pcl != null) && TAG_SKILLPOINTSREMAINING.equals(tag)) { pcl .setSkillPointsRemaining(Integer .parseInt(element.getText())); } else if (TAG_DATA.equals(tag)) { // TODO // for now it's ok to ignore it! } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.UnknownTag", //$NON-NLS-1$ tag, element.getText()); warnings.add(msg); } } // TODO: // process data // // need to add some consistency checks here to avoid // - duplicate entries for one and the same class/level pair // - missing entries for a given class/level pair } private void parseAddTokenInfo(PCGElement element, CDOMObject cdo) { Iterator<PCGElement> it2 = element.getChildren().iterator(); List<TransitionChoice<?>> addList = cdo.getListFor(ListKey.ADD); if (addList == null) { //TODO Error return; } if (!it2.hasNext()) { //TODO Error return; } LoadContext context = Globals.getContext(); PCGElement addType = it2.next(); Class<? extends CDOMObject> cl = StringPClassUtil.getClassFor(addType.getName()); String dString = EntityEncoder.decode(addType.getText()); for (TransitionChoice<?> tc : addList) { ChoiceSet<?> choices = tc.getChoices(); if (dString.equals(choices.getLSTformat())) { //Match while (it2.hasNext()) { String choice = EntityEncoder.decode(it2.next().getText()); CDOMObject obj = context.ref.silentlyGetConstructedCDOMObject(cl, choice); if (obj == null) { //TODO Error } else { thePC.addAssociation(tc, obj); } } } } } /* * ############################################################### * Character Class(es) methods * ############################################################### */ private void parseClassLine(final String line) throws PCGParseException { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { /* * Classes are critical for characters, * need to stop the load process * * Thomas Behr 14-08-02 */ throw new PCGParseException( "parseClassLine", line, pcgpex.getMessage()); //$NON-NLS-1$ } PCClass aPCClass = null; String tag; PCGElement element; final Iterator<PCGElement> it = tokens.getElements().iterator(); // the first element defines the class key name!!! if (it.hasNext()) { element = it.next(); aPCClass = Globals.getContext().ref.silentlyGetConstructedCDOMObject(PCClass.class, EntityEncoder.decode(element .getText())); if (aPCClass != null) { // Icky: Need to redesign the way classes work! // Icky: Having to clone the class here is UGLY! aPCClass = aPCClass.clone(); } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.CouldntAddClass", //$NON-NLS-1$ element.getText()); warnings.add(msg); return; } } int level = -1; int skillPool = -1; while (it.hasNext()) { element = it.next(); tag = element.getName(); if (TAG_SUBCLASS.equals(tag)) { String subClassKey = EntityEncoder.decode(element.getText()); if ((subClassKey.length() > 0) && !subClassKey.equals(Constants.s_NONE)) { SubClass sc = aPCClass.getSubClassKeyed(subClassKey); if (sc == null) { if (subClassKey.equals(aPCClass.getKeyName())) { subClassKey = Constants.s_NONE; } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidSubclass", //$NON-NLS-1$ element.getText()); warnings.add(msg); } } } aPCClass.setSubClassKey(subClassKey); } if (TAG_LEVEL.equals(tag)) { try { level = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidLevel", //$NON-NLS-1$ element.getText()); warnings.add(msg); } } else if (TAG_SKILLPOOL.equals(tag)) { try { skillPool = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidSkillPool", //$NON-NLS-1$ element.getText()); warnings.add(msg); } } else if (TAG_CANCASTPERDAY.equals(tag)) { // TODO } else if (TAG_SPELLBASE.equals(tag)) { final String spellBase = EntityEncoder.decode(element.getText()); if (!Constants.s_NONE.equals(spellBase)) { Globals.getContext().unconditionallyProcess(aPCClass, "SPELLSTAT", spellBase); } } else if (TAG_PROHIBITED.equals(tag)) { PCClassLstToken token = (PCClassLstToken) TokenStore.inst().getTokenMap( PCClassLstToken.class).get(TAG_PROHIBITED); token.parse(aPCClass, EntityEncoder.decode(element.getText()), -9); } } if (level > -1) { thePC.getClassList().add(aPCClass); for (int i = 0; i < level; ++i) { PCLevelInfo levelInfo = thePC.saveLevelInfo(aPCClass.getKeyName()); aPCClass.addLevel(levelInfo, false, thePC); } } if (skillPool > -1) { aPCClass.setSkillPool(skillPool); } } /** * ############################################################### * Character Deity/Domain methods * ############################################################### * @param line **/ private void parseDeityLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalDeity", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } final String deityKey = EntityEncoder.decode(tokens.getElements().get(0).getText()); Deity aDeity = Globals.getContext().ref.silentlyGetConstructedCDOMObject( Deity.class, deityKey); if (aDeity != null) { thePC.setDeity(aDeity); } else if (!Constants.s_NONE.equals(deityKey)) { // TODO // create Deity object from information contained in pcg // for now issue a warning final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.DeityNotFound", //$NON-NLS-1$ deityKey); warnings.add(msg); } } private void parseDomainLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalDomain", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } PCGElement element; String tag; final Iterator<PCGElement> it = tokens.getElements().iterator(); if (it.hasNext()) { element = it.next(); // the first element defines the domain name final String domainKey = EntityEncoder.decode(element.getText()); final Domain aDomain = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Domain.class, domainKey); if ((aDomain == null) && (!Constants.s_NONE.equals(domainKey))) { // TODO // create Domain object from // information contained in pcg // But for now just issue a warning final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.DomainNotFound", //$NON-NLS-1$ domainKey); warnings.add(msg); } else if ((thePC.getCharacterDomainKeyed(domainKey) == null) && (!Constants.s_NONE.equals(domainKey))) { // PC doesn't have the domain, so create a new // one and add it to the PC domain list CharacterDomain aCharacterDomain = new CharacterDomain(); aCharacterDomain.setDomain(aDomain, thePC); while (it.hasNext()) { element = it.next(); tag = element.getName(); if (TAG_SOURCE.equals(tag)) { CharacterDomainParser parser = new CharacterDomainParser(); parser.setDomainSource(aCharacterDomain, sourceElementToString(element)); } else if (TAG_ASSOCIATEDDATA.equals(tag)) { aCharacterDomain.getDomain().addAssociated( EntityEncoder.decode(element.getText())); } } thePC.addCharacterDomain(aCharacterDomain); aDomain.applyDomain(thePC); // TODO // set associated list } else { // PC already has this domain Logging.errorPrintLocalised( "Errors.PCGenParser.DuplicateDomain", //$NON-NLS-1$ domainKey); } } } private void parseDomainSpellsLine(@SuppressWarnings("unused") String line) { // TODO } /** * ############################################################### * EquipSet Temp Bonuses * ############################################################### * @param line **/ private void parseEquipSetTempBonusLine(final String line) { PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalEquipSetTempBonus", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } String tag; String tagString = null; for (PCGElement element : tokens.getElements()) { tag = element.getName(); if (TAG_EQSETBONUS.equals(tag)) { tagString = EntityEncoder.decode(element.getText()); } } if (tagString == null) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidEquipSetTempBonus", //$NON-NLS-1$ line); warnings.add(msg); return; } final EquipSet eSet = thePC.getEquipSetByIdPath(tagString); if (eSet == null) { return; } //# EquipSet Temp Bonuses //EQSETBONUS:0.2|TEMPBONUS:SPELL=Haste|TBTARGET:PC|TEMPBONUS:SPELL=Shield of Faith|TBTARGET:PC final List<BonusObj> aList = new ArrayList<BonusObj>(); for (final PCGElement element : tokens.getElements()) { tag = element.getName(); if (TAG_TEMPBONUSBONUS.equals(tag)) { final String aString = EntityEncoder.decode(element.getText()); // Parse aString looking for // TEMPBONUS and TBTARGET pairs StringTokenizer aTok = new StringTokenizer(aString, TAG_SEPARATOR); if (aTok.countTokens() < 2) { continue; } String sName = aTok.nextToken(); String tName = aTok.nextToken(); aList.addAll(getBonusFromName(sName, tName)); } } eSet.setTempBonusList(aList); } /* * ############################################################### * Character Experience methods * ############################################################### */ private void parseExperienceLine(final String line) throws PCGParseException { final StringTokenizer stok = new StringTokenizer( line.substring(TAG_EXPERIENCE.length() + 1), TAG_END, false); try { thePC.setXP(Integer.parseInt(stok.nextToken())); } catch (NumberFormatException nfe) { throw new PCGParseException( "parseExperienceLine", line, nfe.getMessage()); //$NON-NLS-1$ } } private void parseEyeColorLine(final String line) { thePC.setEyeColor(EntityEncoder.decode(line.substring(TAG_EYECOLOR .length() + 1))); } /* * ############################################################### * Character Ability methods * ############################################################### */ private void parseAbilityLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalAbility", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } AbilityCategory category = null; Ability.Nature nature = Ability.Nature.NORMAL; String abilityCat = null; Ability ability = null; String abilityKey = ""; final Iterator<PCGElement> it = tokens.getElements().iterator(); // the first element defines the AbilityCategory key name if (it.hasNext()) { final PCGElement element = it.next(); final String categoryKey = EntityEncoder.decode(element.getText()); category = SettingsHandler.getGame().getAbilityCategory(categoryKey); if (category == null) { // emit a warning that the category doesn't exists. final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.AbilityCategoryNotFound", //$NON-NLS-1$ categoryKey); warnings.add(msg); // Create one. category = new AbilityCategory(categoryKey); SettingsHandler.getGame().addAbilityCategory(category); } } // The next element will be the nature if (it.hasNext()) { final PCGElement element = it.next(); final String natureKey = EntityEncoder.decode(element.getText()); nature = Ability.Nature.valueOf(natureKey); } // The next element will be the ability's innate category if (it.hasNext()) { final PCGElement element = it.next(); abilityCat = EntityEncoder.decode(element.getText()); } // The next element will be the ability key if (it.hasNext()) { final PCGElement element = it.next(); abilityKey = EntityEncoder.decode(element.getText()); ability = Globals.getAbilityKeyed(abilityCat, abilityKey); if (ability == null) { warnings.add("Unable to Find Ability: " + abilityKey); return; } else { ability = ability.clone(); } } while (it.hasNext()) { final PCGElement element = it.next(); final String tag = element.getName(); if (tag.equals(TAG_APPLIEDTO)) { final String appliedToKey = EntityEncoder.decode(element.getText()); if (appliedToKey.startsWith(TAG_MULTISELECT)) { // // Should be in the form: // MULTISELECCT:maxcount:#chosen:choice1:choice2:...:choicen // final StringTokenizer sTok = new StringTokenizer(appliedToKey, TAG_END, false); if (sTok.countTokens() > 2) { sTok.nextToken(); // should be TAG_MULTISELECT final int maxChoices = Integer.parseInt(sTok.nextToken()); sTok.nextToken(); // toss this--number of choices made final FeatMultipleChoice fmc = new FeatMultipleChoice(); fmc.setMaxChoices(maxChoices); while (sTok.hasMoreTokens()) { fmc.addChoice(sTok.nextToken()); } ability.addAssociated(fmc); } else { final String msg = PropertyFactory .getFormattedString( "Warnings.PCGenParser.IllegalAbilityIgnored", //$NON-NLS-1$ line); warnings.add(msg); } } else if ((ability.getSafe(ObjectKey.MULTIPLE_ALLOWED) && ability.getSafe(ObjectKey.STACKS)) || !ability.containsAssociated(appliedToKey)) { String[] assoc = appliedToKey.split(Constants.COMMA, -1); for (String string : assoc) { ability.addAssociated(string); } } } else if (TAG_SAVE.equals(tag)) { final String saveKey = EntityEncoder.decode(element.getText()); // TODO - This never gets written to the file if (saveKey.startsWith(TAG_BONUS) && (saveKey.length() > 6)) { ability.addBonusList(saveKey.substring(6)); } ability.addSave(saveKey); } } if (ability != null && category != null && nature != null) { if (nature == Ability.Nature.NORMAL) { // If we weren't loading an old character who had feats stored as seperate // lines, save the feat now. if (!featsPresent || category != AbilityCategory.FEAT) { thePC.addAbility(category, ability, null); } } else if (nature == Ability.Nature.VIRTUAL) { ability = AbilityUtilities.addVirtualAbility(ability, new ArrayList<String>(), thePC .getDirectVirtualAbilities(category), null); if (ability == null) { Logging .errorPrint("Failed to create virtual ability from line " + line); final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.CouldntAddAbility", //$NON-NLS-1$ abilityKey); warnings.add(msg); } else { ability.setNeedsSaving(true); } } } } /* * ############################################################### * Character Feats methods * ############################################################### */ private void parseFeatLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalFeat", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } final Iterator<PCGElement> it = tokens.getElements().iterator(); // the first element defines the Ability key name if (it.hasNext()) { final PCGElement element = it.next(); final String abilityKey = EntityEncoder.decode(element.getText()); /* First, check to see if the PC already has this ability. If so, * then we just need to mod it. Otherwise we need to create a new * one and add it using non-aggregate (when using aggregate, we * get clones of the PCs actual feats, which don't get saved or * preserved) */ Ability anAbility = thePC.getRealFeatKeyed(abilityKey); boolean added = false; if (anAbility != null) { added = parseFeatsHandleAppliedToAndSaveTags(it, anAbility, line); } else { // PC does not have the feat anAbility = Globals.getAbilityKeyed(Constants.FEAT_CATEGORY, abilityKey); if (anAbility != null) { // Clone the new feat anAbility = anAbility.clone(); // parse all the tags for this Feat added = parseFeatsHandleAppliedToAndSaveTags(it, anAbility, line); if (!added) { // add it to the list thePC.addFeat(anAbility, null); } featsPresent = true; } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.CouldntAddAbility", //$NON-NLS-1$ abilityKey); warnings.add(msg); return; } } } } private void parseFeatPoolLine(final String line) { try { double featPool = Double.parseDouble(line.substring(TAG_FEATPOOL .length() + 1)); // In earlier versions the featpool included the bonus, so we need to counter it if (compareVersionTo(new int[]{5,11,1}) < 0) { calcFeatPoolAfterLoad = true; baseFeatPool = featPool; } else { thePC.setFeats(featPool); } } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalFeatPool", //$NON-NLS-1$ line); warnings.add(msg); } } private void parseUserPoolLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalAbilityPool", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } final Iterator<PCGElement> it = tokens.getElements().iterator(); final String cat = EntityEncoder.decode(it.next().getText()); final AbilityCategory category = SettingsHandler.getGame().getAbilityCategory(cat); try { thePC.setUserPoolBonus(category, new BigDecimal(it.next().getText())); } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalAbilityPool", //$NON-NLS-1$ line); warnings.add(msg); } } private boolean parseFeatsHandleAppliedToAndSaveTags( final Iterator<PCGElement> it, final Ability aFeat, final String line) { boolean added = false; while (it.hasNext()) { final PCGElement element = it.next(); final String tag = element.getName(); if (TAG_APPLIEDTO.equals(tag)) { final String appliedToKey = EntityEncoder.decode(element.getText()); // This will delete a perfectly valid feat. Removed 03/26/06 boomer70 // if (aFeat.getName().endsWith("Weapon Proficiency")) // { // aPC.addWeaponProf(updateProficiencyName(appliedToKey, false)); // // // addWeaponProf adds the feat to this // // PC's list, so don't add it again! // added = true; // } if (appliedToKey.startsWith(TAG_MULTISELECT)) { // // Should be in the form: // MULTISELECCT:maxcount:#chosen:choice1:choice2:...:choicen // final StringTokenizer sTok = new StringTokenizer(appliedToKey, TAG_END, false); if (sTok.countTokens() > 2) { sTok.nextToken(); // should be TAG_MULTISELECT final int maxChoices = Integer.parseInt(sTok.nextToken()); sTok.nextToken(); // toss this--number of choices made final FeatMultipleChoice fmc = new FeatMultipleChoice(); fmc.setMaxChoices(maxChoices); while (sTok.hasMoreTokens()) { fmc.addChoice(sTok.nextToken()); } aFeat.addAssociated(fmc); } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalFeatIgnored", //$NON-NLS-1$ line); warnings.add(msg); } } else if ((aFeat.getSafe(ObjectKey.MULTIPLE_ALLOWED) && aFeat.getSafe(ObjectKey.STACKS)) || !aFeat.containsAssociated(appliedToKey)) { aFeat.addAssociated(appliedToKey); } } else if (TAG_SAVE.equals(tag)) { final String saveKey = EntityEncoder.decode(element.getText()); if (saveKey.startsWith(TAG_BONUS) && (saveKey.length() > 6)) { aFeat.addBonusList(saveKey.substring(6)); } aFeat.addSave(saveKey); } else if (tag.equals(TAG_LEVELABILITY)) { parseLevelAbilityInfo(element, aFeat); } else if (tag.equals(TAG_ADDTOKEN)) { parseAddTokenInfo(element, aFeat); } } return added; } /* * ############################################################### * Character Follower methods * ############################################################### */ private void parseFollowerLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalFollower", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } final Follower aFollower = new Follower(Constants.EMPTY_STRING, Constants.EMPTY_STRING, Constants.EMPTY_STRING); for (final PCGElement element : tokens.getElements()) { final String tag = element.getName(); if (TAG_FOLLOWER.equals(tag)) { aFollower.setName(EntityEncoder.decode(element.getText())); } else if (TAG_TYPE.equals(tag)) { aFollower.setType(EntityEncoder.decode(element.getText())); } else if (TAG_RACE.equals(tag)) { aFollower.setRace(EntityEncoder.decode(element.getText())); } else if (TAG_HITDICE.equals(tag)) { try { aFollower.setUsedHD(Integer.parseInt(element.getText())); } catch (NumberFormatException nfe) { // nothing we can do about it } } else if (TAG_FILE.equals(tag)) { aFollower.setRelativeFileName(EntityEncoder.decode(element .getText())); } } if (!Constants.EMPTY_STRING.equals(aFollower.getFileName()) && !Constants.EMPTY_STRING.equals(aFollower.getName()) && !Constants.EMPTY_STRING.equals(aFollower.getType())) { thePC.addFollower(aFollower); } } private void parseGameMode(final String line) throws PCGParseException { final String requestedMode = line.substring(TAG_GAMEMODE.length() + 1); final GameMode currentGameMode = SettingsHandler.getGame(); final String currentMode = currentGameMode.getName(); if (!requestedMode.equals(currentMode)) { final String msg = PropertyFactory.getFormattedString( "Exceptions.PCGenParser.WrongGameMode", //$NON-NLS-1$ requestedMode, currentMode); throw new PCGParseException("ParseGameMode", line, msg); //$NON-NLS-1$ } } private void parseGenderLine(final String line) { thePC.setGender(EntityEncoder.decode(line .substring(TAG_GENDER.length() + 1))); } /** * # HTML Output Sheet location * @param line **/ private void parseHTMLOutputSheetLine(final String line) { String aFileName = EntityEncoder.decode(line.substring(TAG_HTMLOUTPUTSHEET .length() + 1)); if (aFileName.length() <= 0) { aFileName = SettingsHandler.getSelectedCharacterHTMLOutputSheet(thePC); } thePC.setSelectedCharacterHTMLOutputSheet(aFileName); } private void parseHairColorLine(final String line) { thePC.setHairColor(EntityEncoder.decode(line.substring(TAG_HAIRCOLOR .length() + 1))); } private void parseHairStyleLine(final String line) { thePC.setHairStyle(EntityEncoder.decode(line.substring(TAG_HAIRSTYLE .length() + 1))); } private void parseHandedLine(final String line) { thePC.setHanded(EntityEncoder.decode(line .substring(TAG_HANDED.length() + 1))); } private void parseHeightLine(final String line) { try { thePC.setHeight(Integer.parseInt(line .substring(TAG_HEIGHT.length() + 1))); } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalHeight", //$NON-NLS-1$ line); warnings.add(msg); } } private void parseInterestsLine(final String line) { thePC.setInterests(EntityEncoder.decode(line.substring(TAG_INTERESTS .length() + 1))); } private void parseKitLine(final String line) { final StringTokenizer stok = new StringTokenizer(line.substring(TAG_KIT.length() + 1), TAG_SEPARATOR, false); if (stok.countTokens() != 2) { // TODO This if switch currently does nothing? } /** final String region = */ stok.nextToken(); //TODO: Is this intended to be thrown away? The value is never used. /** final String kit = stok.nextToken(); */ final Kit aKit = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Kit.class, line.substring(TAG_KIT.length() + 1)); if (aKit == null) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.KitNotFound", //$NON-NLS-1$ line); warnings.add(msg); return; } thePC.addKit(aKit); } /* * ############################################################### * Character Languages methods * ############################################################### */ private void parseLanguageLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalLanguage", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } for (PCGElement element : tokens.getElements()) { final Language aLang = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Language.class, EntityEncoder .decode(element.getText())); if (aLang == null) { final String message = "No longer speaks language: " + element.getText(); //$NON-NLS-1$ warnings.add(message); continue; } thePC.addLanguage(aLang); } } /** * # Load companions with master? * @param line **/ private void parseLoadCompanionLine(final String line) { thePC.setLoadCompanion(line.endsWith(VALUE_Y)); } private void parseLocationLine(final String line) { thePC.setLocation(EntityEncoder.decode(line.substring(TAG_LOCATION .length() + 1))); } private void parseMasterLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalMaster", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } final Follower aMaster = new Follower(Constants.EMPTY_STRING, Constants.EMPTY_STRING, Constants.EMPTY_STRING); for (PCGElement element : tokens.getElements()) { final String tag = element.getName(); if (TAG_MASTER.equals(tag)) { aMaster.setName(EntityEncoder.decode(element.getText())); } else if (TAG_TYPE.equals(tag)) { aMaster.setType(EntityEncoder.decode(element.getText())); } else if (TAG_HITDICE.equals(tag)) { try { aMaster.setUsedHD(Integer.parseInt(element.getText())); } catch (NumberFormatException nfe) { // nothing we can do about it } } else if (TAG_FILE.equals(tag)) { /* * quick and dirty way to handle ':' * need to come up with a clean solution before releasing * * author: Thomas Behr 09-09-02 */ aMaster.setRelativeFileName(EntityEncoder.decode(element .getText())); } else if (TAG_ADJUSTMENT.equals(tag)) { aMaster.setAdjustment(Integer.parseInt(element.getText())); } } if (!Constants.EMPTY_STRING.equals(aMaster.getFileName()) && !Constants.EMPTY_STRING.equals(aMaster.getName()) && !Constants.EMPTY_STRING.equals(aMaster.getType())) { thePC.setMaster(aMaster); } } /* * ############################################################### * Character Notes Tab methods * ############################################################### */ private void parseNoteLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.IllegalNotes", //$NON-NLS-1$ line, pcgpex.getMessage()); warnings.add(msg); return; } final NoteItem ni = new NoteItem(-1, -1, Constants.EMPTY_STRING, Constants.EMPTY_STRING); for (PCGElement element : tokens.getElements()) { final String tag = element.getName(); if (TAG_NOTE.equals(tag)) { ni.setName(EntityEncoder.decode(element.getText())); } else if (TAG_ID.equals(tag)) { try { ni.setIdValue(Integer.parseInt(element.getText())); } catch (NumberFormatException nfe) { ni.setIdValue(-1); final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidNotes", //$NON-NLS-1$ line); warnings.add(msg); break; } } else if (TAG_PARENTID.equals(tag)) { try { ni.setParentId(Integer.parseInt(element.getText())); } catch (NumberFormatException nfe) { ni.setIdValue(-1); final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidNotes", //$NON-NLS-1$ line); warnings.add(msg); break; } } else if (TAG_VALUE.equals(tag)) { ni.setValue(EntityEncoder.decode(element.getText())); } } if (ni.getId() > -1) { thePC.addNotesItem(ni); } } /** * # PDF Output Sheet location * @param line **/ private void parsePDFOutputSheetLine(final String line) { String aFileName = EntityEncoder.decode(line .substring(TAG_PDFOUTPUTSHEET.length() + 1)); if (aFileName.length() <= 0) { aFileName = SettingsHandler.getSelectedCharacterPDFOutputSheet(thePC); } thePC.setSelectedCharacterPDFOutputSheet(aFileName); } private void parsePersonalityTrait1Line(final String line) { thePC.setTrait1(EntityEncoder.decode(line .substring(TAG_PERSONALITYTRAIT1.length() + 1))); } private void parsePersonalityTrait2Line(final String line) { thePC.setTrait2(EntityEncoder.decode(line .substring(TAG_PERSONALITYTRAIT2.length() + 1))); } private void parsePhobiasLine(final String line) { thePC.setPhobias(EntityEncoder.decode(line.substring(TAG_PHOBIAS .length() + 1))); } private void parsePlayerNameLine(final String line) { thePC.setPlayersName(EntityEncoder.decode(line.substring(TAG_PLAYERNAME .length() + 1))); } private void parsePoolPointsLine(final String line) { try { thePC.setPoolAmount(Integer.parseInt(line.substring(TAG_POOLPOINTS .length() + 1))); } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidPoolPoints", //$NON-NLS-1$ line); warnings.add(msg); } } private void parsePoolPointsLine2(final String line) { try { thePC.setPointBuyPoints(Integer.parseInt(line .substring(TAG_POOLPOINTSAVAIL.length() + 1))); thePC .setCostPool(thePC.getPointBuyPoints() - thePC.getPoolAmount()); } catch (NumberFormatException nfe) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.InvalidPoolPoints", //$NON-NLS-1$ line); warnings.add(msg); } } private void parsePortraitLine(final String line) { thePC.setPortraitPath(EntityEncoder.decode(line.substring(TAG_PORTRAIT .length() + 1))); } private void parseRaceLine(final String line) throws PCGParseException { final StringTokenizer sTok = new StringTokenizer(line.substring(TAG_RACE.length() + 1), TAG_SEPARATOR, false); final String race_name = EntityEncoder.decode(sTok.nextToken()); final Race aRace = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Race.class, race_name); if (aRace != null) { thePC.setRace(aRace); if (sTok.hasMoreTokens()) { final String aString = sTok.nextToken(); if (aString.startsWith(TAG_HITPOINTS)) { final StringTokenizer aTok = new StringTokenizer(aString.substring(TAG_HITPOINTS .length()), TAG_END, false); String msgKey = "Warnings.PCGenParser.RaceNoHD"; //$NON-NLS-1$ Race race = thePC.getRace(); LevelCommandFactory lcf = race.get(ObjectKey.MONSTER_CLASS); if (lcf != null) { final PCClass mclass = lcf.getPCClass(); int levels = lcf.getLevelCount().resolve(thePC, "") .intValue(); thePC.incrementClassLevel(levels, mclass, true); final HashMap<String, Integer> hitPointMap = processHitPoints( race_name, levels, aString, aTok); for (String lvlStr : hitPointMap.keySet()) { int lvl = Integer.parseInt(lvlStr); PCLevelInfo info = thePC.getLevelInfo().get(lvl); PCClass pcClass = thePC.getClassKeyed(info .getClassKeyName()); pcClass.setHitPoint(lvl, hitPointMap.get(lvlStr)); } msgKey = "Warnings.PCGenParser.RaceNoHDDefMon"; //$NON-NLS-1$ } final String msg = PropertyFactory.getFormattedString( msgKey, race_name); warnings.add(msg); } else { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.UnknownRaceInfo", //$NON-NLS-1$ aString); warnings.add(msg); } } } else { final String msg = PropertyFactory.getFormattedString( "Exceptions.PCGenParser.RaceNotFound", //$NON-NLS-1$ race_name); throw new PCGParseException("parseRaceLine", line, msg); //$NON-NLS-1$ } // TODO // adjust for more information according to PCGVer1Creator.appendRaceLine } private void parseFavoredClassLine(final String line) { String favClass = EntityEncoder.decode(line.substring(TAG_FAVOREDCLASS.length() + 1)); PCClass cl = Globals.getContext().ref.silentlyGetConstructedCDOMObject( PCClass.class, favClass); if (cl != null) { thePC.setSelectedFavoredClass(cl); } } /** * Translate the string of hitpoint values into a map. * @param race_name The name of the race, for error reporting. * @param hitDice The number of hitdice expected * @param aString The original string, for error reporting * @param aTok The hit point string already tokenized * @return A map of the levels and their hitpoint values. * @throws PCGParseException If the value cannot be parsed. */ private HashMap<String, Integer> processHitPoints(final String race_name, int hitDice, final String aString, final StringTokenizer aTok) throws PCGParseException { int i = 0; final HashMap<String, Integer> hitPointMap = new HashMap<String, Integer>(); while (aTok.hasMoreTokens()) { if (i >= hitDice) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.RaceFewerHD", //$NON-NLS-1$ race_name); warnings.add(msg); break; } try { hitPointMap.put(Integer.toString(i++), Integer .valueOf(aTok.nextToken())); } catch (NumberFormatException ex) { throw new PCGParseException( "parseRaceLine", aString, ex.getMessage()); //$NON-NLS-1$ } } if (i < hitDice) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.RaceMoreHD", //$NON-NLS-1$ race_name); warnings.add(msg); } return hitPointMap; } /* * ############################################################### * Character Region methods * ############################################################### */ private void parseRegionLine(final String line) { final String r = EntityEncoder.decode(line.substring(TAG_REGION.length() + 1)); thePC.setRegion(r); } //this method is obsolete, but left in for backward-compatibility, replaced by parseCityLine() private void parseResidenceLine(final String line) { thePC.setResidence(EntityEncoder.decode(line.substring(TAG_RESIDENCE .length() + 1))); thePC.setDirty(true); // trigger a save prompt so that the PCG will be updated } /* * ############################################################### * Character Skills methods * ############################################################### */ private void parseSkillLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal Skill line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } Skill aSkill = null; final Iterator<PCGElement> it = tokens.getElements().iterator(); // the first element defines the skill key name!!! if (it.hasNext()) { final PCGElement element = it.next(); final String skillKey = EntityEncoder.decode(element.getText()); aSkill = thePC.getSkillKeyed(skillKey); if (aSkill == null) { aSkill = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Skill.class, skillKey); if (aSkill != null) { // Icky: Need to redesign the way skills work! // Icky: Having to clone the skill here is UGLY! aSkill = aSkill.clone(); thePC.getSkillList().add(aSkill); } else { final String message = "Could not add skill: " + skillKey; warnings.add(message); return; } } } while (it.hasNext()) { final PCGElement element = it.next(); final String tag = element.getName(); if (TAG_SYNERGY.equals(tag)) { // TODO // for now it's ok to ignore it! } else if (TAG_OUTPUTORDER.equals(tag)) { int outputindex = 0; try { outputindex = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { // This is not critical. // Maybe warn the user? } aSkill.setOutputIndex(outputindex); } else if (TAG_CLASSBOUGHT.equals(tag)) { PCGElement childClass = null; PCGElement childRanks = null; for (PCGElement child : element.getChildren()) { if (TAG_CLASS.equals(child.getName())) { childClass = child; } else if (TAG_RANKS.equals(child.getName())) { childRanks = child; } } if (childClass == null) { final String message = "Invalid class/ranks specification: " + line; warnings.add(message); continue; } if (childRanks == null) { final String message = "Invalid class/ranks specification: " + line; warnings.add(message); continue; } //None for a class is a special key word. It is used when a familiar inherits a skill from its master PCClass aPCClass = null; if (!childClass.getText().equals("None")) //$NON-NLS-1$ { final String childClassKey = EntityEncoder.decode(childClass.getText()); aPCClass = thePC.getClassKeyed(childClassKey); if (aPCClass == null) { final String message = "Could not find class: " + childClassKey; warnings.add(message); continue; } } try { double ranks = Double.parseDouble(childRanks.getText()); aSkill.modRanks(ranks, aPCClass, true, thePC); } catch (NumberFormatException nfe) { final String message = "Invalid ranks specification: " + childRanks.getText(); warnings.add(message); continue; } } else if (TAG_ASSOCIATEDDATA.equals(tag)) { aSkill.addAssociated(EntityEncoder.decode(element.getText())); } else if (tag.equals(TAG_LEVELABILITY)) { parseLevelAbilityInfo(element, aSkill); } else if (tag.equals(TAG_ADDTOKEN)) { parseAddTokenInfo(element, aSkill); } } } /** * # Skills Output order * @param line **/ private void parseSkillsOutputOrderLine(final String line) { try { thePC.setSkillsOutputOrder(Integer.parseInt(line .substring(TAG_SKILLSOUTPUTORDER.length() + 1))); } catch (NumberFormatException nfe) { final String message = "Illegal Skills Output Order line ignored: " + line; warnings.add(message); } } private void parseSkinColorLine(final String line) { thePC.setSkinColor(EntityEncoder.decode(line.substring(TAG_SKINCOLOR .length() + 1))); } private void parseSpeechPatternLine(final String line) { thePC.setSpeechTendency(EntityEncoder.decode(line .substring(TAG_SPEECHPATTERN.length() + 1))); } /* * ############################################################### * Spell Book Information methods * ############################################################### */ /* * #Spell Book Information * SPELLBOOK:bookname|TYPE:spellbooktype */ private void parseSpellBookLines(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal Spell book ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } SpellBook aSpellBook = null; for (PCGElement element : tokens.getElements()) { final String tag = element.getName(); if (TAG_SPELLBOOK.equals(tag)) { final String bookName = EntityEncoder.decode(element.getText()); aSpellBook = new SpellBook(bookName, SpellBook.TYPE_PREPARED_LIST); } else if (TAG_TYPE.equals(tag)) { try { aSpellBook.setType(Integer.parseInt(element.getText())); } catch (NumberFormatException nfe) { // nothing we can do about it final String message = "Spell book " + aSpellBook.getName() + " had an illegal type: " + element.getText() + " in line " + line; warnings.add(message); } } else if (TAG_AUTOADDKNOWN.equals(tag)) { if (VALUE_Y.equals(element.getText())) { thePC.setSpellBookNameToAutoAddKnown(aSpellBook.getName()); } } } thePC.addSpellBook(aSpellBook); } /* * ############################################################### * Character Spells Information methods * ############################################################### */ private void parseSpellLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal Spell line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } Spell aSpell = null; PCClass aPCClass = null; PObject source = null; String spellBook = null; int times = 1; int spellLevel = 0; int numPages = 0; final List<Ability> metaFeats = new ArrayList<Ability>(); Object obj = null; int ppCost = -1; for (final PCGElement element : tokens.getElements()) { final String tag = element.getName(); if (TAG_SPELLNAME.equals(tag)) { final String spellName = EntityEncoder.decode(element.getText()); // either NULL (no spell), a Spell instance, // or ArrayList of Spells (with same name) obj = Globals.getSpellMap().get(spellName); if (obj instanceof Spell) { aSpell = (Spell) obj; } if (obj == null) { final String message = "Could not find spell named: " + spellName; warnings.add(message); return; } } else if (TAG_TIMES.equals(tag)) { try { times = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { // nothing we can do about it } } else if (TAG_CLASS.equals(tag)) { final String classKey = EntityEncoder.decode(element.getText()); aPCClass = thePC.getClassKeyed(classKey); if (aPCClass == null) { final String message = "Invalid class specification: " + classKey; warnings.add(message); return; } } else if (TAG_SPELL_BOOK.equals(tag)) { spellBook = EntityEncoder.decode(element.getText()); } else if (TAG_SPELLLEVEL.equals(tag)) { try { spellLevel = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { // nothing we can do about it } } else if (TAG_SPELLPPCOST.equals(tag)) { try { ppCost = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { // nothing we can do about it } } else if (TAG_SPELLNUMPAGES.equals(tag)) { try { numPages = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { // nothing we can do about it } } else if (TAG_SOURCE.equals(tag)) { String typeName = Constants.EMPTY_STRING; String objectKey = Constants.EMPTY_STRING; for (final PCGElement child : element.getChildren()) { final String childTag = child.getName(); if (TAG_TYPE.equals(childTag)) { typeName = child.getText().toUpperCase(); } else if (TAG_NAME.equals(childTag)) { objectKey = child.getText(); } } if (TAG_DOMAIN.equals(typeName)) { source = thePC.getCharacterDomainKeyed(objectKey); if (source == null) { final String message = "Could not find domain: " + objectKey; warnings.add(message); return; } } else { // it's either the class, sub-class or a cast-as class // first see if it's the class if (((aPCClass != null) && objectKey.equals(aPCClass .getKeyName())) || (aPCClass.getSpellKey().indexOf( typeName + '|' + objectKey) >= 0)) { source = aPCClass; } else { source = thePC.getClassKeyed(objectKey); // see if PC has the class } } } else if (TAG_FEATLIST.equals(tag)) { for (PCGElement child : element.getChildren()) { final String featKey = EntityEncoder.decode(child.getText()); final Ability anAbility = Globals.getAbilityKeyed(Constants.FEAT_CATEGORY, featKey); if (anAbility != null) { metaFeats.add(anAbility); } } } } if ((obj == null) || (aPCClass == null) || (spellBook == null)) { final String message = "Illegal Spell line ignored: " + line; warnings.add(message); return; } /* * this can only happen if the source type was NOT DOMAIN! */ if (source == null) { source = aPCClass; } if (obj instanceof List) { // find the instance of Spell in this class // best suited to this spell for (final Spell spell : (ArrayList<Spell>) obj) { // valid spell has a non-negative spell level if ((spell != null) && (spell.getFirstLevelForKey(source.getSpellKey(), thePC) >= 0)) { aSpell = spell; break; } } if (aSpell == null) { Logging.errorPrint("Could not resolve spell " + obj.toString()); } } if (aSpell == null) { final String message = "Could not find spell named: " + String.valueOf(obj); warnings.add(message); return; } // just to make sure the spellbook is present thePC.addSpellBook(spellBook); final SpellBook book = thePC.getSpellBookByName(spellBook); final Integer[] spellLevels = aSpell.levelForKey(source.getSpellKey(), thePC); boolean found = false; for (int sindex = 0; sindex < spellLevels.length; ++sindex) { final int level = spellLevels[sindex]; if (level < 0) { final List<PCSpell> aList = source.getSpellList(); if (aList == null) { continue; } for (PCSpell pcSpell : aList) { if (pcSpell == null) { continue; } found = (aSpell.getKeyName().equals(pcSpell.getKeyName()) && pcSpell .getSpellbook().equals(spellBook)); if (found) { break; } } continue; } found = true; // do not load auto knownspells into default spellbook if (spellBook.equals(Globals.getDefaultSpellBook()) && aPCClass.isAutoKnownSpell(aSpell.getKeyName(), level, thePC) && thePC.getAutoSpells()) { continue; } CharacterSpell aCharacterSpell = aPCClass.getSpellSupport().getCharacterSpellForSpell( aSpell, aPCClass); // PC does not have that spell on that classes list // so we'll need to add it to the list if (aCharacterSpell == null) { aCharacterSpell = new CharacterSpell(source, aSpell); if (!(source instanceof Domain)) { aCharacterSpell.addInfo(level, times, spellBook); } aPCClass.getSpellSupport().addCharacterSpell(aCharacterSpell); } SpellInfo aSpellInfo = null; if (source.getKeyName().equals(aPCClass.getKeyName()) || !spellBook.equals(Globals.getDefaultSpellBook())) { aSpellInfo = aCharacterSpell.getSpellInfoFor(spellBook, spellLevel, -1); // This doesn't make sense. What does the // metaFeats list have to do with this? if ((aSpellInfo == null) || !metaFeats.isEmpty()) { aSpellInfo = aCharacterSpell.addInfo(spellLevel, times, spellBook); } } if (aSpellInfo != null) { if (!metaFeats.isEmpty()) { aSpellInfo.addFeatsToList(metaFeats); } aSpellInfo.setActualPPCost(ppCost); aSpellInfo.setNumPages(numPages); book.setNumPagesUsed(book.getNumPagesUsed() + numPages); book.setNumSpells(book.getNumSpells() + 1); } } // end sindex for loop if (!found) { final String message = "Could not find spell " + aSpell.getDisplayName() + " in " + shortClassName(source) + " " + source.getDisplayName(); warnings.add(message); } } /* * ############################################################### * Spell List Information methods * ############################################################### */ /* * #Spell List Information * SPELLLIST:sourceclassname|spelllistentry|spelllistentry */ private void parseSpellListLines(final String line) { final String subLine = line.substring(TAG_SPELLLIST.length() + 1); final StringTokenizer stok = new StringTokenizer(subLine, TAG_SEPARATOR, false); final String classKey = stok.nextToken(); final PCClass aClass = thePC.getClassKeyed(classKey); AbstractReferenceContext refContext = Globals.getContext().ref; while ((aClass != null) && stok.hasMoreTokens()) { final String tok = stok.nextToken(); if (tok.startsWith("CLASS.")) { ClassSpellList csl = refContext .silentlyGetConstructedCDOMObject(ClassSpellList.class, tok.substring(6)); aClass.addClassSpellList(csl); } else if (tok.startsWith("DOMAIN.")) { DomainSpellList dsl = refContext .silentlyGetConstructedCDOMObject( DomainSpellList.class, tok.substring(7)); aClass.addClassSpellList(dsl); } else { /* * This is 5.14-ish, but have to try anyway: */ ClassSpellList csl = refContext .silentlyGetConstructedCDOMObject(ClassSpellList.class, tok); if (csl == null) { DomainSpellList dsl = refContext .silentlyGetConstructedCDOMObject( DomainSpellList.class, tok); if (dsl != null) { aClass.addClassSpellList(dsl); } } else { aClass.addClassSpellList(csl); /* * TODO This makes no sense to me - WHY do we have to add * the spells by hand? - look at Rev 6416 and older for this * behavior, but I don't understand it - thpr, 1 Jun 08 */ PCClass spellClass = refContext.silentlyGetConstructedCDOMObject(PCClass.class, csl.getLSTformat()); if (spellClass != null) { aClass.getSpellSupport().addSpells(-1, spellClass.getSpellList()); } } } } } /* * ############################################################### * Character Attributes methods * ############################################################### */ private void parseStatLine(final String line) throws PCGParseException { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { /* * Ability scores are critical for characters, * need to stop the load process * * Thomas Behr 09-09-02 */ throw new PCGParseException( "parseStatLine", line, pcgpex.getMessage()); //$NON-NLS-1$ } final Iterator<PCGElement> it = tokens.getElements().iterator(); if (it.hasNext()) { PCGElement element = it.next(); final String statName = element.getText(); final int index = SettingsHandler.getGame().getStatFromAbbrev(statName); if ((index > -1) && seenStats.add(statName.toUpperCase()) && (it.hasNext())) { element = it.next(); try { thePC.getStatList().getStatAt(index).setBaseScore( Integer.parseInt(element.getText())); } catch (NumberFormatException nfe) { throw new PCGParseException( "parseStatLine", line, nfe.getMessage()); //$NON-NLS-1$ } } else { final String message = "Invalid attribute specification. " + "Cannot load character."; throw new PCGParseException("parseStatLine", line, message); //$NON-NLS-1$ } } else { final String message = "Invalid attribute specification. " + "Cannot load character."; throw new PCGParseException("parseStatLine", line, message); //$NON-NLS-1$ } } private void parseTabNameLine(final String line) { thePC.setTabName(EntityEncoder.decode(line.substring(TAG_TABNAME .length() + 1))); } /* * ############################################################### * Character Templates methods * ############################################################### */ private void parseTemplateLine(final String line) { if (line.charAt(TAG_TEMPLATESAPPLIED.length() + 1) == '[') { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal Template line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } PCTemplate aPCTemplate = null; Iterator<PCGElement> it = tokens.getElements().iterator(); if (it.hasNext()) { final PCGElement element = it.next(); for (final PCGElement child : element.getChildren()) { final String childTag = child.getName(); if (TAG_NAME.equals(childTag)) { aPCTemplate = addKeyedTemplate(child.getText()); if (aPCTemplate == null) { break; } } else if (TAG_CHOSENFEAT.equals(childTag)) { String mapKey = null; String mapValue = null; for (PCGElement subChild : child.getChildren()) { final String subChildTag = subChild.getName(); if (TAG_MAPKEY.equals(subChildTag)) { mapKey = subChild.getText(); } else if (TAG_MAPVALUE.equals(subChildTag)) { mapValue = subChild.getText(); } } if ((mapKey != null) && (mapValue != null)) { PCTemplate subt = Compatibility.getTemplateFor(aPCTemplate, EntityEncoder.decode(mapKey), EntityEncoder .decode(mapValue)); if (subt != null) { thePC.addChosenFeat(subt, mapValue); } } } else if (TAG_CHOSENTEMPLATE.equals(childTag)) { for (PCGElement subChild : child.getChildren()) { final String subChildTag = subChild.getName(); if (TAG_NAME.equals(subChildTag)) { final String ownedTemplateKey = EntityEncoder .decode(subChild.getText()); final PCTemplate ownedTemplate = Globals .getContext().ref .silentlyGetConstructedCDOMObject( PCTemplate.class, ownedTemplateKey); if (ownedTemplate != null) { thePC.setTemplatesAdded(aPCTemplate, ownedTemplate); } } } } } } } else { addKeyedTemplate(line.substring(TAG_TEMPLATESAPPLIED.length() + 1)); } } /** * # Use temporary mods/bonuses? * @param line **/ private void parseUseTempModsLine(final String line) { thePC.setUseTempMods(line.endsWith(VALUE_Y)); } private void parseVFeatLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal VFeat line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } Ability anAbility = null; final Iterator<PCGElement> it = tokens.getElements().iterator(); // the first element defines the Feat key name if (it.hasNext()) { final PCGElement element = it.next(); final String abilityKey = EntityEncoder.decode(element.getText()); anAbility = Globals .getAbilityKeyed(Constants.FEAT_CATEGORY, abilityKey); if (anAbility == null) { final String message = "Could not add vfeat: " + abilityKey; warnings.add(message); return; } anAbility = AbilityUtilities.addVirtualAbility(Constants.FEAT_CATEGORY, abilityKey, thePC .getDirectVirtualAbilities(AbilityCategory.FEAT), null); anAbility.setNeedsSaving(true); thePC.setDirty(true); } parseFeatsHandleAppliedToAndSaveTags(it, anAbility, line); // TODO // process all additional information } /** * Parses the version information from a PCG file. * @param line The line containing version information * @throws PCGParseException if the line is not a valid version line */ protected void parseVersionLine(final String line) throws PCGParseException { int[] version = {0, 0, 0}; // Check to make sure that this is a version line if (!line.startsWith(TAG_VERSION + TAG_END)) { throw new PCGParseException( "parseVersionLine", line, "Not a Version Line."); //$NON-NLS-1$ } // extract the tokens from the version line String[] tokens = line.substring(TAG_VERSION.length() + 1).split(" |\\.|\\-", 4); //$NON-NLS-1$ for (int idx = 0; idx < 3 && idx < tokens.length; idx++) { try { version[idx] = Integer.parseInt(tokens[idx]); } catch (NumberFormatException e) { if (idx == 2 && (tokens[idx].startsWith("RC"))) { pcgenVersionSuffix = tokens[2]; } else { // Something in the first 3 digits was not an integer throw new PCGParseException( "parseVersionLine", line, "Invalid PCGen version."); //$NON-NLS-1$ } } } if (tokens.length == 4) { pcgenVersionSuffix = tokens[3]; } pcgenVersion = version; } /* * ############################################################### * Character Weapon proficiencies methods * ############################################################### */ private void parseWeaponProficienciesLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal Weapon proficiencies line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } PObject source = null; for (PCGElement element : tokens.getElements()) { if (TAG_SOURCE.equals(element.getName())) { String type = Constants.EMPTY_STRING; String key = Constants.EMPTY_STRING; for (final PCGElement child : element.getChildren()) { final String tag = child.getName(); if (TAG_TYPE.equals(tag)) { type = child.getText().toUpperCase(); } else if (TAG_NAME.equals(tag)) { key = child.getText(); } } if (Constants.EMPTY_STRING.equals(type) || Constants.EMPTY_STRING.equals(key)) { final String message = "Illegal Weapon proficiencies line ignored: " + line; warnings.add(message); return; } if (TAG_RACE.equals(type)) { source = thePC.getRace(); } else if (TAG_PCCLASS.equals(type)) { source = thePC.getClassKeyed(key); } else if (TAG_DOMAIN.equals(type)) { source = thePC.getCharacterDomainKeyed(key); } else if (TAG_DOMAIN.equals(type)) { source = thePC.getFeatKeyed(key); } // Fix for bug 1185344 else if (TAG_ABILITY.equals(type)) { source = thePC.getFeatAutomaticKeyed(key); } // End of Fix if (source == null) { final String message = "Invalid source specification: " + line; warnings.add(message); } break; } } final PCGElement element = tokens.getElements().get(0); if (source == null) { for (PCGElement child : element.getChildren()) { weaponprofs.add(updateProficiencyName(child.getText(), true)); } } else { for (PCGElement child : element.getChildren()) { source.addSelectedWeaponProfBonus(updateProficiencyName(child .getText(), true)); } } } private void parseWeightLine(final String line) { try { thePC.setWeight(Integer.parseInt(line .substring(TAG_WEIGHT.length() + 1))); } catch (NumberFormatException nfe) { final String message = "Illegal Weight line ignored: " + line; warnings.add(message); } } private static String shortClassName(final Object o) { final Class<?> objClass = o.getClass(); final String pckName = objClass.getPackage().getName(); return objClass.getName().substring(pckName.length() + 1); } /** * @deprecated * @param aString * @param decode * @return */ @Deprecated private static String updateProficiencyName(final String aString, final boolean decode) { String profKey = aString; if (decode) { profKey = EntityEncoder.decode(aString); } // TODO This is totally bogus. This will never really work properly. // This logic should be deprecated and removed. if (Globals.getContext().ref.silentlyGetConstructedCDOMObject(WeaponProf.class, profKey) == null) { int idx = profKey.indexOf("1-H"); //$NON-NLS-1$ if (idx >= 0) { profKey = profKey.substring(0, idx) + "Exotic" + profKey.substring(idx + 3); //$NON-NLS-1$ } else { idx = profKey.indexOf("2-H"); //$NON-NLS-1$ if (idx >= 0) { profKey = profKey.substring(0, idx) + "Martial" + profKey.substring(idx + 3); //$NON-NLS-1$ } } } return aString; } private void checkWeaponProficiencies() { // thePC.setAutomaticFeatsStable(false); thePC.setAutomaticAbilitiesStable(null, false); for (final Iterator<String> it = weaponprofs.iterator(); it.hasNext();) { if (thePC.hasWeaponProfKeyed(it.next())) { it.remove(); } } // // For some reason, character had a proficiency that they should not have. Inform // the user that they no longer have the proficiency. // if (weaponprofs.size() > 0) { String s = weaponprofs.toString(); s = s.substring(1, s.length() - 1); final String message = "No longer proficient with following weapon(s):" + Constants.s_LINE_SEP + s; warnings.add(message); } } /** * ############################################################### * Character EquipSet Stuff * ############################################################### * @param line **/ private void parseCalcEquipSet(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { /* * EquipSet is not critical for characters, * no need to stop the load process */ final String message = "Illegal Calc EquipSet line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } final String calcEQId = EntityEncoder.decode(tokens.getElements().get(0).getText()); if (calcEQId != null) { thePC.setCalcEquipSetId(calcEQId); } } /* * ############################################################### * Character Description/Bio/History methods * ############################################################### */ private void parseCharacterBioLine(final String line) { thePC.setBio(EntityEncoder.decode(line.substring(TAG_CHARACTERBIO .length() + 1))); } private void parseEquipmentLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal Equipment line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } final String itemKey; Equipment aEquip; PCGElement element; String tag; // the first element defines the item key name element = tokens.getElements().get(0); itemKey = EntityEncoder.decode(element.getText()); // might be dynamically created container aEquip = thePC.getEquipmentNamed(itemKey); if (aEquip == null) { // Must load custom equipment from the .pcg file // before we check the Global list (which may get // loaded from customeEquipment.lst) as equipment // in the PC's .pcg may contain additional info // such as Charges on a wand, etc // // Make sure that we are not picking up custom items! aEquip = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Equipment.class, itemKey); if (aEquip != null) { if (aEquip.isType(Constants.s_CUSTOM)) { aEquip = null; } else { // standard item aEquip = aEquip.clone(); } } if (line.indexOf(TAG_CUSTOMIZATION) >= 0) { // might be customized item for (Iterator<PCGElement> it = tokens.getElements().iterator(); it .hasNext();) { element = it.next(); if (TAG_CUSTOMIZATION.equals(element.getName())) { String baseItemKey = Constants.EMPTY_STRING; String customProperties = Constants.EMPTY_STRING; for (PCGElement child : element.getChildren()) { final String childTag = child.getName(); if (TAG_BASEITEM.equals(childTag)) { baseItemKey = EntityEncoder.decode(child.getText()); } else if (TAG_DATA.equals(childTag)) { customProperties = EntityEncoder.decode(child.getText()); } } if (aEquip != null && baseItemKey.equals(aEquip.getBaseItemName())) { // We clear out any eqmods that the base item has as the // EQMODs on the saved item override them. aEquip.clearAllEqModifiers(); aEquip.load(customProperties, "$", "=", thePC); //$NON-NLS-1$ //$NON-NLS-2$ } else { // Make sure that we are not picking up custom items! Equipment aEquip2 = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Equipment.class, baseItemKey); if (aEquip2 != null) { // Make sure we are not getting a custom item if (aEquip2.isType(Constants.s_CUSTOM)) { aEquip2 = null; } else { // standard item aEquip = aEquip2.clone(); aEquip.load(customProperties, "$", "=", thePC); //$NON-NLS-1$//$NON-NLS-2$ aEquip.remove(StringKey.OUTPUT_NAME); if (!aEquip.isType(Constants.s_CUSTOM)) { aEquip.addMyType(Constants.s_CUSTOM); } Globals.getContext().ref.importObject(aEquip .clone()); } } } break; } } } if (aEquip == null) { final String msg = PropertyFactory.getFormattedString( "Warnings.PCGenParser.EquipmentNotFound", //$NON-NLS-1$ itemKey); warnings.add(msg); return; } thePC.addEquipment(aEquip); } for (final Iterator<PCGElement> it = tokens.getElements().iterator(); it .hasNext();) { element = it.next(); tag = element.getName(); if (TAG_QUANTITY.equals(tag)) { float oldQty = aEquip.getQty(); aEquip.setQty(element.getText()); thePC.updateEquipmentQty(aEquip, oldQty, aEquip.getQty()); } else if (TAG_OUTPUTORDER.equals(tag)) { int index = 0; try { index = Integer.parseInt(element.getText()); } catch (NumberFormatException nfe) { // nothing we can or have to do about this } aEquip.setOutputIndex(index); if (aEquip.isAutomatic()) { thePC.cacheOutputIndex(aEquip); } } else if (TAG_COST.equals(tag)) { // TODO This else if switch currently does nothing? } else if (TAG_WT.equals(tag)) { // TODO This else if switch currently does nothing? } } } private void parseEquipmentSetLine(final String line) { final PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal EquipSet line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } String setName = null; String setID = null; String itemKey = null; String setNote = null; Float itemQuantity = null; boolean useTempMods = false; for (PCGElement element : tokens.getElements()) { final String tag = element.getName(); if (TAG_EQUIPSET.equals(tag)) { setName = EntityEncoder.decode(element.getText()); } else if (TAG_ID.equals(tag)) { setID = element.getText(); } else if (TAG_VALUE.equals(tag)) { itemKey = EntityEncoder.decode(element.getText()); } else if (TAG_QUANTITY.equals(tag)) { try { itemQuantity = new Float(element.getText()); } catch (NumberFormatException nfe) { itemQuantity = new Float(0.0f); } } else if (TAG_NOTE.equals(tag)) { setNote = EntityEncoder.decode(element.getText()); } else if (TAG_USETEMPMODS.equals(tag)) { useTempMods = element.getText().endsWith(VALUE_Y); } } if ((setName == null) || Constants.EMPTY_STRING.equals(setName) || (setID == null) || Constants.EMPTY_STRING.equals(setID)) { final String message = "Illegal EquipSet line ignored: " + line; warnings.add(message); return; } final EquipSet aEquipSet; Equipment aEquip; Equipment eqI; aEquipSet = new EquipSet(setID, setName); if (setNote != null) { aEquipSet.setNote(setNote); } if (itemKey != null) { aEquipSet.setValue(itemKey); eqI = thePC.getEquipmentNamed(itemKey); if (eqI == null) { eqI = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Equipment.class, itemKey); } if (eqI == null) { final String message = "Could not find equipment: " + itemKey; warnings.add(message); return; } aEquip = eqI.clone(); if (itemQuantity != null) { aEquipSet.setQty(itemQuantity); aEquip.setQty(itemQuantity); aEquip.setNumberCarried(itemQuantity); } // if the idPath is longer than 3 // it's inside a container if ((new StringTokenizer(setID, ".")).countTokens() > 3) //$NON-NLS-1$ { // get parent EquipSet final EquipSet aEquipSet2 = thePC.getEquipSetByIdPath(aEquipSet.getParentIdPath()); // get the container Equipment aEquip2 = null; if (aEquipSet2 != null) { aEquip2 = aEquipSet2.getItem(); } // add the child to container if (aEquip2 != null) { aEquip2.insertChild(thePC, aEquip); aEquip.setParent(aEquip2); } } aEquipSet.setItem(aEquip); } aEquipSet.setUseTempMods(useTempMods); thePC.addEquipSet(aEquipSet); } /* * ############################################################### * Character Equipment methods * ############################################################### */ private void parseMoneyLine(final String line) { thePC.setGold(line.substring(TAG_MONEY.length() + 1)); } /** * ############################################################### * Temporary Bonuses * ############################################################### * @param line **/ private void parseTempBonusLine(final String line) { PCGTokenizer tokens; try { tokens = new PCGTokenizer(line); } catch (PCGParseException pcgpex) { final String message = "Illegal TempBonus line ignored: " + line + Constants.s_LINE_SEP + "Error: " + pcgpex.getMessage(); warnings.add(message); return; } String cTag = null; String tName = null; for (PCGElement element : tokens.getElements()) { final String tag = element.getName(); if (TAG_TEMPBONUS.equals(tag)) { cTag = EntityEncoder.decode(element.getText()); } else if (TAG_TEMPBONUSTARGET.equals(tag)) { tName = EntityEncoder.decode(element.getText()); } } if ((cTag == null) || (tName == null)) { warnings.add("Illegal TempBonus line ignored: " + line); return; } final StringTokenizer aTok = new StringTokenizer(cTag, "=", false); //$NON-NLS-1$ if (aTok.countTokens() < 2) { return; } final String cType = aTok.nextToken(); final String cKey = aTok.nextToken(); Equipment aEq = null; if (!tName.equals(TAG_PC)) { // bonus is applied to an equipment item // so create a new one and add to PC final Equipment eq = thePC.getEquipmentNamed(tName); if (eq == null) { return; } aEq = eq.clone(); //aEq.setWeight("0"); aEq.resetTempBonusList(); } for (PCGElement element : tokens.getElements()) { final String tag = element.getName(); final String bonus; if (TAG_TEMPBONUSBONUS.equals(tag)) { bonus = EntityEncoder.decode(element.getText()); } else { continue; } if ((bonus == null) || (bonus.length() <= 0)) { continue; } BonusObj newB = null; // Check the Creator type so we know what // type of object to set as the creator if (cType.equals(TAG_FEAT)) { final Ability aFeat = Globals.getAbilityKeyed(Constants.FEAT_CATEGORY, cKey); // if (aFeat == null) // { // aFeat = Globals.getAbilityNamed("FEAT", cKey); // } if (aFeat != null) { newB = Bonus.newBonus(bonus); newB.setCreatorObject(aFeat); } } else if (cType.equals(TAG_EQUIPMENT)) { Equipment aEquip = thePC.getEquipmentNamed(cKey); if (aEquip == null) { aEquip = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Equipment.class, cKey); } if (aEquip != null) { newB = Bonus.newBonus(bonus); newB.setCreatorObject(aEquip); } } else if (cType.equals(TAG_CLASS)) { final PCClass aClass = thePC.getClassKeyed(cKey); if (aClass == null) { continue; } int idx = bonus.indexOf('|'); newB = Bonus.newBonus(bonus.substring(idx + 1)); newB.setCreatorObject(aClass); } else if (cType.equals(TAG_TEMPLATE)) { PCTemplate aTemplate = thePC.getTemplateKeyed(cKey); if (aTemplate == null) { aTemplate = Globals.getContext().ref.silentlyGetConstructedCDOMObject(PCTemplate.class, cKey); } if (aTemplate != null) { newB = Bonus.newBonus(bonus); newB.setCreatorObject(aTemplate); } } else if (cType.equals(TAG_SKILL)) { Skill aSkill = thePC.getSkillKeyed(cKey); if (aSkill == null) { aSkill = Globals.getContext().ref.silentlyGetConstructedCDOMObject(Skill.class, cKey); } if (aSkill != null) { newB = Bonus.newBonus(bonus); newB.setCreatorObject(aSkill); } } else if (cType.equals(TAG_SPELL)) { final Spell aSpell = Globals.getSpellKeyed(cKey); if (aSpell != null) { newB = Bonus.newBonus(bonus); newB.setCreatorObject(aSpell); } } if (newB == null) { return; } // Check to see if the target was the PC or an Item if (tName.equals(TAG_PC)) { newB.setApplied(true); newB.setTargetObject(thePC); thePC.addTempBonus(newB); } else { newB.setApplied(true); newB.setTargetObject(aEq); aEq.addTempBonus(newB); thePC.addTempBonus(newB); } } if (aEq != null) { aEq.setAppliedName(cKey); thePC.addTempBonusItemList(aEq); } } private void sortCharacterSpells() { // now sort each classes spell list for (PCClass pcClass : thePC.getClassList()) { pcClass.getSpellSupport().sortCharacterSpellList(); } } /* * currently source is either empty or * PCCLASS|classname|classlevel (means it's a chosen special ability) * PCCLASS=classname|classlevel (means it's a defined special ability) * DEITY=deityname|totallevels */ private static String sourceElementToString(final PCGElement source) { String type = Constants.EMPTY_STRING; String name = Constants.EMPTY_STRING; String level = Constants.EMPTY_STRING; String defined = Constants.EMPTY_STRING; for (PCGElement child : source.getChildren()) { final String tag = child.getName(); if (TAG_TYPE.equals(tag)) { type = child.getText(); } else if (TAG_NAME.equals(tag)) { name = child.getText(); } else if (TAG_LEVEL.equals(tag)) { level = child.getText(); } else if (TAG_DEFINED.equals(tag)) { defined = child.getText().toUpperCase(); } } //TODO:gorm - guestimate good starting buffer size final StringBuffer buffer = new StringBuffer(1000); buffer.append(type); buffer.append((VALUE_Y.equals(defined)) ? '=' : '|'); buffer.append(name); if (!Constants.EMPTY_STRING.equals(level)) { buffer.append('|'); buffer.append(level); } return buffer.toString(); } /* * ############################################################### * Inner classes * ############################################################### */ private static final class PCGElement { private final String name; private List<PCGElement> children; private String text; private PCGElement(final String aName) { this.name = aName; } /** * Returns a string representation of the element. This string is * written in XML format. * @return An XML formatted string. * @see java.lang.Object#toString() */ @Override public String toString() { //TODO:gorm - optimize stringbuffer size final StringBuffer buffer = new StringBuffer(1000); buffer.append('<').append(getName()).append('>').append(LINE_SEP); buffer .append("<text>").append(getText()).append("</text>").append(LINE_SEP); //$NON-NLS-1$//$NON-NLS-2$ for (PCGElement child : getChildren()) { buffer.append(child.toString()).append(LINE_SEP); } buffer.append("</").append(getName()).append('>'); //$NON-NLS-1$ return buffer.toString(); } /** * Returns all the children of this element. * <p><b>Note</b>: This has a side effect of initializing the children * list for the element. * @return A <code>List</code> of children */ public List<PCGElement> getChildren() { if (children == null) { this.children = new ArrayList<PCGElement>(0); } return children; } private String getName() { return name; } private String getText() { return (text != null) ? text : Constants.EMPTY_STRING; } private void addContent(final PCGElement child) { if (children == null) { this.children = new ArrayList<PCGElement>(0); } children.add(child); } private void addContent(final String argText) { text = argText; } } private static final class PCGTokenizer { private final List<PCGElement> elements; private final String innerDelimiter; private final String nestedStartDelimiter; private final String nestedStopDelimiter; private final String outerDelimiter; private final char nestedStartDelimiterChar; private final char nestedStopDelimiterChar; /** * Constructor * @param line * @throws PCGParseException */ private PCGTokenizer(final String line) throws PCGParseException { this(line, ":|[]"); //$NON-NLS-1$ } /** * Constructor * <br> * @param line a String to tokenize * @param delimiters a FOUR-character String specifying the four needed delimiters: * <ol> * <li>the inner delimiter for a PCGElement</li> * <li>the outer delimiter for a PCGElement</li> * <li>the start delimiter for nested PCGElements</li> * <li>the stop delimiter for nested PCGElement</li> * </ol> * @throws PCGParseException */ private PCGTokenizer(final String line, final String delimiters) throws PCGParseException { final char[] dels = delimiters.toCharArray(); this.innerDelimiter = String.valueOf(dels[0]); this.outerDelimiter = String.valueOf(dels[1]); this.nestedStartDelimiter = String.valueOf(dels[2]); this.nestedStopDelimiter = String.valueOf(dels[3]); this.nestedStartDelimiterChar = nestedStartDelimiter.charAt(0); this.nestedStopDelimiterChar = nestedStopDelimiter.charAt(0); this.elements = new ArrayList<PCGElement>(0); tokenizeLine(line); } private List<PCGElement> getElements() { return elements; } private void checkSyntax(final String line) throws PCGParseException { final char[] chars = line.toCharArray(); int delimCount = 0; for (int i = 0; i < chars.length; ++i) { if (chars[i] == nestedStartDelimiterChar) { ++delimCount; } else if (chars[i] == nestedStopDelimiterChar) { --delimCount; } } if (delimCount < 0) { final String message = "Missing " + nestedStartDelimiter; throw new PCGParseException("PCGTokenizer::checkSyntax", line, message); } else if (delimCount > 0) { final String message = "Missing " + nestedStopDelimiter; throw new PCGParseException("PCGTokenizer::checkSyntax", line, message); } } private void tokenizeLine(final String line) throws PCGParseException { checkSyntax(line); final PCGElement root = new PCGElement("root"); //$NON-NLS-1$ tokenizeLine(root, line); elements.addAll(root.getChildren()); } private void tokenizeLine(final PCGElement parent, final String line) throws PCGParseException { final String dels = outerDelimiter + nestedStartDelimiter + nestedStopDelimiter; final StringTokenizer tokens = new StringTokenizer(line, dels, true); int nestedDepth = 0; String tag = null; final StringBuffer buffer = new StringBuffer(1000); while (tokens.hasMoreTokens()) { String token = tokens.nextToken().trim(); if (token.equals(outerDelimiter)) { if (nestedDepth == 0) { if (buffer.length() > 0) { token = buffer.toString(); int index = token.indexOf(innerDelimiter); if (index >= 0) { buffer.delete(0, buffer.length()); final PCGElement element = new PCGElement(token .substring(0, index)); element.addContent(token.substring(index + 1)); parent.addContent(element); } else { final String message = "Malformed PCG element: " + token; throw new PCGParseException( "PCGTokenizer::tokenizeLine", line, message); } } } else { buffer.append(token); } } else if (token.equals(nestedStartDelimiter)) { if (nestedDepth == 0) { token = buffer.toString(); int index = token.indexOf(innerDelimiter); if ((index >= 0) && (index == (token.length() - 1))) { buffer.delete(0, buffer.length()); tag = token.substring(0, index); } else { final String message = "Malformed PCG element: " + token; throw new PCGParseException( "PCGTokenizer::tokenizeLine", line, message); } } else { buffer.append(token); } ++nestedDepth; } else if (token.equals(nestedStopDelimiter)) { --nestedDepth; if (nestedDepth == 0) { final PCGElement element = new PCGElement(tag); tokenizeLine(element, buffer.toString()); parent.addContent(element); buffer.delete(0, buffer.length()); } else { buffer.append(token); } } else { buffer.append(token); } } if (buffer.length() > 0) { final String token = buffer.toString(); final int index = token.indexOf(innerDelimiter); if (index >= 0) { buffer.delete(0, buffer.length()); final PCGElement element = new PCGElement(token.substring(0, index)); element.addContent(token.substring(index + 1)); parent.addContent(element); } else { final String message = "Malformed PCG element: " + token; throw new PCGParseException("PCGTokenizer::tokenizeLine", line, message); } } } } /** * Returns the version of the application that wrote the file * @return An <code>int</code> array containing the 3 digit version */ protected int[] getPcgenVersion() { return pcgenVersion; } /** * Compare the PCG version with a supplied version number * @param inVer The version to compare with the PCG version. Must have at least 3 elements. * @return the value 0 if the PCG version is equal to the supplied version; a * value less than 0 if the PCG version is less than the supplied version; * and a value greater than 0 if the PCG version is greater than the supplied version. */ protected int compareVersionTo(int inVer[]) { return CoreUtility.compareVersions(pcgenVersion, inVer); } /** * Returns any extra version info after the regular version number. * @return String extra version information */ protected String getPcgenVersionSuffix() { return pcgenVersionSuffix; } private void parseLevelAbilityInfo(final PCGElement element, final PObject pObj) { parseLevelAbilityInfo(element, pObj, -9); } private void parseLevelAbilityInfo(final PCGElement element, final PObject pObj, final int level) { final Iterator<PCGElement> it2 = element.getChildren().iterator(); if (it2.hasNext()) { LevelAbility la; final PCLevelInfo pcLevelInfo; final String dString = EntityEncoder.decode(it2.next().getText()); if (pObj instanceof PCClass) { la = pObj.addAddList(level, dString); pcLevelInfo = thePC.getLevelInfoFor(pObj.getKeyName(), level); } else { la = null; for (LevelAbility laTemp : pObj.getLevelAbilityList()) { if (laTemp.getTagData().startsWith(dString)) { la = laTemp; break; } } if (la == null) { warnings.add(pObj.getDisplayName() + "(" + pObj.getClass().getName() + ")\nCould not find LevelAbility: " + dString); // // Couldn't find it, so add it and hope for the best... // la = pObj.addAddList(level, dString); } pcLevelInfo = null; } final List<String> choiceList = new ArrayList<String>(); if (la != null) { la.process(choiceList, thePC, pcLevelInfo); choiceList.clear(); while (it2.hasNext()) { final String choice = EntityEncoder.decode(it2.next().getText()); choiceList.add(choice); } if (pcLevelInfo != null) { la.processChoice(null, choiceList, thePC, pcLevelInfo); } else { la.addAllToAssociated(choiceList); } } } } /** * @return the baseFeatPool */ public double getBaseFeatPool() { return baseFeatPool; } /** * @return the calcFeatPoolAfterLoad */ public boolean isCalcFeatPoolAfterLoad() { return calcFeatPoolAfterLoad; } }

The table below shows all metrics for PCGVer2Parser.java.

MetricValueDescription
BLOCKS798.00Number of blocks
BLOCK_COMMENT364.00Number of block comment lines
COMMENTS759.00Comment lines
COMMENT_DENSITY 0.30Comment density
COMPARISONS570.00Number of comparison operators
CYCLOMATIC660.00Cyclomatic complexity
DECL_COMMENTS65.00Comments in declarations
DOC_COMMENT211.00Number of javadoc comment lines
ELOC2550.00Effective lines of code
EXEC_COMMENTS143.00Comments in executable code
EXITS469.00Procedure exits
FUNCTIONS121.00Number of function declarations
HALSTEAD_DIFFICULTY141.36Halstead difficulty
HALSTEAD_EFFORT 0.00Halstead effort
INTERFACE_COMPLEXITY329.00Interface complexity
JAVA0001 0.00JAVA0001 Package name does not contain only lower case letters
JAVA0002 1.00JAVA0002 Package name does not begin with a top level domain name or country code
JAVA0003 0.00JAVA0003 Minimize use of on-demand (.*) imports
JAVA0004 0.00JAVA0004 Unnecessary import from java.lang
JAVA0005 0.00JAVA0005 Imports not in specified order
JAVA0006 0.00JAVA0006 Empty finally block
JAVA0007 0.00JAVA0007 Should not declare public field
JAVA0008 0.00JAVA0008 Empty catch block
JAVA0009 4.00JAVA0009 Protected member in final class
JAVA0010 0.00JAVA0010 Non-instantiable class does not contain a non-private static member
JAVA0011 0.00JAVA0011 Abstract class does not contain an abstract method
JAVA0012 0.00JAVA0012 Non-constructor method with same name as declaring class
JAVA0013 0.00JAVA0013 Non-blank final field is not static
JAVA0014 0.00JAVA0014 Class with only static members has non-private constructor
JAVA0015 0.00JAVA0015 Package class contains public nested type
JAVA0016 0.00JAVA0016 Abstract class contains public constructor
JAVA0017 0.00JAVA0017 Class name does not have required form
JAVA0018 0.00JAVA0018 Method name does not have required form
JAVA0019 0.00JAVA0019 Interface name does not have required form
JAVA0020 0.00JAVA0020 Field name does not have required form
JAVA0021 0.00JAVA0021 Interface method name does not have required form
JAVA0022 0.00JAVA0022 Static final field name does not have required form
JAVA0023 0.00JAVA0023 Empty finalize method
JAVA0024 0.00JAVA0024 Empty class
JAVA0025 0.00JAVA0025 Method override is empty
JAVA0026 0.00JAVA0026 Finalize method with parameters
JAVA0029 0.00JAVA0029 Private method not used
JAVA0030 0.00JAVA0030 Private field not used
JAVA0031 0.00JAVA0031 Case statement not properly closed
JAVA0032 0.00JAVA0032 Switch statement missing default
JAVA0033 0.00JAVA0033 default: not last case in switch statement
JAVA0034 0.00JAVA0034 Missing braces in if statement
JAVA0035 0.00JAVA0035 Missing braces in for statement
JAVA0036 0.00JAVA0036 Missing braces in while statement
JAVA0038 0.00JAVA0038 Non-case label in switch statement
JAVA0039 0.00JAVA0039 Break statement with label
JAVA0040 0.00JAVA0040 Switch statement contains N cases (maximum: M)
JAVA0041 0.00JAVA0041 Nested synchronized block
JAVA0042 0.00JAVA0042 Empty synchronized statement
JAVA0043 0.00JAVA0043 Inner class does not use outer class
JAVA0044 0.00JAVA0044 Serializable class with no instance variables
JAVA0045 0.00JAVA0045 Serializable class with only transient fields
JAVA0046 0.00JAVA0046 Name of class not derived from Exception ends with 'Exception'
JAVA0047 0.00JAVA0047 Serializable class derives from invalid base class
JAVA0048 0.00JAVA0048 Name of class derived from Exception does not end with 'Exception'
JAVA004919.00JAVA0049 Nested block at depth N (maximum: M)
JAVA0050 0.00JAVA0050 Class derives from java.lang.Error
JAVA0051 0.00JAVA0051 Class derives from java.lang.RuntimeException
JAVA0052 0.00JAVA0052 Class derives from java.lang.Throwable
JAVA0053 0.00JAVA0053 Unused label
JAVA0054 0.00JAVA0054 Inheritance depth N exceeds maximum M
JAVA0055 0.00JAVA0055 Class should be interface
JAVA0056 0.00JAVA0056 Unnecessary abstract modifier for interface or annotation
JAVA0057 0.00JAVA0057 Unnecessary default constructor
JAVA0058 0.00JAVA0058 Constructor calls super()
JAVA0059 0.00JAVA0059 Method override only calls super()
JAVA0061 0.00JAVA0061 Inaccessible member in anonymous class
JAVA0062 0.00JAVA0062 Public class missing public member or protected constructor
JAVA0063 0.00JAVA0063 Identifier name should not contain '$'
JAVA0064 1.00JAVA0064 N variations of identifier name (maximum: M)
JAVA0065 0.00JAVA0065 Unnecessary final modifier for method in final class
JAVA0066 0.00JAVA0066 Unnecessary modifier for interface nested type
JAVA0067 0.00JAVA0067 Array descriptor on identifier name
JAVA0068 0.00JAVA0068 Modifiers not declared in recommended order
JAVA0071 0.00JAVA0071 Strings compared with ==
JAVA0073 0.00JAVA0073 Integer division in floating-point context
JAVA0074 0.00JAVA0074 Use of Object.notify()
JAVA0075 0.00JAVA0075 Method parameter hides field
JAVA007615.00JAVA0076 Use of magic number
JAVA0077 0.00JAVA0077 Private field not used in declaring class
JAVA0078 0.00JAVA0078 Floating point values compared with ==
JAVA0079 0.00JAVA0079 Use of instance to reference static member
JAVA0080 0.00JAVA0080 Import declaration not used
JAVA0081 0.00JAVA0081 Boolean literal in comparison
JAVA0082 0.00JAVA0082 Unnecessary widening cast
JAVA0083 0.00JAVA0083 Unnecessary instanceof test
JAVA0084 0.00JAVA0084 Should use compound assignment operator
JAVA0085 0.00JAVA0085 Use of sun.* class
JAVA0087 0.00JAVA0087 Use of Thread.sleep()
JAVA0089 0.00JAVA0089 Use of restricted package
JAVA0092 0.00JAVA0092 Use of restricted type
JAVA0093 0.00JAVA0093 Redundant assignment
JAVA0094 0.00JAVA0094 Field hides a superclass field
JAVA0095 0.00JAVA0095 Uninitialized private field
JAVA0096 0.00JAVA0096 Field in nested class hides outer field
JAVA0098 0.00JAVA0098 Minimize use of implicit field initializers
JAVA0100 0.00JAVA0100 Class contains N non-final fields (maximum: M)
JAVA0101 0.00JAVA0101 Unnecessary modifier for field in interface
JAVA0102 0.00JAVA0102 Last statement in finalize() not super.finalize()
JAVA0103 0.00JAVA0103 Explicit call to finalize()
JAVA0104 0.00JAVA0104 finalize() only calls super.finalize()
JAVA0105 0.00JAVA0105 Duplicate import declaration
JAVA0106 0.00JAVA0106 Unnecessary import from current package
JAVA0108 2.00JAVA0108 Incorrect javadoc: no @param tag for 'parameter'
JAVA0109 2.00JAVA0109 Incorrect javadoc: no parameter 'parameter'
JAVA0110 0.00JAVA0110 Incorrect javadoc: no @return tag
JAVA0111 0.00JAVA0111 Incorrect javadoc: @return tag for void method
JAVA0112 0.00JAVA0112 Incorrect javadoc: no exception 'exception' in throws
JAVA0113 0.00JAVA0113 Incorrect javadoc: no @author tag
JAVA0114 0.00JAVA0114 Incorrect javadoc: no @version tag
JAVA0115 0.00JAVA0115 Incorrect javadoc: no @throws or @exception tag for 'exception'
JAVA0116 0.00JAVA0116 Missing javadoc: field 'field'
JAVA0117 0.00JAVA0117 Missing javadoc: method 'method'
JAVA0118 0.00JAVA0118 Missing javadoc: type 'type'
JAVA0119 0.00JAVA0119 Control variable changed within body of for loop
JAVA0123 0.00JAVA0123 Use all three components of for loop
JAVA0125 0.00JAVA0125 Continue statement with label
JAVA0126 0.00JAVA0126 Method declares unchecked exception in throws
JAVA0128 0.00JAVA0128 Public constructor in non-public class
JAVA0130 2.00JAVA0130 Non-static method does not use instance fields
JAVA0131 0.00JAVA0131 Compatible method does not override base
JAVA0132 0.00JAVA0132 Method overload with compatible signature
JAVA0133 0.00JAVA0133 Non-synchronized method overrides synchronized method
JAVA0135 0.00JAVA0135 Only one of Object.equals and Object.hashCode defined: missing 'method'
JAVA0136 1.00JAVA0136 N methods defined in class (maximum: M)
JAVA0137 0.00JAVA0137 Non-abstract class missing constructor
JAVA0138 0.00JAVA0138 N parameters defined for method (maximum: M)
JAVA0139 0.00JAVA0139 Definition of main other than public static void main(java.lang.String[])
JAVA0141 0.00JAVA0141 Unnecessary modifier for method in interface
JAVA0143 0.00JAVA0143 Synchronized method
JAVA0144 2.00JAVA0144 Line exceeds maximum M characters
JAVA014515587.00JAVA0145 Tab character used in source file
JAVA0150 0.00JAVA0150 java.lang.Error (or subclass) thrown
JAVA0153 0.00JAVA0153 Inefficient conversion of integer to string
JAVA0159 0.00JAVA0159 Inefficient conversion of string to integer
JAVA0160 0.00JAVA0160 Method does not throw specified exception
JAVA0161 0.00JAVA0161 Conditional wait() not in loop
JAVA0163 0.00JAVA0163 Empty statement
JAVA0165 0.00JAVA0165 Conflicting return statement in finally block
JAVA0166 0.00JAVA0166 Generic exception caught
JAVA0167 0.00JAVA0167 ThreadDeath not rethrown
JAVA0169 0.00JAVA0169 Unnecessary catch block: exception 'exception'
JAVA0170 0.00JAVA0170 Caught exception not derived from java.lang.Exception
JAVA0171 0.00JAVA0171 Unused local variable
JAVA0173 1.00JAVA0173 Unused method parameter
JAVA0174 4.00JAVA0174 Assigned local variable never used
JAVA0175 1.00JAVA0175 Successive assignment to variable
JAVA0176 0.00JAVA0176 Local variable name does not have required form
JAVA017743.00JAVA0177 Variable declaration missing initializer
JAVA0179 0.00JAVA0179 Local variable hides visible field
JAVA0233 0.00JAVA0233 Definition of serialVersionUID other than 'private static final long serialVersionUID'
JAVA0234 0.00JAVA0234 Class is Serializable but does not define serialVersionUID
JAVA0235 0.00JAVA0235 Class defines serialVersionUID but does not implement Serializable
JAVA0236 0.00JAVA0236 Attempt to clone an object which does not implement Cloneable
JAVA0237 0.00JAVA0237 Class implements Cloneable but does not have public clone method
JAVA0238 0.00JAVA0238 Clone method does not call super.clone()
JAVA0239 0.00JAVA0239 Class declares 'readObject' or 'writeObject' but does not implement Serializable
JAVA0240 0.00JAVA0240 Serializable class which declares readObject or writeObject but not both
JAVA0241 0.00JAVA0241 'readObject' or 'writeObject' should be declared private in Serializable class
JAVA0242 0.00JAVA0242 Transient field in non-Serializable class
JAVA0243 0.00JAVA0243 'readResolve' or 'writeReplace' should be declared private or protected
JAVA0244 0.00JAVA0244 Field or method name in subclass differs only by case from inherited field or method
JAVA0245 0.00JAVA0245 JUnit TestCase with non-trivial constructor
JAVA0246 0.00JAVA0246 JUnit assertXXX statement missing message parameter
JAVA0247 0.00JAVA0247 JUnit 'setUp()' and 'tearDown()' should call super method
JAVA0248 0.00JAVA0248 JUnit method 'setUp' or 'tearDown' with incorrect signature
JAVA0249 0.00JAVA0249 JUnit TestCase 'suite()' should be declared static
JAVA0250 0.00JAVA0250 JUnit TestCase declares testXXX method with incorrect signature
JAVA0251 0.00JAVA0251 Use '%n' for line breaks in printf/format for platform independence
JAVA0252 0.00JAVA0252 'enum' is a Java 1.5 reserved word
JAVA0253 0.00JAVA0253 Not all enum constants consumed in switch statement
JAVA0254 3.00JAVA0254 Use enhanced for loop construct instead of Iterator
JAVA0255 0.00JAVA0255 Result of method invocation not used
JAVA0256 0.00JAVA0256 Assignment of external collection/array to field
JAVA0257 1.00JAVA0257 Use of 'Constant Interface' anti-pattern
JAVA0258 0.00JAVA0258 Implement Iterable for foreach compatibility
JAVA0259 3.00JAVA0259 Return of collection/array field
JAVA0260 0.00JAVA0260 Use 'enum' instead of Enumerated Type pattern
JAVA0261 0.00JAVA0261 Use specialized Enum collection types
JAVA0262 0.00JAVA0262 Use of char in integer context
JAVA0263 0.00JAVA0263 Long literal ends with 'l' instead of 'L'
JAVA0264 0.00JAVA0264 Integer math in long context - check for overflow
JAVA0265 0.00JAVA0265 Use of Throwable.printStackTrace()
JAVA0266 0.00JAVA0266 Use of System.out
JAVA0267 0.00JAVA0267 Use of System.err
JAVA0269 0.00JAVA0269 Contents of StringBuffer never used
JAVA0270 3.00JAVA0270 Use Java 5.0 enhanced for loop construct to iterate over all elements in an array
JAVA0271 0.00JAVA0271 Minimize use of on-demand (.*) static imports
JAVA0272 0.00JAVA0272 Thread.run() called
JAVA0273 0.00JAVA0273 Non-final derivative of Thread calls start() in constructor
JAVA0274 0.00JAVA0274 Serializable class has a synchronized readObject()
JAVA0275 0.00JAVA0275 Serializable class has a synchronized writeObject() and no other synchronized methods
JAVA0276 0.00JAVA0276 Unnecessary use of String constructor
JAVA0277 0.00JAVA0277 Iterator.next() implementation does not throw NoSuchElementException
JAVA0278 0.00JAVA0278 Unnecessary use of Boolean constructor
JAVA0279 0.00JAVA0279 Serialization method readObject or readObjectNoData calls an overridable method
JAVA0280 0.00JAVA0280 IllegalMonitorStateException caught
JAVA0281 0.00JAVA0281 Iterator.next() not called in loop
JAVA0282 0.00JAVA0282 Call to Iterator.next() in loop which does not test Iterator.hasNext()
JAVA0283 1.00JAVA0283 Control variable not updated in loop body
JAVA0284 0.00JAVA0284 Explicit garbage collection
JAVA028534.00JAVA0285 Dereference of potentially null variable
JAVA0286 0.00JAVA0286 Dereference of null variable
JAVA0287 0.00JAVA0287 Unnecessary null check
JAVA0288 0.00JAVA0288 Inconsistent null check
LINES5592.00Number of lines in the source file
LINE_COMMENT184.00Number of line comments
LOC4152.00Lines of code
LOGICAL_LINES1283.00Number of statements
LOOPS23.00Number of loops
NEST_DEPTH 9.00Maximum nesting depth
OPERANDS7279.00Number of operands
OPERATORS13908.00Number of operators
PARAMS120.00Number of formal parameter declarations
PROGRAM_LENGTH21187.00Halstead program length
PROGRAM_VOCAB1685.00Halstead program vocabulary
PROGRAM_VOLUME 0.00Halstead program volume
RETURNS209.00Number of return points from functions
SIZE131212.00Size of the file in bytes
UNIQUE_OPERANDS1622.00Number of unique operands
UNIQUE_OPERATORS63.00Number of unique operators
WHITESPACE681.00Number of whitespace lines