/* * $Id$ * * Copyright (c) 2000-2012 by Rodney Kinney, Brent Easton * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASSAL.build; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.List; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import VASSAL.build.module.properties.PropertyNameSource; import VASSAL.configure.ValidationReport; import VASSAL.configure.ValidityChecker; import VASSAL.i18n.Localization; import VASSAL.i18n.Translatable; /** * Abstract implementation of the Buildable interface To make a Buildable * component, extend this class. You'll need to implement the methods and * specify the Buildable attributes of this class, and the build process is * handled automatically. */ public abstract class AbstractBuildable implements Buildable, ValidityChecker, PropertyNameSource { protected List<Buildable> buildComponents = new ArrayList<Buildable>(); // Sub-classes can set this reference to perform validity checking protected ValidityChecker validator; /** * Build this component by getting all XML attributes of the XML element and * calling {@link #setAttribute} with the String value of the attribute */ public void build(Element e) { if (e != null) { final NamedNodeMap n = e.getAttributes(); for (int i = 0; i < n.getLength(); ++i) { final Attr att = (Attr) n.item(i); setAttribute(att.getName(), att.getValue()); /* * Save a record of all Attributes for later translation. Need to save * all attributes, not just translatable ones as the current component * has not been completely built yet and a ComponentI18nData object * cannot be built. */ if (this instanceof Translatable) { Localization.getInstance().saveTranslatableAttribute((Translatable) this, att.getName(), att.getValue()); } } Builder.build(e, this); } } /** * @return a list of all attribute names for this component */ public abstract String[] getAttributeNames(); /** * Sets an attribute value for this component. The <code>key</code> * parameter will be one of those listed in {@link #getAttributeNames}. If * the <code>value</code> parameter is a String, it will be the value * returned by {@link #getAttributeValueString} for the same * <code>key</code>. If the implementing class extends * {@link AbstractConfigurable}, then <code>value</code> will be an * instance of the corresponding Class listed in * {@link AbstractConfigurable#getAttributeTypes} * * @param key * the name of the attribute. Will be one of those listed in * {@link #getAttributeNames} */ public abstract void setAttribute(String key, Object value); /** * Return a String representation of the attribute with the given name. When * initializing a module, this String value will be passed to * {@link #setAttribute}. * * @param key * the name of the attribute. Will be one of those listed in * {@link #getAttributeNames} */ public abstract String getAttributeValueString(String key); /** * @return all build components that are an instance of the given class * @deprecated Use {@link #getComponentsOf(Class<T>)} instead. */ @Deprecated public <T> Enumeration<T> getComponents(Class<T> target) { return Collections.enumeration(getComponentsOf(target)); } /** * @return all build components that are an instance of the given class */ public <T> List<T> getComponentsOf(Class<T> target) { final ArrayList<T> l = new ArrayList<T>(); for (Buildable b : buildComponents) { if (target.isInstance(b)) { l.add(target.cast(b)); } } return l; } /** * Recursively descend the build tree and return an enumeration of all * components that are instances of the given class * * @param target * @return * @deprecated Use {@link #getAllDescendantComponentsOf(Class<T>)} instead. */ @Deprecated public <T> Enumeration<T> getAllDescendantComponents(Class<T> target) { return Collections.enumeration(getAllDescendantComponentsOf(target)); } /** * Recursively descend the build tree and return a {@link List} of all * components that are instances of the given class * * @param target * @return */ public <T> List<T> getAllDescendantComponentsOf(Class<T> target) { ArrayList<T> l = new ArrayList<T>(); addComponents(target, l); return l; } private <T> void addComponents(Class<T> target, List<T> l) { if (target.isInstance(this)) { l.add(target.cast(this)); } for (Buildable b : buildComponents) { if (target.isInstance(b)) { l.add(target.cast(b)); } else if (b instanceof AbstractBuildable) { ((AbstractBuildable) b).addComponents(target, l); } } } public org.w3c.dom.Element getBuildElement(org.w3c.dom.Document doc) { Element el = doc.createElement(getClass().getName()); String[] names = getAttributeNames(); for (int i = 0; i < names.length; ++i) { String val = getAttributeValueString(names[i]); if (val != null) { el.setAttribute(names[i], val); } } for (Buildable b : getBuildables()) { el.appendChild(b.getBuildElement(doc)); } return el; } /** * Add a Buildable object to this object */ public void add(Buildable b) { buildComponents.add(b); } /** * Returns an enumeration of Buildable objects which are the direct children * of this object in the Buildable containment hierarchy. The * {@link #getBuildElement} method uses these objects to construct the XML * element from which this object can be built. * * @deprecated Use {@link #getBuildables()} instead. */ @Deprecated public Enumeration<Buildable> getBuildComponents() { return Collections.enumeration(buildComponents); } /** * Returns a Collection of Buildable objects which are the direct children * of this object in the Buildable containment hierarchy. The * {@link #getBuildElement} method uses these objects to construct the XML * element from which this object can be built. */ public List<Buildable> getBuildables() { return Collections.unmodifiableList(buildComponents); } public void validate(Buildable target, ValidationReport report) { if (validator != null) { validator.validate(target, report); } for (Buildable child : buildComponents) { if (child instanceof ValidityChecker) { ((ValidityChecker) child).validate(child, report); } } } /** * Default implementation of PropertyNameSource - No properties exposed */ public List<String> getPropertyNames() { return new ArrayList<String>(); } }