package de.uni_passau.fim.infosun.prophet.util.qTree; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * A simple key/value pair that can have sub-attributes. */ public class Attribute implements Cloneable { private String key; private String value; private Map<String, Attribute> subAttributes; /** * Constructs a new <code>Attribute</code> representing the given key/value pair. * * @param key the key for the <code>Attribute</code> * @param value the value for the <code>Attribute</code> * * @throws NullPointerException if key or value is <code>null</code> */ public Attribute(String key, String value) { Objects.requireNonNull(key, "key must not be null!"); Objects.requireNonNull(value, "value must not be null!"); this.key = key; this.value = value; this.subAttributes = new HashMap<>(); } /** * Constructs a new <code>Attribute</code> representing the given key with the empty <code>String</code> as * its value. * * @param key * the key for the <code>Attribute</code> * * @throws NullPointerException * if key is <code>null</code> */ public Attribute(String key) { this(key, ""); } /** * Gets the key. * * @return the key */ public String getKey() { return key; } /** * Gets the value. * * @return the value */ public String getValue() { return value; } /** * Sets the value. * * @param value the new value * * @throws NullPointerException if <code>value</code> is <code>null</code> */ public void setValue(String value) { Objects.requireNonNull(value, "value must not be null!"); this.value = value; } /** * Gets the sub-attribute with the given key. If it does not exist it will be created with the empty * <code>String</code> as its content. * * @param key the key of the sub-attribute * @return the <code>Attribute</code> */ public Attribute getSubAttribute(String key) { Objects.requireNonNull(key, "key must not be null!"); Attribute subAttribute = subAttributes.get(key); if (subAttribute == null) { subAttribute = new Attribute(key); addSubAttribute(subAttribute); } return subAttribute; } /** * Tests whether an <code>Attribute</code> with the given key exists for this <code>QTreeNode</code>. * * @param key * the key of the <code>Attribute</code> * * @return true iff an <code>Attribute</code> with the given key exists */ public boolean containsSubAttribute(String key) { Objects.requireNonNull(key, "key must not be null!"); return subAttributes.containsKey(key); } /** * Gets the sub-attributes of this <code>Attribute</code>. * * @return the sub-attributes */ public Collection<Attribute> getSubAttributes() { return subAttributes.values(); } /** * Adds the given attribute to this attributes sub-attributes. If an attribute with the key of the given * attribute has previously been added to this attribute it will be overwritten. * * @param attribute the <code>Attribute</code> to be added */ public void addSubAttribute(Attribute attribute) { Objects.requireNonNull(attribute, "attribute must not be null!"); subAttributes.put(attribute.getKey(), attribute); } @Override protected Object clone() throws CloneNotSupportedException { Attribute clone = (Attribute) super.clone(); Map<String, Attribute> cSubAttributes = new HashMap<>(); // clone the sub attributes for (Map.Entry<String, Attribute> entry : subAttributes.entrySet()) { cSubAttributes.put(entry.getKey(), (Attribute) entry.getValue().clone()); } clone.subAttributes = cSubAttributes; return clone; } /** * This method will be called (by the JVM) after this object was deserialised. It is implemented because * the XML representation for this object uses implicit collections/maps whose fields will be <code>null</code> * if they were empty at the time of serialisation. These fields will be initialised with empty collections. * * @return always returns <code>this</code> */ private Object readResolve() { if (subAttributes == null) { subAttributes = new HashMap<>(); } return this; } }