| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.tamaya.spi; |
| |
| import java.util.*; |
| import java.util.function.Predicate; |
| import java.util.function.Supplier; |
| import java.util.logging.Logger; |
| import java.util.stream.Collectors; |
| |
| /** |
| * Class modelling the result of a request for a property value. A property value is basically identified by its key. |
| * There might be reasons, where one want to further analyze, which PropertySources provided a value and which not, so |
| * it is possible to createObject a PropertyValue with a null value. |
| * |
| * A PropertyValue represents an abstract data point in a configuration structure read. PropertyValues actually |
| * represent a tree, with additional functionality for representing data lists/arrays using indexed children |
| * names. This allows to support a full mapping of common document based configuration formats, such as JSON, YAML, |
| * XML and more. |
| */ |
| public final class ObjectValue extends PropertyValue{ |
| |
| private static final long serialVersionUID = 1L; |
| |
| private static final Logger LOG = Logger.getLogger(ObjectValue.class.getName()); |
| |
| /** List of child properties. */ |
| private Map<String, PropertyValue> fields = new HashMap<>(); |
| |
| |
| /** |
| * Creates a new instance |
| * @param key the key, not {@code null}. |
| * @param parent the parent. |
| */ |
| ObjectValue(PropertyValue parent, String key){ |
| super(parent, key); |
| } |
| |
| /** |
| * Get the item's current value type. |
| * @return the value type, never null. |
| */ |
| public ValueType getValueType() { |
| return ValueType.MAP; |
| } |
| |
| /** |
| * Get the fields of this instance. |
| * @return the current fields, never null. |
| */ |
| public Collection<PropertyValue> getValues(){ |
| return Collections.unmodifiableCollection(this.fields.values()); |
| } |
| |
| /** |
| * Get the fields of this instance, filtered with the given predicate. |
| * @param predicate the predicate, not null. |
| * @return the current fields, never null. |
| */ |
| public Collection<PropertyValue> getValues(Predicate<PropertyValue> predicate){ |
| return Collections.unmodifiableCollection(this.fields.values().stream() |
| .filter(predicate).collect(Collectors.toList())); |
| } |
| |
| /** |
| * Get a single child getValue by name. |
| * @param name the child's name, not null. |
| * @return the child found, or null. |
| * @throws IllegalArgumentException if multiple getValues with the given name are existing (ambigous). |
| */ |
| public PropertyValue getValue(String name){ |
| return this.fields.get(name); |
| } |
| |
| /** |
| * Get a single child getValue with the given name, creates it if not existing. |
| * @param name the child's name, not null. |
| * @param valueSupplier the supplier to create a new instance, if no value is present, not null. |
| * @param <T> the target type. |
| * @return the child found or created, never null. |
| * @throws IllegalArgumentException if multiple getValues with the given name are existing (ambigous). |
| * @throws IllegalStateException if the instance is immutable. |
| * @see #isImmutable() |
| */ |
| public <T extends PropertyValue> T getOrSetValue(String name, Supplier<T> valueSupplier){ |
| T field = (T)this.fields.get(name); |
| if(field==null){ |
| checkImmutable(); |
| field = valueSupplier.get(); |
| this.fields.put(name, field); |
| incrementVersion(); |
| } |
| return field; |
| } |
| |
| /** |
| * Get the value's number of elements. |
| * @return the getNumChilds of this multi value. |
| */ |
| @Override |
| public int getSize() { |
| return this.fields.size(); |
| } |
| |
| @Override |
| public PropertyValue toPropertyValue(){ |
| PropertyValue value = new PropertyValue(getParent(), getKey(), getValue()); |
| value.setMeta(getMeta()); |
| value.setVersion(getVersion()); |
| return value; |
| } |
| |
| @Override |
| public ObjectValue toObjectValue(){ |
| return this; |
| } |
| |
| @Override |
| public ListValue toListValue(){ |
| ListValue array = new ListValue(getParent(), getKey()); |
| array.setValue(getValue()); |
| array.setMeta(getMeta()); |
| array.setVersion(getVersion()); |
| int index = 0; |
| for(PropertyValue val:fields.values()){ |
| array.add(val.deepClone()); |
| } |
| return array; |
| } |
| |
| |
| @Override |
| public Iterator<PropertyValue> iterator() { |
| return Collections.unmodifiableCollection(this.fields.values()).iterator(); |
| } |
| |
| /** |
| * Adds text values to the createObject. |
| * @param values the child's values, not null. |
| * @return the created values, not null. |
| * @throws IllegalStateException if the instance is immutable. |
| * @see #isImmutable() |
| */ |
| public Collection<PropertyValue> setValues(Map<String, String> values) { |
| checkImmutable(); |
| List<PropertyValue> result = new ArrayList<>(); |
| for(Map.Entry<String, String> en:values.entrySet()) { |
| PropertyValue val = setValue(en.getKey(), en.getValue()); |
| result.add(val); |
| } |
| return result; |
| } |
| |
| /** |
| * Adds another existing node, hereby setting the corresponding parent node. |
| * @param value the value, not null |
| * @param <T> the value type. |
| * @return the value added, not null. |
| * @throws IllegalStateException if the instance is immutable. |
| * @see #isImmutable() |
| */ |
| public <T extends PropertyValue> T set(T value) { |
| checkImmutable(); |
| value.setParent(this); |
| this.fields.put(value.getKey(), value); |
| return value; |
| } |
| |
| /** |
| * Sets the given key, value pair. |
| * @param k the key, not null. |
| * @param v the value, not null. |
| * @return the value added, not null. |
| */ |
| public PropertyValue setValue(String k, String v) { |
| return set(PropertyValue.createValue(k,v)); |
| } |
| |
| /** |
| * Sets the given list value. |
| * @param key the key, not null. |
| * @return the value added, not null. |
| */ |
| public ListValue setList(String key) { |
| return set(PropertyValue.createList(key)); |
| } |
| |
| /** |
| * Sets the given object vaƶue. |
| * @param key the key, not null. |
| * @return the value added, not null. |
| */ |
| public ObjectValue setObject(String key) { |
| return set(PropertyValue.createObject(key)); |
| } |
| |
| /** |
| * Adds a new child getValue, where the getValue is given in '.'-separated property notation, |
| * e.g. {@code a.b.c}. |
| * @param key the property key, e.g. {@code a.b.c} |
| * @param value the property value |
| * @return the new leaf-getValue created. |
| * @throws IllegalStateException if the instance is immutable. |
| * @see #isImmutable() |
| */ |
| public PropertyValue setValueWithCompositeKey(String key, String value) { |
| checkImmutable(); |
| ObjectValue node = this; |
| StringTokenizer tokenizer = new StringTokenizer(key, "\\.", false); |
| while(tokenizer.hasMoreTokens()){ |
| String token = tokenizer.nextToken().trim(); |
| if(tokenizer.hasMoreTokens()) { |
| node = node.getOrSetValue(token, () -> PropertyValue.createObject(token)); |
| }else{ |
| return node.set(PropertyValue.createValue(token, value)); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Adds multiple values, where the keys are given in '.'-separated property notation, |
| * e.g. {@code a.b.c}. |
| * @param values the values, not null. |
| * @return the value instances created. |
| * @throws IllegalStateException if the instance is immutable. |
| * @see #isImmutable() |
| */ |
| public Collection<PropertyValue> setValueWithCompositeKey(Map<String,String> values) { |
| checkImmutable(); |
| List<PropertyValue> result = new ArrayList<>(); |
| for(Map.Entry<String,String> en:values.entrySet()){ |
| result.add(setValueWithCompositeKey(en.getKey(), en.getValue())); |
| } |
| return result; |
| } |
| |
| /** |
| * Convert the getValue tree to a property map. |
| * @return the corresponding property map, not null. |
| */ |
| @Override |
| public Map<String,String> toMap(){ |
| Map<String, String> map = new TreeMap<>(); |
| for (PropertyValue n : fields.values()) { |
| switch(n.getValueType()){ |
| case VALUE: |
| map.put(n.getQualifiedKey(), n.getValue()); |
| break; |
| default: |
| for(PropertyValue val:n) { |
| map.putAll(val.toMap()); |
| } |
| } |
| } |
| return map; |
| } |
| |
| |
| /** |
| * Clones this instance and all it's children, marking as mutable value. |
| * @return the new value clone. |
| */ |
| @Override |
| public ObjectValue mutable(){ |
| return (ObjectValue)super.mutable(); |
| } |
| |
| @Override |
| protected ObjectValue deepClone(){ |
| ObjectValue newProp = new ObjectValue(getParent(), getKey()); |
| newProp.setMeta(getMeta()); |
| fields.values().forEach(c -> newProp.set(c.deepClone().mutable())); |
| newProp.setVersion(getVersion()); |
| newProp.setValue(getValue()); |
| return newProp; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) { |
| return true; |
| } |
| if (!(o instanceof ObjectValue)) { |
| return false; |
| } |
| ObjectValue dataNode = (ObjectValue) o; |
| return Objects.equals(getKey(), dataNode.getKey()) && |
| Objects.equals(fields, dataNode.fields) && |
| Objects.equals(getMeta(), dataNode.getMeta()); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(getParent(), getKey(), fields, getMeta()); |
| } |
| |
| |
| @Override |
| public String toString() { |
| return "PropertyValue[MAP]{" + |
| '\'' +getQualifiedKey() + '\'' + |
| (getValue()!=null?", value='" + getValue() + '\'':"") + |
| ", size='" + getSize() + '\'' + |
| (getMeta().isEmpty()?"":", metaData=" + getMeta()) + |
| '}'; |
| } |
| |
| } |