blob: ff7d69d0320c366be5f9892ae3e7dcc3183362f0 [file] [log] [blame]
/*
* 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.*;
/**
* Class modelling the result of a request for a property createValue. A property createValue is basically identified by its key.
* There might be reasons, where one want to further analyze, which PropertySources provided a createValue and which not, so
* it is possible to createObject a PropertyValue with a null createValue.
*
* 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 ListValue extends PropertyValue{
private static final long serialVersionUID = 1L;
/** List of child properties. */
private List<PropertyValue> list = new ArrayList<>();
/**
* Creates a new instance
* @param key the key, not {@code null}.
*/
ListValue(String key){
super(key);
}
/**
* Get the item's current createValue type.
* @return the createValue type, never null.
*/
public ValueType getValueType() {
return ValueType.ARRAY;
}
/**
* Get the index of the given member value.
* @param member the member, not null.
* @return the index, or -1.
*/
public int getIndex(PropertyValue member) {
return this.list.indexOf(member);
}
/**
* Get the createValue's number of elements.
* @return the getNumChilds of this multi createValue.
*/
@Override
public int getSize() {
return this.list.size();
}
@Override
public Iterator<PropertyValue> iterator() {
return Collections.unmodifiableList(this.list).iterator();
}
/**
* Adds a createValue to the array.
* @param value the createValue, not null
* @return this instance, for chaining.
* @throws IllegalStateException if the instance is immutable.
* @see #isImmutable()
*/
public ListValue addPropertyValue(PropertyValue value) {
checkImmutable();
if(!this.list.stream().filter(p -> p==value).findAny().isPresent()){
value.setKey(generateListKey());
value.setParent(this);
this.list.add(value);
}
return this;
}
/**
* Adds an anonymous text value to the array.
* @param value the child's value, not null.
* @return the created value, not null.
* @throws IllegalStateException if the instance is immutable.
* @see #isImmutable()
*/
public ListValue addValue(String value) {
return addPropertyValue(new PropertyValue(generateListKey(), value));
}
/**
* Adds text values to the array.
* @param values the child's values, not null.
* @return the created values, not null.
* @throws IllegalStateException if the instance is immutable.
* @see #isImmutable()
*/
public ListValue addValues(String... values) {
List<PropertyValue> result = new ArrayList<>();
for(String val:values) {
result.add(addPropertyValue(new PropertyValue(generateListKey(), val)));
}
return this;
}
/**
* Adds an anonymous child createObject to the array.
* @return the created createObject, not null.
* @throws IllegalStateException if the instance is immutable.
* @see #isImmutable()
*/
public ObjectValue addObject() {
ObjectValue ov = new ObjectValue(generateListKey());
addPropertyValue(ov);
return ov;
}
/**
* Adds an anonymous array.
* @return this instance, for chaining.
* @throws IllegalStateException if the instance is immutable.
* @see #isImmutable()
*/
public ListValue addList() {
ListValue lv = new ListValue(generateListKey());
addPropertyValue(lv);
return lv;
}
/**
* Get a String value with the given key, if possible. If a node is present, but no value is setPropertyValue, then the
* node's {@code toString()} method is used as a result.
* @param index the target index.
* @return the value found, or null.
*/
public String getValue(int index) {
PropertyValue value = getPropertyValue(index);
if(value!=null){
if(value.getValueType()==ValueType.VALUE) {
return value.getValue();
}
return value.toString();
}
return null;
}
/**
* Get the n-th element of the children.
* @param n the index.
* @return the element found
* @throws NoSuchElementException if no such element exists.
*/
public PropertyValue getPropertyValue(int n) {
return this.list.get(n);
}
/**
* Get the array elements, filtered by the given predicate.
* @param name the name of the objects, null selects all.
* @return this values matching, never null.
*/
public List<ObjectValue> getObjects(String name) {
List<ObjectValue> result = new ArrayList<>();
this.list.forEach(el -> {
if (el instanceof ObjectValue && name.equals(el.getKey())) {
result.add((ObjectValue) el);
}
});
return result;
}
/**
* Get the array elements, filtered by the given name.
* @param name the name of the objects, null selects all.
* @return this values matching, never null.
*/
public List<ListValue> getLists(String name) {
List<ListValue> result = new ArrayList<>();
this.list.forEach(el -> {
if (el instanceof ListValue && name.equals(el.getKey())) {
result.add((ListValue) el);
}
});
return result;
}
/**
* Get all array elements of type {@link ListValue}.
* @return this values matching, never null.
*/
public List<ListValue> getLists() {
List<ListValue> result = new ArrayList<>();
this.list.forEach(el -> {
if (el instanceof ListValue) {
result.add((ListValue) el);
}
});
return result;
}
/**
* Get the text elements, filtered by the given name.
* @param name the name of the objects, null selects all.
* @return this values matching, never null.
*/
public List<PropertyValue> getPropertyValues(String name) {
List<PropertyValue> result = new ArrayList<>();
if (name == null) {
result.addAll(this.list);
} else {
this.list.forEach(el -> {
if (name.equals(el.getKey())) {
result.add(el);
}
});
}
return result;
}
@Override
public ObjectValue toObjectValue(){
ObjectValue object = new ObjectValue(getKey());
object.setParent(getParent());
object.setMeta(getMeta());
object.setVersion(getVersion());
int index = 0;
for(PropertyValue val: list){
object.setPropertyValue(val.deepClone().setKey(val.getKey()));
index++;
}
return object;
}
@Override
public ListValue toListValue(){
return this;
}
/**
* Get the node's createValue.
* @return the createValue, or null.
*/
public String getValue() {
return "List: " + this.list;
}
@Override
public PropertyValue setValue(String value) {
throw new UnsupportedOperationException("Cannot set value on list value.");
}
/**
* Clones this instance and all it's children, marking as mutable value.
* @return the new value clone.
*/
@Override
public ListValue mutable(){
return (ListValue)super.mutable();
}
@Override
protected ListValue deepClone(){
ListValue newProp = new ListValue(getKey());
newProp.setParent(getParent());
newProp.setMeta(getMeta());
list.forEach(c -> newProp.addPropertyValue(c.deepClone().mutable()));
newProp.setVersion(getVersion());
return newProp;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ListValue)) {
return false;
}
ListValue dataNode = (ListValue) o;
return Objects.equals(getKey(), dataNode.getKey()) &&
Objects.equals(getValue(), dataNode.getValue()) &&
Objects.equals(list, dataNode.list) &&
Objects.equals(getMeta(), dataNode.getMeta());
}
private String generateListKey(){
return "["+this.list.size()+"]";
}
@Override
public int hashCode() {
return Objects.hash(getKey(), list, getValue(), getMeta());
}
@Override
public String toString() {
return "List{" +
", size='" + getSize() + '\'' +
", values=" + list +
(getMeta().isEmpty()?"":", metaData=" + getMeta()) +
'}';
}
/**
* Merges multiple values into one single node.
* @param values the values to merge, not null.
* @return the merged instance, or null.
*/
public static ListValue from(Collection<PropertyValue> values) {
ListValue merged = PropertyValue.createList();
for(PropertyValue val:values){
merged.addPropertyValue(val);
}
return merged;
}
}