/* Copyright (c) 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gdata.data;
import com.google.gdata.util.common.xml.XmlNamespace;
import com.google.gdata.util.common.xml.XmlWriter;
import com.google.gdata.util.ParseException;
import com.google.gdata.util.XmlParser;
import org.xml.sax.Attributes;
import java.io.IOException;
/**
* The ValueConstruct class is an abstract Extension class that can
* be used to subclassed to create a GData extension element with a
* single value, like:
* <code>
* <foo:bar value="some value"/>
* </code>
* or
* <code>
* <foo:baz>some value</foo:baz>
* </code>
* Using constructor parameters, a customized subclass has full
* control over the value construct element namespace and tag name,
* as well whether the value is contained within an XML attribute
* or the element text value.
* <p>
* A subclass can override the {@link #setValue(String)}
* method to do customized validation of any value set directly
* by a client or as a result of XML parsing.
* <p>
* Two ValueConstruct instances are considered equal if they have the same
* concrete subclass and the value of the two instances are equal. The
* namespace, tagname, and attribute names <b>are not</b> taken into account
* by the equality comparison; they are assumed to be equivalent for all
* instances of a particular concrete subclass.
*
*
*
*/
public abstract class ValueConstruct extends AbstractExtension {
/**
* The XML attribute name for the value construct. When {@code null},
* indicates that the value is contained within the XML element text
* content.
*/
protected final String attrName;
/**
* The value of the value construct.
*/
private String value;
/**
* Indicates that the value is required ({@code true} by default).
*/
private boolean required = true;
public final boolean isRequired() { return required; }
protected final void setRequired(boolean isRequired) {
this.required = isRequired;
}
/**
* Constructs a value construct bound to a specific XML representation. The
* concrete subclass must have an {@link ExtensionDescription.Default}
* attribute defined, and should always use constant values for the {@code
* attrName} parameter.
*
* @param attrName the name of attribute that contains the value, or {@code
* null} if the value is contained within the element
* content.
*/
protected ValueConstruct(String attrName) {
super();
this.attrName = attrName;
}
/**
* Constructs an ValueConstruct bound to a specific XML representation
* A concrete subclass should always use constants value for all three
* parameters.
*
* @param namespace the namespace of the value element.
* @param localName the local name of the value element.
* @param attrName the name of attribute that contains the value, or
* {@code null} if the value is contained within the element content.
*/
protected ValueConstruct(XmlNamespace namespace,
String localName,
String attrName) {
this(namespace, localName, attrName, null);
}
/**
* Constructs a new ValueConstruct instance bound to a specific XML
* representation. A concrete subclass should always use constant values
* for the {@code namespace}, {@code localName}, and {@code attrName}
* parameters. If an initial value is provided and it is not {@code null},
* an immutable instance will be created that is initialized to this
* value and may not be modified by {@link #setValue(String)}.
*
* @param namespace the namespace of the value element.
* @param localName the local name of the value element.
* @param attrName the name of attribute that contains the value, or
* {@code null} if the value is contained within the element content.
* @param value the value that should be used to initialize the value
* construct instance. After construction, the value will be
* immutable.
*/
protected ValueConstruct(XmlNamespace namespace,
String localName,
String attrName,
String value) {
super(namespace, localName);
this.attrName = attrName;
if (value != null) {
setValue(value);
setImmutable(true);
}
}
/**
* Returns the value of the value construct.
* @return the current value.
*/
public String getValue() { return value; }
/**
* Sets the value. Subclasses can override this method to do
* additional validation of the value.
*
* @param v new value for the value construct or <code>null</code> to reset.
* @throws IllegalArgumentException if the value is invalid for the construct.
* @throws IllegalStateException if the value construct is read only
*/
public void setValue(String v) {
throwExceptionIfImmutable();
value = v; }
/**
* Returns whether it has the value.
*
* @return whether it has the value
*/
public boolean hasValue() {
return value != null;
}
@Override
public void putAttributes(AttributeGenerator generator) {
if (attrName != null) {
generator.put(attrName, value);
} else {
generator.setContent(value);
}
}
@Override
protected void consumeAttributes(AttributeHelper helper)
throws ParseException {
String actualValue;
if (attrName != null) {
actualValue = helper.consume(this.attrName, isRequired());
} else {
actualValue = helper.consumeContent(isRequired());
}
try {
setValue(actualValue);
} catch (IllegalArgumentException iae) {
throw new ParseException(iae.getMessage(), iae);
}
}
@Override
public void generate(XmlWriter w, ExtensionProfile p)
throws IOException {
// for backwards compatibility don't generate if no value
if (value != null) {
super.generate(w, p);
}
}
@Override
public XmlParser.ElementHandler getHandler(ExtensionProfile p,
String namespace, String localName, Attributes attrs)
throws ParseException {
// for backwards compatibility set value to null first
value = null;
return super.getHandler(p, namespace, localName, attrs);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!sameClassAs(o)) {
return false;
}
ValueConstruct vc = (ValueConstruct) o;
return eq(value, vc.value);
}
@Override
public int hashCode() {
int result = getClass().hashCode();
if (value != null) {
result = 37 * result + value.hashCode();
}
return result;
}
}