gt:             if (existingUnit == null) {
context: //
  //  @(#)Location.java		4/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.world;
  
  import dip.misc.Utils;
  import dip.order.OrderException;
  import dip.world.Unit.Type;
  
  import java.util.List;
  import java.util.Objects;
  import java.util.stream.Collectors;
  
  
  /**
   * A Location defines where an object on the map exists.
   * <p>
   * Locations are immutable.
   * Convenience methods are provided for obtaining adjacency
   * information.
   */
  public class Location implements Cloneable {
  
      // il8n text strings
      private static final String LOC_VWM_MULTICOAST = "LOC_VWM_MULTICOAST";
      private static final String LOC_VAD_NO_UNIT = "LOC_VAD_NO_UNIT";
      private static final String LOC_VS_UNDEFCOAST = "LOC_VS_UNDEFCOAST";
      private static final String LOC_V_ARMY_IN_SEA = "LOC_V_ARMY_IN_SEA";
      private static final String LOC_V_FLEET_LANDLOCKED = "LOC_V_FLEET_LANDLOCKED";
      private static final String LOC_V_COAST_BAD = "LOC_V_COAST_BAD";
      private static final String LOC_V_COAST_SINGLE = "LOC_V_COAST_SINGLE";
  
  
      // instance variables
      // note: all fields should be final to ensure immutability
      private final Province province;
      private final Coast coast;
  
  
      /**
       * Create a Location object
       */
      public Location(final Province province, final Coast coast) {
          Objects.requireNonNull(province);
          Objects.requireNonNull(coast);
  
          this.province = province;
          this.coast = coast;
      }// getLocation()
  
      public Location(final Location location) {
          this(location.province, location.coast);
      }
  
  
      /**
       * Get the Province for this Location
       */
      public final Province getProvince() {
          return province;
      }// getProvince()
  
      /**
       * Get the Coast for this Location
       */
      public final Coast getCoast() {
          return coast;
      }// getCoast()
  
  
      /**
       * Convenience method. Determines if the Location has a convoyable coastal Province.
       */
      public boolean isConvoyableCoast() {
          return province.isConvoyableCoast();
      }// isConvoyableCoast()
  
  
      /**
       * Returns <code>true</code> if this location is touching
       * the given province. Note that "Touching" does not
       * nescessarily imply connectedness (adjacency).
       */
      public boolean isTouching(final Province destProvince) {
          return province.isTouching(destProvince);
      }// isTouching()
  
  
      /**
       * Determines if the two locations are adjacent (connected).
       * <p>
       * This uses the Province/Coast information of this object, but
       * only uses the Province information supplied; thus it does not
       * check the 'destination' coast.
       */
      public boolean isAdjacent(final Province destProvince) {
          return province.isAdjacent(coast, destProvince);
      }// isAdjacent()
  
  
      /**
       * Determines if the two locations are adjacent (connected),
       * taking coasts into account as well.
       */
      public boolean isAdjacent(final Location location) {
          return province.isAdjacent(coast, location);
      }// isAdjacent()
  
  
      /**
       * Returns <code>true</code> if the Province in both locations
       * is equal, ignoring the coasts.
       */
      public boolean isProvinceEqual(final Location location) {
          return Objects.equals(province, location.province);
      }// isProvinceEqual()
  
      /**
       * Returns <code>true</code> if the Province in this location
       * is equal to the province argument; coasts are ignored.
       */
      public boolean isProvinceEqual(final Province province) {
          return Objects.equals(this.province, province);
      }// isProvinceEqual()
  
  
      /**
       * Implements a clone. Note that this is not a strict
       * implementation, in that a constructor is invoked
       * for performance reasons, rather than using super.clone().
       */
      @Override
      public Location clone() {
          return new Location(this);
      }// clone()
  
      /**
       * Determines if two Locations are equal.
       */
      @Override
      public boolean equals(final Object obj) {
          if (obj == this) {
              return true;
          }
          if (obj instanceof Location) {
              final Location loc = (Location) obj;
              return province.equals(loc.province) && coast == loc.coast;
          }
  
          return false;
      }// equals()
  
  
      /**
       * Hashcode for Location.
       */
      @Override
      public int hashCode() {
          return Objects.hash(province, coast);
      }// hashCode()
  
  
      /**
       * Determines if two Locations are "loosely" equal. This means that
       * the Province must match, but, an Undefined coast will match
       * any coast (including another undefined coast). If coasts are defined,
       * they must match to return true.
       */
      public boolean equalsLoosely(final Location loc) {
          return Objects.equals(province,
                  loc.province) && (coast == loc.coast || coast == Coast.UNDEFINED || loc.coast == Coast.UNDEFINED);
      }// equalsLoosely()
  
  
      /**
       * Returns the short Location name (as per appendBrief()) as a String
       */
      @Override
      public String toString() {
          return getBrief();
      }// toString()
  
      /**
       * Returns the full (long) Locatin name (as per appendFull()) as a String
       */
      public String toLongString() {
          return getFull();
      }// toString()
  
  
      /**
       * Append the brief Location name to the StringBuffer (e.g., spa/sc)
       */
      public String getBrief() {
          return coast.isDirectional() ? String
                  .format("%s/%s", province.getShortName(),
                          coast.getAbbreviation()) : province.getShortName();
      }// appendBrief()
  
  
      /**
       * Append the full Location name to the StringBuffer. (e.g., Spain/South Coast)
       */
      public String getFull() {
          return coast.isDirectional() ? String
                  .format("%s(%s)", province, coast.getName()) : province
                  .toString();
      }// appendFull()
  
  
      /**
       * <pre>
       * This extends the simple validate method, be performing additional
       * checking for move orders.
       *
       * For example:
       * F gas-spain
       *
       * Now, using the simple validate() method, the above order would fail, becuase
       * spain/nc must be specified. However, a fleet in gascony can ONLY move to
       * spain/nc; thus the specification should be optional. If the fleet could move
       * to EITHER coast, then this method offers no gain, and an error will occur.
       *
       * Essentially, this 'loosens up' the validate method to account for orders that
       * are legal and yet not ambiguous.
       *
       * However, we need to know the 'from' location.
       *
       * This only applies to fleets that are moving into multicoastal provinces.
       *
       * some examples:
       * F gas-spa       ** illegal; no coast specified
       * F gas-spa/nc    ** ok
       * F gas-spa/sc    ** illegal; non-adjacent
       * F mar-spa/sc    ** ok
       * A gas-spa       ** ok (army)
       * A gas-spa/nc    ** ok (coast ignored for armies)
       * A gas-spa/sc    ** ok (coast ignored for armies)
       *
       *
       *
       *
       * This should only be used in Move orders; it should not be used in Convoy orders,
       * since only Armies can be convied, nor should it be used for Support orders, since
       * the destination province may legally be a loose or 'unreachable coast' match.
       *
       * NOTE: 'from' MUST BE adjacent to this location for this to work, with a defined
       * coast. Thus the 'from' location should be fully validated before this method is used.
       *
       * NOTE: This method may return a new Location object, if changes were made,
       * or the same Location object, if no changes were made.
       * </pre>
       */
      public Location getValidatedWithMove(final Type unitType,
                                           final Location from) throws OrderException {
          // simple validate
          final Location newLoc = getValidated(unitType);
  
          // DO NOT use internal 'coast' and 'province' variables past here
          //
          // extended validation, if coast is undefined.
          final Coast newCoast = newLoc.coast;
          if (unitType == Type.FLEET && newCoast == Coast.UNDEFINED && newLoc.province
                  .isMultiCoastal()) {
              // TODO: this can be optimized. If we find a coast, we should assign it
              // and if we find another coast,
              // then we should throw an exception
              // (this prevents iterating completely thru all coasts each time)
              //
              final List<Location> locs = from.province
                      .getAdjacentLocations(from.coast);
              final List<Coast> coasts = locs.stream().filter(loc -> Objects
                      .equals(loc.province, newLoc.province))
                      .map(loc -> loc.coast).collect(Collectors.toList());
  
              // if we have 1 and only 1 coast, we can assign a coast; otherwise,
              // we must report an error.
              if (coasts.size() != 1) {
                  throw new OrderException(
                          Utils.getLocalString(LOC_VWM_MULTICOAST,
                                  newLoc.province.getFullName()));
              }
              final Coast coast0 = coasts.get(0);
              return coast0 == newLoc.coast ? newLoc : new Location(
                      newLoc.province, coast0);
          }
  
          return newLoc;
      }// getValidatedWithMove()
  
  
      /**
       * <pre>
       * This extends the simple getValidated() method, by performing
       * additional validation/checking given turnstate information.
       *
       * It is most appropriate for unit source orders. Given the following
       * order:
       * F xxx SUPPORTS yyy-zzz
       * It would be most appropriate to validate the location "xxx" with this
       * method. This is because a unit exists in "xxx", and we can derive/match
       * any existing information (coast type, in particular) from the unit.
       *
       * It would also be appropriate to validate location yyy with this, because
       * we know that a unit must exist in yyy (otherwise the order is invalid).
       *
       * Thus coast ambiguities are removed.
       *
       * This should NOT be used for validation of destination locations. This is because
       * no unit may be in the destination location, and even if there is it wouldn't make
       * sense; for example:
       * F gas-spain/nc
       * A spain
       * If we used getValidAndDerived() for "spain/nc", we would derive the coast from the
       * army in spain, which would be illegal. Thus a simple validate or a move validate
       * should be used instead.
       *
       *
       * So: The method works as follows:
       * 1) calls validate()     "simple validate"
       * 2) if unit type is a fleet: (if army, we're done)
       * a) if coast undefined, derive coast from unit
       * b) if coast is defined, ensure that it matches the
       * coast of the unit.
       *
       * Fixed: we must give the unit (or null, which may cause a failure).
       * The reason to give the unit is that a dislodged unit or
       * non-dislodged unit may be req'd depending on what we are
       * validating (e.g., retreat orders derive against dislodged
       * units)
       *
       * A Coast will never be undefined after this method completes.
       *
       * NOTE: This method may return a new Location object, if changes were made,
       * or the same Location object, if no changes were made.
       * </pre>
       */
      public Location getValidatedAndDerived(final Type unitType,
                                             final Unit existingUnit) throws OrderException {
          // simple validate
          final Location newLoc = getValidated(unitType);
          Coast newCoast = newLoc.coast;
  
          // DO NOT use internal 'coast' and 'province' variables past here
          //
          // extended validation; only for Fleets
          if (unitType == Type.FLEET && newLoc.province.isMultiCoastal()) {
              final String provinceName = newLoc.province.getFullName();
  
  