/*
* Carrot2 project.
*
* Copyright (C) 2002-2016, Dawid Weiss, Stanisław Osiński.
* All rights reserved.
*
* Refer to the full license file "carrot2.LICENSE"
* in the root folder of the repository checkout or at:
* http://www.carrot2.org/carrot2.LICENSE
*/
package org.carrot2.util.attribute;
import java.util.Map;
import java.util.TreeMap;
import org.carrot2.util.simplexml.*;
import org.simpleframework.xml.*;
import org.simpleframework.xml.core.Commit;
import org.simpleframework.xml.core.Persist;
import org.carrot2.shaded.guava.common.collect.Maps;
/**
* Maintains a named set of attribute values. Allows one {@link AttributeValueSet} (A) to
* be "based" on another {@link AttributeValueSet} (B), whereby the main
* {@link AttributeValueSet} (A) inherits all values from the base
* {@link AttributeValueSet} (B) and can override some of them. Any depth of the base
* attribute sets hierarchy is possible.
*/
@Root(name = "value-set")
public class AttributeValueSet
{
/**
* Human-readable value of this attribute value set. <b>Only for read-only use.</b>
*/
@Element
public String label;
/**
* Human-readable description of this attribute value set. <b>Only for read-only use.</b>
*/
@Element(required = false)
public String description;
/**
* Holds values of attributes overridden by this attribute set.
*/
Map<String, Object> overridenAttributeValues;
/**
* The base attribute values set, source of all not overridden values.
*/
AttributeValueSet baseAttributeValueSet;
/**
* Identifier of the attribute value set this set is based on. Used only for
* serialization/ deserialization purposes.
*/
@org.simpleframework.xml.Attribute(name = "based-on", required = false)
String baseAttributeValueSetId;
/**
* This collection is used only for serialization/ deserialization purposes, see
* {@link #convertAttributeValuesToStrings()} and
* {@link #convertAttributeValuesFromStrings()}.
*/
@ElementMap(entry = "attribute", key = "key", attribute = true, inline = true, required = false)
private TreeMap<String, SimpleXmlWrapperValue> overridenAttributeValuesForSerialization;
AttributeValueSet()
{
}
/**
* Creates a new empty {@link AttributeValueSet} with a <code>null</code> description
* and a <code>null</code> base attribute value set.
*
* @param label human-readable label for this attribute value set
*/
public AttributeValueSet(String label)
{
this(label, null);
}
/**
* Creates a new empty {@link AttributeValueSet} with a <code>null</code> description.
*
* @param label human-readable label for this attribute value set
* @param base the attribute value set this set should be based on.
*/
public AttributeValueSet(String label, AttributeValueSet base)
{
this(label, null, base);
}
/**
* Creates a new empty {@link AttributeValueSet}.
*
* @param label human-readable label for this attribute value set
* @param description human-readable description for this attribute value set, can be
* <code>null</code>
* @param base the attribute value set this set should be based on, can be
* <code>null</code>.
*/
public AttributeValueSet(String label, String description, AttributeValueSet base)
{
this.label = label;
this.description = description;
this.baseAttributeValueSet = base;
this.overridenAttributeValues = Maps.newHashMap();
}
/**
* Returns value of the attribute with the provided <code>key</code>. Attribute values
* are resolved in the following order:
* <ul>
* <li>If this set contains a value for the attribute with given <code>key</code> set
* by {{@link #setAttributeValue(String, Object)} or {@link #setAttributeValues(Map)},
* the value is returned.</li>
* <li>Otherwise, if the base attribute value set is not <code>null</code>, attribute
* value is retrieved from the base set by calling the same method on it. If any of
* the base attribute sets in the hierarchy contains a value for the provided key,
* that value is returned.</li>
* <li>Otherwise, <code>null</code> is returned.</li>
* </ul>
*
* @param key key of the attribute for which value is to be returned
* @return value of the attribute or <code>null</code>.
*/
public Object getAttributeValue(String key)
{
if (overridenAttributeValues.containsKey(key))
{
return overridenAttributeValues.get(key);
}
else if (baseAttributeValueSet != null)
{
return baseAttributeValueSet.getAttributeValue(key);
}
else
{
return null;
}
}
/**
* Returns attribute values defined by this {@link AttributeValueSet} and all other
* {@link AttributeValueSet}s that this set is based on. The returned map is
* independent of this {@link AttributeValueSet}, so any modifications to that map
* will not be reflected in this {@link AttributeValueSet}.
*/
public Map<String, Object> getAttributeValues()
{
final Map<String, Object> result = Maps.newHashMap();
if (baseAttributeValueSet != null)
{
result.putAll(baseAttributeValueSet.getAttributeValues());
}
result.putAll(overridenAttributeValues);
return result;
}
/**
* Sets a <code>value</code> corresponding to the provided <code>key</code> in this
* attribute value set. If the set previously contained some value under the provided
* <code>key</code>, that value is returned. Values set using this method override
* values found in the base attribute sets of this set.
*
* @param key attribute key
* @param value attribute value
* @return previous value of the attribute or <code>null</code>
*/
public Object setAttributeValue(String key, Object value)
{
return overridenAttributeValues.put(key, value);
}
/**
* Copies all <code>values</code> to this attribute value set. If this attribute value
* set already contains mappings for some of the provided key, the mappings will be
* overwritten. Values set using this method override values found in the base
* attribute sets of this set.
*
* @param values values to be set on this attribute value set.
*/
public void setAttributeValues(Map<String, Object> values)
{
overridenAttributeValues.putAll(values);
}
/**
* Returns attribute values from the provided {@link AttributeValueSet} or
* <code>null</code> if the provided {@link AttributeValueSet} is <code>null</code>.
*/
public static Map<String, Object> getAttributeValues(
AttributeValueSet attributeValueSet)
{
return attributeValueSet != null ? attributeValueSet.getAttributeValues() : null;
}
/**
* Converts attribute values to {@link ISimpleXmlWrapper}s for serialization.
*/
@Persist
private void convertAttributeValuesToStrings()
{
overridenAttributeValuesForSerialization = new TreeMap<String, SimpleXmlWrapperValue>(
String.CASE_INSENSITIVE_ORDER);
overridenAttributeValuesForSerialization.putAll(SimpleXmlWrappers
.wrap(overridenAttributeValues));
}
/**
* Converts attribute values to {@link ISimpleXmlWrapper}s after deserialization.
*/
@Commit
private void convertAttributeValuesFromStrings() throws Exception
{
if (overridenAttributeValuesForSerialization == null)
{
overridenAttributeValues = Maps.newHashMap();
}
else
{
overridenAttributeValues = SimpleXmlWrappers
.unwrap(overridenAttributeValuesForSerialization);
}
}
/*
*
*/
@Override
public String toString()
{
final StringBuilder b = new StringBuilder();
b.append("AttributeValueSet [");
boolean first = true;
for (Map.Entry<String, Object> e : getAttributeValues().entrySet())
{
if (!first) b.append(", ");
b.append(e.getKey() != null ? e.getKey() : "null");
b.append('=');
b.append(e.getValue() != null ? e.getValue().toString() : "null");
first = false;
}
b.append("]");
return b.toString();
}
}