Skip to Content.
Sympa Menu

xom-interest - [XOM-interest] Visitor pattern: could be nice addon for the library

xom-interest AT lists.ibiblio.org

Subject: XOM API for Processing XML with Java

List archive

Chronological Thread  
  • From: Dmitry Katsubo <dma_k AT mail.ru>
  • To: xom-interest AT lists.ibiblio.org
  • Subject: [XOM-interest] Visitor pattern: could be nice addon for the library
  • Date: Sat, 27 Mar 2010 13:19:58 +0100

Dear XOM developers and XOM users!

I would like to share with you my opinion about the functionality I miss
in XOM library and also some piece of code, that somebody might find
useful. Maybe they already exist in the library, but I refer the latest
one available in Maven (v1.1)

First of all, I have read the authors view on Visitors [1] and I find it
reasonable: maybe it does not make sense to push all algorithms into
Node or Document class. However, I think some algorithms are very basic
and demanded very often in XML-manipulation libraries. This is mainly
tree traversal with additional functionality on the top:
- Find a node with a given ID
- Find nodes with a given name
- Find a node with some conditions which are not known in advance
(defined in some closure).

I also have read few posts into this maillist ([2,3]) and I also see the
demand of custom serialization of nodes.

I believe that having these algorithms somewehre in a separate XMLUtils
class as static helpers will not harm. I think, this is good practice,
as shown by java.util.Collections and java.util.Arrays classes.

I attach the example of Java class, that I had to program on the top of
XOM library to satisfy my demands. Maybe someone find it helpful. The
example demonstrates also how the Visitor pattern can benefit from
generics. The same way one can generalize optional exception that is
allowed to be thrown by the visitor.

Any feedback or opinion is welcome.

[1] http://www.xom.nu/faq.xhtml#d0e373
[2] https://lists.ibiblio.org/sympa/arc/xom-interest/2006-March/002897.html
[3]
https://lists.ibiblio.org/sympa/arc/xom-interest/2002-September/000077.html

--
With best regards,
Dmitry


package com.panels;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.io.IOUtils;

import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Comment;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Node;
import nu.xom.Nodes;
import nu.xom.ParsingException;
import nu.xom.ValidityException;

public class PackagePanel {


/**
* This function performs the transformation of main spring
configuration file. In particular it:
*
* <ul>
* <li>Removes "import" statements for packages, which are not
included into the package.
* <li>Corrects the ontology alias definition.
* </ul>
*/
String transformSpringConfiguration(InputStream is, final boolean
isOntologyFilePackageSelected) throws IOException {
final Builder builder = new Builder();

Document document;

try {
document = builder.build(is);
} catch (ValidityException e) {
throw new IOException(e);
} catch (ParsingException e) {
throw new IOException(e);
}

document.insertChild(new Comment(COMMENT), 0);

traverseTree(document, new Visitor<Object>() {

@Override
public boolean visit(Node node, Object arg) {
if (node instanceof Element) {
final Element element = (Element)
node;

if
("import".equals(element.getLocalName())) {
final String resource =
element.getAttributeValue("resource");

if (resource != null
&&
((resource.contains("ontology-db") && isOntologyFilePackageSelected) ||
(resource

.contains("ontology-file") && !isOntologyFilePackageSelected))) {
node.detach();
}
} else if
("alias".equals(element.getLocalName())) {
final String alias =
element.getAttributeValue("alias");

if (alias.equals("ontology"))
{

element.getAttribute("name").setValue(

isOntologyFilePackageSelected ? "singleFileOntology" : "ontologyDb");
}
}
}

return true;
}
}, null);

return document.toXML();
}

private static final Nodes getChildren(Node node) {
final Nodes nodes = new Nodes();

for (int i = 0; i < node.getChildCount(); i++) {
nodes.append(node.getChild(i));
}

return nodes;
}

/**
* Calls the given visitor for each tree node.
*/
private static final <T> boolean traverseTree(Node node, Visitor<T>
visitor, T arg) {
// Copy children nodes, as visitors are allowed to remove
nodes:
final Nodes children = getChildren(node);

for (int i = 0; i < children.size(); i++) {
final Node child = children.get(i);

if (!visitor.visit(child, arg)) {
return false;
}

traverseTree(child, visitor, arg);
}

return true;
}

/**
* Visitors are allowed to remove nodes. If visitor inserts some node
into the tree, it may or may not be traversed,
* depending on the traversal algorithm and insertion place.
*/
private interface Visitor<T> {

/**
* Is called on each node in the tree.
*
* @return <code>true</code> if tree iteration should be
continued
*/
boolean visit(Node node, T arg);
}

private class GetElementByName implements Visitor<Nodes> {
private final String localNodeName;

public GetElementByName(String localNodeName) {
this.localNodeName = localNodeName;
}

@Override
public boolean visit(Node node, Nodes arg) {
if (node instanceof Element) {
final Element elemtent = (Element) node;

if
(localNodeName.equals(elemtent.getLocalName())) {
arg.append(node);

return false;
}
}

return true;
}
}

private class GetNodesByName implements Visitor<Nodes> {
private final String localNodeName;

public GetNodesByName(String localNodeName) {
this.localNodeName = localNodeName;
}

@Override
public boolean visit(Node node, Nodes arg) {
if (node instanceof Element) {
final Element elemtent = (Element) node;

if
(localNodeName.equals(elemtent.getLocalName())) {
arg.append(node);
}
}

return true;
}
}
}




  • [XOM-interest] Visitor pattern: could be nice addon for the library, Dmitry Katsubo, 03/27/2010

Archive powered by MHonArc 2.6.24.

Top of Page