/*
* MegaMek - Copyright (C) 2003,2004 Ben Mazur (bmazur@sev.org)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
package megamek.common;
import gd.xml.ParseException;
import gd.xml.XMLParser;
import gd.xml.XMLResponder;
import java.io.InputStream;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import megamek.common.loaders.EntityLoadingException;
/**
* This class parses an XML input stream. If the stream is well formed, no
* <code>Exception</code> will be thrown. If the stream adheres to the format
* described by the file, "xml-spec.txt", then this class can return entities.
* If unexpected entities are encountered while parsing a well-formed stream, a
* warning message will be available.
*
* @author Suvarov454@sourceforge.net (James A. Damour )
* @version $Revision: 7017 $
*/
public class XMLStreamParser implements XMLResponder {
// Private attributes and helper functions.
/**
* The buffer containing the warning message.
*/
private StringBuffer warning = new StringBuffer();
/**
* The entities parsed from the input stream.
*/
private Vector<Entity> entities = new Vector<Entity>();
/**
* The parser for this object.
*/
private XMLParser parser = new XMLParser();
/**
* The stream currently being parsed.
*/
private InputStream inStream = null;
/**
* The current entity being parsed from the stream.
*/
private Entity entity = null;
/**
* The current location in the entity being parsed.
*/
private int loc = Entity.LOC_NONE;
/**
* Flag that indicates the current location is destroyed.
*/
private boolean locDestroyed = false;
/**
* Counter for the amount of ammo already handled for the current location.
*/
private int locAmmoCount = 0;
/**
* Marks all equipment in a location on an <code>Entity<code> as destroyed.
*
* @param en - the <code>Entity</code> whose location is destroyed.
* @param loc - the <code>int</code> index of the destroyed location.
*/
private void destroyLocation(Entity en, int loc) {
// mark armor, internal as destroyed
en.setArmor(IArmorState.ARMOR_DESTROYED, loc, false);
en.setInternal(IArmorState.ARMOR_DESTROYED, loc);
if (en.hasRearArmor(loc)) {
en.setArmor(IArmorState.ARMOR_DESTROYED, loc, true);
}
// equipment marked missing
for (Mounted mounted : en.getEquipment()) {
if (mounted.getLocation() == loc) {
mounted.setDestroyed(true);
}
}
// all critical slots set as missing
for (int i = 0; i < en.getNumberOfCriticals(loc); i++) {
final CriticalSlot cs = en.getCritical(loc, i);
if (cs != null) {
cs.setDestroyed(true);
}
}
// Mark dependent locations as destroyed.
if (en.getDependentLocation(loc) != Entity.LOC_NONE) {
destroyLocation(en, en.getDependentLocation(loc));
}
}
// Public and Protected constants, constructors, and methods.
/**
* The names of the various elements recognized by this parser.
*/
public static final String UNIT = "unit";
public static final String TEMPLATE = "template";
public static final String ENTITY = "entity";
public static final String FLUFF = "fluff";
public static final String PILOT = "pilot";
public static final String LOCATION = "location";
public static final String ARMOR = "armor";
public static final String SLOT = "slot";
public static final String MOVEMENT = "movement";
public static final String TURRETLOCK = "turretlock";
public static final String SI = "structural";
public static final String HEAT = "Heat";
public static final String FUEL = "fuel";
public static final String KF = "KF";
public static final String SAIL = "sail";
public static final String AEROCRIT = "acriticals";
/**
* The names of the attributes recognized by this parser. Not every
* attribute is valid for every element.
*/
public static final String CHASSIS = "chassis";
public static final String MODEL = "model";
public static final String NAME = "name";
public static final String EXT_ID = "externalId";
public static final String GUNNERY = "gunnery";
public static final String GUNNERYL = "gunneryL";
public static final String GUNNERYM = "gunneryM";
public static final String GUNNERYB = "gunneryB";
public static final String PILOTING = "piloting";
public static final String INITB = "initB";
public static final String COMMANDB = "commandB";
public static final String HITS = "hits";
public static final String ADVS = "advantages";
public static final String IMPLANTS = "implants";
public static final String AUTOEJECT = "autoeject";
public static final String INDEX = "index";
public static final String IS_DESTROYED = "isDestroyed";
public static final String POINTS = "points";
public static final String TYPE = "type";
public static final String IS_REAR = "isRear";
public static final String SHOTS = "shots";
public static final String IS_HIT = "isHit";
public static final String MUNITION = "munition";
public static final String SPEED = "speed";
public static final String DIRECTION = "direction";
public static final String INTEGRITY = "integrity";
public static final String SINK = "sinks";
public static final String LEFT = "left";
public static final String AVIONICS = "avionics";
public static final String SENSORS = "sensors";
public static final String ENGINE = "engine";
public static final String FCS = "fcs";
public static final String CIC = "cic";
public static final String LEFT_THRUST = "leftThrust";
public static final String RIGHT_THRUST = "rightThrust";
public static final String LIFE_SUPPORT = "lifeSupport";
public static final String GEAR = "gear";
/**
* Special values recognized by this parser.
*/
public static final String DEAD = "Dead";
public static final String NA = "N/A";
public static final String DESTROYED = "Destroyed";
public static final String FRONT = "Front";
public static final String REAR = "Rear";
public static final String INTERNAL = "Internal";
public static final String EMPTY = "Empty";
public static final String SYSTEM = "System";
/**
* No <code>Entity</code>s or warning message are available if the
* default constructor is used.
*/
public XMLStreamParser() { /* do nothing */
}
/**
* Parse the indicated XML stream. Any warning message or
* <code>Entity</code>s from a previously parsed stream will be
* discarded.
*
* @param input - the <code>InputStream</code> to be parsed.
* @exception ParseException is thrown if a fatal error occurs during
* parsing. Typically, this only occurs when the XML is not
* well-formed.
*/
public void parse(InputStream input) throws ParseException {
// Reset the warning message.
this.warning = new StringBuffer();
// Clear the entities.
this.entities.removeAllElements();
// Parse the input stream.
this.inStream = input;
this.parser.parseXML(this);
}
/**
* Construct an object and parse the XML stream. Any warning message or
* <code>Entity</code>s from a previously parsed stream will be
* discarded.
*
* @param input - the <code>InputStream</code> to be parsed.
* @exception ParseException is thrown if a fatal warning occurs during
* parsing. Typically, this only occurs when the XML is not
* well-formed.
*/
public XMLStreamParser(InputStream input) throws ParseException {
this.parse(input);
}
/**
* Determine if unexpected XML entities were encountered during parsing.
*
* @return <code>true</code> if a non-fatal warning occured.
*/
public boolean hasWarningMessage() {
return (this.warning.length() > 0);
}
/**
* Get the warning message from the last parse.
*
* @return The <code>String</code> warning message from the last parse. If
* there is no warning message, then an <code>null</code> value is
* returned.
*/
public String getWarningMessage() {
if (this.warning.length() > 0) {
return this.warning.toString();
}
return null;
}
/**
* Get any <code>Entity</code>s parsed from the last input stream.
* Entities may have been parsed out of the stream, even if errors were
* encountered.
*
* @return A <code>Vector</code> containing <code>Entity</code>s parsed
* from the stream. This <code>Vector</code> may be empty, but it
* will never be <code>null</code>.
*/
public Vector<Entity> getEntities() {
// ASSUMPTION : it is safe to return a modifiable reference to the
// vector. If assumption is wrong, clone the vector.
return this.entities;
}
// Implementation of the XMLResponder interface:
public void recordNotationDeclaration(String name, String pubID,
String sysID) throws ParseException {
// Do nothing.
}
public void recordEntityDeclaration(String name, String value,
String pubID, String sysID, String notation) throws ParseException {
// Do nothing.
}
public void recordElementDeclaration(String name, String content)
throws ParseException {
// Do nothing.
}
public void recordAttlistDeclaration(String element, String attr,
boolean notation, String type, String defmod, String def)
throws ParseException {
// Do nothing.
}
public void recordDoctypeDeclaration(String name, String pubID, String sysID)
throws ParseException {
// Do nothing.
}
public void recordDocStart() {
// Do nothing.
}
public void recordDocEnd() {
// Do nothing.
}
@SuppressWarnings("unchecked")
public void recordElementStart(String name, Hashtable attr)
throws ParseException {
// TODO: handle template files.
// What kind of element have we started?
if (name.equals(UNIT)) {
// Are we in the middle of parsing an Entity?
if (this.entity != null) {
this.warning.append("Found a unit while parsing an Entity.\n");
}
// Are there *multiple* units?
else if (!this.entities.isEmpty()) {
this.warning
.append("Found a second unit. Clearing first unit.\n");
// Restart the unit list.
this.entities.removeAllElements();
}
} else if (name.equals(TEMPLATE)) {
// Do nothing.
} else if (name.equals(ENTITY)) {
// Are we in the middle of parsing an Entity?
if (this.entity != null) {
this.warning
.append("Found another Entity while parsing an Entity.\n");
}
// Are we in the middle of parsing an Entity's location?
else if (this.loc != Entity.LOC_NONE) {
this.warning
.append("Found another Entity while parsing a location.\n");
}
// Start a new entity.
else {
// Look for the element's attributes.
String chassis = (String) attr.get(CHASSIS);
String model = (String) attr.get(MODEL);
// Did we find required attributes?
if (chassis == null || chassis.length() == 0) {
this.warning.append("Could not find chassis for Entity.\n");
} else {
// Try to find the entity.
MechSummary ms = null;
StringBuffer key = new StringBuffer(chassis);
ms = MechSummaryCache.getInstance().getMech(key.toString());
if (model != null && model.length() > 0) {
key.append(" ").append(model);
ms = MechSummaryCache.getInstance().getMech(
key.toString());
// That didn't work. Try swaping model and chassis.
if (ms == null) {
key = new StringBuffer(model);
key.append(" ").append(chassis);
ms = MechSummaryCache.getInstance().getMech(
key.toString());
}
}
// We should have found the mech.
if (ms == null) {
this.warning
.append("Could not find Entity with chassis: ");
this.warning.append(chassis);
if (model != null && model.length() > 0) {
this.warning.append(", and model: ");
this.warning.append(model);
}
this.warning.append(".\n");
} else {
// Try to load the new mech.
try {
this.entity = new MechFileParser(
ms.getSourceFile(), ms.getEntryName())
.getEntity();
} catch (EntityLoadingException excep) {
excep.printStackTrace(System.err);
this.warning.append("Unable to load mech: ")
.append(ms.getSourceFile()).append(": ")
.append(ms.getEntryName()).append(": ")
.append(excep.getMessage());
}
} // End found-MechSummary
} // End have-chassis
//external id
String extId = (String) attr.get(EXT_ID);
int id = Entity.NONE;
if (null != extId && extId.length() > 0) {
try {
id = Integer.parseInt(extId);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
}
if(null != entity) {
entity.setExternalId(id);
}
} // End ready-for-new-Entity
} else if (name.equals(FLUFF)) {
// Do nothing.
} else if (name.equals(PILOT)) {
// Are we in the outside of an Entity?
if (this.entity == null) {
this.warning.append("Found a pilot outside of an Entity.\n");
}
// Are we in the middle of parsing an Entity's location?
else if (this.loc != Entity.LOC_NONE) {
this.warning
.append("Found a pilot while parsing a location.\n");
}
// Handle the pilot.
else {
// Look for the element's attributes.
String pilotName = (String) attr.get(NAME);
String gunnery = (String) attr.get(GUNNERY);
String gunneryL = (String) attr.get(GUNNERYL);
String gunneryM = (String) attr.get(GUNNERYM);
String gunneryB = (String) attr.get(GUNNERYB);
String piloting = (String) attr.get(PILOTING);
String initB = (String) attr.get(INITB);
String commandB = (String) attr.get(COMMANDB);
String hits = (String) attr.get(HITS);
String advantages = (String) attr.get(ADVS);
String implants = (String) attr.get(IMPLANTS);
String autoeject = (String) attr.get(AUTOEJECT);
// Did we find required attributes?
if (gunnery == null || gunnery.length() == 0) {
this.warning.append("Could not find gunnery for pilot.\n");
} else if (piloting == null || piloting.length() == 0) {
this.warning.append("Could not find piloting for pilot.\n");
} else {
// Try to get a good gunnery value.
int gunVal = -1;
try {
gunVal = Integer.parseInt(gunnery);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (gunVal < 0 || gunVal > 7) {
this.warning.append("Found invalid gunnery value: ")
.append(gunnery).append(".\n");
return;
}
// Try to get a good piloting value.
int pilotVal = -1;
try {
pilotVal = Integer.parseInt(piloting);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (pilotVal < 0 || pilotVal > 7) {
this.warning.append("Found invalid piloting value: ")
.append(piloting).append(".\n");
return;
}
//init bonus
int initBVal = 0;
if (null != initB && initB.length() > 0) {
try {
initBVal = Integer.parseInt(initB);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
}
int commandBVal = 0;
if (null != commandB && commandB.length() > 0) {
try {
commandBVal = Integer.parseInt(commandB);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
}
// get RPG skills
int gunneryLVal = gunVal;
int gunneryMVal = gunVal;
int gunneryBVal = gunVal;
if (null != gunneryL && gunneryL.length() > 0) {
try {
gunneryLVal = Integer.parseInt(gunneryL);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (gunneryLVal < 0 || gunneryLVal > 7) {
this.warning.append(
"Found invalid piloting value: ").append(
gunneryL).append(".\n");
return;
}
}
if (null != gunneryM && gunneryM.length() > 0) {
try {
gunneryMVal = Integer.parseInt(gunneryM);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (gunneryMVal < 0 || gunneryMVal > 7) {
this.warning.append(
"Found invalid piloting value: ").append(
gunneryM).append(".\n");
return;
}
}
if (null != gunneryB && gunneryB.length() > 0) {
try {
gunneryBVal = Integer.parseInt(gunneryB);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (gunneryBVal < 0 || gunneryBVal > 7) {
this.warning.append(
"Found invalid piloting value: ").append(
gunneryB).append(".\n");
return;
}
}
// Update the entity's crew.
Pilot crew = entity.getCrew();
if (null == pilotName || pilotName.length() == 0) {
pilotName = crew.getName();
}
crew = new Pilot(pilotName, gunneryLVal, gunneryMVal,
gunneryBVal, pilotVal);
crew.setInitBonus(initBVal);
crew.setCommandBonus(commandBVal);
if ((null != advantages)
&& (advantages.trim().length() > 0)) {
StringTokenizer st = new StringTokenizer(advantages,
"::");
while (st.hasMoreTokens()) {
String adv = st.nextToken();
String advName = Pilot.parseAdvantageName(adv);
Object value = Pilot.parseAdvantageValue(adv);
try {
crew.getOptions().getOption(advName).setValue(
value);
} catch (Exception e) {
this.warning.append(
"Error restoring advantage: ").append(
adv).append(".\n");
}
}
}
if ((null != implants) && (implants.trim().length() > 0)) {
StringTokenizer st = new StringTokenizer(implants, "::");
while (st.hasMoreTokens()) {
String implant = st.nextToken();
String implantName = Pilot
.parseAdvantageName(implant);
Object value = Pilot.parseAdvantageValue(implant);
try {
crew.getOptions().getOption(implantName)
.setValue(value);
} catch (Exception e) {
this.warning.append(
"Error restoring advantage: ").append(
implant).append(".\n");
}
}
}
// Was the crew wounded?
if (hits != null) {
// Try to get a good hits value.
int hitVal = -1;
try {
hitVal = Integer.parseInt(hits);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (hits.equals(DEAD)) {
crew.setDead(true);
this.warning.append("The pilot, ")
.append(pilotName).append(", is dead.\n");
} else if (hitVal < 0 || hitVal > 5) {
this.warning.append("Found invalid hits value: ")
.append(hits).append(".\n");
} else {
crew.setHits(hitVal);
}
} // End have-hits
// Set the crew for this entity.
this.entity.setCrew(crew);
if (autoeject != null) {
if (autoeject.equals("true")) {
((Mech) this.entity).setAutoEject(true);
} else {
((Mech) this.entity).setAutoEject(false);
}
}
} // End have-required-fields
} // End ready-for-pilot
} else if (name.equals(LOCATION)) {
// Are we in the outside of an Entity?
if (this.entity == null) {
this.warning.append("Found a location outside of an Entity.\n");
}
// Are we in the middle of parsing an Entity's location?
else if (this.loc != Entity.LOC_NONE) {
this.warning
.append("Found a location while parsing a location.\n");
}
// Handle the location.
else {
// Look for the element's attributes.
String index = (String) attr.get(INDEX);
String destroyed = (String) attr.get(IS_DESTROYED);
// Did we find required attributes?
if (index == null || index.length() == 0) {
this.warning.append("Could not find index for location.\n");
} else {
// Try to get a good index value.
int indexVal = -1;
try {
indexVal = Integer.parseInt(index);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (indexVal < 0 || indexVal > 7) {
this.warning.append(
"Found invalid index value for location: ")
.append(index).append(".\n");
return;
} else if (indexVal >= entity.locations()) {
this.warning.append("The entity, ").append(
entity.getShortName()).append(
" does not have a location at index: ").append(
indexVal).append(".\n");
return;
} else {
// We're now parsing the indexed location.
this.loc = indexVal;
// Reset the ammo count.
this.locAmmoCount = 0;
// Is the location destroyed?
this.locDestroyed = false;
try {
if (destroyed != null) {
this.locDestroyed = destroyed.equals("true");
}
} catch (Throwable excep) {
this.warning.append(
"Found invalid isDestroyed value: ")
.append(destroyed).append(".\n");
}
} // End have-valid-index
} // End have-required-fields
} // End ready-for-location
} else if (name.equals(TURRETLOCK)) {
// Are we in the outside of an Entity?
if (this.entity == null) {
this.warning
.append("Found turret lock outside of an Entity.\n");
} else if (!(this.entity instanceof Tank)) {
this.warning
.append("Turret crit record found outside a Tank.\n");
}
String value = (String) attr.get(DIRECTION);
try {
int turDir = Integer.parseInt(value);
((Tank) this.entity).setSecondaryFacing(turDir);
((Tank) this.entity).lockTurret();
} catch (Exception e) {
System.err.println(e);
e.printStackTrace();
this.warning
.append("Invalid turret lock direction value in movement tag.\n");
}
} else if (name.equals(MOVEMENT)) {
// Are we in the outside of an Entity?
if (this.entity == null) {
this.warning
.append("Found movement crit outside of an Entity.\n");
} else if (!(this.entity instanceof Tank)) {
this.warning
.append("Movement crit record found outside a Tank.\n");
}
String value = (String) attr.get(SPEED);
if (value.equals("immobile")) {
((Tank) (this.entity)).immobilize();
} else {
try {
int newSpeed = Integer.parseInt(value);
this.entity.setOriginalWalkMP(newSpeed);
} catch (Exception e) {
this.warning
.append("Invalid speed value in movement tag.\n");
}
}
} else if (name.equals(ARMOR)) {
// Are we in the outside of an Entity?
if (this.entity == null) {
this.warning.append("Found armor outside of an Entity.\n");
}
// Are we in the outside of parsing an Entity's location?
else if (this.loc == Entity.LOC_NONE) {
this.warning
.append("Found armor while outside of a location.\n");
}
// Handle the location.
else {
// Look for the element's attributes.
String points = (String) attr.get(POINTS);
String type = (String) attr.get(TYPE);
// Did we find required attributes?
if (points == null || points.length() == 0) {
this.warning.append("Could not find points for armor.\n");
} else {
// Try to get a good points value.
int pointsVal = -1;
try {
pointsVal = Integer.parseInt(points);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (points.equals(NA)) {
pointsVal = IArmorState.ARMOR_NA;
} else if (points.equals(DESTROYED)) {
pointsVal = IArmorState.ARMOR_DESTROYED;
} else if (pointsVal < 0 || pointsVal > 2000) {
this.warning.append("Found invalid points value: ")
.append(points).append(".\n");
return;
}
// Assign the points to the correct location.
// Sanity check the armor value before setting it.
if (type == null || type.equals(FRONT)) {
if (this.entity.getOArmor(this.loc) < pointsVal) {
this.warning.append("The entity, ").append(
this.entity.getShortName()).append(
" does not start with ").append(pointsVal)
.append(" points of armor for location: ")
.append(this.loc).append(".\n");
} else {
this.entity.setArmor(pointsVal, this.loc);
}
} else if (type.equals(INTERNAL)) {
if (this.entity.getOInternal(this.loc) < pointsVal) {
this.warning
.append("The entity, ")
.append(this.entity.getShortName())
.append(" does not start with ")
.append(pointsVal)
.append(
" points of internal structure for location: ")
.append(this.loc).append(".\n");
} else {
this.entity.setInternal(pointsVal, this.loc);
}
} else if (type.equals(REAR)) {
if (!this.entity.hasRearArmor(this.loc)) {
this.warning.append("The entity, ").append(
this.entity.getShortName()).append(
" has no rear armor for location: ")
.append(this.loc).append(".\n");
} else if (this.entity.getOArmor(this.loc, true) < pointsVal) {
this.warning
.append("The entity, ")
.append(this.entity.getShortName())
.append(" does not start with ")
.append(pointsVal)
.append(
" points of rear armor for location: ")
.append(this.loc).append(".\n");
} else {
this.entity.setArmor(pointsVal, this.loc, true);
}
}
} // End have-required-fields
} // End ready-for-armor
} else if ( name.equals(SI) ) {
if ( this.entity == null ) {
this.warning.append
( "Found structural integrity outside of an Entity.\n" );
} else if (!(this.entity instanceof Aero)) {
this.warning.append
( "structural integrity record found outside an Aero.\n" );
}
String value = (String) attr.get( INTEGRITY );
try {
int newSI = Integer.parseInt(value);
((Aero)this.entity).setSI(newSI);
} catch (Exception e) {
this.warning.append
( "Invalid SI value in structural integrity tag.\n" );
}
}
else if ( name.equals(HEAT) ) {
if ( this.entity == null ) {
this.warning.append
( "Found heat sink outside of an Entity.\n" );
} else if (!(this.entity instanceof Aero)) {
this.warning.append
( "heat sink record found outside an Aero.\n" );
}
String value = (String) attr.get( SINK );
try {
int newSinks = Integer.parseInt(value);
((Aero)this.entity).setHeatSinks(newSinks);
} catch (Exception e) {
this.warning.append
( "Invalid heat sink value in heat sink tag.\n" );
}
}
else if ( name.equals(FUEL) ) {
if ( this.entity == null ) {
this.warning.append
( "Found fuel outside of an Entity.\n" );
} else if (!(this.entity instanceof Aero)) {
this.warning.append
( "fuel record found outside an Aero.\n" );
}
String value = (String) attr.get( LEFT );
try {
int newFuel = Integer.parseInt(value);
((Aero)this.entity).setFuel(newFuel);
} catch (Exception e) {
this.warning.append
( "Invalid fuel value in fuel tag.\n" );
}
}
else if ( name.equals(KF) ) {
if ( this.entity == null ) {
this.warning.append
( "Found KF integrity outside of an Entity.\n" );
} else if (!(this.entity instanceof Jumpship)) {
this.warning.append
( "KF integrity record found outside a Jumpship.\n" );
}
String value = (String) attr.get( INTEGRITY );
try {
int newIntegrity = Integer.parseInt(value);
((Jumpship)this.entity).setKFIntegrity(newIntegrity);
} catch (Exception e) {
this.warning.append
( "Invalid KF integrity value in KF integrity tag.\n" );
}
}
else if ( name.equals(SAIL) ) {
if ( this.entity == null ) {
this.warning.append
( "Found sail integrity outside of an Entity.\n" );
} else if (!(this.entity instanceof Jumpship)) {
this.warning.append
( "sail integrity record found outside a Jumpship.\n" );
}
String value = (String) attr.get( INTEGRITY );
try {
int newIntegrity = Integer.parseInt(value);
((Jumpship)this.entity).setSailIntegrity(newIntegrity);
} catch (Exception e) {
this.warning.append
( "Invalid sail integrity value in sail integrity tag.\n" );
}
}
else if ( name.equals(AEROCRIT) ) {
if ( this.entity == null ) {
this.warning.append
( "Found aero crits outside of an Entity.\n" );
} else if (!(this.entity instanceof Aero)) {
this.warning.append
( "Found aero crits outside of an Aero.\n" );
}
else
{
String avionics = (String) attr.get( AVIONICS );
String sensors = (String) attr.get( SENSORS );
String engine = (String) attr.get( ENGINE );
String fcs = (String) attr.get( FCS );
String cic = (String) attr.get( CIC );
String leftThrust = (String) attr.get( LEFT_THRUST );
String rightThrust = (String) attr.get( RIGHT_THRUST );
String lifeSupport = (String) attr.get( LIFE_SUPPORT );
String gear = (String) attr.get( GEAR );
Aero a = (Aero)this.entity;
if ( avionics != null ) {
a.setAvionicsHits(Integer.parseInt( avionics ));
}
if ( sensors != null ) {
a.setSensorHits(Integer.parseInt( sensors ));
}
if ( engine != null ) {
a.setEngineHits(Integer.parseInt( engine ));
}
if ( fcs != null ) {
a.setFCSHits(Integer.parseInt( fcs ));
}
if ( cic != null ) {
a.setCICHits(Integer.parseInt( cic ));
}
if ( leftThrust != null ) {
a.setLeftThrustHits(Integer.parseInt( leftThrust ));
}
if ( rightThrust != null ) {
a.setRightThrustHits(Integer.parseInt( rightThrust ));
}
if ( lifeSupport != null ) {
a.setLifeSupport(false);
}
if ( gear != null ) {
a.setGearHit(true);
}
}
} else if (name.equals(SLOT)) {
// Are we in the outside of an Entity?
if (this.entity == null) {
this.warning.append("Found a slot outside of an Entity.\n");
}
// Are we in the outside of parsing an Entity's location?
else if (this.loc == Entity.LOC_NONE) {
this.warning
.append("Found a slot while outside of a location.\n");
}
// Handle the location.
else {
// Look for the element's attributes.
String index = (String) attr.get(INDEX);
String type = (String) attr.get(TYPE);
// String rear = (String) attr.get( IS_REAR ); // is never read.
String shots = (String) attr.get(SHOTS);
String hit = (String) attr.get(IS_HIT);
String destroyed = (String) attr.get(IS_DESTROYED);
String munition = (String) attr.get(MUNITION);
// Did we find required attributes?
if (index == null || index.length() == 0) {
this.warning.append("Could not find index for slot.\n");
} else if (type == null || type.length() == 0) {
this.warning.append("Could not find type for slot.\n");
} else {
// Try to get a good index value.
// Remember, slot index starts at 1.
int indexVal = -1;
try {
indexVal = Integer.parseInt(index);
indexVal -= 1;
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (index.equals(NA)) {
indexVal = IArmorState.ARMOR_NA;
// Tanks don't have slots, and Protomechs only have
// system slots, so we have to handle the ammo
// specially.
if (entity instanceof Tank
|| entity instanceof Protomech) {
// Get the saved ammo load.
EquipmentType newLoad = EquipmentType.get(type);
if (newLoad instanceof AmmoType) {
int counter = -1;
Iterator<Mounted> ammo = entity.getAmmo()
.iterator();
while (ammo.hasNext()
&& counter < this.locAmmoCount) {
// Is this mounted in the current location?
Mounted mounted = ammo.next();
if (mounted.getLocation() == loc) {
// Increment the loop counter.
counter++;
// Is this the one we want to handle?
if (counter == this.locAmmoCount) {
// Increment the counter of ammo
// handled for this location.
this.locAmmoCount++;
// Reset transient values.
mounted.restore();
// Try to get a good shots value.
int shotsVal = -1;
try {
shotsVal = Integer
.parseInt(shots);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (shots.equals(NA)) {
shotsVal = IArmorState.ARMOR_NA;
this.warning
.append(
"Expected to find number of shots for ")
.append(type).append(
", but found ")
.append(shots).append(
" instead.\n");
} else if (shotsVal < 0
|| shotsVal > 200) {
this.warning
.append(
"Found invalid shots value for slot: ")
.append(shots).append(
".\n");
} else {
// Change to the saved
// ammo type and shots.
mounted
.changeAmmoType((AmmoType) newLoad);
mounted.setShotsLeft(shotsVal);
} // End have-good-shots-value
// Stop looking for a match.
break;
} // End found-match-for-slot
} // End ammo-in-this-loc
} // Check the next ammo.
} else {
// Bad XML equipment.
this.warning
.append("XML file lists ")
.append(type)
.append(" equipment at location ")
.append(this.loc)
.append(
". XML parser expected ammo.\n");
} // End not-ammo-type
} // End is-tank
// TODO: handle slotless equipment.
return;
} else if (indexVal < 0 || indexVal > 12) {
this.warning.append(
"Found invalid index value for slot: ").append(
index).append(".\n");
return;
}
// Is this index valid for this entity?
if (indexVal > entity.getNumberOfCriticals(this.loc)) {
this.warning.append("The entity, ").append(
this.entity.getShortName()).append(
" does not have ").append(index).append(
" slots in location ").append(this.loc).append(
".\n");
return;
}
// Try to get a good isHit value.
boolean hitFlag = false;
try {
if (hit != null) {
hitFlag = hit.equals("true");
}
} catch (Throwable excep) {
this.warning.append("Found invalid isHit value: ")
.append(hit).append(".\n");
}
// Is the location destroyed?
boolean destFlag = false;
try {
if (destroyed != null) {
destFlag = destroyed.equals("true");
}
} catch (Throwable excep) {
this.warning
.append("Found invalid isDestroyed value: ")
.append(destroyed).append(".\n");
}
// Try to get the critical slot.
CriticalSlot slot = this.entity.getCritical(this.loc,
indexVal);
// Did we get it?
if (slot == null) {
if (!type.equals(EMPTY)) {
this.warning.append("Could not find the ").append(
type).append(
" equipment that was expected at index ")
.append(indexVal).append(" of location ")
.append(this.loc).append(".\n");
}
return;
}
// Is the slot for a critical system?
if (slot.getType() == CriticalSlot.TYPE_SYSTEM) {
// Does the XML file have some other kind of equipment?
if (!type.equals(SYSTEM)) {
this.warning.append("XML file expects to find ")
.append(type)
.append(" equipment at index ").append(
indexVal).append(" of location ")
.append(this.loc).append(
", but Entity has a system.\n");
}
} else {
// Nope, we've got equipment. Get this slot's mounted.
Mounted mounted = this.entity.getEquipment(slot
.getIndex());
// Reset transient values.
mounted.restore();
// Hit and destroy the mounted, according to the flags.
mounted.setDestroyed(hitFlag || destFlag);
// Is the mounted a type of ammo?
if (mounted.getType() instanceof AmmoType) {
// Get the saved ammo load.
EquipmentType newLoad = EquipmentType.get(type);
if (newLoad instanceof AmmoType) {
// Try to get a good shots value.
int shotsVal = -1;
try {
shotsVal = Integer.parseInt(shots);
} catch (NumberFormatException excep) {
// Handled by the next if test.
}
if (shots.equals(NA)) {
shotsVal = IArmorState.ARMOR_NA;
this.warning
.append(
"Expected to find number of shots for ")
.append(type)
.append(", but found ").append(
shots)
.append(" instead.\n");
} else if (shotsVal < 0 || shotsVal > 200) {
this.warning
.append(
"Found invalid shots value for slot: ")
.append(shots).append(".\n");
} else {
// Change to the saved ammo type and shots.
mounted.changeAmmoType((AmmoType) newLoad);
mounted.setShotsLeft(shotsVal);
} // End have-good-shots-value
} else {
// Bad XML equipment.
this.warning.append("XML file expects ")
.append(type).append(
" equipment at index ").append(
indexVal).append(
" of location ").append(
this.loc).append(
", but Entity has ").append(
mounted.getType()
.getInternalName())
.append("there .\n");
}
} // End slot-for-ammo
// Not an ammo slot... does file agree with template?
else if (!mounted.getType().getInternalName().equals(
type)) {
// Bad XML equipment.
this.warning
.append("XML file expects ")
.append(type)
.append(" equipment at index ")
.append(indexVal)
.append(" of location ")
.append(this.loc)
.append(", but Entity has ")
.append(mounted.getType().getInternalName())
.append("there .\n");
}
// Check for munition attribute.
if (munition != null) {
// Retrieve munition by name.
EquipmentType munType = EquipmentType.get(munition);
// Make sure munition is a type of ammo.
if (munType instanceof AmmoType) {
// Change to the saved munition type.
mounted.getLinked().changeAmmoType(
(AmmoType) munType);
} else {
// Bad XML equipment.
this.warning
.append("XML file expects ")
.append(
" ammo for munition argument of ")
.append(" slot tag.\n");
}
}
} // End have-equipment
// Hit and destroy the slot, according to the flags.
slot.setHit(hitFlag);
slot.setDestroyed(destFlag);
} // End have-required-fields
} // End ready-for-slot
}
} // End public void recordElementStart( String, Hashtable )
public void recordElementEnd(String name) throws ParseException {
// TODO: handle template files.
// What kind of element have we started?
if (name.equals(UNIT)) {
// Are we in the middle of parsing an Entity?
if (this.entity != null) {
this.warning.append("End of unit while parsing an Entity.\n");
// Are we in the middle of parsing an Entity's location?
if (this.loc != Entity.LOC_NONE) {
this.warning
.append("Found end of unit while parsing a location.\n");
// If the open location is marked destroyed, destroy it.
if (this.locDestroyed) {
this.destroyLocation(this.entity, this.loc);
}
this.loc = Entity.LOC_NONE;
}
// Add the entity to the vector.
this.entities.addElement(this.entity);
this.entity = null;
}
// Is this an empty unit?
else if (this.entities.isEmpty()) {
this.warning.append("Found an empty unit.\n");
}
} else if (name.equals(TEMPLATE)) {
// Do nothing.
} else if (name.equals(ENTITY)) {
// We should be in the middle of parsing an Entity.
if (this.entity == null) {
this.warning
.append("Found end of Entity, but not parsing an Entity.\n");
} else {
// Are we in the middle of parsing an Entity's location?
if (this.loc != Entity.LOC_NONE) {
this.warning
.append("Found end of Entity while parsing a location.\n");
// If the open location is marked destroyed, destroy it.
if (this.locDestroyed) {
this.destroyLocation(this.entity, this.loc);
}
this.loc = Entity.LOC_NONE;
}
// Add the entity to the vector.
this.entities.addElement(this.entity);
this.entity = null;
} // End save-entity
} else if (name.equals(FLUFF)) {
// Do nothing.
} else if (name.equals(PILOT)) {
// Do nothing.
} else if (name.equals(LOCATION)) {
// We should be in the middle of parsing an Entity.
if (this.entity == null) {
this.warning
.append("Found end of location, but not parsing an Entity.\n");
}
// Are we in the middle of parsing an Entity's location?
else if (this.loc == Entity.LOC_NONE) {
this.warning
.append("Found end of location, but not parsing a location.\n");
} else {
// If the location is marked destroyed, destroy the location.
if (this.locDestroyed) {
this.destroyLocation(this.entity, this.loc);
}
// Reset the location.
this.loc = Entity.LOC_NONE;
} // End finish-location
} else if (name.equals(ARMOR)) {
// Do nothing.
} else if (name.equals(SLOT)) {
// Do nothing.
}
}
public void recordPI(String name, String pValue) {
// Do nothing.
}
public void recordCharData(String charData) {
// Do nothing.
}
public void recordComment(String comment) {
// Do nothing.
}
public InputStream getDocumentStream() throws ParseException {
if (this.inStream == null) {
throw new ParseException("Input document stream not defined.");
}
return this.inStream;
}
public InputStream resolveExternalEntity(String name, String pubID,
String sysID) throws ParseException {
// Return nothing.
return null;
}
public InputStream resolveDTDEntity(String name, String pubID, String sysID)
throws ParseException {
// Return nothing.
return null;
}
} // End public class XMLStreamParser implements XMLResponder