com.publicobject.misc.xml.XMLTagPath Maven / Gradle / Ivy
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package com.publicobject.misc.xml;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* An XMLTagPath simply represents a position within an XML Document. Much like
* a directory represents a logical position relative to the root of a filesystem:
*
* \files\code\source\tests
*
*
an XMLTagPath represents a valid path of tags from the root of an XML
* Document that specifies a position with that Document, such as:
*
*
* - <customer> <fullname> <firstname>
*
- <customer> <fullname> <intial>
*
- <customer> <fullname> <lastname>
*
- <customer> <fullname>
*
- <customer>
*
*
* within an XML Document like:
*
*
* <customer>
* <fullname>
* <firstname>James</firstname>
* <initial>P</initial>
* <lastname>Lemieux</lastname>
* </fullname>
* </customer>
*
*
* An XMLTagPath
represents either the start, end, body text
* or attribute of an XML tag.
*
*
This class is intentionally immutable. All methods produce new
* XMLTagPath
objects.
*
* @author James Lemieux
* @author Jesse Wilson
*/
public final class XMLTagPath {
/** A virtual attribute representing the tag opening */
private static final String START = "_START";
/** A virtual attribute representing the tag closing */
private static final String END = "_END";
/** A virtual attribute representing the tag body text */
private static final String BODY = "_BODY";
/** The list of tag names specifying the location of this XMLTagPath relative to the Document root. */
private final List path;
/**
* The name of an attribute within the tag, or the type of tag such
* as {@link #START}, {@link #END} or {@link #BODY} if this is not
* an attribute tag.
*/
private final String attribute;
/**
* Constructs a new {@link XMLTagPath} representing the body text of
* the specified root tag.
*/
public XMLTagPath(String root) {
this(Collections.singletonList(root), BODY);
}
/**
* General constructor for an arbitrary tag path.
*
* @param path the sequence of nested tags defining this path.
* @param attribute the XML attribute named by this path, or a virtual
* attribute such as {@link #START}, {@link #END} or {@link #BODY}.
*/
private XMLTagPath(List path, String attribute) {
if (path == null) {
throw new IllegalArgumentException("path must not be null");
}
if (attribute == null) {
throw new IllegalArgumentException("attribute must not be null");
}
this.path = Collections.unmodifiableList(path);
this.attribute = attribute;
}
/**
* Returns A special {@link XMLTagPath} that does not contain any path. It can be
* thought of as representing the entire Document.
*/
static XMLTagPath emptyPath() {
return new XMLTagPath(Collections.emptyList(), BODY);
}
/**
* Produces a new XMLTagPath by appending the given tag
to
* this XMLTagPath. The attribute is kept the same.
*/
public XMLTagPath child(String tag) {
final List newParts = new ArrayList(path.size() + 1);
newParts.addAll(path);
newParts.add(tag);
return new XMLTagPath(newParts, attribute);
}
/**
* Produces a new XMLTagPath representing the given attribute
* within the current tag.
*/
public XMLTagPath attribute(String attribute) {
if(this.attribute == attribute) return this;
return new XMLTagPath(path, attribute);
}
/**
* Produces a new XMLTagPath by changing the location field of this
* XMLTagPath to be {@link #START}.
*/
public XMLTagPath start() {
return attribute(START);
}
/**
* Produces a new XMLTagPath by changing the location field of this
* XMLTagPath to be {@link #END}.
*/
public XMLTagPath end() {
return attribute(END);
}
/**
* Produces a new XMLTagPath representing the body text for this tag.
*/
public XMLTagPath body() {
return attribute(BODY);
}
/**
* The containing tag with the same attribute.
*/
public XMLTagPath parent() {
return new XMLTagPath(path.subList(0, path.size() - 1), attribute);
}
/** {@inheritDoc} */
@Override
public boolean equals(Object o) {
if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false;
XMLTagPath that = (XMLTagPath) o;
if(!attribute.equals(that.attribute)) return false;
if(!path.equals(that.path)) return false;
return true;
}
/** {@inheritDoc} */
@Override
public int hashCode() {
int result;
result = path.hashCode();
result = 31 * result + attribute.hashCode();
return result;
}
/** @inheritDoc */
@Override
public String toString() {
final StringBuffer formattedPath = new StringBuffer();
for (Iterator i = path.iterator(); i.hasNext();) {
formattedPath.append(i.next());
if (i.hasNext()) formattedPath.append("/");
}
if(attribute == START) {
formattedPath.append("[start]");
} else if(attribute == BODY) {
formattedPath.append("[body]");
} else if(attribute == END) {
formattedPath.append("[end]");
} else {
formattedPath.append("#").append(attribute);
}
return formattedPath.toString();
}
}