package com.kartoflane.superluminal2.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.Scanner; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.input.JDOMParseException; import com.kartoflane.superluminal2.components.enums.BoardingStrategies; import com.kartoflane.superluminal2.components.enums.Directions; import com.kartoflane.superluminal2.components.enums.Images; import com.kartoflane.superluminal2.components.enums.LayoutObjects; import com.kartoflane.superluminal2.components.enums.Races; import com.kartoflane.superluminal2.components.enums.Systems; import com.kartoflane.superluminal2.core.Database; import com.kartoflane.superluminal2.ftl.AugmentObject; import com.kartoflane.superluminal2.ftl.DoorObject; import com.kartoflane.superluminal2.ftl.DroneList; import com.kartoflane.superluminal2.ftl.DroneObject; import com.kartoflane.superluminal2.ftl.GibObject; import com.kartoflane.superluminal2.ftl.GlowObject; import com.kartoflane.superluminal2.ftl.GlowSet; import com.kartoflane.superluminal2.ftl.MountObject; import com.kartoflane.superluminal2.ftl.RoomObject; import com.kartoflane.superluminal2.ftl.ShipObject; import com.kartoflane.superluminal2.ftl.StationObject; import com.kartoflane.superluminal2.ftl.SystemObject; import com.kartoflane.superluminal2.ftl.WeaponList; import com.kartoflane.superluminal2.ftl.WeaponObject; /** * This class contains methods used to interpret XML code and .ftl files as a ship. * * @author kartoFlane * */ public class ShipLoadUtils { /** * Interprets the XML element as a shipBlueprint tag, loading its contents * and creating a ShipObject to store the data. * * @param e * the XML element for the shipBlueprint tag * @return ShipObject instance representing the ship * * @throws IllegalArgumentException * when the argument is null, or when a tag or attribute is missing * @throws NumberFormatException * when a tag or attribute has incorrect value * @throws FileNotFoundException * when one of the files referenced by the ship's blueprint doesn't exist in the game's files * @throws IOException * when an error occurs while loading the XML element * @throws JDOMParseException * when the XML element is wrongly formatted, or a parser error occurs */ public static ShipObject loadShipXML(Element e) throws IllegalArgumentException, FileNotFoundException, IOException, NumberFormatException, JDOMParseException { if (e == null) throw new IllegalArgumentException("Element must not be null."); Database db = Database.getInstance(); String attr = null; Element child = null; // Get the blueprint name of the ship first, to determine whether it is a player ship attr = e.getAttributeValue("name"); if (attr == null) throw new IllegalArgumentException("Missing 'name' attribute."); String blueprintName = attr; // Create the ship object boolean isPlayer = db.isPlayerShip(blueprintName); ShipObject ship = new ShipObject(isPlayer); ship.setBlueprintName(blueprintName); // Load the TXT layout of the ship (rooms, doors, offsets, shield) attr = e.getAttributeValue("layout"); if (attr == null) throw new IllegalArgumentException("Missing 'layout' attribute."); ship.setLayout(attr); if (!db.contains(ship.getLayoutTXT())) throw new FileNotFoundException("TXT layout file could not be found in game's archives: " + ship.getLayoutTXT()); InputStream is = db.getInputStream(ship.getLayoutTXT()); loadLayoutTXT(ship, is); // Load ship's image namespace attr = e.getAttributeValue("img"); if (attr == null) throw new IllegalArgumentException("Missing 'img' attribute."); ship.setImageNamespace(attr); String namespace = attr; // Ship images can be located in either ship/, ships_glow/ or ships_noglow/ String[] prefixes = { "db:img/ship/", "db:img/ships_glow/", "db:img/ships_noglow/" }; // Load the hull image ship.setImage(Images.HULL, firstExisting(prefixes, namespace + "_base.png", db)); // Load the cloak image, check for override child = e.getChild("cloakImage"); if (child != null) namespace = child.getValue(); ship.setImage(Images.CLOAK, firstExisting(prefixes, namespace + "_cloak.png", db)); namespace = attr; // Floor and shield images are exclusive to player ships. // Enemies use a common shield image that has its dimensions // defined by the ELLIPSE layout object. if (isPlayer) { // Load the floor image, check for override child = e.getChild("floorImage"); if (child != null) namespace = child.getValue(); ship.setImage(Images.FLOOR, firstExisting(prefixes, namespace + "_floor.png", db)); namespace = attr; // Load the shield image, check for override child = e.getChild("shieldImage"); if (child != null) namespace = child.getValue(); ship.setImage(Images.SHIELD, firstExisting(prefixes, namespace + "_shields1.png", db)); namespace = attr; // Load the thumbnail/miniship image path (not represented in the editor) ship.setImage(Images.THUMBNAIL, firstExisting(new String[] { "db:img/customizeUI/miniship_" }, namespace + ".png", db)); } // Load the XML layout of the ship (images' offsets, gibs, weapon mounts) if (!db.contains(ship.getLayoutXML())) throw new FileNotFoundException("XML layout file could not be found in game's archives: " + ship.getLayoutXML()); is = db.getInputStream(ship.getLayoutXML()); loadLayoutXML(ship, is); // Load gib images for (GibObject gib : ship.getGibs()) { gib.setImagePath(firstExisting(prefixes, String.format("%s_gib%s.png", ship.getImageNamespace(), gib.getId()), db)); } // Get the class of the ship child = e.getChild("class"); if (child == null) throw new IllegalArgumentException("Missing <class> tag."); ship.setShipClass(child.getValue()); // Get the name of the ship // Exclusive to player ships if (isPlayer) { child = e.getChild("name"); if (child == null) throw new IllegalArgumentException("Missing <name> tag."); ship.setShipName(child.getValue()); } // Get the description of the ship // Exclusive to player ships if (isPlayer) { child = e.getChild("desc"); if (child == null) ship.setShipDescription("<desc> tag was missing!"); else ship.setShipDescription(child.getValue()); } // Get the list of systems installed on the ship child = e.getChild("systemList"); if (child == null) throw new IllegalArgumentException("Missing <systemList> tag."); for (Systems sys : Systems.getSystems()) { int count = 0; for (Element sysEl : child.getChildren(sys.name().toLowerCase())) { SystemObject system = null; ArrayList<SystemObject> systems = ship.getSystems(sys); if (count >= systems.size()) { if (sys == Systems.ARTILLERY) { system = new SystemObject(Systems.ARTILLERY, ship); ship.add(system); count++; } else { throw new IllegalArgumentException("Multiple entries for system " + sys.toString()); } } else { system = systems.get(count); count++; } if (sys == Systems.ARTILLERY) system.setAlias("" + count); // Get the min level the system can have, or the starting level of the system attr = sysEl.getAttributeValue("power"); if (attr == null) throw new IllegalArgumentException(sys.toString() + " is missing 'power' attribute."); system.setLevelStart(Integer.valueOf(attr)); // Get the max level the system can have // Exclusive to enemy ships if (!isPlayer) { attr = sysEl.getAttributeValue("max"); if (attr != null) { system.setLevelMax(Integer.valueOf(attr)); } else { // Some ships (mostly BOSS) are missing the 'max' attribute // Guess-default to the system's level cap system.setLevelMax(system.getLevelCap()); } } // Get the room to which the system is assigned attr = sysEl.getAttributeValue("room"); if (attr == null) throw new IllegalArgumentException(sys.toString() + " is missing 'room' attribute."); int id = Integer.valueOf(attr); system.setRoom(ship.getRoomById(id)); // Whether the ship starts with the system installed or not // Optional attr = sysEl.getAttributeValue("start"); if (attr != null) { system.setAvailable(Boolean.valueOf(attr)); } else { // Default to true system.setAvailable(true); } // Get the interior image used for this system // System objects are assigned default interior images on their creation // Optional attr = sysEl.getAttributeValue("img"); if (attr != null) { system.setInteriorNamespace(attr); system.setInteriorPath("db:img/ship/interior/" + system.getInteriorNamespace() + ".png"); } else if (isPlayer) { // No 'img' attribute, use default system.setInteriorNamespace(system.getSystemId().getDefaultInteriorNamespace()); system.setInteriorPath(null); } else { // Enemy ships' systems don't use interior images or glows system.setInteriorNamespace(null); system.setInteriorPath(null); } // Load manning glow image for the system // If the specified glow image can't be found, use the default one if (system.canContainGlow()) { if (attr == null) attr = sys.getDefaultInteriorNamespace(); if (system.getSystemId() == Systems.CLOAKING) { GlowSet glowSet = db.getGlowSet(attr); if (glowSet == null) { system.getGlow().setGlowSet(Database.DEFAULT_GLOW_SET); } else { system.getGlow().setGlowSet(glowSet); } } else { attr = attr.replace("room_", ""); GlowObject glowObject = db.getGlow(attr); if (glowObject == null) { String glow = system.getSystemId().getDefaultInteriorNamespace(); glow = glow.replace("room_", ""); GlowObject obj = Database.getInstance().getGlow(glow); if (obj != null) { glowObject = obj; } else { glowObject = Database.DEFAULT_GLOW_OBJ; } } else { system.setGlow(glowObject); } } } // Get the weapon used by this system // Exclusive to artillery if (sys == Systems.ARTILLERY) { attr = sysEl.getAttributeValue("weapon"); if (attr == null) throw new IllegalArgumentException("Artillery is missing 'weapon' attribute."); WeaponObject weapon = db.getWeapon(attr); if (weapon == null) throw new IllegalArgumentException(String.format("%s - WeaponBlueprint not found: %s", system.toString(), attr)); system.setWeapon(weapon); } // Load station position and direction for this system if (system.canContainStation()) { Element stEl = sysEl.getChild("slot"); // Station objects are instantiated with default values for their system // Optional if (stEl != null) { StationObject station = system.getStation(); Element slotEl = stEl.getChild("number"); if (slotEl != null) station.setSlotId(Integer.valueOf(slotEl.getValue())); Element dirEl = stEl.getChild("direction"); if (dirEl != null) station.setSlotDirection(Directions.valueOf(dirEl.getValue().toUpperCase())); } else if (!isPlayer) { // Enemy ships' stations are placed and rotated mostly randomly // No reason to try to emulate this, so just hide the station StationObject station = system.getStation(); station.setSlotId(-2); } } } } child = e.getChild("weaponSlots"); if (child == null) { ship.setWeaponSlots(4); // Default } else { ship.setWeaponSlots(Integer.valueOf(child.getValue())); } child = e.getChild("droneSlots"); if (child == null) { ship.setDroneSlots(2); // Default } else { ship.setDroneSlots(Integer.valueOf(child.getValue())); } child = e.getChild("weaponList"); if (child == null) { ship.setMissilesAmount(0); } else { attr = child.getAttributeValue("missiles"); if (attr == null) ship.setMissilesAmount(0); else ship.setMissilesAmount(Integer.valueOf(attr)); attr = child.getAttributeValue("count"); int count = -1; // Default - load all if (attr != null) count = Integer.valueOf(attr); attr = child.getAttributeValue("load"); if (attr == null && !isPlayer) { ship.setWeaponsByList(false); } else if (attr != null && isPlayer) { throw new IllegalArgumentException("Player ships cannot delcare weapons by list."); } if (ship.getWeaponsByList()) { WeaponList list = db.getWeaponList(attr); if (list == null) throw new IllegalArgumentException("BlueprintList could not be found: " + attr); ship.setWeaponList(list); } else { int loaded = 0; for (Element weapon : child.getChildren("weapon")) { if (count != -1 && loaded >= count) break; attr = weapon.getAttributeValue("name"); if (attr == null) throw new IllegalArgumentException("A weapon in <weaponList> is missing 'name' attribute."); WeaponObject weaponObject = db.getWeapon(attr); if (weaponObject == null) throw new IllegalArgumentException("WeaponBlueprint not found: " + attr); ship.changeWeapon(Database.DEFAULT_WEAPON_OBJ, weaponObject); loaded++; } } } child = e.getChild("droneList"); if (child == null) { ship.setDronePartsAmount(0); } else { attr = child.getAttributeValue("drones"); if (attr == null) ship.setDronePartsAmount(0); else ship.setDronePartsAmount(Integer.valueOf(attr)); attr = child.getAttributeValue("count"); int count = -1; if (attr != null) count = Integer.valueOf(attr); attr = child.getAttributeValue("load"); if (attr == null && !isPlayer) { ship.setDronesByList(false); } else if (attr != null && isPlayer) { throw new IllegalArgumentException("Player ships cannot delcare dronesby list."); } if (ship.getDronesByList()) { DroneList list = db.getDroneList(attr); if (list == null) throw new IllegalArgumentException("BlueprintList could not be found: " + attr); ship.setDroneList(list); } else { int loaded = 0; for (Element drone : child.getChildren("drone")) { if (count != -1 && loaded >= count) break; attr = drone.getAttributeValue("name"); if (attr == null) throw new IllegalArgumentException("A drone in <droneList> is missing 'name' attribute."); DroneObject droneObject = db.getDrone(attr); if (droneObject == null) throw new IllegalArgumentException("DroneBlueprint not found: " + attr); ship.changeDrone(Database.DEFAULT_DRONE_OBJ, droneObject); loaded++; } } } child = e.getChild("health"); if (child == null) throw new IllegalArgumentException("Missing <health> tag"); attr = child.getAttributeValue("amount"); if (attr == null) throw new IllegalArgumentException("<health> tag is missing 'amount' attribute."); ship.setHealth(Integer.valueOf(attr)); child = e.getChild("maxPower"); if (child == null) throw new IllegalArgumentException("Missing <maxPower> tag"); attr = child.getAttributeValue("amount"); if (attr == null) throw new IllegalArgumentException("<maxPower> tag is missing 'amount' attribute."); ship.setPower(Integer.valueOf(attr)); if (!ship.isPlayerShip()) { child = e.getChild("minSector"); if (child == null) ship.setMinSector(1); else { attr = child.getValue(); ship.setMinSector(Integer.valueOf(attr)); } child = e.getChild("maxSector"); if (child == null) ship.setMaxSector(8); else { attr = child.getValue(); ship.setMaxSector(Integer.valueOf(attr)); } child = e.getChild("boardingAI"); if (child == null) { ship.setBoardingAI(BoardingStrategies.SABOTAGE); // Default } else { attr = child.getValue(); ship.setBoardingAI(BoardingStrategies.valueOf(attr.toUpperCase())); } } for (Element crew : e.getChildren("crewCount")) { attr = crew.getAttributeValue("class"); if (attr == null) throw new IllegalArgumentException("<crewCount> tag is missing 'class' attribute."); Races race = null; try { race = Races.valueOf(attr.toUpperCase()); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("Race class not recognised: " + attr); } if (ship.isPlayerShip()) { attr = crew.getAttributeValue("amount"); if (attr == null) throw new IllegalArgumentException("<crewCount> tag is missing 'amount' attribute."); for (int i = 0; i < Integer.valueOf(attr); i++) ship.changeCrew(Races.NO_CREW, race); } else { attr = crew.getAttributeValue("amount"); if (attr == null) throw new IllegalArgumentException("<crewCount> tag is missing 'amount' attribute."); ship.setCrewMin(race, Integer.valueOf(attr)); attr = crew.getAttributeValue("max"); if (attr == null) { // Some ships are missing the 'max' attribute // Guess-default to 'amount' value. ship.setCrewMax(race, ship.getCrewMin(race)); } else { ship.setCrewMax(race, Integer.valueOf(attr)); } } } for (Element aug : e.getChildren("aug")) { attr = aug.getAttributeValue("name"); if (attr == null) throw new IllegalArgumentException("An augment is missing 'name' attribute."); AugmentObject augmentObject = db.getAugment(attr); if (augmentObject == null) throw new IllegalArgumentException("AugBlueprint not found: " + attr); ship.changeAugment(Database.DEFAULT_AUGMENT_OBJ, augmentObject); } return ship; } /** * Loads the layout from the given stream, and adds it to the given ship object. * * @param ship * the ship object in which the loaded data will be saved * @param is * input stream from which the data is read. The stream is always closed by this method. * @throws IllegalArgumentException * when the file is wrongly formatted */ public static void loadLayoutTXT(ShipObject ship, InputStream is) throws IllegalArgumentException { if (ship == null) throw new IllegalArgumentException("Ship object must not be null."); if (is == null) throw new IllegalArgumentException("Stream must not be null."); Scanner sc = new Scanner(is); HashMap<DoorObject, Integer> leftMap = new HashMap<DoorObject, Integer>(); HashMap<DoorObject, Integer> rightMap = new HashMap<DoorObject, Integer>(); try { while (sc.hasNext()) { String line = sc.nextLine(); if (line == null || line.trim().equals("")) continue; LayoutObjects layoutObject = null; try { layoutObject = LayoutObjects.valueOf(line); } catch (IllegalArgumentException e) { try { Integer.parseInt(line); continue; } catch (NumberFormatException ex) { // Not a number throw new IllegalArgumentException(ship.getLayoutTXT() + " contained an unknown layout object: " + line); } } switch (layoutObject) { case X_OFFSET: ship.setXOffset(sc.nextInt()); break; case Y_OFFSET: ship.setYOffset(sc.nextInt()); break; case HORIZONTAL: ship.setHorizontal(sc.nextInt()); break; case VERTICAL: ship.setVertical(sc.nextInt()); break; case ELLIPSE: ship.setEllipseWidth(sc.nextInt()); ship.setEllipseHeight(sc.nextInt()); ship.setEllipseX(sc.nextInt()); ship.setEllipseY(sc.nextInt() + (ship.isPlayerShip() ? 0 : Database.ENEMY_SHIELD_Y_OFFSET)); break; case ROOM: RoomObject room = new RoomObject(); room.setId(sc.nextInt()); room.setLocation(sc.nextInt(), sc.nextInt()); room.setSize(sc.nextInt(), sc.nextInt()); ship.add(room); break; case DOOR: DoorObject door = new DoorObject(); door.setLocation(sc.nextInt(), sc.nextInt()); leftMap.put(door, sc.nextInt()); rightMap.put(door, sc.nextInt()); door.setHorizontal(sc.nextInt() == 0); ship.add(door); break; default: throw new IllegalArgumentException("Unrecognised layout object: " + layoutObject); } } // Link doors to rooms for (DoorObject door : ship.getDoors()) { door.setLeftRoom(ship.getRoomById(leftMap.get(door))); door.setRightRoom(ship.getRoomById(rightMap.get(door))); } } finally { sc.close(); } } /** * Loads the layout from the given file, and adds it to the given ship object. * * @param ship * the ship object in which the loaded data will be saved * @param f * file from which the data is read * @throws IllegalArgumentException * when the file is wrongly formatted * @throws IOException * when a general IO error occurs */ public static void loadLayoutTXT(ShipObject ship, File f) throws IllegalArgumentException, IOException { if (ship == null) throw new IllegalArgumentException("Ship object must not be null."); if (f == null) throw new IllegalArgumentException("File must not be null."); loadLayoutTXT(ship, new FileInputStream(f)); } /** * Loads the XML layout from the stream. * * @param ship * ship object in which the loaded data will be saved * @param is * stream from which the data is read. The stream is always closed by this method. * @throws IllegalArgumentException * when the file is wrongly formatted - a tag or an attribute is missing * @throws JDOMParseException * when a parsing error occurs * @throws IOException * when a general IO error occurs */ public static void loadLayoutXML(ShipObject ship, InputStream is) throws IllegalArgumentException, JDOMParseException, IOException { if (ship == null) throw new IllegalArgumentException("Ship object must not be null."); if (is == null) throw new IllegalArgumentException("Stream must not be null."); try { Document doc = IOUtils.readStreamXML(is, ship.getLayoutXML()); Element root = doc.getRootElement(); Element child = null; String attr = null; // Load the total offset of the image set child = root.getChild("img"); if (child == null) throw new IllegalArgumentException("Missing <img> tag"); Rectangle hullDimensions = new Rectangle(0, 0, 0, 0); attr = child.getAttributeValue("x"); if (attr == null) throw new IllegalArgumentException("Img missing 'x' attribute"); hullDimensions.x = Integer.valueOf(attr); attr = child.getAttributeValue("y"); if (attr == null) throw new IllegalArgumentException("Img missing 'y' attribute"); hullDimensions.y = Integer.valueOf(attr); attr = child.getAttributeValue("w"); if (attr == null) throw new IllegalArgumentException("Img missing 'w' attribute"); hullDimensions.width = Integer.valueOf(attr); attr = child.getAttributeValue("h"); if (attr == null) throw new IllegalArgumentException("Img missing 'h' attribute"); hullDimensions.height = Integer.valueOf(attr); ship.setHullDimensions(hullDimensions); // Ignore <glowOffset> - only concerns iPad version of FTL // Load additional offsets for other images Point offset = new Point(0, 0); Element offsets = root.getChild("offsets"); if (offsets != null) { child = offsets.getChild("cloak"); if (child != null) { attr = child.getAttributeValue("x"); if (attr == null) throw new IllegalArgumentException("Cloak missing 'x' attribute"); offset.x = Integer.valueOf(attr); attr = child.getAttributeValue("y"); if (attr == null) throw new IllegalArgumentException("Cloak missing 'y' attribute"); offset.y = Integer.valueOf(attr); ship.setCloakOffset(offset); } child = offsets.getChild("floor"); if (child != null) { attr = child.getAttributeValue("x"); if (attr == null) throw new IllegalArgumentException("Floor missing 'x' attribute"); offset.x = Integer.valueOf(attr); attr = child.getAttributeValue("y"); if (attr == null) throw new IllegalArgumentException("Floor missing 'y' attribute"); offset.y = Integer.valueOf(attr); ship.setFloorOffset(offset); } } HashMap<MountObject, Integer> gibMap = new HashMap<MountObject, Integer>(); // Load weapon mounts child = root.getChild("weaponMounts"); if (child == null) throw new IllegalArgumentException("Missing <weaponMounts> tag"); int id = 0; for (Element mountEl : child.getChildren("mount")) { MountObject mount = new MountObject(); mount.setId(id++); attr = mountEl.getAttributeValue("x"); if (attr == null) throw new IllegalArgumentException("Mount missing 'x' attribute"); offset.x = Integer.valueOf(attr); attr = mountEl.getAttributeValue("y"); if (attr == null) throw new IllegalArgumentException("Mount missing 'y' attribute"); offset.y = Integer.valueOf(attr); mount.setLocation(offset.x, offset.y); attr = mountEl.getAttributeValue("rotate"); if (attr == null) throw new IllegalArgumentException("Mount missing 'rotate' attribute"); mount.setRotated(Boolean.valueOf(attr)); attr = mountEl.getAttributeValue("mirror"); if (attr == null) throw new IllegalArgumentException("Moumt missing 'mirror' attribute"); mount.setMirrored(Boolean.valueOf(attr)); attr = mountEl.getAttributeValue("gib"); if (attr == null) throw new IllegalArgumentException("Moumt missing 'gib' attribute"); gibMap.put(mount, Integer.valueOf(attr)); attr = mountEl.getAttributeValue("slide"); if (attr != null) mount.setDirection(Directions.parseDir(attr.toUpperCase())); else mount.setDirection(Directions.NONE); ship.add(mount); } // Load gibs child = root.getChild("explosion"); if (child == null) throw new IllegalArgumentException("Missing <explosion> tag"); for (Element gibEl : child.getChildren()) { if (gibEl.getName().startsWith("gib")) { GibObject gib = new GibObject(); attr = gibEl.getName().substring(3); gib.setId(Integer.valueOf(attr)); child = gibEl.getChild("x"); if (child == null) throw new IllegalArgumentException("Gib missing <x> tag"); offset.x = Integer.valueOf(child.getValue()); child = gibEl.getChild("y"); if (child == null) throw new IllegalArgumentException("Gib missing <y> tag"); offset.y = Integer.valueOf(child.getValue()); gib.setOffset(offset.x, offset.y); child = gibEl.getChild("velocity"); if (child == null) throw new IllegalArgumentException("Gib missing <velocity> tag"); attr = child.getAttributeValue("min"); if (attr == null) throw new IllegalArgumentException("Velocity missing 'min' attribute"); gib.setVelocityMin(Double.valueOf(attr)); attr = child.getAttributeValue("max"); if (attr == null) throw new IllegalArgumentException("Velocity missing 'max' attribute"); gib.setVelocityMax(Double.valueOf(attr)); child = gibEl.getChild("direction"); if (child == null) throw new IllegalArgumentException("Missing <direction> tag"); attr = child.getAttributeValue("min"); if (attr == null) throw new IllegalArgumentException("Direction missing 'min' attribute"); gib.setDirectionMin(Integer.valueOf(attr)); attr = child.getAttributeValue("max"); if (attr == null) throw new IllegalArgumentException("Direction missing 'max' attribute"); gib.setDirectionMax(Integer.valueOf(attr)); child = gibEl.getChild("angular"); if (child == null) throw new IllegalArgumentException("Missing <angular> tag"); attr = child.getAttributeValue("min"); if (attr == null) throw new IllegalArgumentException("Angular missing 'min' attribute"); gib.setAngularMin(Double.valueOf(attr)); attr = child.getAttributeValue("max"); if (attr == null) throw new IllegalArgumentException("Angular missing 'max' attribute"); gib.setAngularMax(Double.valueOf(attr)); ship.add(gib); } } // Link mounts to gibs for (MountObject mount : gibMap.keySet()) { GibObject gib = ship.getGibById(gibMap.get(mount)); if (gib == null) mount.setGib(Database.DEFAULT_GIB_OBJ); else mount.setGib(gib); } gibMap.clear(); } finally { is.close(); } } /** * * @param ship * ship object in which the loaded data will be saved * @param f * file from which the data is read * @throws IllegalArgumentException * when the file is wrongly formatted - a tag or an attribute is missing * @throws JDOMParseException * when a parsing error occurs * @throws IOException * when a general IO error occurs */ public static void loadLayoutXML(ShipObject ship, File f) throws IllegalArgumentException, JDOMParseException, IOException { if (ship == null) throw new IllegalArgumentException("Ship object must not be null."); if (f == null) throw new IllegalArgumentException("File must not be null."); loadLayoutXML(ship, new FileInputStream(f)); } private static String firstExisting(String[] prefixes, String suffix, Database db) { for (String prefix : prefixes) { if (db.contains(IOUtils.trimProtocol(prefix) + suffix)) return prefix + suffix; } return null; } }