/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Attribute.java * Created: 5-Feb-2003 * By: Rick Cameron */ package org.openquark.util.attributes; import java.awt.Color; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import org.openquark.util.CaseInsensitiveMap; import org.openquark.util.time.Time; import org.openquark.util.xml.XMLSerializationManager; import org.w3c.dom.Element; /** * An Attribute is an association of a String (the name) and an Object (the * value, or list of values). * This class is immutable. */ public final class Attribute { private static final AttributeComparator attributeComparator = new AttributeComparator(); /** Attribute type constants */ public enum Type { UNKNOWN, COLOR, BOOLEAN, INTEGER, DOUBLE, STRING, ATTRIBUTE_SET, TIME } /** The name of the attribute. */ private final String name; /** The attribute value (or list of values). */ private final Object value; /** A method to correctly compare 2 attribute names. */ public static boolean equalNames (String s1, String s2) { return CaseInsensitiveMap.equalsCaseInsensitive(s1, s2); } /** * Attribute constructor. * @param name the attribute name * @param colour the attribute value */ public Attribute(String name, Color colour) { this(name, (Object) colour); } /** * Attribute constructor. * @param name the attribute name * @param boolVal the attribute value */ public Attribute(String name, boolean boolVal) { this(name, Boolean.valueOf(boolVal)); } /** * Attribute constructor. * @param name the attribute name * @param intVal the attribute value */ public Attribute(String name, int intVal) { this(name, Integer.valueOf(intVal)); } /** * Attribute constructor. * @param name the attribute name * @param doubleVal the attribute value */ public Attribute(String name, double doubleVal) { this(name, Double.valueOf(doubleVal)); } /** * Attribute constructor. * @param name the attribute name * @param stringVal the attribute value */ public Attribute(String name, String stringVal) { this(name, (Object) stringVal); } /** * Attribute constructor. * @param name the attribute name * @param timeVal the attribute value */ public Attribute(String name, Time timeVal) { this(name, (Object) timeVal); } /** * Attribute constructor. * @param name the attribute name * @param attrSet the attribute value */ public Attribute(String name, AttributeSet attrSet) { this(name, (Object) attrSet); } /** * Attribute constructor. * @param name the attribute name * @param values the attribute values */ public Attribute(String name, Collection<?> values) { this(name, (Object) values); } /** * Attribute constructor. * @param name the attribute name * @param value the attribute value */ public Attribute (String name, Object value) { this.name = name; if (value instanceof Collection) { // Check that all items in the collection are the same type. // and of a type supported by this class. Collection<?> collection = (Collection<?>) value; if (collection.isEmpty()) { this.value = Collections.EMPTY_LIST; } else { List<Object> valueList = new ArrayList<Object>(); Class<?> firstValueType = null; // Leave out any null values or values of types not supported by this class. // Also, only add values which are of the same type as the first value added. for (Object val : collection) { if (val != null) { if (firstValueType == null && isSupportedAttributeType(val.getClass())) { firstValueType = val.getClass(); valueList.add(val); } else if (val.getClass().equals(firstValueType)){ valueList.add(val); } } } this.value = valueList; } } else { this.value = value; } } /** * Returns whether the specified value type can be stored as an attribute value. * <p> * Subclasses can override this method to change the supported attribute type set. * @param valueClass the class of a value * @return boolean <code>true</code> if the value type can be stored as an attribute value */ protected boolean isSupportedAttributeType(Class<?> valueClass) { // The supported attribute type is primarily determined by the serializer XMLSerializationManager serializer = XMLSerializationManager.getDefaultInstance(); return serializer.getSupportedValueClasses().contains(valueClass); } /** * @see java.lang.Object#toString() * @return a string to assist in debugging */ public String toString () { return getName () + "=" + valueToString(value); //$NON-NLS-1$ } /** * A helper function for converting values to strings. * The string is intended for debugging purposes only. * @param value an attribute value (or list of values) * @return a human readable string representing the value */ private static String valueToString (Object value) { if (value == null) { return "<null>"; //$NON-NLS-1$ } else if (value instanceof Color) { Color colour = (Color) value; int red = colour.getRed(); int green = colour.getGreen(); int blue = colour.getBlue(); return "R:" + red + " G:" + green + " B:" + blue; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } else if (value instanceof List) { List<?> list = (List<?>) value; StringBuilder sb = new StringBuilder("["); //$NON-NLS-1$ for (int valueN = 0, nValues = list.size(); valueN < nValues; ++valueN) { sb.append (valueToString (list.get (valueN))); if (valueN < nValues - 1) sb.append(", "); //$NON-NLS-1$ } sb.append(']'); return sb.toString(); } else if (value instanceof Boolean) { return (((Boolean) value).booleanValue()) ? "True" : "False"; //$NON-NLS-1$ //$NON-NLS-2$ } else { return value.toString(); } } /** * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(Object obj) { if (obj == null || getClass() != obj.getClass()) return false; Attribute other = (Attribute) obj; if ((value == null) != (other.value == null)) return false; if (value != null && !value.equals(other.value)) return false; if (!equalNames(getName(), other.getName())) return false; return true; } /** Cache the hash code for the attribute, as it is relatively expensive to calculate. */ private int hash = 0; /** * @see java.lang.Object#hashCode() */ public int hashCode() { // Note: ensure any modifications are thread safe. See java.lang.String for an example. int h = hash; if (h == 0) { h = 37 + CaseInsensitiveMap.caseInsensitiveHashCode(getName()); if (value != null) { h = h * 17 + value.hashCode(); } hash = h; } return h; } /** * Returns the attribute name. * @return the attribute name */ public String getName () { return name; } /** * Returns a single value for the attribute. * If there are multiple values, then the last one in the list (if any) will be returned. * @return a single value of the attribute */ public Object getSingleValue () { if (value instanceof List) { List<?> l = (List<?>) value; if (l.isEmpty ()) return null; return l.get (l.size () - 1); } else { return value; } } /** * Returns the list of values for the attribute. * If there is only one value, then it will be put into a list. * @return the list of the attribute values */ public List<?> getValues () { if (value instanceof List) { return Collections.unmodifiableList((List<?>)value); } else if (value instanceof Collection) { return new ArrayList<Object> ((Collection<?>)value); } else { return Collections.singletonList (value); } } /** * Load the attribute from the specified XML element. * @param attrElement the root element for the attribute XML data * @return the new attribute created by loading it from the XML element */ public static Attribute Load (Element attrElement) { XMLSerializationManager serializer = XMLSerializationManager.getDefaultInstance(); return (Attribute) serializer.loadFromElement(attrElement, XMLSerializationManager.getAttributeSerializer()); } /** * Store the attribute in the specified XML element. * @param parentElem the parent XML element under which the attribute data will be stored */ public void store (Element parentElem) { XMLSerializationManager serializer = XMLSerializationManager.getDefaultInstance(); serializer.storeToElement(parentElem, this, XMLSerializationManager.getAttributeSerializer()); } /** * Returns the {@link Attribute.Type} enum */ public Type getAttributeType() { Object val = getSingleValue(); if (val instanceof Color) return Type.COLOR; else if (val instanceof Boolean) return Type.BOOLEAN; else if (val instanceof Integer) return Type.INTEGER; else if (val instanceof Double) return Type.DOUBLE; else if (val instanceof String) return Type.STRING; else if (val instanceof AttributeSet) return Type.ATTRIBUTE_SET; else if (val instanceof Time) return Type.TIME; return Type.UNKNOWN; } /** * This is a helper for CAL - as CAL still needs to deal with ordinals * @return * 0 - Unknown * 1 - Colour * 2 - Boolean * 3 - Integer * 4 - Double * 5 - String * 6 - AttributeSet * 7 - Time * @deprecated */ public int getAttributeTypeOrdinal() { return getAttributeType().ordinal(); } /** * Creates a new attribute with the specified name and value list. * @param name the attribute name * @param values the value list for the attribute * @return a new attribute */ public static Attribute makeAttribute(String name, List<?> values) { return new Attribute(name, values); } /** * Returns a <code>Comparator</code> that compares two attributes by their names. * @return Comparator */ public static Comparator<Attribute> getNameComparator() { return attributeComparator; } /** * A <code>Comparator</code> that compares two attributes by their names. */ private static final class AttributeComparator implements Comparator<Attribute> { public int compare(Attribute attr1, Attribute attr2) { return attr1.getName().compareToIgnoreCase(attr2.getName()); } } }