package org.infinispan.commons.configuration.attributes; import java.util.Collection; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.infinispan.commons.logging.Log; import org.infinispan.commons.logging.LogFactory; /** * AttributeSet is a container for {@link Attribute}s. It is constructed by passing in a list of {@link AttributeDefinition}s. * AttributeSets are initially unprotected, which means that the contained attributes can be modified. If the {@link #protect()} method is invoked * then only attributes which are not {@link AttributeDefinition#isImmutable()} can be modified from then on. * * @author Tristan Tarrant * @since 7.2 */ public class AttributeSet implements AttributeListener<Object> { private static final Log log = LogFactory.getLog(AttributeSet.class); private final Class<?> klass; private final String name; private final Map<String, Attribute<?>> attributes; private boolean protect; public AttributeSet(Class<?> klass, AttributeDefinition<?>... attributeDefinitions) { this(klass, klass.getSimpleName(), null, attributeDefinitions); } public AttributeSet(String name, AttributeDefinition<?>... attributeDefinitions) { this(name, null, attributeDefinitions); } public AttributeSet(Class<?> klass, AttributeSet attributeSet, AttributeDefinition<?>... attributeDefinitions) { this(klass, klass.getSimpleName(), attributeSet, attributeDefinitions); } public AttributeSet(String name, AttributeSet attributeSet, AttributeDefinition<?>[] attributeDefinitions) { this(null, name, attributeSet, attributeDefinitions); } private AttributeSet(Class<?> klass, String name, AttributeSet attributeSet, AttributeDefinition<?>[] attributeDefinitions) { this.klass = klass; this.name = name; if (attributeSet != null) { this.attributes = new LinkedHashMap<>(attributeDefinitions.length + attributeSet.attributes.size()); for (Attribute<?> attribute : attributeSet.attributes.values()) { this.attributes.put(attribute.name(), attribute.getAttributeDefinition().toAttribute()); } } else { this.attributes = new LinkedHashMap<>(attributeDefinitions.length); } for (AttributeDefinition<?> def : attributeDefinitions) { if (attributes.containsKey(def.name())) { throw log.attributeSetDuplicateAttribute(def.name(), name); } Attribute<Object> attribute = (Attribute<Object>) def.toAttribute(); if (!attribute.isImmutable()) attribute.addListener(this); this.attributes.put(def.name(), attribute); } } public Class<?> getKlass() { return klass; } public String getName() { return name; } /** * Returns whether this attribute set contains the specified named attribute * @param name the name of the attribute */ public boolean contains(String name) { return attributes.containsKey(name); } /** * Returns whether this set contains the specified attribute definition * * @param def the {@link AttributeDefinition} */ public <T> boolean contains(AttributeDefinition<T> def) { return contains(def.name()); } /** * Returns the named attribute * @param name the name of the attribute to return * @return the attribute */ @SuppressWarnings("unchecked") public <T> Attribute<T> attribute(String name) { return (Attribute<T>) this.attributes.get(name); } /** * Returns the attribute identified by the supplied {@link AttributeDefinition} * @param def the attribute definition * @return the attribute */ public <T> Attribute<T> attribute(AttributeDefinition<T> def) { Attribute<T> attribute = attribute(def.name()); if (attribute != null) return attribute; else throw log.noSuchAttribute(def.name(), name); } /** * Copies all attribute from another AttributeSet * * @param other the source AttributeSet */ public void read(AttributeSet other) { for (Iterator<Attribute<?>> iterator = attributes.values().iterator(); iterator.hasNext();) { Attribute<Object> attribute = (Attribute<Object>) iterator.next(); Attribute<Object> a = other.attribute(attribute.name()); if (a.isModified()) { attribute.read(a); } } } /** * Returns a new ValueSet where immutable {@link Attribute}s are write-protected * * @return */ public AttributeSet protect() { AttributeDefinition<?> attrDefs[] = new AttributeDefinition[attributes.size()]; int i = 0; for (Attribute<?> attribute : attributes.values()) { attrDefs[i++] = attribute.getAttributeDefinition(); } AttributeSet protectedSet = new AttributeSet(klass, name, null, attrDefs); for (Attribute<?> attribute : protectedSet.attributes.values()) { Attribute<?> localAttr = this.attributes.get(attribute.name()); attribute.read((Attribute)localAttr); attribute.protect(); } protectedSet.protect = true; return protectedSet; } /** * Returns whether any attributes in this set have been modified */ public boolean isModified() { for(Attribute<?> attribute : attributes.values()) { if (attribute.isModified()) return true; } return false; } /** * Returns whether this attribute set is protected */ public boolean isProtected() { return protect; } /** * Writer a single attribute to the specified {@link XMLStreamWriter} using the attribute's xmlName * @param writer the writer * @param def the Attribute definition * @throws XMLStreamException */ public void write(XMLStreamWriter writer, AttributeDefinition<?> def) throws XMLStreamException { write(writer, def, def.xmlName()); } /** * Writer a single attribute to the specified {@link XMLStreamWriter} using the supplied name * @param writer the writer * @param def the Attribute definition * @param name the XML tag name for the attribute * @throws XMLStreamException */ public void write(XMLStreamWriter writer, AttributeDefinition<?> def, Enum<?> name) throws XMLStreamException { write(writer, def, name.toString()); } /** * Writer a single attribute to the specified {@link XMLStreamWriter} using the supplied name * @param writer the writer * @param def the Attribute definition * @param name the XML tag name for the attribute * @throws XMLStreamException */ public void write(XMLStreamWriter writer, AttributeDefinition<?> def, String name) throws XMLStreamException { Attribute<?> attribute = attribute(def); attribute.write(writer, name); } /** * Writes this attributeset to the specified XMLStreamWriter as an element * @param writer */ public void write(XMLStreamWriter writer, String xmlElementName) throws XMLStreamException { if (isModified()) { writer.writeStartElement(xmlElementName); write(writer); writer.writeEndElement(); } } /** * Writes the specified attributes in this attributeset to the specified XMLStreamWriter as an element * @param writer */ public void write(XMLStreamWriter writer, String xmlElementName, AttributeDefinition<?>... defs) throws XMLStreamException { boolean skip = true; for (AttributeDefinition def : defs) { skip = skip && !attribute(def).isModified(); } if (!skip) { writer.writeStartElement(xmlElementName); for (AttributeDefinition def : defs) { Attribute attr = attribute(def); attr.write(writer, attr.getAttributeDefinition().xmlName()); } writer.writeEndElement(); } } /** * Writes the attributes of this attributeset as part of the current element * @param writer */ public void write(XMLStreamWriter writer) throws XMLStreamException { for (Attribute<?> attr : attributes.values()) { if (attr.isPersistent()) attr.write(writer, attr.getAttributeDefinition().xmlName()); } } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((attributes == null) ? 0 : attributes.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; AttributeSet other = (AttributeSet) obj; if (attributes == null) { if (other.attributes != null) return false; } else if (!attributes.equals(other.attributes)) return false; return true; } @Override public String toString() { return toString(null); } public String toString(String name) { StringBuilder sb = new StringBuilder(); if (name != null) { sb.append(name); sb.append(" = "); } sb.append("["); boolean comma = false; for (Attribute<?> value : attributes.values()) { if (comma) { sb.append(", "); } else { comma = true; } sb.append(value.toString()); } sb.append("]"); return sb.toString(); } public AttributeSet checkProtection() { if (!protect) { throw log.unprotectedAttributeSet(name); } return this; } public void reset() { if (protect) { throw log.protectedAttributeSet(name); } for (Attribute<?> attribute : attributes.values()) { attribute.reset(); } } @Override public void attributeChanged(Attribute<Object> attribute, Object oldValue) { // TODO } public Collection<Attribute<?>> attributes() { return attributes.values(); } }