/**
   * Assign a carrier for this treasure.
   *
   * @param lb A <code>LogBuilder</code> to log to.
   * @return A suitable carrier <code>AIUnit</code>, to which this unit has been queued for
   *     transport.
   */
  private AIUnit assignCarrier(LogBuilder lb) {
    final AIUnit aiUnit = getAIUnit();
    final Unit unit = getUnit();
    final Player player = unit.getOwner();
    final Europe europe = player.getEurope();

    List<Unit> carriers = player.getCarriersForUnit(unit);
    if (carriers.isEmpty()) return null;

    // Pick the closest carrier and queue this unit.
    final Location here = unit.getLocation();
    int turns = INFINITY;
    Unit closest = null;
    for (Unit c : carriers) {
      int t = c.getTurnsToReach(here);
      if (turns > t) {
        turns = t;
        closest = c;
      }
    }
    final AIMain aiMain = getAIMain();
    TransportMission tm;
    AIUnit aiCarrier;
    if (closest != null
        && (aiCarrier = aiMain.getAIUnit(closest)) != null
        && (tm = aiCarrier.getMission(TransportMission.class)) != null) {
      setTarget(europe);
      aiUnit.changeTransport(aiCarrier);
      if (tm.forceCollection(aiUnit, lb)) {
        lb.add(" forced collection on ", aiCarrier.getUnit());
        return aiCarrier;
      }
    }
    return null;
  }
  /**
   * Get a FreeCol AI object from an attribute in a stream.
   *
   * @param aiMain The <code>AIMain</code> that contains the object.
   * @param attributeName The attribute name.
   * @param returnClass The <code>AIObject</code> type to expect.
   * @param defaultValue The default value.
   * @return The <code>AIObject</code> found, or the default value if not.
   */
  public <T extends AIObject> T getAttribute(
      AIMain aiMain, String attributeName, Class<T> returnClass, T defaultValue) {
    final String attrib =
        // @compat 0.10.7
        (FreeColObject.ID_ATTRIBUTE_TAG.equals(attributeName))
            ? readId()
            :
            // end @compat
            getAttribute(attributeName, (String) null);

    return (attrib == null) ? defaultValue : aiMain.getAIObject(attrib, returnClass);
  }
  /**
   * Either get an existing <code>AIObject</code> from a stream attribute or create it if it does
   * not exist.
   *
   * <p>Use this routine when the object may not necessarily already be present in the game, but is
   * expected to be defined eventually.
   *
   * @param aiMain The <code>AIMain</code> that contains the object.
   * @param attributeName The attribute name.
   * @param returnClass The <code>AIObject</code> type to expect.
   * @param defaultValue The default value.
   * @exception XMLStreamException if there is problem reading the stream.
   * @return The <code>AIObject</code> found, or the default value if not.
   */
  public <T extends AIObject> T makeAIObject(
      AIMain aiMain, String attributeName, Class<T> returnClass, T defaultValue, boolean required)
      throws XMLStreamException {

    final String id =
        // @compat 0.10.7
        (FreeColObject.ID_ATTRIBUTE_TAG.equals(attributeName))
            ? readId()
            :
            // end @compat
            getAttribute(attributeName, (String) null);

    T ret = null;
    if (id == null) {
      if (required) {
        throw new XMLStreamException(
            "Missing " + attributeName + " for " + returnClass.getName() + ": " + currentTag());
      }
    } else {
      ret = aiMain.getAIObject(id, returnClass);
      if (ret == null) {
        try {
          Constructor<T> c = returnClass.getConstructor(AIMain.class, String.class);
          ret = returnClass.cast(c.newInstance(aiMain, id));
          if (required && ret == null) {
            throw new XMLStreamException(
                "Constructed null " + returnClass.getName() + " for " + id + ": " + currentTag());
          }
        } catch (NoSuchMethodException
            | SecurityException
            | InstantiationException
            | IllegalAccessException
            | IllegalArgumentException
            | InvocationTargetException
            | XMLStreamException e) {
          if (required) {
            throw new XMLStreamException(e);
          } else {
            logger.log(Level.WARNING, "Failed to create AIObject: " + id, e);
          }
        }
      }
    }
    return ret;
  }