/* * 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 - 2017 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.libraries.xmlns.common; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * A attribute map holding <namespace;name>-value pairs. * * @author Thomas Morgner */ public class AttributeMap<T> implements Serializable, Cloneable { public static class DualKey implements Serializable { public final String namespace; public final String name; private DualKey( final String namespace, final String name ) { this.namespace = namespace; this.name = name; } public boolean equals( final Object o ) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } final DualKey dualKey = (DualKey) o; if ( !name.equals( dualKey.name ) ) { return false; } if ( !namespace.equals( dualKey.namespace ) ) { return false; } return true; } public int hashCode() { int result = name.hashCode(); result = 31 * result + namespace.hashCode(); return result; } public String toString() { return String.format( "DualKey(%s, %s)", namespace, name ); } } private static final String[] EMPTY_NAMESPACES = new String[0]; private static final long serialVersionUID = -7442871030874215436L; private LinkedHashMap<DualKey, T> content; /** * Default constructor. */ public AttributeMap() { } /** * Creates a new attribute map using the given parameter as source for the initial values. * * @param copy the attribute map that should be copied. * @noinspection unchecked */ public AttributeMap( final AttributeMap copy ) { if ( copy == null ) { return; } if ( copy.content != null ) { this.content = (LinkedHashMap<DualKey, T>) copy.content.clone(); } } /** * Creates a copy of this map. * * @return the clone. * @noinspection CloneDoesntDeclareCloneNotSupportedException, unchecked */ public AttributeMap<T> clone() { try { final AttributeMap<T> map = (AttributeMap<T>) super.clone(); if ( content != null ) { map.content = (LinkedHashMap<DualKey, T>) content.clone(); } return map; } catch ( final CloneNotSupportedException cne ) { // ignored throw new IllegalStateException( "Cannot happen: Clone not supported exception" ); } } /** * Defines the attribute for the given namespace and attribute name. * * @param namespace the namespace under which the value should be stored. * @param attribute the attribute name under which the value should be stored within the namespace. * @param value the value. * @return the previously stored value at that position. */ public T setAttribute( final String namespace, final String attribute, final T value ) { if ( namespace == null ) { throw new NullPointerException( "Attribute namespace must not be null" ); } if ( attribute == null ) { throw new NullPointerException( "Attribute name must not be null" ); } if ( content == null ) { content = new LinkedHashMap<DualKey, T>(); } if ( value != null ) { return content.put( new DualKey( namespace, attribute ), value ); } else { return content.remove( new DualKey( namespace, attribute ) ); } } /** * Returns the attribute value for the given namespace and attribute-name. * * @param namespace the namespace. * @param attribute the attribute name. * @return the value or null, if there is no such namespace/attribute name combination. */ public T getAttribute( final String namespace, final String attribute ) { if ( namespace == null ) { throw new NullPointerException( "Attribute namespace must not be null" ); } if ( attribute == null ) { throw new NullPointerException( "Attribute name must not be null" ); } if ( content == null ) { return null; } return content.get( new DualKey( namespace, attribute ) ); } /** * Looks up all namespaces and returns the value from the first namespace that has this attribute defined. * * @param attribute the the attribute name. * @return the object from the first namespace that carries this attribute or null, if none of the namespaces has such * an attribute defined. */ public T getFirstAttribute( final String attribute ) { if ( attribute == null ) { throw new NullPointerException( "Attribute name must not be null" ); } if ( content != null ) { for ( final Map.Entry<DualKey, T> entry : content.entrySet() ) { if ( attribute.equals( entry.getKey().name ) ) { return entry.getValue(); } } } return null; } /** * Returns all attributes of the given namespace as unmodifiable map. * * @param namespace the namespace for which the attributes should be returned. * @return the map, never null. */ public Map<String, T> getAttributes( final String namespace ) { if ( namespace == null ) { throw new NullPointerException( "Attribute namespace must not be null" ); } if ( content == null ) { return Collections.emptyMap(); } LinkedHashMap<String, T> entries = new LinkedHashMap<String, T>(); for ( final Map.Entry<DualKey, T> entry : content.entrySet() ) { DualKey key = entry.getKey(); if ( namespace.equals( key.namespace ) ) { entries.put( key.name, entry.getValue() ); } } return Collections.unmodifiableMap( entries ); } /** * Returns all names for the given namespace that have values in this map. * * @param namespace the namespace for which known attribute names should be looked up. * @return the names stored for the given namespace. */ public String[] getNames( final String namespace ) { if ( namespace == null ) { throw new NullPointerException( "Attribute namespace must not be null" ); } if ( content == null ) { return AttributeMap.EMPTY_NAMESPACES; } List<String> entries = new ArrayList<String>(); for ( final Map.Entry<DualKey, T> entry : content.entrySet() ) { DualKey key = entry.getKey(); if ( namespace.equals( key.namespace ) ) { entries.add( key.name ); } } return entries.toArray( new String[ entries.size() ] ); } public Set<DualKey> keySet() { if ( content == null ) { return Collections.emptySet(); } return content.keySet(); } /** * Returns all namespaces that have values in this map. * * @return the namespaces stored in this map. */ public String[] getNameSpaces() { if ( content == null ) { return AttributeMap.EMPTY_NAMESPACES; } LinkedHashSet<String> entries = new LinkedHashSet<String>(); for ( final Map.Entry<DualKey, T> entry : content.entrySet() ) { entries.add( entry.getKey().namespace ); } return entries.toArray( new String[entries.size()] ); } public void putAll( final AttributeMap<T> attributeMap ) { if ( attributeMap.content == null ) { return; } if ( content == null ) { content = (LinkedHashMap<DualKey, T>) attributeMap.content.clone(); } else { content.putAll( attributeMap.content ); } } public boolean equals( final Object o ) { if ( this == o ) { return true; } if ( o == null || getClass() != o.getClass() ) { return false; } final AttributeMap that = (AttributeMap) o; if ( content != null ? !content.equals( that.content ) : that.content != null ) { return false; } return true; } public int hashCode() { if ( content != null ) { return content.hashCode(); } return 0; } public String toString() { final StringBuilder sb = new StringBuilder(); sb.append( "AttributeMap" ); sb.append( "{content=" ).append( content ); sb.append( '}' ); return sb.toString(); } public void clear() { if ( content != null ) { content.clear(); } } }