// // @(#)Order.java 12/2002 // // Copyright 2002 Zachary DelProposto. All rights reserved. // Use is subject to license terms. // // 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. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Or from http://www.gnu.org/package dip.order.result; // package dip.order; import dip.world.*; import dip.process.Adjudicator; import dip.process.OrderState; import dip.misc.Utils; import java.util.List; import java.util.ArrayList; import java.util.Iterator; import java.io.*; import org.apache.commons.lang.builder.HashCodeBuilder; /** * This is the base class for all Order objects. * <p> * <b>When referring to an Order subclass, it is best to refer to an object * as an "Orderable" object rather than an "Order" object. * </b> * <br> * For example: * <pre> * GUIOrderFactory gof = new GUIOrderFactory(); * * Order order = OrderFactory.getDefault().createHold(a,b,c); // ok * Orderable order = OrderFactory.getDefault().createHold(a,b,c); // dangerous * * Order order = gof.createHold(a,b,c); // this may fail!! * Orderable order = gof.createHold(a,b,c); // ok * </pre> * <p> * This class provides default implementations for many Order interface * methods, as well as protected convenience methods. * <p> * A note on serialization: provided are several internal fields * which can be used to 'upgrade' objects as needed. These fields * are for future use, but their presence enables future upgradibility. * * * */ public abstract class Order extends Object implements Orderable, java.io.Serializable { /** * */ private static final long serialVersionUID = 5487204449795813995L; // resource keys private static final String ORD_VAL_NOUNIT = "ORD_VAL_NOUNIT"; private static final String ORD_VAL_BADPOWER = "ORD_VAL_BADPOWER"; private static final String ORD_VAL_BADTYPE = "ORD_VAL_BADTYPE"; private static final String ORD_VAL_SZ_RETREAT = "ORD_VAL_SZ_RETREAT"; private static final String ORD_VAL_SZ_ADJUST = "ORD_VAL_SZ_ADJUST"; private static final String ORD_VAL_SZ_MOVEMENT = "ORD_VAL_SZ_MOVEMENT"; //private static final String ORD_VAL_SZ_SETUP = "ORD_VAL_SZ_SETUP"; //private static final String ORD_VAL_PWR_DISORDER = "ORD_VAL_PWR_DISORDER"; private static final String ORD_VAL_PWR_ELIMINATED = "ORD_VAL_PWR_ELIMINATED"; private static final String ORD_VAL_PWR_INACTIVE = "ORD_VAL_PWR_INACTIVE"; protected static final String ORD_VAL_BORDER = "ORD_VAL_BORDER"; /** Power who gave the order to the unit */ protected Power power = null; /** Location of the ordered unit */ protected Location src = null; /** Type of the ordered unit */ protected Unit.Type srcUnitType = null; /** * No-arg constructor */ protected Order() { }// Order() /** * Constructor for the Order object * *@param power Power giving the Order *@param src Location of the ordered unit *@param srcUnit Unit type *@since */ protected Order(Power power, Location src, Unit.Type srcUnit) { if(power == null || src == null || srcUnit == null) { throw new IllegalArgumentException("null parameter(s)"); } this.power = power; this.src = src; this.srcUnitType = srcUnit; }// Order() public final Location getSource() { return src; }// getSource() public final Unit.Type getSourceUnitType() { return srcUnitType; }// getSourceUnitType() public final Power getPower() { return power; }// getPower() // // Format methods // public String toFormattedString(OrderFormatOptions ofo) { return OrderFormat.format(ofo, getDefaultFormat(), this); }// toFormattedString() // // Adjudicator methods // /** * Validate the order. * <p> * This checks the order for legality. An order is not considered to be * well-formed until it has been validated. * It validates locations (at the least), and other order * syntax as appropriate, throwing exceptions if there is a conflict. * <p> * This is used by the UI to flag bad orders, as well as by the adjudicator * to eliminate bad orders before processing. * <p> * Important Note: * <p> * The only state that should be assumed is the following: * 1) orders for this power (if entered) * 2) the positions of all units. * Thus, the orders of units not controlled by this power may * not be known (in multiplayer games, this information would not * be available until adjudication took place). DO NOT assume order * information for the units of any power other than that of this unit/order! * <p> * Usage Notes: * <p> * Subclasses should generally call super.validate() before performing * additional validation. * <p> * There should be no side effects of calling validate() multiple times, nor * should behavior differ between multiple calls of validate(), given the same * order. * * *@param state Current turn state *@param valOpts Current validation options *@exception OrderException */ public void validate(TurnState state, ValidationOptions valOpts, RuleOptions ruleOpts) throws OrderException { Position position = state.getPosition(); Unit unit = position.getUnit( src.getProvince() ); validate(valOpts, unit); }// validate() // // Convenience methods // // /** * Convenience method typically used in determineDependencies(); this * method gets all Support and Move orders to this space and adds * them to the dependency list. * <p> * Only "hold" supports are added. Invalid Move orders are NEVER added. * */ protected final void addSupportsOfAndMovesToSource(Adjudicator adjudicator) { OrderState thisOS = adjudicator.findOrderStateBySrc(getSource()); ArrayList depMTS = null; ArrayList depSup = null; OrderState[] orderStates = adjudicator.getOrderStates(); for(int osIdx=0; osIdx<orderStates.length; osIdx++) { OrderState dependentOS = orderStates[osIdx]; Order order = dependentOS.getOrder(); if(order != this) // always exclude self { if( order instanceof Move && ((Move) order).getDest().isProvinceEqual(this.getSource()) ) { if(depMTS == null) { depMTS = new ArrayList(5); } depMTS.add(dependentOS); } else if(order instanceof Support) { Support support = (Support) order; // if we don't check for hold-type support (Support.isSupportingHold() == true) // we will accidentally add move-supports! (bad) if( support.isSupportingHold() && support.getSupportedSrc().isProvinceEqual(this.getSource()) ) { if(depSup == null) { depSup = new ArrayList(5); } depSup.add(dependentOS); } } } } // set supports / endangering moves in OrderState if(depMTS != null) { thisOS.setDependentMovesToSource(depMTS); } if(depSup != null) { thisOS.setDependentSupports(depSup); } }// addSupportsOfAndMovesToSource() /** * Validates a dislodged or non-dislodged unit, depending upon what type of * unit is specified */ protected void validate(ValidationOptions valOpts, Unit unit) throws OrderException { // Basic Validation: [not in correct order] // 1) must be a unit in the province ordered. // 2) Power of unit in Province must == Power of this Order // 3) if unit type is undefined, determine correct type. // if type mismatch, throw exception. // 4) validate the source location given the unit type. // (ensure coast is correct) // Province srcProvince = src.getProvince(); // unit-existence if(unit == null) { throw new OrderException( Utils.getLocalString(ORD_VAL_NOUNIT, srcProvince) ); } // power-matching if( !power.equals(unit.getPower()) ) { throw new OrderException( Utils.getLocalString(ORD_VAL_BADPOWER) ); } // unit type matching srcUnitType = getValidatedUnitType(srcProvince, srcUnitType, unit); // Location verification; derive info if missing from unit, since // we know it to exist. src = src.getValidatedAndDerived(srcUnitType, unit); }// validate() /** * Convenience method for matching unit types. * <p> * If a type is undefined, the type is derived from the existing unit. If the * existing unit is not found (or mismatched), an exception is thrown. */ protected final Unit.Type getValidatedUnitType(Province province, Unit.Type unitType, Unit unit) throws OrderException { if(unit == null) { throw new OrderException( Utils.getLocalString(ORD_VAL_NOUNIT, province) ); } if(unitType.equals(Unit.Type.UNDEFINED)) { // make unitType correct. return unit.getType(); } else { if( !unitType.equals(unit.getType()) ) { throw new OrderException( Utils.getLocalString(ORD_VAL_BADTYPE, province, unit.getType().getFullNameWithArticle(), unitType.getFullNameWithArticle() )); } } return unitType; }// getValidatedUnitType() /** Validates the given Power */ protected final void checkPower(Power power, TurnState turnState, boolean checkIfActive) throws OrderException { Position position = turnState.getPosition(); if(position.isEliminated(power)) { throw new OrderException( Utils.getLocalString(ORD_VAL_PWR_ELIMINATED, power) ); } if(!power.isActive() && checkIfActive) { throw new OrderException( Utils.getLocalString(ORD_VAL_PWR_INACTIVE, power) ); } }// checkPower() /** Convenience method to check that we are in the Retreat phase */ protected final void checkSeasonRetreat(TurnState state, String orderName) throws OrderException { if( state.getPhase().getPhaseType() != Phase.PhaseType.RETREAT ) { throw new OrderException(Utils.getLocalString(ORD_VAL_SZ_RETREAT, orderName)); } }// checkSeasonRetreat() /** Convenience method to check that we are in the Adjustment phase */ protected final void checkSeasonAdjustment(TurnState state, String orderName) throws OrderException { if( state.getPhase().getPhaseType() != Phase.PhaseType.ADJUSTMENT ) { throw new OrderException(Utils.getLocalString(ORD_VAL_SZ_ADJUST, orderName)); } }// checkSeasonAdjustment() /** Convenience method to check that we are in the Movement phase */ protected final void checkSeasonMovement(TurnState state, String orderName) throws OrderException { if( state.getPhase().getPhaseType() != Phase.PhaseType.MOVEMENT ) { throw new OrderException(Utils.getLocalString(ORD_VAL_SZ_MOVEMENT, orderName)); } }// checkSeasonMovement() /** * Convenience Method: prints the beginning of an order in a verbose format. * <br> * Example: France: Army Spain/sc */ protected final void appendFull(StringBuffer sb) { sb.append(power); sb.append(": "); sb.append(srcUnitType.getFullName()); sb.append(' '); src.appendFull(sb); }// appendFull() /** * Convenience Method: prints the beginning of an order in a brief format. * <br> * Example: France: Army spa/sc */ protected final void appendBrief(StringBuffer sb) { sb.append(power); sb.append(": "); sb.append(srcUnitType.getShortName()); sb.append(' '); src.appendBrief(sb); }// appendBrief() // // // java.lang.Object method implementations // // /** For debugging: calls toBriefString(). Note this will fail if order is null. */ public String toString() { return toBriefString(); }// toString() /** * Determines if the orders are equal. * <p> * Note that full equality MUST be implemented for each * subclassed Order object! Subclasses are advised to call * the super method for assistance. */ public boolean equals(Object obj) { // speedy reference check if(this == obj) { return true; } else if(obj instanceof Order) { Order o = (Order) obj; if( power.equals(o.power) && src.equals(o.src) && srcUnitType.equals(o.srcUnitType) ) { return true; } } return false; }// equals() @Override public int hashCode() { return new HashCodeBuilder(11, 31).append(power).append(src).append(srcUnitType).toHashCode(); } }// abstract class Order