/** * Copyright (C) 2002-2012 The FreeCol Team * * This file is part of FreeCol. * * FreeCol 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. * * FreeCol 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 FreeCol. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.freecol.common.model; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.freecolandroid.debug.FCLog; import org.freecolandroid.xml.stream.XMLInputFactory; import org.freecolandroid.xml.stream.XMLOutputFactory; import org.freecolandroid.xml.stream.XMLStreamConstants; import org.freecolandroid.xml.stream.XMLStreamException; import org.freecolandroid.xml.stream.XMLStreamReader; import org.freecolandroid.xml.stream.XMLStreamWriter; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.InputSource; import org.xml.sax.SAXException; public abstract class FreeColObject { protected static Logger logger = Logger.getLogger(FreeColObject.class.getName()); public static final int INFINITY = Integer.MAX_VALUE; public static final int UNDEFINED = Integer.MIN_VALUE; public static final String NO_ID = "NO_ID"; public static final String ID_ATTRIBUTE = "ID"; /** * XML tag name for value attribute. */ protected static final String VALUE_TAG = "value"; /** * XML tag name for ID attribute. */ // this is what we use for the specification // TODO: standardize on this spelling public static final String ID_ATTRIBUTE_TAG = "id"; /** * XML tag name for array elements. */ protected static final String ARRAY_SIZE = "xLength"; /** * XML attribute tag to denote partial updates. */ protected static final String PARTIAL_ATTRIBUTE = "PARTIAL"; /** * Unique identifier of an object */ private String id; /** * The <code>specification</code> this object uses. May be null. */ private Specification specification; private PropertyChangeSupport pcs = null; /** * Get the <code>Id</code> value. * * @return a <code>String</code> value */ public String getId() { return id; } /** * Set the <code>Id</code> value. * * @param newId The new Id value. */ protected void setId(final String newId) { this.id = newId; } /** * Describe <code>getSpecification</code> method here. * * @return a <code>Specification</code> value */ public Specification getSpecification() { return specification; } /** * Sets the specification for this object. This method should only * ever be used by the object's constructor. * * @param specification a <code>Specification</code> value */ protected void setSpecification(Specification specification) { this.specification = specification; } /** * Describe <code>hasAbility</code> method here. * * @param id a <code>String</code> value * @return a <code>boolean</code> value */ public boolean hasAbility(String id) { return false; } /** * Debugging tool, dump object XML to System.err. */ public void dumpObject() { save(System.err); } /** * Writes the object to the given file. * * @param file the save file * @throws FileNotFoundException */ public void save(File file) throws FileNotFoundException { save(new FileOutputStream(file)); } /** * Writes the object to the given output stream * * @param out the OutputStream */ public void save(OutputStream out) { XMLOutputFactory xof = XMLOutputFactory.newInstance(); XMLStreamWriter xsw = null; try { xsw = xof.createXMLStreamWriter(out, "UTF-8"); xsw.writeStartDocument("UTF-8", "1.0"); this.toXML(xsw, null, true, true); xsw.writeEndDocument(); xsw.flush(); } catch (Exception e) { logger.log(Level.WARNING, "Exception writing object.", e); } finally { try { if (xsw != null) xsw.close(); } catch (Exception e) { logger.log(Level.WARNING, "Exception closing save stream.", e); } } } /** * This method writes an XML-representation of this object to * the given stream. * * @param document The <code>Document</code>. * @return An XML-representation of this object. */ public Element toXMLElement(Document document) { // since the player is null, showAll must be true return toXMLElement(null, document, true, false); } /** * This method writes an XML-representation of this object to * the given stream. * * <br><br> * * Only attributes visible to the given <code>Player</code> will * be added to that representation if <code>showAll</code> is * set to <code>false</code>. * * @param player The <code>Player</code> this XML-representation * should be made for, or <code>null</code> if * <code>showAll == true</code>. * @param document The <code>Document</code>. * @return An XML-representation of this object. */ public Element toXMLElement(Player player, Document document) { return toXMLElement(player, document, true, false); } /** * This method writes an XML-representation of this object to * the given stream. * * <br><br> * * Only attributes visible to the given <code>Player</code> will * be added to that representation if <code>showAll</code> is * set to <code>false</code>. * * @param player The <code>Player</code> this XML-representation * should be made for, or <code>null</code> if * <code>showAll == true</code>. * @param document The <code>Document</code>. * @param showAll Only attributes visible to <code>player</code> * will be added to the representation if <code>showAll</code> * is set to <i>false</i>. * @param toSavedGame If <code>true</code> then information that * is only needed when saving a game is added. * @return An XML-representation of this object. */ public Element toXMLElement(Player player, Document document, boolean showAll, boolean toSavedGame) { return toXMLElement(player, document, showAll, toSavedGame, null); } /** * This method writes a partial XML-representation of this object to * an element using only the mandatory and specified fields. * * @param document The <code>Document</code>. * @param fields The fields to write. * @return An XML-representation of this object. */ public Element toXMLElementPartial(Document document, String... fields) { return toXMLElement(null, document, true, false, fields); } /** * This method writes an XML-representation of this object to * the given stream. * * <br><br> * * Only attributes visible to the given <code>Player</code> will * be added to that representation if <code>showAll</code> is * set to <code>false</code>. * * @param player The <code>Player</code> this XML-representation * should be made for, or <code>null</code> if * <code>showAll == true</code>. * @param document The <code>Document</code>. * @param showAll Only attributes visible to <code>player</code> * will be added to the representation if <code>showAll</code> * is set to <i>false</i>. * @param toSavedGame If <code>true</code> then information that * is only needed when saving a game is added. * @param fields An array of field names, which if non-null * indicates this should be a partial write. * @return An XML-representation of this object. */ public Element toXMLElement(Player player, Document document, boolean showAll, boolean toSavedGame, String[] fields) { try { StringWriter sw = new StringWriter(); XMLOutputFactory xif = XMLOutputFactory.newInstance(); XMLStreamWriter xsw = xif.createXMLStreamWriter(sw); if (fields == null) { toXML(xsw, player, showAll, toSavedGame); } else { toXMLPartialImpl(xsw, fields); } xsw.close(); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); Document tempDocument = null; try { DocumentBuilder builder = factory.newDocumentBuilder(); tempDocument = builder.parse(new InputSource(new StringReader(sw.toString()))); return (Element) document.importNode(tempDocument.getDocumentElement(), true); } catch (ParserConfigurationException pce) { // Parser with specified options can't be built StringWriter swe = new StringWriter(); pce.printStackTrace(new PrintWriter(swe)); logger.warning(swe.toString()); throw new IllegalStateException("ParserConfigurationException: " + pce.getMessage()); } catch (SAXException se) { StringWriter swe = new StringWriter(); se.printStackTrace(new PrintWriter(swe)); logger.warning(swe.toString()); throw new IllegalStateException("SAXException: " + se.getMessage()); } catch (IOException ie) { StringWriter swe = new StringWriter(); ie.printStackTrace(new PrintWriter(swe)); logger.warning(swe.toString()); throw new IllegalStateException("IOException: " + ie.getMessage()); } } catch (XMLStreamException e) { logger.warning(e.toString()); throw new IllegalStateException("XMLStreamException: " + e.getMessage()); } } /** * This method writes an XML-representation of this object to * the given stream. * * All attributes will be made visible. * * @param out The target stream. * @throws XMLStreamException if there are any problems writing * to the stream. * @see #toXML(XMLStreamWriter, Player, boolean, boolean) */ public void toXML(XMLStreamWriter out) throws XMLStreamException { toXMLImpl(out); } /** * This method writes an XML-representation of this object with * a specified tag to the given stream. * * Almost all FreeColObjects end up calling this, and implementing * their own write{Attributes,Children} methods which begin by * calling their superclass. This allows a clean nesting of the * serialization routines throughout the class hierarchy. * * All attributes will be made visible. * * @param out The target stream. * @param tag The tag to use. * @throws XMLStreamException if there are any problems writing * to the stream. */ public void toXML(XMLStreamWriter out, String tag) throws XMLStreamException { out.writeStartElement(tag); writeAttributes(out); writeChildren(out); out.writeEndElement(); } /** * Write the attributes of this object to a stream. * To be overridden by any object that uses * the toXML(XMLStreamWriter, String) call. * * @param out The target stream. * @throws XMLStreamException if there are any problems writing * to the stream. */ protected void writeAttributes(XMLStreamWriter out) throws XMLStreamException { if (getId() == null) { logger.warning("FreeColObject with null id: " + toString()); } else { out.writeAttribute(ID_ATTRIBUTE_TAG, getId()); } } /** * Write the children of this object to a stream. * To be overridden by any object that has children and uses the * toXML(XMLStreamWriter, String) call. * * @param out The target stream. * @throws XMLStreamException if there are any problems writing * to the stream. */ protected void writeChildren(XMLStreamWriter out) throws XMLStreamException { // do nothing } /** * This method writes an XML-representation of this object to * the given stream. * * Only attributes visible to the given <code>Player</code> will * be added to that representation if <code>showAll</code> is * set to <code>false</code>. * * @param out The target stream. * @param player The <code>Player</code> this XML-representation * should be made for, or <code>null</code> if * <code>showAll == true</code>. * @param showAll Only attributes visible to <code>player</code> * will be added to the representation if <code>showAll</code> * is set to <i>false</i>. * @param toSavedGame If <code>true</code> then information that * is only needed when saving a game is added. * @throws XMLStreamException if there are any problems writing * to the stream. */ public void toXML(XMLStreamWriter out, Player player, boolean showAll, boolean toSavedGame) throws XMLStreamException { // FreeColObjects are not to contain data that varies with // the observer, so the extra arguments are moot here. // However, this method is overridden in FreeColGameObject // where they are meaningful, and we need a version here for // toXMLElement() to call. toXMLImpl(out); } /** * This method writes an XML-representation of this object to * the given stream. * * @param out The target stream. * @throws XMLStreamException if there are any problems writing * to the stream. */ abstract protected void toXMLImpl(XMLStreamWriter out) throws XMLStreamException; /** * This method writes a partial XML-representation of this object to * the given stream using only the mandatory and specified fields. * Ideally this would be abstract, but as not all FreeColObject-subtypes * need partial updates we provide a non-operating stub here which is * to be overridden where needed. * * @param out The target stream. * @param fields The fields to write. * @throws XMLStreamException if there are any problems writing * to the stream. */ protected void toXMLPartialImpl(XMLStreamWriter out, String[] fields) throws XMLStreamException { throw new UnsupportedOperationException("Partial update of unsupported type."); } /** * Initialize this object from an XML-representation of this object. * @param element An XML-element that will be used to initialize * this object. */ public void readFromXMLElement(Element element) { XMLInputFactory xif = XMLInputFactory.newInstance(); try { try { TransformerFactory factory = TransformerFactory.newInstance(); Transformer xmlTransformer = factory.newTransformer(); StringWriter stringWriter = new StringWriter(); xmlTransformer.transform(new DOMSource(element), new StreamResult(stringWriter)); String xml = stringWriter.toString(); XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(xml)); xsr.nextTag(); readFromXML(xsr); } catch (TransformerException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); logger.warning(sw.toString()); throw new IllegalStateException("TransformerException"); } } catch (XMLStreamException e) { logger.warning(e.toString()); throw new IllegalStateException("XMLStreamException"); } } /** * Initializes this object from an XML-representation of this object, * unless the PARTIAL_ATTRIBUTE tag is present which indicates * a partial update of an existing object. * * @param in The input stream with the XML. * @throws XMLStreamException if there are any problems writing * to the stream. */ public void readFromXML(XMLStreamReader in) throws XMLStreamException { if (in.getAttributeValue(null, PARTIAL_ATTRIBUTE) == null) { readFromXMLImpl(in); } else { readFromXMLPartialImpl(in); } } /** * Reads an XML-representation of an array. * * @param tagName The tagname for the <code>Element</code> * representing the array. * @param in The input stream with the XML. * @param arrayType The type of array to be read. * @return The array. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected int[] readFromArrayElement(String tagName, XMLStreamReader in, int[] arrayType) throws XMLStreamException { if (!in.getLocalName().equals(tagName)) { in.nextTag(); } int[] array = new int[Integer.parseInt(in.getAttributeValue(null, ARRAY_SIZE))]; for (int x=0; x<array.length; x++) { array[x] = Integer.parseInt(in.getAttributeValue(null, "x" + Integer.toString(x))); } in.nextTag(); return array; } /** * Reads an XML-representation of a list. * * @param tagName The tagname for the <code>Element</code> * representing the array. * @param in The input stream with the XML. * @param type The type of the items to be added. This type * needs to have a constructor accepting a single * <code>String</code>. * @return The list. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected <T> List<T> readFromListElement(String tagName, XMLStreamReader in, Class<T> type) throws XMLStreamException { if (!in.getLocalName().equals(tagName)) { throw new XMLStreamException(tagName + " expected, not:" + in.getLocalName()); } final int length = Integer.parseInt(in.getAttributeValue(null, ARRAY_SIZE)); List<T> list = new ArrayList<T>(length); for (int x = 0; x < length; x++) { try { final String value = in.getAttributeValue(null, "x" + Integer.toString(x)); final T object; if (value != null) { Constructor<T> c = type.getConstructor(type); object = c.newInstance(new Object[] {value}); } else { object = null; } list.add(object); } catch (InvocationTargetException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } } if (in.nextTag() != XMLStreamConstants.END_ELEMENT) { throw new XMLStreamException(tagName + " end expected, not: " + in.getLocalName()); } return list; } /** * Reads an XML-representation of an array. * * @param tagName The tagname for the <code>Element</code> * representing the array. * @param in The input stream with the XML. * @param arrayType The type of array to be read. * @return The array. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected String[] readFromArrayElement(String tagName, XMLStreamReader in, String[] arrayType) throws XMLStreamException { if (!in.getLocalName().equals(tagName)) { in.nextTag(); } String[] array = new String[Integer.parseInt(in.getAttributeValue(null, ARRAY_SIZE))]; for (int x=0; x<array.length; x++) { array[x] = in.getAttributeValue(null, "x" + Integer.toString(x)); } in.nextTag(); return array; } /** * Return an attribute value or the default value. * * @param in a <code>XMLStreamReader</code> value * @param attributeName An attribute name * @return an <code>int</code> value */ public boolean hasAttribute(XMLStreamReader in, String attributeName) { final String attributeString = in.getAttributeValue(null, attributeName); return attributeString != null; } /** * Return an attribute value or the default value. * * @param in a <code>XMLStreamReader</code> value * @param attributeName An attribute name * @param defaultValue an <code>int</code> value * @return an <code>int</code> value */ public int getAttribute(XMLStreamReader in, String attributeName, int defaultValue) { final String attributeString = in.getAttributeValue(null, attributeName); int result = defaultValue; if (attributeString != null) { try { result = Integer.parseInt(attributeString); } catch(NumberFormatException e) { logger.warning("Attribute '" + attributeName + "' should be an integer, not '" + attributeString + "'."); } } return result; } /** * Return an attribute value or the default value. * * @param in a <code>XMLStreamReader</code> value * @param attributeName An attribute name * @param defaultValue an <code>int</code> value * @return an <code>int</code> value */ public <T extends Enum<T>> T getAttribute(XMLStreamReader in, String attributeName, Class<T> returnType, T defaultValue) { final String attributeString = in.getAttributeValue(null, attributeName); T result = defaultValue; if (attributeString != null) { try { result = Enum.valueOf(returnType, attributeString); } catch(NumberFormatException e) { logger.warning("Attribute '" + attributeName + "' should be a " + defaultValue.getClass().getName() + " value, not '" + attributeString + "'."); } } return result; } /** * Return an attribute value or the default value. * * @param in a <code>XMLStreamReader</code> value * @param attributeName An attribute name * @param defaultValue a <code>float</code> value * @return an <code>int</code> value */ public float getAttribute(XMLStreamReader in, String attributeName, float defaultValue) { final String attributeString = in.getAttributeValue(null, attributeName); float result = defaultValue; if (attributeString != null) { try { result = Float.parseFloat(attributeString); } catch(NumberFormatException e) { logger.warning("Attribute '" + attributeName + "' should be a float, not '" + attributeString + "'."); } } return result; } /** * Return an attribute value or the default value. * * @param in a <code>XMLStreamReader</code> value * @param attributeName An attribute name * @param defaultValue a <code>boolean</code> value * @return an <code>boolean</code> value */ public static boolean getAttribute(XMLStreamReader in, String attributeName, boolean defaultValue) { final String attributeString = in.getAttributeValue(null, attributeName); if (attributeString != null) { return Boolean.parseBoolean(attributeString); } else { return defaultValue; } } /** * Return an attribute value or the default value. * * @param in a <code>XMLStreamReader</code> value * @param attributeName An attribute name * @param defaultValue an <code>String</code> value * @return an <code>String</code> value */ public String getAttribute(XMLStreamReader in, String attributeName, String defaultValue) { final String attributeString = in.getAttributeValue(null, attributeName); if (attributeString != null) { return attributeString; } else { return defaultValue; } } /** * Write an ID attribute if object is not null. * * @param out a <code>XMLStreamWriter</code> value * @param attributeName a <code>String</code> value * @param object a <code>FreeColObject</code> value * @exception XMLStreamException if an error occurs */ public void writeAttribute(XMLStreamWriter out, String attributeName, FreeColObject object) throws XMLStreamException { if (object != null) { out.writeAttribute(attributeName, object.getId()); } } /** * Initialize this object from an XML-representation of this object. * * @param in The XML input stream. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readFromXMLImpl(XMLStreamReader in) throws XMLStreamException { readAttributes(in); readChildren(in); } /** * Initialize this object from an XML-representation of this object. * * @param in The XML input stream. * @param specification A <code>Specification</code> to use. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readFromXMLImpl(XMLStreamReader in, Specification specification) throws XMLStreamException { readAttributes(in, specification); readChildren(in, specification); } /** * Reads the attributes of this object from an XML stream. * * @param in The XML input stream. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readAttributes(XMLStreamReader in) throws XMLStreamException { readAttributes(in, null); } /** * Reads the attributes of this object from an XML stream. * * @param in The XML input stream. * @param specification A <code>Specification</code> to use. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readAttributes(XMLStreamReader in, Specification specification) throws XMLStreamException { String newId = in.getAttributeValue(null, ID_ATTRIBUTE_TAG); // @compat 0.9.x if (newId == null) { setId(in.getAttributeValue(null, ID_ATTRIBUTE)); // end compatibility code } else { setId(newId); } } /** * Reads the children of this object from an XML stream. * * @param in The XML input stream. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readChildren(XMLStreamReader in) throws XMLStreamException { readChildren(in, null); } /** * Reads the children of this object from an XML stream. * * @param in The XML input stream. * @param specification A <code>Specification</code> to use. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readChildren(XMLStreamReader in, Specification specification) throws XMLStreamException { while (in.nextTag() != XMLStreamConstants.END_ELEMENT) { readChild(in, specification); } } /** * Reads a single child object. This method does calls readChild * with a null specification parameter. * * @param in The XML input stream. * @exception XMLStreamException if an error occurs */ protected void readChild(XMLStreamReader in) throws XMLStreamException { readChild(in, null); } /** * Reads a single child object. This method does nothing. Override * it if necessary. * * @param in The XML input stream. * @param specification a <code>Specification</code> value * @exception XMLStreamException if an error occurs */ protected void readChild(XMLStreamReader in, Specification specification) throws XMLStreamException { // do nothing } /** * Updates this object from an XML-representation of this object. * Ideally this would be abstract, but as not all FreeColObject-subtypes * need partial updates we provide a non-operating stub here which is * to be overridden where needed. * * @param in The input stream with the XML. * @throws XMLStreamException if a problem was encountered * during parsing. */ protected void readFromXMLPartialImpl(XMLStreamReader in) throws XMLStreamException { throw new UnsupportedOperationException("Partial update of unsupported type"); } // ---------- PROPERTY CHANGE SUPPORT DELEGATES ---------- public void addPropertyChangeListener(PropertyChangeListener listener) { if (pcs == null) { pcs = new PropertyChangeSupport(this); } pcs.addPropertyChangeListener(listener); } public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (pcs == null) { pcs = new PropertyChangeSupport(this); } pcs.addPropertyChangeListener(propertyName, listener); } public void fireIndexedPropertyChange(String propertyName, int index, boolean oldValue, boolean newValue) { if (pcs != null) { pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue); } } public void fireIndexedPropertyChange(String propertyName, int index, int oldValue, int newValue) { if (pcs != null) { pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue); } } public void fireIndexedPropertyChange(String propertyName, int index, Object oldValue, Object newValue) { if (pcs != null) { pcs.fireIndexedPropertyChange(propertyName, index, oldValue, newValue); } } public void firePropertyChange(PropertyChangeEvent event) { if (pcs != null) { pcs.firePropertyChange(event); } } public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { if (pcs != null) { pcs.firePropertyChange(propertyName, oldValue, newValue); } } public void firePropertyChange(String propertyName, int oldValue, int newValue) { if (pcs != null) { pcs.firePropertyChange(propertyName, oldValue, newValue); } } public void firePropertyChange(String propertyName, Object oldValue, Object newValue) { if (pcs != null) { pcs.firePropertyChange(propertyName, oldValue, newValue); } } public PropertyChangeListener[] getPropertyChangeListeners() { if (pcs == null) { return new PropertyChangeListener[0]; } else { return pcs.getPropertyChangeListeners(); } } public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) { if (pcs == null) { return new PropertyChangeListener[0]; } else { return pcs.getPropertyChangeListeners(propertyName); } } public boolean hasListeners(String propertyName) { if (pcs == null) { return false; } else { return pcs.hasListeners(propertyName); } } public void removePropertyChangeListener(PropertyChangeListener listener) { if (pcs != null) { pcs.removePropertyChangeListener(listener); } } public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (pcs != null) { pcs.removePropertyChangeListener(propertyName, listener); } } /** * Gets the tag name used to serialize this object, generally the * class name starting with a lower case letter. This method * should be overridden by all subclasses that need to be * serialized. * * @return <code>null</code>. */ public static String getXMLElementTagName() { return null; } }