/*
Copyright (C) 2001, 2006 United States Government as represented by
the Administrator of the National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.util;
/**
* RestorableSupport provides convenient read and write access to restorable state located in a simple XML
* document format. This document is rooted by the <code>restorableState</code> element. State is stored in
* <code>stateObject</code> elements. Each <code>stateObject</code> element is identified by its <code>name</code>
* attribute. The value of a <code>stateObject</code> can either be simple text content, or nested
* <code>stateObject</code> elements.
* <p>
* For example, this document stores four states: the string "Hello World!", the largest value an unsigned byte
* can hold, the value of PI to six digits, and a boolean "true".
* <code>
* <pre>
* {@literal <?xml version=�1.0� encoding=�UTF-8�?>}
* {@literal <restorableState>}
* {@literal <stateObject name=�helloWorldString�>Hello World!</stateObject>}
* {@literal <stateObject name=�maxUnsignedByteValue�>255</stateObject>}
* {@literal <stateObject name=�pi�>3.141592</stateObject>}
* {@literal <stateObject name=�booleanTrue�>true</stateObject>}
* {@literal </restorableState>}
* </pre>
* </code>
* Callers can create a new RestorableSupport with no state content, or create a
* RestorableSupport from an existing XML document string. Callers can then add state by name and value,
* and query state by name. RestorableSupport provides convenience methods for addding and querying
* state values as Strings, Integers, Doubles, and Booleans.
*
* @author dcollins
* @version $Id: RestorableSupport.java 4707 2008-03-15 07:56:52Z dcollins $
* @see gov.nasa.worldwind.Restorable
*/
public class RestorableSupport
{
private static final String DEFAULT_DOCUMENT_ELEMENT_TAG_NAME = "restorableState";
private static final String DEFAULT_STATE_OBJECT_TAG_NAME = "stateObject";
private org.w3c.dom.Document doc;
private javax.xml.xpath.XPath xpath;
private String stateObjectTagName;
private RestorableSupport(org.w3c.dom.Document doc)
{
if (doc == null)
{
String message = Logging.getMessage("nullValue.DocumentIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.doc = doc;
javax.xml.xpath.XPathFactory pathFactory = javax.xml.xpath.XPathFactory.newInstance();
this.xpath = pathFactory.newXPath();
this.stateObjectTagName = DEFAULT_STATE_OBJECT_TAG_NAME;
}
/**
* Creates a new RestorableSupport with no contents.
*
* @return a new, empty RestorableSupport instance.
*/
public static RestorableSupport newRestorableSupport()
{
javax.xml.parsers.DocumentBuilderFactory docBuilderFactory =
javax.xml.parsers.DocumentBuilderFactory.newInstance();
try
{
javax.xml.parsers.DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document doc = docBuilder.newDocument();
// Create the "restorableState" document root element.
createDocumentElement(doc, DEFAULT_DOCUMENT_ELEMENT_TAG_NAME);
return new RestorableSupport(doc);
}
catch (javax.xml.parsers.ParserConfigurationException e)
{
String message = Logging.getMessage("RestorableSupport.ExceptionCreatingParser");
Logging.logger().severe(message);
throw new IllegalStateException(message, e);
}
}
/**
* Creates a new RestorableSupport with the contents of the specified state document.
*
* @param stateInXml the XML document to parse for state.
* @return a new RestorableSupport instance with the specified state.
* @throws IllegalArgumentException If <code>stateInXml</code> is null, or the its contents are not a well formed
* XML document.
*/
public static RestorableSupport parse(String stateInXml)
{
if (stateInXml == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
javax.xml.parsers.DocumentBuilderFactory docBuilderFactory =
javax.xml.parsers.DocumentBuilderFactory.newInstance();
try
{
javax.xml.parsers.DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document doc = docBuilder.parse(
new org.xml.sax.InputSource(new java.io.StringReader(stateInXml)));
return new RestorableSupport(doc);
}
catch (java.io.IOException e)
{
String message = Logging.getMessage("RestorableSupport.ExceptionParsingXml", stateInXml);
Logging.logger().severe(message);
throw new IllegalArgumentException(message, e);
}
catch (org.xml.sax.SAXException e)
{
String message = Logging.getMessage("RestorableSupport.ExceptionParsingXml", stateInXml);
Logging.logger().severe(message);
throw new IllegalArgumentException(message, e);
}
catch (javax.xml.parsers.ParserConfigurationException e)
{
String message = Logging.getMessage("RestorableSupport.ExceptionCreatingParser");
Logging.logger().severe(message);
throw new IllegalStateException(message, e);
}
}
private org.w3c.dom.Element getDocumentElement()
{
return this.doc.getDocumentElement();
}
private static void createDocumentElement(org.w3c.dom.Document doc, String tagName)
{
if (doc == null)
{
String message = Logging.getMessage("nullValue.DocumentIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (tagName == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Document already has a root element.
if (doc.getDocumentElement() != null)
return;
org.w3c.dom.Element elem = doc.createElement(tagName);
doc.appendChild(elem);
}
/**
* Returns an XML document string describing this RestorableSupport's current set of state objects.
* If this RestorableSupport cannot be converted, this will return null.
*
* @return an XML state document string.
*/
public String getStateAsXml()
{
javax.xml.transform.TransformerFactory transformerFactory =
javax.xml.transform.TransformerFactory.newInstance();
try
{
// The StringWriter will receive the document xml.
java.io.StringWriter stringWriter = new java.io.StringWriter();
// Attempt to write the Document to the StringWriter.
javax.xml.transform.Transformer transformer = transformerFactory.newTransformer();
transformer.transform(
new javax.xml.transform.dom.DOMSource(this.doc),
new javax.xml.transform.stream.StreamResult(stringWriter));
// If successful, return the StringWriter contents as a String.
return stringWriter.toString();
}
catch (javax.xml.transform.TransformerConfigurationException e)
{
String message = Logging.getMessage("RestorableSupport.ExceptionWritingXml");
Logging.logger().severe(message);
return null;
}
catch (javax.xml.transform.TransformerException e)
{
String message = Logging.getMessage("RestorableSupport.ExceptionWritingXml");
Logging.logger().severe(message);
return null;
}
}
/**
* Returns an XML document string describing this RestorableSupport's current set of state objects.
* Calling <code>toString</code> is equivalent to calling <code>getStateAsXml</code>.
*
* @return an XML state document string.
*/
public String toString()
{
return getStateAsXml();
}
/**
* An interface to the <code>stateObject</code> elements in an XML state document, as defined by
* {@link gov.nasa.worldwind.util.RestorableSupport}. The <code>name</code> and simple String <code>value</code>
* of a <code>stateObject</code> can be queried or set through StateObject. This also serves as a context
* through which nested <code>stateObjects</code> can be found or created.
*/
public static class StateObject
{
final org.w3c.dom.Element elem;
StateObject(org.w3c.dom.Element element)
{
if (element == null)
{
String message = Logging.getMessage("nullValue.ElementIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.elem = element;
}
/**
* Returns the name of this StateObject as a String, or null if this StateObject has no name.
*
* @return this StateObject's name.
*/
public String getName()
{
return this.elem.getAttribute("name");
}
/**
* Sets the name of this StateObject to the specified String.
*
* @param name the new name of this StateObject.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public void setName(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.elem.setAttribute("name", name);
}
/**
* Returns the value of this StateObject as a String, or null if this
* StateObject has no value. If there are StateObjects nested beneath this one,
* then the entire tree beneath this StateObject is converted to a String
* and returned.
*
* @return the value of this StateObject as a String.
*/
public String getValue()
{
return this.elem.getTextContent();
}
/**
* Sets the value of this StateObject to the specified String. If there are
* StateObjects nested beneath this one, then the entire tree beneath this
* StateObject is replaced with the specified value.
*
* @param value String value that will replace this StateObject's value.
* @throws IllegalArgumentException If <code>value</code> is null.
*/
public void setValue(String value)
{
if (value == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.elem.setTextContent(value);
}
}
/**
* Returns the String to be used for each state object's tag name. This tag name will be used as a
* search parameter to find a state object, and will be used as the tag name when a new state object is created.
* The default tag name is "stateObject".
*
* @return String to be used for each state object's tag name
*/
public String getStateObjectTagName()
{
return this.stateObjectTagName;
}
/**
* Sets the String to be used for each state object's tag name. This tag name will be used as a
* search parameter to find a state object, and will be used as the tag name when a new state object is created.
* Setting this value will not retroactively set tag names for existing state objects. The default tag name is
* "stateObject".
*
* @param stateObjectTagName String to be used for each state object's tag name.
* @throws IllegalArgumentException If <code>stateObjectTagName</code> is null.
*/
public void setStateObjectTagName(String stateObjectTagName)
{
if (stateObjectTagName == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.stateObjectTagName = stateObjectTagName;
}
private StateObject findStateObject(org.w3c.dom.Node context, String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Search for the state element with the specified name.
String expression = String.format("%s[@name=\"%s\"]", getStateObjectTagName(), name);
try
{
Object result = this.xpath.evaluate(
expression,
// If non-null, search from the specified context. Otherwise, search from the
// document root element.
(context != null ? context : getDocumentElement()),
javax.xml.xpath.XPathConstants.NODE);
if (result == null)
return null;
// If the result is an Element node, return a new StateObject with the result as its content.
// Otherwise return null.
return (result instanceof org.w3c.dom.Element) ? new StateObject((org.w3c.dom.Element) result) : null;
}
catch (javax.xml.xpath.XPathExpressionException e)
{
return null;
}
}
private StateObject[] findAllStateObjects(org.w3c.dom.Node context, String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Search for the state elements beneath the context with the specified name.
String expression;
if (name.length() != 0)
expression = String.format("%s[@name=\"%s\"]", getStateObjectTagName(), name);
else
expression = String.format("%s//.", getStateObjectTagName());
try
{
Object result = this.xpath.evaluate(
expression,
// If non-null, search from the specified context. Otherwise, search from the
// document root element.
(context != null ? context : getDocumentElement()),
javax.xml.xpath.XPathConstants.NODESET);
if (result == null
|| !(result instanceof org.w3c.dom.NodeList)
|| ((org.w3c.dom.NodeList) result).getLength() == 0)
return null;
// If the result is a NodeList, return an array of StateObjects for each Element node in that list.
org.w3c.dom.NodeList nodeList = (org.w3c.dom.NodeList) result;
StateObject[] stateObjects = new StateObject[nodeList.getLength()];
for (int i = 0; i < nodeList.getLength(); i++)
{
org.w3c.dom.Node node = nodeList.item(i);
if (node instanceof org.w3c.dom.Element)
stateObjects[i] = new StateObject((org.w3c.dom.Element) node);
}
return stateObjects;
}
catch (javax.xml.xpath.XPathExpressionException e)
{
return null;
}
}
private StateObject[] extractStateObjects(org.w3c.dom.Element context)
{
org.w3c.dom.NodeList nodeList = (context != null ? context : getDocumentElement()).getChildNodes();
StateObject[] stateObjects = new StateObject[0];
if (nodeList != null)
{
stateObjects = new StateObject[nodeList.getLength()];
for (int i = 0; i < nodeList.getLength(); i++)
{
org.w3c.dom.Node node = nodeList.item(i);
if (node instanceof org.w3c.dom.Element
&& node.getNodeName() != null
&& node.getNodeName().equals(getStateObjectTagName()))
{
stateObjects[i] = new StateObject((org.w3c.dom.Element) node);
}
}
}
return stateObjects;
}
private StateObject createStateObject(org.w3c.dom.Element context, String name, String value)
{
return createStateObject(context, name, value, false);
}
private StateObject createStateObject(org.w3c.dom.Element context, String name, String value, boolean escapeValue)
{
org.w3c.dom.Element elem = this.doc.createElement(getStateObjectTagName());
// If non-null, name goes in an attribute entitled "name".
if (name != null)
elem.setAttribute("name", name);
// If non-null, value goes in the element text content.
if (value != null)
{
// If escapeValue is true, we place value in a CDATA node beneath elem.
if (escapeValue)
elem.appendChild(this.doc.createCDATASection(value));
// Otherwise, just set the text value of elem normally.
else
elem.setTextContent(value);
}
// If non-null, add the StateObject element to the specified context. Otherwise, add it to the
// document root element.
(context != null ? context : getDocumentElement()).appendChild(elem);
return new StateObject(elem);
}
private boolean containsElement(org.w3c.dom.Element elem)
{
if (elem == null)
{
String message = Logging.getMessage("nullValue.ElementIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return elem.getOwnerDocument().equals(this.doc);
}
/**
* Returns the StateObject with the specified <code>name</code>. This will search the StateObjects directly
* beneath the document root. If no StateObject with that name exists, this will return null.
*
* @param name the StateObject name to search for.
* @return the StateObject instance, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public StateObject getStateObject(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return getStateObject(null, name);
}
/**
* Returns the StateObject with the specified <code>name</code>. If context is not null, this will search the
* StateObjects directly below the specified <code>context</code>. Otherwise, this will search the StateObjects
* directly beneath the document root. If no StateObject with that name exists, this will return null.
*
* @param context StateObject context to search, or null to search the document root.
* @param name the StateObject name to search for.
* @return the StateObject instance, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public StateObject getStateObject(StateObject context, String name)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return findStateObject(context != null ? context.elem : null, name);
}
/**
* Returns all StateObjects directly beneath the a context StateObject.
* If context is not null, this will return all the StateObjects directly below the specified <code>context</code>.
* Otherwise, this will return all the StateObjects directly beneath the document root.
*
* @param context StateObject context to search, or null to search the document root.
* @return an array of the StateObject instances, which will have zero length if none exist.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public StateObject[] getAllStateObjects(StateObject context)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return extractStateObjects(context != null ? context.elem : null);
}
/**
* Returns any StateObjects directly beneath the document root that have the specified
* <code>name</code>. If no StateObjects with that name exist, this will return a valid StateObject array
* with zero length.
*
* @param name the StateObject name to search for.
* @return an array of the StateObject instances, which will have zero length if none exist.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public StateObject[] getAllStateObjects(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return getAllStateObjects(null, name);
}
/**
* Returns all StateObjects with the specified <code>name</code>. If context is not null, this will search the
* StateObjects directly below the specified <code>context</code>. Otherwise, this will search the StateObjects
* directly beneath the document root. If no StateObjects with that name exist, this will return a valid
* StateObject array with zero length.
*
* @param context StateObject context to search, or null to search the document root.
* @param name the StateObject name to search for.
* @return an array of the StateObject instances, which will have zero length if none exist.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public StateObject[] getAllStateObjects(StateObject context, String name)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return findAllStateObjects(context != null ? context.elem : null, name);
}
/**
* Adds a new StateObject with the specified <code>name</code>. The new StateObject will be placed directly
* beneath the document root. If a StateObject with this name already exists, a new one is still created.
*
* @param name the new StateObject's name.
* @return the new StateObject instance.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public StateObject addStateObject(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return addStateObject(null, name);
}
/**
* Adds a new StateObject with the specified <code>name</code>. If <code>context</code> is not null, the new
* StateObject will be nested directly beneath the specified <code>context</code>. Otherwise, the new StateObject
* will be placed directly beneath the document root. If a StateObject with this name already exists, a new one
* is still created.
*
* @param context the StateObject under which the new StateObject will be created, or null to place it under
* the document root.
* @param name the new StateObject's name.
* @return the new StateObject instance.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public StateObject addStateObject(StateObject context, String name)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Create the state object with no value.
return createStateObject(context != null ? context.elem : null, name, null);
}
/*************************************************************************************************************/
/** Convenience methods for adding and querying state values. **/
/*************************************************************************************************************/
/**
* Returns the value of the StateObject with the specified <code>name</code> as a String. This will search the
* StateObjects directly beneath the document root. If no StateObject with that name exists, or if the value of
* that StateObject is not a String, this will return null.
*
* @param name the StateObject name to search for.
* @return the value of the StateObject as a String, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public String getStateValueAsString(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return getStateValueAsString(null, name);
}
/**
* Returns the value of the StateObject with the specified <code>name</code> as a String. If context is not
* null, this will search the StateObjects directly below the specified <code>context</code>. Otherwise,
* this will search the StateObjects directly beneath the document root. If no StateObject with that name
* exists, or if the value of that StateObject is not a String, this will return null.
*
* @param context StateObject context to search, or null to search the document root.
* @param name the StateObject name to search for.
* @return the value of the StateObject as a String, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public String getStateValueAsString(StateObject context, String name)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
StateObject stateObject = findStateObject(context != null ? context.elem : null, name);
return stateObject != null ? stateObject.getValue() : null;
}
/**
* Returns the value of the StateObject with the specified <code>name</code> as an Integer. This will search the
* StateObjects directly beneath the document root. If no StateObject with that name exists, or if the value of
* that StateObject is not an Integer, this will return null.
*
* @param name the StateObject name to search for.
* @return the value of the StateObject as an Integer, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public Integer getStateValueAsInteger(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return getStateValueAsInteger(null, name);
}
/**
* Returns the value of the StateObject with the specified <code>name</code> as an Integer. If context is not
* null, this will search the StateObjects directly below the specified <code>context</code>. Otherwise,
* this will search the StateObjects directly beneath the document root. If no StateObject with that name
* exists, or if the value of that StateObject is not an Integer, this will return null.
*
* @param context StateObject context to search, or null to search the document root.
* @param name the StateObject name to search for.
* @return the value of the StateObject as an Integer, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public Integer getStateValueAsInteger(StateObject context, String name)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
String stringValue = getStateValueAsString(context, name);
if (stringValue == null)
return null;
try
{
return Integer.valueOf(stringValue);
}
catch (NumberFormatException e)
{
String message = Logging.getMessage("RestorableSupport.ConversionError", stringValue);
Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
return null;
}
}
/**
* Returns the value of the StateObject with the specified <code>name</code> as a Double. This will search the
* StateObjects directly beneath the document root. If no StateObject with that name exists, or if the value of
* that StateObject is not a Double, this will return null.
*
* @param name the StateObject name to search for.
* @return the value of the StateObject as a Double, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public Double getStateValueAsDouble(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return getStateValueAsDouble(null, name);
}
/**
* Returns the value of the StateObject with the specified <code>name</code> as a Double. If context is not
* null, this will search the StateObjects directly below the specified <code>context</code>. Otherwise,
* this will search the StateObjects directly beneath the document root. If no StateObject with that name
* exists, or if the value of that StateObject is not a Double, this will return null.
*
* @param context StateObject context to search, or null to search the document root.
* @param name the StateObject name to search for.
* @return the value of the StateObject as a Double, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public Double getStateValueAsDouble(StateObject context, String name)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
String stringValue = getStateValueAsString(context, name);
if (stringValue == null)
return null;
try
{
return Double.valueOf(stringValue);
}
catch (NumberFormatException e)
{
String message = Logging.getMessage("RestorableSupport.ConversionError", stringValue);
Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
return null;
}
}
/**
* Returns the value of the StateObject with the specified <code>name</code> as a Boolean. This will search the
* StateObjects directly beneath the document root. If no StateObject with that name exists, this will return null.
* Otherwise, the Boolean value returned is equivalent to passing the StateObject's value to
* <code>Boolean.valueOf</code>.
*
* @param name the StateObject name to search for.
* @return the value of the StateObject as a Boolean, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public Boolean getStateValueAsBoolean(String name)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return getStateValueAsBoolean(null, name);
}
/**
* Returns the value of the StateObject with the specified <code>name</code> as a Boolean. If context is not
* null, this will search the StateObjects directly below the specified <code>context</code>. Otherwise,
* this will search the StateObjects directly beneath the document root. If no StateObject with that name
* exists, this will return null. Otherwise, the Boolean value returned is equivalent to passing the
* StateObject's value to <code>Boolean.valueOf</code>.
*
* @param context StateObject context to search, or null to search the document root.
* @param name the StateObject name to search for.
* @return the value of the StateObject as a Boolean, or null if none exists.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public Boolean getStateValueAsBoolean(StateObject context, String name)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
String stringValue = getStateValueAsString(context, name);
if (stringValue == null)
return null;
try
{
return Boolean.valueOf(stringValue);
}
catch (NumberFormatException e)
{
String message = Logging.getMessage("RestorableSupport.ConversionError", stringValue);
Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
return null;
}
}
/**
* Adds a new StateObject with the specified <code>name</code> and String <code>value</code>. The new
* StateObject will be placed beneath the document root. If a StateObject with this name already exists,
* a new one is still created.
*
* @param name the new StateObject's name.
* @param value the new StateObject's String value.
* @throws IllegalArgumentException If either <code>name</code> or <code>value</code> is null.
*/
public void addStateValueAsString(String name, String value)
{
if (name == null || value == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsString(null, name, value, false);
}
/**
* Adds a new StateObject with the specified <code>name</code> and String <code>value</code>. The new
* StateObject will be placed beneath the document root. If a StateObject with this name already exists,
* a new one is still created. If <code>escapeValue</code> is true, the text in <code>value</code> will be
* escaped in a CDATA section. Otherwise, no special processing is performed on <code>value</code>. Once
* <code>value</code> has been escaped and added, it can be extracted exactly like any other String value.
*
* @param name the new StateObject's name.
* @param value the new StateObject's String value.
* @param escapeValue whether to escape the String <code>value</code> or not.
* @throws IllegalArgumentException If either <code>name</code> or <code>value</code> is null.
*/
public void addStateValueAsString(String name, String value, boolean escapeValue)
{
if (name == null || value == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsString(null, name, value, escapeValue);
}
/**
* Adds a new StateObject with the specified <code>name</code> and String <code>value</code>. If
* <code>context</code> is not null, the new StateObject will be nested directly beneath the specified
* <code>context</code>. Otherwise, the new StateObject will be placed directly beneath the document root.
* If a StateObject with this name already exists, a new one is still created.
*
* @param context the StateObject context under which the new StateObject will be created, or null to place
* it under the document root.
* @param name the new StateObject's name.
* @param value the new StateObject's String value.
* @throws IllegalArgumentException If either <code>name</code> or <code>value</code> is null, or if
* <code>context</code> is not null and does not belong to this RestorableSupport.
*/
public void addStateValueAsString(StateObject context, String name, String value)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null || value == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsString(context, name, value, false);
}
/**
* Adds a new StateObject with the specified <code>name</code> and String <code>value</code>. If
* <code>context</code> is not null, the new StateObject will be nested directly beneath the specified
* <code>context</code>. Otherwise, the new StateObject will be placed directly beneath the document root.
* If a StateObject with this name already exists, a new one is still created. If <code>escapeValue</code>
* is true, the text in <code>value</code> will be escaped in a CDATA section. Otherwise, no special
* processing is performed on <code>value</code>. Once <code>value</code> has been escaped and added,
* it can be extracted exactly like any other String value.
*
* @param context the StateObject context under which the new StateObject will be created, or null to place
* it under the document root.
* @param name the new StateObject's name.
* @param value the new StateObject's String value.
* @param escapeValue whether to escape the String <code>value</code> or not.
* @throws IllegalArgumentException If either <code>name</code> or <code>value</code> is null, or if
* <code>context</code> is not null and does not belong to this RestorableSupport.
*/
public void addStateValueAsString(StateObject context, String name, String value, boolean escapeValue)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null || value == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
createStateObject(context != null ? context.elem : null, name, value, escapeValue);
}
/**
* Adds a new StateObject with the specified <code>name</code> and Integer <code>value</code>. The new
* StateObject will be placed beneath the document root. If a StateObject with this name already exists,
* a new one is still created.
*
* @param name the new StateObject's name.
* @param intValue the new StateObject's Integer value.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public void addStateValueAsInteger(String name, int intValue)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsInteger(null, name, intValue);
}
/**
* Adds a new StateObject with the specified <code>name</code> and Integer <code>value</code>. If
* <code>context</code> is not null, the new StateObject will be nested directly beneath the specified
* <code>context</code>. Otherwise, the new StateObject will be placed directly beneath the document root.
* If a StateObject with this name already exists, a new one is still created.
*
* @param context the StateObject context under which the new StateObject will be created, or null to place
* it under the document root.
* @param name the new StateObject's name.
* @param intValue the new StateObject's Integer value.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public void addStateValueAsInteger(StateObject context, String name, int intValue)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsString(context, name, Integer.toString(intValue));
}
/**
* Adds a new StateObject with the specified <code>name</code> and Double <code>value</code>. The new
* StateObject will be placed beneath the document root. If a StateObject with this name already exists,
* a new one is still created.
*
* @param name the new StateObject's name.
* @param doubleValue the new StateObject's Double value.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public void addStateValueAsDouble(String name, double doubleValue)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsDouble(null, name, doubleValue);
}
/**
* Adds a new StateObject with the specified <code>name</code> and Double <code>value</code>. If
* <code>context</code> is not null, the new StateObject will be nested directly beneath the specified
* <code>context</code>. Otherwise, the new StateObject will be placed directly beneath the document root.
* If a StateObject with this name already exists, a new one is still created.
*
* @param context the StateObject context under which the new StateObject will be created, or null to place
* it under the document root.
* @param name the new StateObject's name.
* @param doubleValue the new StateObject's Double value.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public void addStateValueAsDouble(StateObject context, String name, double doubleValue)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsString(context, name, Double.toString(doubleValue));
}
/**
* Adds a new StateObject with the specified <code>name</code> and Boolean <code>value</code>. The new
* StateObject will be placed beneath the document root. If a StateObject with this name already exists,
* a new one is still created.
*
* @param name the new StateObject's name.
* @param booleanValue the new StateObject's Boolean value.
* @throws IllegalArgumentException If <code>name</code> is null.
*/
public void addStateValueAsBoolean(String name, boolean booleanValue)
{
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsBoolean(null, name, booleanValue);
}
/**
* Adds a new StateObject with the specified <code>name</code> and Boolean <code>value</code>. If
* <code>context</code> is not null, the new StateObject will be nested directly beneath the specified
* <code>context</code>. Otherwise, the new StateObject will be placed directly beneath the document root.
* If a StateObject with this name already exists, a new one is still created.
*
* @param context the StateObject context under which the new StateObject will be created, or null to place
* it under the document root.
* @param name the new StateObject's name.
* @param booleanValue the new StateObject's Boolean value.
* @throws IllegalArgumentException If <code>name</code> is null, or if <code>context</code> is not null and
* does not belong to this RestorableSupport.
*/
public void addStateValueAsBoolean(StateObject context, String name, boolean booleanValue)
{
if (context != null && !containsElement(context.elem))
{
String message = Logging.getMessage("RestorableSupport.InvalidStateObject");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (name == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
addStateValueAsString(context, name, Boolean.toString(booleanValue));
}
/*************************************************************************************************************/
/** Convenience methods for adding and querying state values. **/
/*************************************************************************************************************/
/**
* Returns a String encoding of the specified <code>color</code>. The Color can be restored with a call to
* {@link #decodeColor(String)}.
*
* @param color Color to encode.
* @return String encoding of the specified <code>color</code>.
* @throws IllegalArgumentException If <code>color</code> is null.
*/
public static String encodeColor(java.awt.Color color)
{
if (color == null)
{
String message = Logging.getMessage("nullValue.ColorIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Encode the red, green, blue, and alpha components
int rgba = (color.getRed() & 0xFF) << 24
| (color.getGreen() & 0xFF) << 16
| (color.getBlue() & 0xFF) << 8
| (color.getAlpha() & 0xFF);
return String.format("%#08X", rgba);
}
/**
* Returns the Color described by the String <code>encodedString</code>. This understands Colors encoded
* with a call to {@link #encodeColor(java.awt.Color)}. If <code>encodedString</code> cannot be decoded,
* this will return null.
*
* @param encodedString String to decode.
* @return Color decoded from the specified <code>encodedString</code>, or null if the String cannot be decoded.
* @throws IllegalArgumentException If <code>encodedString</code> is null.
*/
public static java.awt.Color decodeColor(String encodedString)
{
if (encodedString == null)
{
String message = Logging.getMessage("nullValue.StringIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// The hexadecimal representation for an RGBA color can result in a value larger than
// Integer.MAX_VALUE (for example, 0XFFFF). Therefore we decode the string as a long,
// then keep only the lower four bytes.
Long longValue;
try
{
longValue = Long.decode(encodedString);
}
catch (NumberFormatException e)
{
String message = Logging.getMessage("RestorableSupport.ConversionError", encodedString);
Logging.logger().log(java.util.logging.Level.SEVERE, message, e);
return null;
}
int i = (int) (longValue & 0xFFFFFFFFL);
return new java.awt.Color(
(i >> 24) & 0xFF,
(i >> 16) & 0xFF,
(i >> 8) & 0xFF,
i & 0xFF);
}
}