package games.strategy.engine.data.export; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import com.google.common.base.Joiner; import games.strategy.debug.ClientLogger; import games.strategy.engine.ClientContext; import games.strategy.engine.data.GameData; import games.strategy.engine.data.GameMap; import games.strategy.engine.data.GameParser; import games.strategy.engine.data.GameStep; import games.strategy.engine.data.IAttachment; import games.strategy.engine.data.NamedAttachable; import games.strategy.engine.data.PlayerID; import games.strategy.engine.data.ProductionFrontier; import games.strategy.engine.data.ProductionRule; import games.strategy.engine.data.RelationshipTracker; import games.strategy.engine.data.RelationshipType; import games.strategy.engine.data.RepairFrontier; import games.strategy.engine.data.RepairRule; import games.strategy.engine.data.Resource; import games.strategy.engine.data.TechnologyFrontier; import games.strategy.engine.data.Territory; import games.strategy.engine.data.TerritoryEffect; import games.strategy.engine.data.UnitCollection; import games.strategy.engine.data.UnitType; import games.strategy.engine.data.properties.BooleanProperty; import games.strategy.engine.data.properties.ColorProperty; import games.strategy.engine.data.properties.ComboProperty; import games.strategy.engine.data.properties.FileProperty; import games.strategy.engine.data.properties.GameProperties; import games.strategy.engine.data.properties.IEditableProperty; import games.strategy.engine.data.properties.NumberProperty; import games.strategy.engine.data.properties.StringProperty; import games.strategy.engine.delegate.IDelegate; import games.strategy.engine.framework.ServerGame; import games.strategy.triplea.Constants; import games.strategy.triplea.delegate.TechAdvance; import games.strategy.util.IntegerMap; import games.strategy.util.Tuple; public class GameDataExporter { private final StringBuffer xmlfile; public GameDataExporter(final GameData data) { xmlfile = new StringBuffer(); init(data); tripleaMinimumVersion(); diceSides(data); map(data); resourceList(data); playerList(data); unitList(data); relationshipTypeList(data); territoryEffectList(data); gamePlay(data); production(data); technology(data); attachments(data); initialize(data); propertyList(data); finish(); } private void tripleaMinimumVersion() { // Since we do not keep the minimum version info in the game data, just put the current version of triplea here // (since we have // successfully started the map, it is basically correct) xmlfile.append(" <triplea minimumVersion=\"").append(ClientContext.engineVersion()).append("\"/>\n"); } private void diceSides(final GameData data) { final int diceSides = data.getDiceSides(); xmlfile.append(" <diceSides value=\"").append(diceSides).append("\"/>\n"); } private void technology(final GameData data) { final String technologies = technologies(data); final String playerTechs = playertechs(data); if (technologies.length() > 0 || playerTechs.length() > 0) { xmlfile.append(" <technology>\n"); xmlfile.append(technologies); xmlfile.append(playerTechs); xmlfile.append(" </technology>\n"); } } private String playertechs(final GameData data) { final StringBuffer returnValue = new StringBuffer(); for (final PlayerID player : data.getPlayerList()) { if (player.getTechnologyFrontierList().getFrontiers().size() > 0) { returnValue.append(" <playerTech player=\"").append(player.getName()).append("\">\n"); for (final TechnologyFrontier frontier : player.getTechnologyFrontierList().getFrontiers()) { returnValue.append(" <category name=\"").append(frontier.getName()).append("\">\n"); for (final TechAdvance tech : frontier.getTechs()) { String name = tech.getName(); final String cat = tech.getProperty(); for (final String definedName : TechAdvance.s_allPreDefinedTechnologyNames) { if (definedName.equals(name)) { name = cat; } } returnValue.append(" <tech name=\"").append(name).append("\"/>\n"); } returnValue.append(" </category>\n"); } returnValue.append(" </playerTech>\n"); } } return returnValue.toString(); } private String technologies(final GameData data) { final StringBuffer returnValue = new StringBuffer(); if (data.getTechnologyFrontier().getTechs().size() > 0) { returnValue.append(" <technologies>\n"); for (final TechAdvance tech : data.getTechnologyFrontier().getTechs()) { String name = tech.getName(); final String cat = tech.getProperty(); // definedAdvances are handled differently by gameparser, they are set in xml with the category as the name but // stored in java with the normal category and name, this causes an xml bug when exporting. for (final String definedName : TechAdvance.s_allPreDefinedTechnologyNames) { if (definedName.equals(name)) { name = cat; } } returnValue.append(" <techname name=\"").append(name).append("\""); if (!name.equals(cat)) { returnValue.append(" tech=\"").append(cat).append("\" "); } returnValue.append("/>\n"); } returnValue.append(" </technologies>\n"); } return returnValue.toString(); } @SuppressWarnings("unchecked") private void propertyList(final GameData data) { // TODO: Unchecked Reflection xmlfile.append(" <propertyList>\n"); final GameProperties gameProperties = data.getProperties(); try { // TODO: unchecked reflection below.. this is bad stuff.. find ways to remove final Field conPropField = GameProperties.class.getDeclaredField(GameProperties.CONSTANT_PROPERTIES_FIELD_NAME); conPropField.setAccessible(true); final Field edPropField = GameProperties.class.getDeclaredField(GameProperties.EDITABLE_PROPERTIES_FIELD_NAME); edPropField.setAccessible(true); printConstantProperties((Map<String, Object>) conPropField.get(gameProperties)); printEditableProperties((Map<String, IEditableProperty>) edPropField.get(gameProperties)); } catch (final SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { ClientLogger.logError("An Error occured whilst trying trying to setup the Property List", e); } xmlfile.append(" </propertyList>\n"); } private void printEditableProperties(final Map<String, IEditableProperty> edProperties) { for (final String property : edProperties.keySet()) { printEditableProperty(edProperties.get(property)); } } @SuppressWarnings("unchecked") private void printEditableProperty(final IEditableProperty prop) { String typeString = ""; String value = "" + prop.getValue(); if (prop.getClass().equals(BooleanProperty.class)) { typeString = " <boolean/>\n"; } if (prop.getClass().equals(FileProperty.class)) { typeString = " <file/>\n"; } if (prop.getClass().equals(StringProperty.class)) { typeString = " <string/>\n"; } if (prop.getClass().equals(ColorProperty.class)) { typeString = " <color/>\n"; value = "0x" + Integer.toHexString((((Integer) prop.getValue()).intValue())).toUpperCase(); } if (prop.getClass().equals(ComboProperty.class)) { Field listField; try { // TODO: unchecked reflection listField = ComboProperty.class.getDeclaredField(ComboProperty.POSSIBLE_VALUES_FIELD_NAME); listField.setAccessible(true); typeString = " <list>" + Joiner.on(',').join((List<String>) listField.get(prop)) + "</list>\n"; } catch (final NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { ClientLogger.logError("An Error occured whilst trying to print the Property \"" + value + "\"", e); } } if (prop.getClass().equals(NumberProperty.class)) { try { // TODO: unchecked reflection final Field maxField = NumberProperty.class.getDeclaredField(NumberProperty.MAX_PROPERTY_NAME); final Field minField = NumberProperty.class.getDeclaredField(NumberProperty.MIN_PROPERTY_NAME); maxField.setAccessible(true); minField.setAccessible(true); final int max = maxField.getInt(prop); final int min = minField.getInt(prop); typeString = " <number min=\"" + min + "\" max=\"" + max + "\"/>\n"; } catch (final SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { ClientLogger.logError("An Error occured whilst trying to print a Number-XML Tag", e); } } xmlfile.append(" <property name=\"").append(prop.getName()).append("\" value=\"").append(value) .append("\" editable=\"true\">\n"); xmlfile.append(typeString); xmlfile.append(" </property>\n"); } private void printConstantProperties(final Map<String, Object> conProperties) { for (final String propName : conProperties.keySet()) { if (propName.equals("notes")) { // TODO: unchecked reflection // Special handling of notes property printNotes((String) conProperties.get(propName)); } else if (propName.equals("EditMode") || propName.equals("GAME_UUID") || propName.equals(ServerGame.GAME_HAS_BEEN_SAVED_PROPERTY)) { // Don't print these options } else { printConstantProperty(propName, conProperties.get(propName)); } } } private void printNotes(final String notes) { xmlfile.append(" <property name=\"notes\">\n"); xmlfile.append(" <value>\n"); xmlfile.append(" <![CDATA[\n"); xmlfile.append(notes); xmlfile.append("]]>\n"); xmlfile.append(" </value>\n"); xmlfile.append(" </property>\n"); } private void printConstantProperty(final String propName, final Object property) { xmlfile.append(" <property name=\"").append(propName).append("\" value=\"").append(property.toString()) .append("\" editable=\"false\">\n"); if (property.getClass().equals(java.lang.String.class)) { xmlfile.append(" <string/>\n"); } if (property.getClass().equals(java.io.File.class)) { xmlfile.append(" <file/>\n"); } if (property.getClass().equals(java.lang.Boolean.class)) { xmlfile.append(" <boolean/>\n"); } xmlfile.append(" </property>\n"); } private void initialize(final GameData data) { xmlfile.append(" <initialize>\n"); ownerInitialize(data); unitInitialize(data); resourceInitialize(data); relationshipInitialize(data); xmlfile.append(" </initialize>\n"); } private void relationshipInitialize(final GameData data) { if (data.getRelationshipTypeList().getAllRelationshipTypes().size() <= 4) { return; } final RelationshipTracker rt = data.getRelationshipTracker(); xmlfile.append(" <relationshipInitialize>\n"); final Collection<PlayerID> players = data.getPlayerList().getPlayers(); final Collection<PlayerID> playersAlreadyDone = new HashSet<>(); for (final PlayerID p1 : players) { for (final PlayerID p2 : players) { if (p1.equals(p2) || playersAlreadyDone.contains(p2)) { continue; } final RelationshipType type = rt.getRelationshipType(p1, p2); final int roundValue = rt.getRoundRelationshipWasCreated(p1, p2); xmlfile.append(" <relationship type=\"").append(type.getName()).append("\" player1=\"") .append(p1.getName()).append("\" player2=\"").append(p2.getName()).append("\" roundValue=\"") .append(roundValue).append("\"/>\n"); } playersAlreadyDone.add(p1); } xmlfile.append(" </relationshipInitialize>\n"); } private void resourceInitialize(final GameData data) { xmlfile.append(" <resourceInitialize>\n"); for (final PlayerID player : data.getPlayerList()) { for (final Resource resource : data.getResourceList().getResources()) { if (player.getResources().getQuantity(resource.getName()) > 0) { xmlfile.append(" <resourceGiven player=\"").append(player.getName()).append("\" resource=\"") .append(resource.getName()).append("\" quantity=\"") .append(player.getResources().getQuantity(resource.getName())).append("\"/>\n"); } } } xmlfile.append(" </resourceInitialize>\n"); } private void unitInitialize(final GameData data) { xmlfile.append(" <unitInitialize>\n"); for (final Territory terr : data.getMap().getTerritories()) { final UnitCollection uc = terr.getUnits(); for (final PlayerID player : uc.getPlayersWithUnits()) { final IntegerMap<UnitType> ucp = uc.getUnitsByType(player); for (final UnitType unit : ucp.keySet()) { if (player == null || player.getName().equals(Constants.PLAYER_NAME_NEUTRAL)) { xmlfile.append(" <unitPlacement unitType=\"").append(unit.getName()).append("\" territory=\"") .append(terr.getName()).append("\" quantity=\"").append(ucp.getInt(unit)).append("\"/>\n"); } else { xmlfile.append(" <unitPlacement unitType=\"").append(unit.getName()).append("\" territory=\"") .append(terr.getName()).append("\" quantity=\"").append(ucp.getInt(unit)).append("\" owner=\"") .append(player.getName()).append("\"/>\n"); } } } } xmlfile.append(" </unitInitialize>\n"); } private void ownerInitialize(final GameData data) { xmlfile.append(" <ownerInitialize>\n"); for (final Territory terr : data.getMap().getTerritories()) { if (!terr.getOwner().getName().equals(Constants.PLAYER_NAME_NEUTRAL)) { xmlfile.append(" <territoryOwner territory=\"").append(terr.getName()).append("\" owner=\"") .append(terr.getOwner().getName()).append("\"/>\n"); } } xmlfile.append(" </ownerInitialize>\n"); } private void attachments(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <attachmentList>\n"); for (final Tuple<IAttachment, ArrayList<Tuple<String, String>>> attachment : data.getAttachmentOrderAndValues()) { // TODO: use a ui switch to determine if we are printing the xml as it was created, or as it stands right now // (including changes to // the game data) final Tuple<IAttachment, ArrayList<Tuple<String, String>>> current = attachment; printAttachments(current); } xmlfile.append(" </attachmentList>\n"); } private String printAttachmentOptionsBasedOnOriginalXML(final ArrayList<Tuple<String, String>> attachmentPlusValues, final IAttachment attachment) { if (attachmentPlusValues.isEmpty()) { return ""; } final StringBuffer sb = new StringBuffer(""); boolean alreadyHasOccupiedTerrOf = false; for (final Tuple<String, String> current : attachmentPlusValues) { sb.append(" <option name=\"").append(current.getFirst()).append("\" value=\"") .append(current.getSecond()).append("\"/>\n"); if (current.getFirst().equals("occupiedTerrOf")) { alreadyHasOccupiedTerrOf = true; } } // add occupiedTerrOf until we fix engine to only use originalOwner if (!alreadyHasOccupiedTerrOf && attachment instanceof games.strategy.triplea.attachments.TerritoryAttachment) { final games.strategy.triplea.attachments.TerritoryAttachment ta = (games.strategy.triplea.attachments.TerritoryAttachment) attachment; if (ta.getOriginalOwner() != null) { sb.append(" <option name=\"occupiedTerrOf\" value=\"").append(ta.getOriginalOwner().getName()) .append("\"/>\n"); } } return sb.toString(); } private void printAttachments(final Tuple<IAttachment, ArrayList<Tuple<String, String>>> attachmentPlusValues) { final IAttachment attachment = attachmentPlusValues.getFirst(); try { // TODO: none of the attachment exporter classes have been updated since TripleA version 1.3.2.2 final String attachmentOptions; attachmentOptions = printAttachmentOptionsBasedOnOriginalXML(attachmentPlusValues.getSecond(), attachment); final NamedAttachable attachTo = (NamedAttachable) attachment.getAttachedTo(); // TODO: keep this list updated String type = ""; if (attachTo.getClass().equals(PlayerID.class)) { type = "player"; } if (attachTo.getClass().equals(UnitType.class)) { type = "unitType"; } if (attachTo.getClass().equals(Territory.class)) { type = "territory"; } if (attachTo.getClass().equals(TerritoryEffect.class)) { type = "territoryEffect"; } if (attachTo.getClass().equals(Resource.class)) { type = "resource"; } if (attachTo.getClass().equals(RelationshipType.class)) { type = "relationship"; } if (TechAdvance.class.isAssignableFrom(attachTo.getClass())) { type = "technology"; } if (type.equals("")) { throw new AttachmentExportException("no attachmentType known for " + attachTo.getClass().getCanonicalName()); } if (attachmentOptions.length() > 0) { xmlfile.append(" <attachment name=\"").append(attachment.getName()).append("\" attachTo=\"") .append(attachTo.getName()).append("\" javaClass=\"").append(attachment.getClass().getCanonicalName()) .append("\" type=\"").append(type).append("\">\n"); xmlfile.append(attachmentOptions); xmlfile.append(" </attachment>\n"); } } catch (final Exception e) { ClientLogger.logError("An Error occured whilst trying to print the Attachment \"" + attachment.getName() + "\"", e); } } private void production(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <production>\n"); productionRules(data); repairRules(data); repairFrontiers(data); productionFrontiers(data); playerProduction(data); playerRepair(data); xmlfile.append(" </production>\n"); } private void repairRules(final GameData data) { for (final RepairRule rr : data.getRepairRuleList().getRepairRules()) { xmlfile.append(" <repairRule name=\"").append(rr.getName()).append("\">\n"); for (final Resource cost : rr.getCosts().keySet()) { xmlfile.append(" <cost resource=\"").append(cost.getName()).append("\" quantity=\"") .append(rr.getCosts().getInt(cost)).append("\"/>\n"); } for (final NamedAttachable result : rr.getResults().keySet()) { xmlfile.append(" <result resourceOrUnit=\"").append(result.getName()).append("\" quantity=\"") .append(rr.getResults().getInt(result)).append("\"/>\n"); } xmlfile.append(" </repairRule>\n"); } } private void repairFrontiers(final GameData data) { for (final String frontierName : data.getRepairFrontierList().getRepairFrontierNames()) { final RepairFrontier frontier = data.getRepairFrontierList().getRepairFrontier(frontierName); xmlfile.append("\n"); xmlfile.append(" <repairFrontier name=\"").append(frontier.getName()).append("\">\n"); for (final RepairRule rule : frontier.getRules()) { xmlfile.append(" <repairRules name=\"").append(rule.getName()).append("\"/>\n"); } xmlfile.append(" </repairFrontier>\n"); } xmlfile.append("\n"); } private void playerRepair(final GameData data) { for (final PlayerID player : data.getPlayerList()) { try { final String playerRepair = player.getRepairFrontier().getName(); final String playername = player.getName(); xmlfile.append(" <playerRepair player=\"").append(playername).append("\" frontier=\"") .append(playerRepair).append("\"/>\n"); } catch (final NullPointerException npe) { // neutral? } } } private void playerProduction(final GameData data) { for (final PlayerID player : data.getPlayerList()) { try { final String playerfrontier = player.getProductionFrontier().getName(); final String playername = player.getName(); xmlfile.append(" <playerProduction player=\"").append(playername).append("\" frontier=\"") .append(playerfrontier).append("\"/>\n"); } catch (final NullPointerException npe) { // neutral? } } } private void productionFrontiers(final GameData data) { for (final String frontierName : data.getProductionFrontierList().getProductionFrontierNames()) { final ProductionFrontier frontier = data.getProductionFrontierList().getProductionFrontier(frontierName); xmlfile.append("\n"); xmlfile.append(" <productionFrontier name=\"").append(frontier.getName()).append("\">\n"); for (final ProductionRule rule : frontier.getRules()) { xmlfile.append(" <frontierRules name=\"").append(rule.getName()).append("\"/>\n"); } xmlfile.append(" </productionFrontier>\n"); } xmlfile.append("\n"); } private void productionRules(final GameData data) { for (final ProductionRule pr : data.getProductionRuleList().getProductionRules()) { xmlfile.append(" <productionRule name=\"").append(pr.getName()).append("\">\n"); for (final Resource cost : pr.getCosts().keySet()) { xmlfile.append(" <cost resource=\"").append(cost.getName()).append("\" quantity=\"") .append(pr.getCosts().getInt(cost)).append("\"/>\n"); } for (final NamedAttachable result : pr.getResults().keySet()) { xmlfile.append(" <result resourceOrUnit=\"").append(result.getName()).append("\" quantity=\"") .append(pr.getResults().getInt(result)).append("\"/>\n"); } xmlfile.append(" </productionRule>\n"); } } private void gamePlay(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <gamePlay>\n"); for (final IDelegate delegate : data.getDelegateList()) { if (!delegate.getName().equals("edit")) { xmlfile.append(" <delegate name=\"").append(delegate.getName()).append("\" javaClass=\"") .append(delegate.getClass().getCanonicalName()).append("\" display=\"").append(delegate.getDisplayName()) .append("\"/>\n"); } } sequence(data); xmlfile.append(" <offset round=\"").append(data.getSequence().getRound() - 1).append("\"/>\n"); xmlfile.append(" </gamePlay>\n"); } private void sequence(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <sequence>\n"); for (final GameStep step : data.getSequence()) { try { final Field mDelegateField = GameStep.class.getDeclaredField("m_delegate"); // TODO: unchecked reflection mDelegateField.setAccessible(true); final String delegate = (String) mDelegateField.get(step); xmlfile.append(" <step name=\"").append(step.getName()).append("\" delegate=\"").append(delegate) .append("\""); } catch (final NullPointerException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) { ClientLogger.logError("An Error occured whilst trying to sequence in game " + data.getGameName(), e); } if (step.getPlayerID() != null) { xmlfile.append(" player=\"").append(step.getPlayerID().getName()).append("\""); } if (step.getDisplayName() != null) { xmlfile.append(" display=\"").append(step.getDisplayName()).append("\""); } if (step.getMaxRunCount() > -1) { int maxRun = step.getMaxRunCount(); if (maxRun == 0) { maxRun = 1; } xmlfile.append(" maxRunCount=\"").append(maxRun).append("\""); } xmlfile.append("/>\n"); } xmlfile.append(" </sequence>\n"); } private void unitList(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <unitList>\n"); for (final UnitType unit : data.getUnitTypeList()) { xmlfile.append(" <unit name=\"").append(unit.getName()).append("\"/>\n"); } xmlfile.append(" </unitList>\n"); } private void playerList(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <playerList>\n"); for (final PlayerID player : data.getPlayerList().getPlayers()) { xmlfile.append(" <player name=\"").append(player.getName()).append("\" optional=\"") .append(player.getOptional()).append("\"/>\n"); } for (final String allianceName : data.getAllianceTracker().getAlliances()) { for (final PlayerID alliedPlayer : data.getAllianceTracker().getPlayersInAlliance(allianceName)) { xmlfile.append(" <alliance player=\"").append(alliedPlayer.getName()).append("\" alliance=\"") .append(allianceName).append("\"/>\n"); } } xmlfile.append(" </playerList>\n"); } private void relationshipTypeList(final GameData data) { final Collection<RelationshipType> types = data.getRelationshipTypeList().getAllRelationshipTypes(); if (types.size() <= 4) { return; } xmlfile.append("\n"); xmlfile.append(" <relationshipTypes>\n"); for (final RelationshipType current : types) { final String name = current.getName(); if (name.equals(Constants.RELATIONSHIP_TYPE_SELF) || name.equals(Constants.RELATIONSHIP_TYPE_NULL) || name.equals(Constants.RELATIONSHIP_TYPE_DEFAULT_WAR) || name.equals(Constants.RELATIONSHIP_TYPE_DEFAULT_ALLIED)) { continue; } xmlfile.append(" <relationshipType name=\"").append(name).append("\"/>\n"); } xmlfile.append(" </relationshipTypes>\n"); } private void territoryEffectList(final GameData data) { final Collection<TerritoryEffect> types = data.getTerritoryEffectList().values(); if (types.isEmpty()) { return; } xmlfile.append("\n"); xmlfile.append(" <territoryEffectList>\n"); for (final TerritoryEffect current : types) { xmlfile.append(" <territoryEffect name=\"").append(current.getName()).append("\"/>\n"); } xmlfile.append(" </territoryEffectList>\n"); } private void resourceList(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <resourceList>\n"); for (final Resource resource : data.getResourceList().getResources()) { xmlfile.append(" <resource name=\"").append(resource.getName()).append("\"/>\n"); } xmlfile.append(" </resourceList>\n"); } private void map(final GameData data) { xmlfile.append("\n"); xmlfile.append(" <map>\n"); xmlfile.append(" <!-- Territory Definitions -->\n"); final GameMap map = data.getMap(); for (final Territory ter : map.getTerritories()) { xmlfile.append(" <territory name=\"").append(ter.getName()).append("\""); if (ter.isWater()) { xmlfile.append(" water=\"true\""); } xmlfile.append("/>\n"); } connections(data); xmlfile.append(" </map>\n"); } private class Connection { private final Territory _t1; private final Territory _t2; private Connection(final Territory t1, final Territory t2) { _t1 = t1; _t2 = t2; } @Override public int hashCode() { return _t1.hashCode() + _t2.hashCode(); } @Override public boolean equals(final Object o) { if (o == null) { return false; } final Connection con = (Connection) o; return (_t1 == con._t1 && _t2 == con._t2); } } private void connections(final GameData data) { xmlfile.append(" <!-- Territory Connections -->\n"); final GameMap map = data.getMap(); final List<Connection> reverseConnectionTracker = new ArrayList<>(); for (final Territory ter : map.getTerritories()) { for (final Territory nb : map.getNeighbors(ter)) { if (!reverseConnectionTracker.contains(new Connection(ter, nb))) { xmlfile.append(" <connection t1=\"").append(ter.getName()).append("\" t2=\"").append(nb.getName()) .append("\"/>\n"); reverseConnectionTracker.add(new Connection(nb, ter)); } } } } private void init(final GameData data) { xmlfile.append("<?xml version=\"1.0\"?>\n"); xmlfile.append("<!DOCTYPE game SYSTEM \"" + GameParser.DTD_FILE_NAME + "\">\n"); xmlfile.append("<game>\n"); xmlfile.append(" <info name=\"").append(data.getGameName()).append("\" version=\"") .append(data.getGameVersion().toString()).append("\"/>\n"); xmlfile.append(" <loader javaClass=\"").append(data.getGameLoader().getClass().getCanonicalName()) .append("\"/>\n"); } private void finish() { xmlfile.append("\n"); xmlfile.append("</game>\n"); } public String getXML() { return xmlfile.toString(); } }