/** * Copyright (C) 2002-2012 The FreeCol Team * * This file is part of FreeCol. * * FreeCol 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. * * FreeCol 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. * * You should have received a copy of the GNU General Public License * along with FreeCol. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.freecol.common.model; import java.util.ArrayList; import java.util.List; import java.util.Locale; import org.freecolandroid.xml.stream.XMLStreamConstants; import org.freecolandroid.xml.stream.XMLStreamException; import org.freecolandroid.xml.stream.XMLStreamReader; import org.freecolandroid.xml.stream.XMLStreamWriter; /** * A named region on the map. */ public class Region extends FreeColGameObject implements Nameable { public static final String PACIFIC_NAME_KEY = "model.region.pacific"; public static final String CHILD_TAG = "child"; public static enum RegionType { OCEAN, COAST, LAKE, RIVER, LAND, MOUNTAIN, DESERT; /** * Gets a name index key for this region type. * * @return A name index key. */ public String getNameIndexKey() { return "index." + toString().toLowerCase(Locale.US); } } /** * The name of this Region. */ private String name; /** * Key used to retrieve description from Messages. */ private String nameKey; /** * The parent Region of this Region. */ private Region parent; /** * Whether this Region is claimable. Ocean Regions and non-leaf * Regions should not be claimable. */ private boolean claimable = false; /** * Whether this Region is discoverable. The Eastern Ocean regions * should not be discoverable. In general, non-leaf regions should * not be discoverable. The Pacific Ocean is an exception, however. */ private boolean discoverable = false; /** * Which Turn the Region was discovered in. */ private Turn discoveredIn; /** * Which Player the Region was discovered by. */ private Player discoveredBy; /** * Whether the Region is already discovered when the game starts. */ private boolean prediscovered = false; /** * How much discovering this Region contributes to your score. * This should be zero unless the Region is discoverable. */ private int scoreValue = 0; /** * Describe type here. */ private RegionType type; /** * The children Regions of this Region. */ private List<Region> children = new ArrayList<Region>(); /** * Creates a new <code>Region</code> instance. * * @param game a <code>Game</code> value */ public Region(Game game) { super(game); } /** * Creates a new <code>Region</code> instance. * * @param game a <code>Game</code> value * @param id a <code>String</code> value */ public Region(Game game, String id) { super(game, id); } /** * Initiates a new <code>Region</code> from an XML representation. * * @param game The <code>Game</code> this object belongs to. * @param in The input stream containing the XML. * @throws XMLStreamException if an error occurred during parsing. */ public Region(Game game, XMLStreamReader in) throws XMLStreamException { super(game, in); readFromXMLImpl(in); } /** * Get the <code>NameKey</code> value. * * @return a <code>String</code> value */ public final String getNameKey() { return nameKey; } /** * Set the <code>NameKey</code> value. * * @param newNameKey The new NameKey value. */ public final void setNameKey(final String newNameKey) { this.nameKey = newNameKey; } /** * Returns <code>true</code> if this Region is the Pacific * Ocean. The Pacific Ocean is special in so far as it is the only * Region that could be discovered in the original game. * * @return a <code>boolean</code> value */ public boolean isPacific() { if (PACIFIC_NAME_KEY.equals(nameKey)) { return true; } else if (parent != null) { return parent.isPacific(); } else { return false; } } /** * Get the <code>Name</code> value. * * @return a <code>String</code> value */ public final String getName() { return name; } /** * Set the <code>Name</code> value. * * @param newName The new Name value. */ public final void setName(final String newName) { this.name = newName; } /** * Returns the name or default name of this Region. * * @return a <code>String</code> value */ public StringTemplate getLabel() { if (prediscovered || isPacific()) { return StringTemplate.key(nameKey); } else if (name == null) { return StringTemplate.key("model.region." + type.toString().toLowerCase(Locale.US) + ".unknown"); } else { return StringTemplate.name(name); } } public String getTypeNameKey() { return "model.region." + type.toString().toLowerCase(Locale.US) + ".name"; } /** * Get the <code>Parent</code> value. * * @return a <code>Region</code> value */ public final Region getParent() { return parent; } /** * Set the <code>Parent</code> value. * * @param newParent The new Parent value. */ public final void setParent(final Region newParent) { this.parent = newParent; } /** * Get the <code>Children</code> value. * * @return a <code>List<Region></code> value */ public final List<Region> getChildren() { return children; } /** * Set the <code>Children</code> value. * * @param newChildren The new Children value. */ public final void setChildren(final List<Region> newChildren) { this.children = newChildren; } /** * Add a child region to this region. * * @param child The child <code>Region</code> to add. */ public void addChild(Region child) { children.add(child); } /** * Get the <code>Claimable</code> value. * * @return a <code>boolean</code> value */ public final boolean isClaimable() { return claimable; } /** * Set the <code>Claimable</code> value. * * @param newClaimable The new Claimable value. */ public final void setClaimable(final boolean newClaimable) { this.claimable = newClaimable; } /** * Get the <code>Discoverable</code> value. * * @return a <code>boolean</code> value */ public final boolean isDiscoverable() { return discoverable; } /** * Set the <code>Discoverable</code> value. * * @param newDiscoverable The new Discoverable value. */ public final void setDiscoverable(final boolean newDiscoverable) { this.discoverable = newDiscoverable; if (discoverable) { prediscovered = false; } } /** * Get the <code>Prediscovered</code> value. * * @return a <code>boolean</code> value */ public final boolean isPrediscovered() { return prediscovered; } /** * Set the <code>Prediscovered</code> value. * * @param newPrediscovered The new Prediscovered value. */ public final void setPrediscovered(final boolean newPrediscovered) { this.prediscovered = newPrediscovered; } /** * Get the <code>ScoreValue</code> value. * * @return an <code>int</code> value */ public final int getScoreValue() { return scoreValue; } /** * Set the <code>ScoreValue</code> value. * * @param newScoreValue The new ScoreValue value. */ public final void setScoreValue(final int newScoreValue) { this.scoreValue = newScoreValue; } /** * Get the <code>Type</code> value. * * @return a <code>RegionType</code> value */ public final RegionType getType() { return type; } /** * Set the <code>Type</code> value. * * @param newType The new Type value. */ public final void setType(final RegionType newType) { this.type = newType; } /** * Returns true if this is the whole map Region. * * @return a <code>boolean</code> value */ public boolean isRoot() { return parent == null; } /** * Returns true if this is a leaf node. * * @return a <code>boolean</code> value */ public boolean isLeaf() { return children == null; } /** * Returns a discoverable Region or null. If this region is * discoverable, it is returned. If not, a discoverable parent is * returned, unless there is none. This is intended for * discovering the Pacific Ocean when discovering one of its * sub-Regions. * * @return a <code>Region</code> value */ public Region getDiscoverableRegion() { if (isDiscoverable()) { return this; } else if (parent != null) { return parent.getDiscoverableRegion(); } else { return null; } } /** * Get the <code>DiscoveredIn</code> value. * * @return a <code>Turn</code> value */ public final Turn getDiscoveredIn() { return discoveredIn; } /** * Set the <code>DiscoveredIn</code> value. * * @param newDiscoveredIn The new DiscoveredIn value. */ public final void setDiscoveredIn(final Turn newDiscoveredIn) { this.discoveredIn = newDiscoveredIn; } /** * Get the <code>DiscoveredBy</code> value. * * @return a <code>Player</code> value */ public final Player getDiscoveredBy() { return discoveredBy; } /** * Set the <code>DiscoveredBy</code> value. * * @param newDiscoveredBy The new DiscoveredBy value. */ public final void setDiscoveredBy(final Player newDiscoveredBy) { this.discoveredBy = newDiscoveredBy; } /** * Mark the Region as discovered. * * @param player a <code>Player</code> value * @param turn a <code>Turn</code> value * @param newName a <code>String</code> value */ public HistoryEvent discover(Player player, Turn turn, String newName) { discoveredBy = player; discoveredIn = turn; name = newName; discoverable = false; if (getSpecification().getBoolean(GameOptions.EXPLORATION_POINTS) || isPacific()) { player.modifyScore(getScoreValue()); } return new HistoryEvent(turn, HistoryEvent.EventType.DISCOVER_REGION) .addStringTemplate("%nation%", player.getNationName()) .addName("%region%", newName); } /** * This method writes an XML-representation of this object to the given * stream. * * <br> * <br> * * Only attributes visible to the given <code>Player</code> will be added * to that representation if <code>showAll</code> is set to * <code>false</code>. * * @param out The target stream. * @param player The <code>Player</code> this XML-representation should be * made for, or <code>null</code> if * <code>showAll == true</code>. * @param showAll Only attributes visible to <code>player</code> will be * added to the representation if <code>showAll</code> is set * to <i>false</i>. * @param toSavedGame If <code>true</code> then information that is only * needed when saving a game is added. * @throws XMLStreamException if there are any problems writing to the * stream. */ protected void toXMLImpl(XMLStreamWriter out, Player player, boolean showAll, boolean toSavedGame) throws XMLStreamException { out.writeStartElement(getXMLElementTagName()); out.writeAttribute(ID_ATTRIBUTE, getId()); out.writeAttribute("nameKey", nameKey); out.writeAttribute("type", type.toString()); if (name != null) { out.writeAttribute("name", name); } if (prediscovered) { out.writeAttribute("prediscovered", Boolean.toString(prediscovered)); } if (claimable) { out.writeAttribute("claimable", Boolean.toString(claimable)); } if (discoverable) { out.writeAttribute("discoverable", Boolean.toString(discoverable)); } if (parent != null) { out.writeAttribute("parent", parent.getId()); } if (discoveredIn != null) { out.writeAttribute("discoveredIn", String.valueOf(discoveredIn.getNumber())); } if (discoveredBy != null) { out.writeAttribute("discoveredBy", discoveredBy.getId()); } if (scoreValue > 0) { out.writeAttribute("scoreValue", String.valueOf(scoreValue)); } if (children != null) { for (Region child : children) { out.writeStartElement(CHILD_TAG); out.writeAttribute(ID_ATTRIBUTE_TAG, child.getId()); out.writeEndElement(); } } out.writeEndElement(); } /** * Initialize this object from an XML-representation of this object. * @param in The input stream with the XML. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readFromXMLImpl(XMLStreamReader in) throws XMLStreamException { setId(in.getAttributeValue(null, ID_ATTRIBUTE)); nameKey = in.getAttributeValue(null, "nameKey"); name = in.getAttributeValue(null, "name"); claimable = getAttribute(in, "claimable", false); discoverable = getAttribute(in, "discoverable", false); prediscovered = getAttribute(in, "prediscovered", false); scoreValue = getAttribute(in, "scoreValue", 0); type = Enum.valueOf(RegionType.class, in.getAttributeValue(null, "type")); int turn = getAttribute(in, "discoveredIn", -1); if (turn > 0) { discoveredIn = new Turn(turn); } discoveredBy = getFreeColGameObject(in, "discoveredBy", Player.class, null); parent = getFreeColGameObject(in, "parent", Region.class); children = new ArrayList<Region>(); while (in.nextTag() != XMLStreamConstants.END_ELEMENT) { if (in.getLocalName().equals("children")) { // TODO: remove support for old format String[] childArray = readFromArrayElement("children", in, new String[0]); for (String child : childArray) { children.add(getGame().getMap().getRegion(child)); } } else if (CHILD_TAG.equals(in.getLocalName())) { children.add(getFreeColGameObject(in, ID_ATTRIBUTE_TAG, Region.class)); in.nextTag(); } } if (children.isEmpty()) { children = null; } } @Override public String toString() { return nameKey; } /** * Gets the tag name of the root element representing this object. * * @return "region". */ public static String getXMLElementTagName() { return "region"; } }