package games.strategy.engine.data; import games.strategy.engine.data.annotations.InternalDoNotExport; import games.strategy.triplea.Constants; import games.strategy.util.PropertyUtil; /** * Contains some utility methods that subclasses can use to make writing attachments easier. * FYI: You may never have a hashmap/linkedhashmap of any other "attachment" within an attachment. * This is because there will be a circular reference from this hashmap -> attachment1 -> playerid -> attachment2 -> * hashmap -> attachment1, * and this causes major problems for Java's deserializing. * When deserializing the attachments will not be resolved before their hashcode is taken, resulting in the wrong * hashcode and the * attachment going in the wrong bucket, * so that a .get(attachment1) will result in a null instead of giving the key for attachment1. So just don't have maps * of attachments, in * an attachment. Thx, Veqryn. */ public abstract class DefaultAttachment extends GameDataComponent implements IAttachment { private static final long serialVersionUID = -1985116207387301730L; @InternalDoNotExport private Attachable m_attachedTo; @InternalDoNotExport private String m_name; protected DefaultAttachment(final String name, final Attachable attachable, final GameData gameData) { super(gameData); setName(name); setAttachedTo(attachable); } /** * Called after ALL attachments are created. */ @Override public abstract void validate(final GameData data) throws GameParseException; /** * Throws an error if format is invalid. */ protected static int getInt(final String aString) { try { return Integer.parseInt(aString); } catch (final NumberFormatException nfe) { throw new IllegalArgumentException("Attachments: " + aString + " is not a valid int value"); } } /** * Throws an error if format is invalid. Must be either true or false ignoring case. */ protected static boolean getBool(final String value) { if (value.equalsIgnoreCase(Constants.PROPERTY_TRUE)) { return true; } else if (value.equalsIgnoreCase(Constants.PROPERTY_FALSE)) { return false; } else { throw new IllegalArgumentException("Attachments: " + value + " is not a valid boolean"); } } protected static IllegalArgumentException getSetterExceptionMessage(final DefaultAttachment failingObject, final String propertyName, final String givenValue, final String... allowedValues) { final StringBuilder rVal = new StringBuilder(); rVal.append(failingObject.getClass().getName()).append(": ").append(failingObject.getName()).append(": property ") .append(propertyName).append(" must be either "); rVal.append(allowedValues[0]); for (int i = 1; i < allowedValues.length; ++i) { rVal.append(" or ").append(allowedValues[i]); } return new IllegalArgumentException(rVal.toString() + " ([Not Allowed] Given: " + givenValue + ")"); } protected String thisErrorMsg() { return " for: " + this.toString(); } /** * @return null or the toString() of the field value. */ public String getRawPropertyString(final String property) { final Object obj = PropertyUtil.getPropertyFieldObject(property, this); if (obj == null) { return null; } return obj.toString(); } @Override public Attachable getAttachedTo() { return m_attachedTo; } @Override @InternalDoNotExport public void setAttachedTo(final Attachable attachable) { m_attachedTo = attachable; } @Override public String getName() { return m_name; } @Override @InternalDoNotExport public void setName(final String aString) { m_name = aString; } /** * Any overriding method for toString needs to include at least the Class, m_attachedTo, and m_name. * Or call super.toString() */ @Override public String toString() { return getClass().getSimpleName() + " attached to:" + m_attachedTo + " with name:" + m_name; } @Override public int hashCode() { // System.out.println(toString() + "\n Right now its hash is: " + toString().hashCode() + "\n\n"); if (m_attachedTo == null && m_name == null) { return 0; } return toString().hashCode(); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final DefaultAttachment other = (DefaultAttachment) obj; if (m_attachedTo == null) { if (other.m_attachedTo != null) { return false; } } else if (!m_attachedTo.toString().equals(other.m_attachedTo.toString())) { return false; } // else if (!m_attachedTo.equals(other.m_attachedTo)) // m_attachedTo does not override equals, so we should not // test it // return false; if (m_name == null) { if (other.m_name != null) { return false; } } else if (!m_name.equals(other.m_name)) { return false; } return this.toString().equals(other.toString()); } }