/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.libraries.xmlns.common;
import org.pentaho.reporting.libraries.base.util.LinkedMap;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import java.util.Arrays;
import java.util.Iterator;
/**
* The attribute list is used by a writer to specify the attributes of an XML element in a certain order.
*
* @author Thomas Morgner
*/
public class AttributeList {
/**
* A constant containing the XML-Namespace namespace identifier.
*/
public static final String XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/";
/**
* A constant containing the XML namespace identifier.
*/
public static final String XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace";
/**
* A name/value pair of the attribute list.
*/
public static class AttributeEntry {
/**
* The namespace of the attribute entry.
*/
private String namespace;
/**
* The name of the attribute entry.
*/
private String name;
/**
* The value of the attribute entry.
*/
private String value;
/**
* Creates a new attribute entry for the given name and value.
*
* @param namespace the namespace of the attribute.
* @param name the attribute name (<code>null</code> not permitted).
* @param value the attribute value (<code>null</code> not permitted).
*/
public AttributeEntry( final String namespace,
final String name,
final String value ) {
if ( name == null ) {
throw new NullPointerException( "Name must not be null. ["
+ name + ", " + value + ']' );
}
if ( value == null ) {
throw new NullPointerException( "Value must not be null. ["
+ name + ", " + value + ']' );
}
this.namespace = namespace;
this.name = name;
this.value = value;
}
/**
* Returns the attribute name.
*
* @return the name.
*/
public String getName() {
return this.name;
}
/**
* Returns the value of this attribute entry.
*
* @return the value of the entry.
*/
public String getValue() {
return this.value;
}
/**
* Returns the attribute namespace (which can be null).
*
* @return the namespace.
*/
public String getNamespace() {
return namespace;
}
/**
* Compares this attribute entry for equality with an other object.
*
* @param o the other object.
* @return true, if this object is equal, false otherwise.
*/
public boolean equals( final Object o ) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final AttributeEntry that = (AttributeEntry) o;
if ( !name.equals( that.name ) ) {
return false;
}
if ( namespace != null ? !namespace.equals( that.namespace ) : that.namespace != null ) {
return false;
}
return true;
}
/**
* Computes a hashcode for this attribute entry.
*
* @return the attribute entry's hashcode.
*/
public int hashCode() {
int result = ( namespace != null ? namespace.hashCode() : 0 );
result = 29 * result + name.hashCode();
return result;
}
protected void update( final String namespace, final String name, final String value ) {
if ( name == null ) {
throw new NullPointerException( "Name must not be null. ["
+ name + ", " + value + ']' );
}
if ( value == null ) {
throw new NullPointerException( "Value must not be null. ["
+ name + ", " + value + ']' );
}
this.namespace = namespace;
this.name = name;
this.value = value;
}
}
/**
* The storage for all entries of this list.
*/
private LinkedMap entryList;
private AttributeEntry lookupKey;
private transient AttributeEntry[] arrayCache;
/**
* Creates an empty attribute list with no default values.
*/
public AttributeList() {
this.entryList = new LinkedMap();
this.lookupKey = new AttributeEntry( null, "lookup", "value" );
}
/**
* Returns an iterator over the entry list. The iterator returns AttributeList.AttributeEntry objects.
*
* @return the iterator over the entries contained in this list.
* @deprecated use toArray instead.
*/
public Iterator iterator() {
return Arrays.asList( entryList.values() ).iterator();
}
public AttributeEntry[] toArray() {
if ( arrayCache == null ) {
arrayCache = (AttributeEntry[]) entryList.values( new AttributeEntry[ entryList.size() ] );
}
return (AttributeEntry[]) arrayCache.clone();
}
/**
* Defines an attribute.
*
* @param namespace the namespace of the attribute.
* @param name the name of the attribute to be defined
* @param value the value of the attribute.
*/
public void setAttribute( final String namespace,
final String name,
final String value ) {
if ( value == null ) {
removeAttribute( namespace, name );
return;
}
final AttributeEntry entry = new AttributeEntry( namespace, name, value );
this.arrayCache = null;
this.entryList.put( entry, entry );
}
/**
* Returns the attribute value for the given attribute name or null, if the attribute is not defined in this list.
*
* @param namespace the namespace of the attribute.
* @param name the name of the attribute
* @return the attribute value or null.
*/
public String getAttribute( final String namespace,
final String name ) {
return getAttribute( namespace, name, null );
}
/**
* Returns the attribute value for the given attribute name or the given defaultvalue, if the attribute is not defined
* in this list.
*
* @param namespace the namespace of the attribute.
* @param name the name of the attribute.
* @param defaultValue the default value.
* @return the attribute value or the defaultValue.
*/
public String getAttribute( final String namespace,
final String name,
final String defaultValue ) {
lookupKey.update( namespace, name, "" );
final AttributeEntry entry = (AttributeEntry) this.entryList.get( lookupKey );
if ( entry != null ) {
return entry.getValue();
}
return defaultValue;
}
/**
* Removes the attribute with the given name from the list.
*
* @param namespace the namespace of the attribute that should be removed.
* @param name the name of the attribute which should be removed..
*/
public void removeAttribute( final String namespace,
final String name ) {
lookupKey.update( namespace, name, "" );
entryList.remove( lookupKey );
this.arrayCache = null;
}
/**
* Checks, whether this list is empty.
*
* @return true, if the list is empty, false otherwise.
*/
public boolean isEmpty() {
return this.entryList.isEmpty();
}
/**
* Adds a namespace declaration. In XML, Namespaces are declared by using a special attribute-syntax. As this syntax
* is confusing and complicated, this method encapsulates this and make defining namespaces less confusing.
*
* @param prefix the desired namespace prefix (can be null or empty to define the default namespace.
* @param namespaceUri the URI of the namespace.
*/
public void addNamespaceDeclaration( final String prefix,
final String namespaceUri ) {
if ( namespaceUri == null ) {
throw new NullPointerException();
}
if ( prefix == null || "".equals( prefix ) ) {
setAttribute( AttributeList.XMLNS_NAMESPACE, "", namespaceUri );
} else {
setAttribute( AttributeList.XMLNS_NAMESPACE, prefix, namespaceUri );
}
}
/**
* Removes a namespace declaration from this attribute list.
*
* @param prefix the declared namespace prefix.
*/
public void removeNamespaceDeclaration( final String prefix ) {
if ( prefix == null || "".equals( prefix ) ) {
removeAttribute( AttributeList.XMLNS_NAMESPACE, "" );
} else {
removeAttribute( AttributeList.XMLNS_NAMESPACE, prefix );
}
}
/**
* Checks, whether the given prefix is defined.
*
* @param prefix the namespace prefix.
* @return true, if the prefix is defined, false otherwise.
*/
public boolean isNamespacePrefixDefined( final String prefix ) {
return getAttribute( AttributeList.XMLNS_NAMESPACE, prefix ) != null;
}
/**
* Checks, whether the given namespace URI has a defined prefix.
*
* @param uri the uri.
* @return true, if there is at least one namespace declaration matching the given URI, false otherwise.
*/
public boolean isNamespaceUriDefined( final String uri ) {
if ( this.entryList.isEmpty() ) {
return false;
}
final AttributeEntry[] objects = toArray();
for ( int i = 0; i < objects.length; i++ ) {
final AttributeEntry ae = objects[ i ];
if ( ObjectUtilities.equal( ae.getValue(), uri ) &&
AttributeList.XMLNS_NAMESPACE.equals( ae.getNamespace() ) ) {
return true;
}
}
return false;
}
}