com.cedarsoft.serialization.test.utils.XmlNamespaceTranslator Maven / Gradle / Ivy
package com.cedarsoft.serialization.test.utils;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* @author Johannes Schneider ([email protected])
*/
public class XmlNamespaceTranslator {
@Nonnull
private Map, Value> translations = new HashMap, Value>();
public XmlNamespaceTranslator addTranslation( @Nullable String fromNamespaceURI, @Nonnull String toNamespaceURI ) {
Key key = new Key( fromNamespaceURI );
Value value = new Value( toNamespaceURI );
this.translations.put( key, value );
return this;
}
public void translateNamespaces( @Nonnull Document xmlDoc, boolean addNsToAttributes ) {
Stack nodes = new Stack();
nodes.push( xmlDoc.getDocumentElement() );
while ( !nodes.isEmpty() ) {
Node node = nodes.pop();
switch ( node.getNodeType() ) {
case Node.ATTRIBUTE_NODE:
case Node.ELEMENT_NODE:
Value value = this.translations.get( new Key( node.getNamespaceURI() ) );
if ( value != null ) {
// the reassignment to node is very important. as per javadoc renameNode will
// try to modify node (first parameter) in place. If that is not possible it
// will replace that node for a new created one and return it to the caller.
// if we did not reassign node we will get no childs in the loop below.
node = xmlDoc.renameNode( node, value.getValue(), node.getNodeName() );
}
break;
}
if ( addNsToAttributes ) {
// for attributes of this node
NamedNodeMap attributes = node.getAttributes();
if ( !( attributes == null || attributes.getLength() == 0 ) ) {
for ( int i = 0, count = attributes.getLength(); i < count; ++i ) {
Node attribute = attributes.item( i );
if ( attribute != null ) {
nodes.push( attribute );
}
}
}
}
// for child nodes of this node
NodeList childNodes = node.getChildNodes();
if ( !( childNodes == null || childNodes.getLength() == 0 ) ) {
for ( int i = 0, count = childNodes.getLength(); i < count; ++i ) {
Node childNode = childNodes.item( i );
if ( childNode != null ) {
nodes.push( childNode );
}
}
}
}
}
// these will allow null values to be stored on a map so that we can distinguish
// from values being on the map or not. map implementation returns null if the there
// is no map element with a given key. If the value is null there is no way to
// distinguish from value not being on the map or value being null. these classes
// remove ambiguity.
private static class Holder {
protected final T value;
public Holder( T value ) {
this.value = value;
}
public T getValue() {
return value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( value == null ) ? 0 : value.hashCode() );
return result;
}
@Override
public boolean equals( Object obj ) {
if ( this == obj )
return true;
if ( obj == null )
return false;
if ( getClass() != obj.getClass() )
return false;
Holder> other = ( Holder> ) obj;
if ( value == null ) {
if ( other.value != null )
return false;
} else if ( !value.equals( other.value ) )
return false;
return true;
}
}
private static class Key extends Holder {
public Key( T value ) {
super( value );
}
}
private static class Value extends Holder {
public Value( T value ) {
super( value );
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy