/**
* 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.server.ai;
import java.util.logging.Logger;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamWriter;
import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.Europe;
import net.sf.freecol.common.model.FreeColGameObject;
import net.sf.freecol.common.model.Goods;
import net.sf.freecol.common.model.GoodsContainer;
import net.sf.freecol.common.model.GoodsType;
import net.sf.freecol.common.model.Locatable;
import net.sf.freecol.common.model.Location;
import net.sf.freecol.common.model.Tile;
import net.sf.freecol.server.ai.mission.TransportMission;
import org.w3c.dom.Element;
/**
* Objects of this class contains AI-information for a single {@link Goods}.
*/
public class AIGoods extends AIObject implements Transportable {
private static final Logger logger = Logger.getLogger(AIGoods.class.getName());
/** The underlying goods. */
private Goods goods;
/** The destination location for the goods. */
private Location destination;
/** The transport priority. */
private int transportPriority;
/** The AI unit assigned to provide the transport. */
private AIUnit transport = null;
/**
* Creates a new uninitialized <code>AIGoods</code>.
*
* @param aiMain The main AI-object.
* @param id The unique ID of this object.
*/
public AIGoods(AIMain aiMain, String id) {
super(aiMain, id);
goods = null;
destination = null;
transportPriority = -1;
transport = null;
}
/**
* Creates a new <code>AIGoods</code>.
*
* @param aiMain The main AI-object.
* @param location The location of the goods.
* @param type The type of goods.
* @param amount The amount of goods.
* @param destination The destination of the goods. This is the
* <code>Location</code> to which the goods should be transported.
*/
public AIGoods(AIMain aiMain, Location location, GoodsType type,
int amount, Location destination) {
this(aiMain, getXMLElementTagName() + ":" + aiMain.getNextId());
goods = new Goods(aiMain.getGame(), location, type, amount);
this.destination = destination;
uninitialized = false;
}
/**
* Creates a new <code>AIGoods</code> from the given
* XML-representation.
*
* @param aiMain The main AI-object.
* @param element The root element for the XML-representation
* of a <code>Wish</code>.
*/
public AIGoods(AIMain aiMain, Element element) {
super(aiMain, element);
uninitialized = getGoods() == null;
}
/**
* Creates a new <code>AIGoods</code> from the given
* XML-representation.
*
* @param aiMain The main AI-object.
* @param in The input stream containing the XML.
* @throws XMLStreamException if a problem was encountered
* during parsing.
*/
public AIGoods(AIMain aiMain, XMLStreamReader in)
throws XMLStreamException {
super(aiMain, in);
uninitialized = getGoods() == null;
}
/**
* Disposes this object.
*/
public void dispose() {
setTransport(null);
if (destination != null) {
if (destination instanceof Colony) {
AIColony aic = getAIMain().getAIColony((Colony)destination);
if (aic != null) aic.removeAIGoods(this);
} else if (destination instanceof Europe) {
// Nothing to remove.
} else {
logger.warning("Unknown type of destination: " + destination);
}
destination = null;
}
goods = null;
super.dispose();
}
/**
* Gets the goods this <code>AIGoods</code> is controlling.
*
* @return The <code>Goods</code>.
*/
public Goods getGoods() {
return goods;
}
/**
* Sets the goods this <code>AIGoods</code> is controlling.
*
* @param goods The new <code>Goods</code>.
*/
public void setGoods(Goods goods) {
this.goods = goods;
}
/**
* Checks the integrity of a this AIGoods.
*
* @return True if the goods are valid.
*/
public boolean checkIntegrity() {
String why = (!super.checkIntegrity()) ? "super"
: (goods == null) ? "null-goods"
: (goods.getType() == null) ? "null-goods-type"
: (goods.getAmount() <= 0) ? "non-positive-goods-amount"
: (goods.getLocation() == null) ? "null-location"
: (((FreeColGameObject)goods.getLocation()).isDisposed()) ? "disposed-location"
: (destination == null) ? "null-destination"
: (((FreeColGameObject)destination).isDisposed()) ? "disposed-destination"
: "ok";
if (!"ok".equals(why)) logger.finest("checkIntegrity(" + this.toString() + ") = " + why);
return super.checkIntegrity()
&& (goods != null
&& goods.getType() != null
&& goods.getAmount() > 0
&& goods.getLocation() != null
&& !((FreeColGameObject)goods.getLocation()).isDisposed())
&& (destination != null
&& !((FreeColGameObject)destination).isDisposed());
}
// Transportable interface
/**
* Gets the number of cargo slots taken by these AI goods.
*
* @return The number of cargo slots.
*/
public int getSpaceTaken() {
return (goods == null) ? 0 : goods.getSpaceTaken();
}
/**
* Returns the source for this <code>Transportable</code>.
* This is normally the location of the
* {@link #getTransportLocatable locatable}.
*
* @return The source for this <code>Transportable</code>.
*/
public Location getTransportSource() {
return (goods == null) ? null : goods.getLocation();
}
/**
* Returns the destination for this <code>Transportable</code>.
* This can either be the target {@link Tile} of the transport
* or the target for the entire <code>Transportable</code>'s
* mission. The target for the tansport is determined by
* {@link TransportMission} in the latter case.
*
* @return The destination for this <code>Transportable</code>.
*/
public Location getTransportDestination() {
return destination;
}
/**
* Sets the destination for this <code>Transportable</code>.
* This should only be called when a goods transportable destination
* becomes invalid and we need to retarget.
*
* @param destination The new destination <code>Location</code>.
*/
public void setTransportDestination(Location destination) {
this.destination = destination;
}
/**
* Gets the priority of transporting this <code>Transportable</code>
* to it's destination.
*
* @return The priority of the transport.
*/
public int getTransportPriority() {
return (goods.getAmount() <= GoodsContainer.CARGO_SIZE)
? goods.getAmount()
: transportPriority;
}
/**
* Sets the priority of getting the goods to the {@link
* #getTransportDestination}.
*
* @param transportPriority The priority.
*/
public void setTransportPriority(int transportPriority) {
this.transportPriority = transportPriority;
}
/**
* Increases the transport priority of this <code>Transportable</code>.
* This method gets called every turn the <code>Transportable</code>
* have not been put on a carrier's transport list.
*/
public void increaseTransportPriority() {
transportPriority++;
}
/**
* Gets the <code>Locatable</code> which should be transported.
* @return The <code>Locatable</code>.
*/
public Locatable getTransportLocatable() {
return getGoods();
}
/**
* Gets the carrier responsible for transporting this
* <code>Transportable</code>.
*
* @return The <code>AIUnit</code> which has this
* <code>Transportable</code> in it's transport list. This
* <code>Transportable</code> has not been scheduled for
* transport if this value is <code>null</code>.
*
*/
public AIUnit getTransport() {
return transport;
}
/**
* Sets the carrier responsible for transporting this
* <code>Transportable</code>.
*
* @param transport The <code>AIUnit</code> which has this
* <code>Transportable</code> in it's transport list. This
* <code>Transportable</code> has not been scheduled for
* transport if this value is <code>null</code>.
*/
public void setTransport(AIUnit transport) {
if (this.transport == transport) return;
AIUnit oldTransport = this.transport;
this.transport = transport;
if (oldTransport != null) {
// Remove from old carrier:
if (oldTransport.getMission() != null
&& oldTransport.getMission() instanceof TransportMission) {
TransportMission tm = (TransportMission) oldTransport.getMission();
if (tm.isOnTransportList(this)) {
tm.removeFromTransportList(this);
}
}
}
if (transport != null
&& transport.getMission() instanceof TransportMission
&& !((TransportMission) transport.getMission()).isOnTransportList(this)) {
// Add to new carrier:
((TransportMission) transport.getMission()).addToTransportList(this);
}
}
/**
* Aborts the given <code>Wish</code>.
*
* @param w The <code>Wish</code> to be aborted.
*/
public void abortWish(Wish w) {
if (destination == w.getDestination()) destination = null;
if (w.getTransportable() == this) w.dispose();
}
// Serialization
/**
* Writes this object to an XML stream.
*
* @param out The target stream.
* @throws XMLStreamException if there are any problems writing
* to the stream.
*/
protected void toXMLImpl(XMLStreamWriter out) throws XMLStreamException {
out.writeStartElement(getXMLElementTagName());
out.writeAttribute(ID_ATTRIBUTE, getId());
if (destination != null) {
out.writeAttribute("destination", destination.getId());
}
out.writeAttribute("transportPriority",
Integer.toString(transportPriority));
if (transport != null) {
if (getAIMain().getAIObject(transport.getId()) == null) {
logger.warning("broken reference to transport");
} else if (transport.getMission() != null
&& transport.getMission() instanceof TransportMission
&& !((TransportMission) transport.getMission()).isOnTransportList(this)) {
logger.warning("We should not be on the transport list.");
} else {
out.writeAttribute("transport", transport.getId());
}
}
goods.toXML(out);
out.writeEndElement();
}
/**
* Reads information for this object from an XML stream.
*
* @param in The input stream with the XML.
* @throws XMLStreamException if there are any problems reading
* from the stream.
*/
protected void readAttributes(XMLStreamReader in) throws XMLStreamException {
final AIMain aiMain = getAIMain();
setId(in.getAttributeValue(null, ID_ATTRIBUTE));
String str = in.getAttributeValue(null, "destination");
destination = aiMain.getGame().getFreeColLocation(str);
transportPriority = getAttribute(in, "transportPriority", -1);
if ((str = in.getAttributeValue(null, "transport")) != null) {
if ((transport = (AIUnit)aiMain.getAIObject(str)) == null) {
transport = new AIUnit(aiMain, str);
}
} else {
transport = null;
}
}
protected void readChild(XMLStreamReader in) throws XMLStreamException {
if (Goods.getXMLElementTagName().equals(in.getLocalName())) {
if (goods != null) {
goods.readFromXML(in);
} else {
goods = new Goods(getAIMain().getGame(), in);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "[" + getId()
+ " " + goods
+ ((goods == null) ? "" : " at " + goods.getLocation())
+ " -> " + destination
+ ((transport == null) ? "" : " using " + transport)
+ " /" + transportPriority + "]";
}
/**
* Returns the tag name of the root element representing this object.
*
* @return "aiGoods"
*/
public static String getXMLElementTagName() {
return "aiGoods";
}
}