org.jboss.hal.dmr.ModelNode Maven / Gradle / Ivy
Show all versions of hal-dmr Show documentation
/*
* Copyright 2022 Red Hat
*
* Licensed 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
*
* https://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.jboss.hal.dmr;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import com.google.common.base.CharMatcher;
import static org.jboss.hal.dmr.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.hal.dmr.ModelDescriptionConstants.OUTCOME;
import static org.jboss.hal.dmr.ModelDescriptionConstants.SUCCESS;
/**
* A dynamic model representation node object.
*
* @author David M. Lloyd
*/
public class ModelNode implements Cloneable {
/**
* Creates a new node from a base64 encoded string
*
* @param encoded The base64 encoded string.
*
* @return the new model node
*/
public static ModelNode fromBase64(String encoded) {
// Bloody IE can't cope with line breaks when decoding base64!
String safeEncoded = CharMatcher.breakingWhitespace().removeFrom(encoded);
ModelNode node = new ModelNode();
String decoded = Base64.decode(safeEncoded);
node.readExternal(new DataInput(toBytes(decoded)));
return node;
}
private static native byte[] toBytes(String str) /*-{
var bytes = [];
for (var i = 0; i < str.length; ++i) {
bytes.push(str.charCodeAt(i));
}
return bytes;
}-*/;
private static final String NEW_VALUE_IS_NULL = "newValue is null";
private boolean protect = false;
private ModelValue value;
public ModelNode() {
this.value = ModelValue.UNDEFINED;
}
ModelNode(ModelValue value) {
this.value = value;
}
/**
* Prevent further modifications to this node and its sub-nodes. Note that copies of this node made after this method call
* will not be protected.
*/
public void protect() {
if (!protect) {
protect = true;
value = value.protect();
}
}
/**
* Get the value of this node as a {@code long}. Collection types will return the size of the collection for this value.
* Other types may attempt a string conversion.
*
* @return the long value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public long asLong() throws IllegalArgumentException {
return value.asLong();
}
/**
* Get the value of this node as a {@code long}. Collection types will return the size of the collection for this value.
* Other types may attempt a string conversion.
*
* @param defVal the default value if no conversion is possible
*
* @return the long value
*/
public long asLong(long defVal) {
return value.asLong(defVal);
}
/**
* Get the value of this node as number. Collection types will return the size of the collection for this value. Other types
* may attempt a string conversion.
*
* @return the numeric value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public int asInt() throws IllegalArgumentException {
return value.asInt();
}
/**
* Get the value of this node as number. Collection types will return the size of the collection for this value. Other types
* may attempt a string conversion.
*
* @param defVal the default value if no conversion is possible
*
* @return the int value
*/
public int asInt(int defVal) {
return value.asInt(defVal);
}
/**
* Get the value of this node as a boolean. Collection types return true for non-empty collections. Numerical types return
* true for non-zero values.
*
* @return the boolean value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public boolean asBoolean() throws IllegalArgumentException {
return value.asBoolean();
}
/**
* Get the value of this node as a {@code boolean}. Collection types return {@code true} for non-empty collections.
* Numerical types return {@code true} for non-zero values.
*
* @param defVal the default value if no conversion is possible
*
* @return the boolean value
*/
public boolean asBoolean(boolean defVal) {
return value.asBoolean(defVal);
}
/**
* Get the value as a string. This is the literal value of this model node. More than one node type may yield the same value
* for this method.
*
* @return the string value
*/
public String asString() {
return value.asString();
}
/**
* Get the value of this node as a {@code double}. Collection types will return the size of the collection for this value.
* Other types may attempt a string conversion.
*
* @return the double value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public double asDouble() throws IllegalArgumentException {
return value.asDouble();
}
/**
* Get the value of this node as an {@code double}. Collection types will return the size of the collection for this value.
* Other types may attempt a string conversion.
*
* @param defVal the default value if no conversion is possible
*
* @return the int value
*/
public double asDouble(double defVal) {
return value.asDouble(defVal);
}
/**
* Get the value of this node as a type, expressed using the {@code ModelType} enum. The string value of this node must be
* convertible to a type.
*
* @return the {@code ModelType} value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public ModelType asType() throws IllegalArgumentException {
return value.asType();
}
/**
* Get the value of this node as a {@code BigDecimal}. Collection types will return the size of the collection for this
* value. Other types may attempt a string conversion.
*
* @return the {@code BigDecimal} value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public BigDecimal asBigDecimal() throws IllegalArgumentException {
return value.asBigDecimal();
}
/**
* Get the value of this node as a {@code BigInteger}. Collection types will return the size of the collection for this
* value. Other types may attempt a string conversion.
*
* @return the {@code BigInteger} value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public BigInteger asBigInteger() throws IllegalArgumentException {
return value.asBigInteger();
}
/**
* Get the value of this node as a byte array. Strings and string-like values will return the UTF-8 encoding of the string.
* Numerical values will return the byte representation of the number.
*
* @return the bytes
*
* @throws IllegalArgumentException if no conversion is possible
*/
public byte[] asBytes() throws IllegalArgumentException {
return value.asBytes();
}
/**
* Get the value of this node as a property. Object values will return a property if there is exactly one property in the
* object. List values will return a property if there are exactly two items in the list, and if the first is convertible to
* a string.
*
* @return the property value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public Property asProperty() throws IllegalArgumentException {
return value.asProperty();
}
/**
* Get the value of this node as a property list. Object values will return a list of properties representing each key-value
* pair in the object. List values will return all the values of the list, failing if any of the values are not convertible
* to a property value.
*
* @return the property list value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public List asPropertyList() throws IllegalArgumentException {
return value.asPropertyList();
}
/**
* Get a copy of this value as an object. Object values will simply copy themselves.
*
* Property values will return a single-entry object whose key and value are copied from the property key and value.
*
* List values will attempt to interpolate the list into an object by iterating each item, mapping each property into an
* object entry and otherwise taking pairs of list entries, converting the first to a string, and using the pair of entries
* as a single object entry. If an object key appears more than once in the source object, the last key takes precedence.
*
* @return the object value
*
* @throws IllegalArgumentException if no conversion is possible
*/
public ModelNode asObject() throws IllegalArgumentException {
return value.asObject();
}
/**
* Get the value of this node as {@code type}. This method simply delegates to the various {@code asXXX()} methods depending
* on the specified type.
*
* @return the value as {@code type} or null if the type is {@link ModelType#UNDEFINED}
*
* @throws IllegalArgumentException if no conversion is possible
*/
public Object as(ModelType type) {
Object result;
switch (type) {
case BIG_DECIMAL:
result = asBigDecimal();
break;
case BIG_INTEGER:
result = asBigInteger();
break;
case BOOLEAN:
result = asBoolean();
break;
case BYTES:
result = asBytes();
break;
case DOUBLE:
result = asDouble();
break;
case EXPRESSION:
result = asString();
break;
case INT:
result = asInt();
break;
case LIST:
result = asList();
break;
case LONG:
result = asLong();
break;
case OBJECT:
result = asObject();
break;
case PROPERTY:
result = asProperty();
break;
case STRING:
result = asString();
break;
case TYPE:
result = asType();
break;
case UNDEFINED:
result = null;
break;
default:
result = null;
break;
}
return result;
}
/**
* Determine whether this node is defined.
*
* @return true if this node's value is defined, false otherwise
*/
public boolean isDefined() {
return getType() != ModelType.UNDEFINED;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(int newValue) {
checkProtect();
value = new IntModelValue(newValue);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(long newValue) {
checkProtect();
value = new LongModelValue(newValue);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(double newValue) {
checkProtect();
value = new DoubleModelValue(newValue);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(boolean newValue) {
checkProtect();
value = BooleanModelValue.valueOf(newValue);
return this;
}
/**
* Change this node's value to the given expression value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode setExpression(String newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
value = new ExpressionValue(newValue);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(String newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
value = new StringModelValue(newValue);
return this;
}
public ModelNode set(ModelType type, Object propValue) {
if (type.equals(ModelType.STRING)) {
set((String) propValue);
} else if (type.equals(ModelType.INT)) {
set((Integer) propValue);
} else if (type.equals(ModelType.DOUBLE)) {
set((Double) propValue);
} else if (type.equals(ModelType.LONG)) {
set((Long) propValue);
} else if (type.equals(ModelType.BOOLEAN)) {
set((Boolean) propValue);
} else if (type.equals(ModelType.LIST)) {
setEmptyList();
List list = (List) propValue;
for (Object item : list) {
add(String.valueOf(item));
}
} else {
throw new RuntimeException("Type conversion not implemented for " + type);
}
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(BigDecimal newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
value = new BigDecimalModelValue(newValue);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(BigInteger newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
value = new BigIntegerModelValue(newValue);
return this;
}
/**
* Change this node's value to the given value. The value is copied from the parameter.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(ModelNode newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
value = newValue.value.copy();
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(byte[] newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
byte[] clone = new byte[newValue.length];
System.arraycopy(newValue, 0, clone, 0, newValue.length);
value = new BytesModelValue(newValue.length == 0 ? newValue : clone);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(ModelType newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
value = TypeModelValue.of(newValue);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
*
* @return this node
*/
public ModelNode set(Property newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
set(newValue.getName(), newValue.getValue());
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, ModelNode propertyValue) {
checkProtect();
value = new PropertyModelValue(propertyName, propertyValue);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, int propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, long propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, double propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, boolean propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, String propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and expression value.
*
* @param propertyName the property name
* @param propertyValue the property expression value
*
* @return this node
*/
public ModelNode setExpression(String propertyName, String propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.setExpression(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, BigDecimal propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, BigInteger propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, byte[] propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a property with the given name and value.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode set(String propertyName, ModelType propertyValue) {
checkProtect();
ModelNode node = new ModelNode();
node.set(propertyValue);
value = new PropertyModelValue(propertyName, node);
return this;
}
/**
* Change this node's value to a list whose values are copied from the given collection.
*
* @param newValue the list value
*
* @return this node
*/
public ModelNode set(Collection newValue) {
if (newValue == null) {
throw new IllegalArgumentException(NEW_VALUE_IS_NULL);
}
checkProtect();
ArrayList list = new ArrayList<>(newValue.size());
for (ModelNode node : newValue) {
if (node == null) {
list.add(new ModelNode());
} else {
list.add(node.clone());
}
}
value = new ListModelValue(list);
return this;
}
/**
* Change this node's value to an empty list.
*
* @return this node
*/
public ModelNode setEmptyList() {
checkProtect();
value = new ListModelValue();
return this;
}
/**
* Change this node's value to an empty object.
*
* @return this node
*/
public ModelNode setEmptyObject() {
checkProtect();
value = new ObjectModelValue();
return this;
}
/**
* Clear this node's value and change its type to undefined.
*
* @return this node
*/
public ModelNode clear() {
checkProtect();
value = ModelValue.UNDEFINED;
return this;
}
/**
* Get the child of this node with the given name. If no such child exists, create it. If the node is undefined, it will be
* initialized to be of type object.
*
* When called on property values, the name must match the property name.
*
* @param name the child name
*
* @return the child
*
* @throws IllegalArgumentException if this node does not support getting a child with the given name
*/
public ModelNode get(String name) {
ModelValue value = this.value;
if (value == ModelValue.UNDEFINED) {
checkProtect();
this.value = new ObjectModelValue();
return this.value.getChild(name);
}
return value.getChild(name);
}
/**
* Require the existence of a child of this node with the given name, returning the child. If no such child exists, an
* exception is thrown.
*
* When called on property values, the name must match the property name.
*
* @param name the child name
*
* @return the child
*
* @throws NoSuchElementException if the element does not exist
*/
public ModelNode require(String name) throws NoSuchElementException {
return value.requireChild(name);
}
/**
* Remove a child of this node, returning the child. If no such child exists, an exception is thrown.
*
* When called on property values, the name must match the property name.
*
* @param name the child name
*
* @return the child
*
* @throws NoSuchElementException if the element does not exist
*/
public ModelNode remove(String name) throws NoSuchElementException {
return value.removeChild(name);
}
/**
* Get the child of this node with the given index. If no such child exists, create it (adding list entries as needed). If
* the node is undefined, it will be initialized to be of type {@link ModelType#LIST}.
*
* When called on property values, the index must be zero.
*
* @param index the child index
*
* @return the child
*
* @throws IllegalArgumentException if this node does not support getting a child with the given index
*/
public ModelNode get(int index) {
ModelValue value = this.value;
if (value == ModelValue.UNDEFINED) {
checkProtect();
return (this.value = new ListModelValue()).getChild(index);
}
return value.getChild(index);
}
/**
* Require the existence of a child of this node with the given index, returning the child. If no such child exists, an
* exception is thrown.
*
* When called on property values, the index must be zero.
*
* @param index the child index
*
* @return the child
*
* @throws NoSuchElementException if the element does not exist
*/
public ModelNode require(int index) {
return value.requireChild(index);
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(int newValue) {
add().set(newValue);
return this;
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(long newValue) {
add().set(newValue);
return this;
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(double newValue) {
add().set(newValue);
return this;
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(boolean newValue) {
add().set(newValue);
return this;
}
/**
* Add the given expression to the end of this node's value list. If the node is undefined, it will be initialized to be of
* type {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode addExpression(String newValue) {
add().setExpression(newValue);
return this;
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(String newValue) {
add().set(newValue);
return this;
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(BigDecimal newValue) {
add().set(newValue);
return this;
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(BigInteger newValue) {
add().set(newValue);
return this;
}
/**
* Add a copy of the given value to the end of this node's value list. If the node is undefined, it will be initialized to
* be of type {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(ModelNode newValue) {
add().set(newValue);
return this;
}
/**
* Add the given value to the end of this node's value list. If the node is undefined, it will be initialized to be of type
* {@link ModelType#LIST}.
*
* @param newValue the new value to add
*
* @return this node
*/
public ModelNode add(byte[] newValue) {
add().set(newValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, int propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, long propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, double propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, boolean propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, String propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, BigDecimal propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, BigInteger propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, ModelNode propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a property with the given name and value to the end of this node's value list. If the node is undefined, it will be
* initialized to be of type {@link ModelType#LIST}.
*
* @param propertyName the property name
* @param propertyValue the property value
*
* @return this node
*/
public ModelNode add(String propertyName, byte[] propertyValue) {
add().set(propertyName, propertyValue);
return this;
}
/**
* Add a node to the end of this node's value list and return it. If the node is undefined, it will be initialized to be of
* type list.
*
* @return the new node
*/
public ModelNode add() {
checkProtect();
ModelValue value = this.value;
if (value == ModelValue.UNDEFINED) {
this.value = new ListModelValue();
return this.value.addChild();
}
return value.addChild();
}
/**
* Add a node of type {@link ModelType#LIST} to the end of this node's value list and return it. If this node is undefined,
* it will be initialized to be of type {@link ModelType#LIST}.
*
* @return the new node
*/
public ModelNode addEmptyList() {
ModelNode node = add();
node.setEmptyList();
return node;
}
/**
* Add a node of type {@link ModelType#OBJECT} to the end of this node's value list and return it. If this node is
* undefined, it will be initialized to be of type {@link ModelType#LIST}.
*
* @return the new node
*/
public ModelNode addEmptyObject() {
ModelNode node = add();
node.setEmptyObject();
return node;
}
/**
* Determine whether this node has a child with the given index. Property node types always contain exactly one value.
*
* @param index the index
*
* @return {@code true} if there is a (possibly undefined) node at the given index
*/
public boolean has(int index) {
return value.has(index);
}
/**
* Determine whether this node has a child with the given name. Property node types always contain exactly one value with a
* key equal to the property name.
*
* @param key the name
*
* @return true if there is a (possibly undefined) node at the given key
*/
public boolean has(String key) {
return value.has(key);
}
/**
* Determine whether this node has a defined child with the given index. Property node types always contain exactly one
* value.
*
* @param index the index
*
* @return {@code true} if there is a node at the given index and its {@link #getType() type} is not
* {@link ModelType#UNDEFINED}
*/
public boolean hasDefined(int index) {
return value.has(index) && get(index).isDefined();
}
/**
* Determine whether this node has a defined child with the given name. Property node types always contain exactly one value
* with a key equal to the property name.
*
* @param key the name
*
* @return true if there is a node at the given index and its type is not undefined
*/
public boolean hasDefined(String key) {
return value.has(key) && get(key).isDefined();
}
/**
* Get the set of keys contained in this object. Property node types always contain exactly one value with a key equal to
* the property name. Other non-object types will return an empty set.
*
* @return the key set
*/
public Set keys() {
return value.getKeys();
}
/**
* Get the list of entries contained in this object. Property node types always contain exactly one entry (itself). Lists
* will return an unmodifiable view of their contained list. Objects will return a list of properties corresponding to the
* mappings within the object. Other types will return an empty list.
*
* @return the entry list
*/
public List asList() {
return value.asList();
}
/**
* Recursively get the children of this node with the given names. If any child along the path does not exist, create it. If
* any node is the path is undefined, it will be initialized to be of type {@link ModelType#OBJECT}.
*
* @param names the child names
*
* @return the child
*
* @throws IllegalArgumentException if a node does not support getting a child with the given name path
*/
public ModelNode get(String... names) {
ModelNode current = this;
for (String part : names) {
current = current.get(part);
}
return current;
}
/**
* Get a human-readable string representation of this model node, formatted nicely (possibly on multiple lines).
*
* @return the string representation
*/
@Override
public String toString() {
return value.toString();
}
/**
* Get a JSON string representation of this model node, formatted nicely, if requested.
*
* @param compact Flag that indicates whether or not the string should be all on one line (i.e. {@code true}) or should be
* printed on multiple lines ({@code false}).
*
* @return The JSON string.
*/
public String toJSONString(boolean compact) {
return value.toJSONString(compact);
}
public String toJSONString() {
return value.toJSONString(false);
}
public String toBase64String() {
DataOutput out = new DataOutput();
writeExternal(out);
return Base64.encode(out.toString());
}
/**
* Return a copy of this model node, with all system property expressions locally resolved. The caller must have permission
* to access all of the system properties named in the node tree.
*
* @return the resolved copy
*/
public ModelNode resolve() {
ModelNode newNode = new ModelNode();
newNode.value = value.resolve();
return newNode;
}
/**
* Determine whether this object is equal to another.
*
* @param other the other object
*
* @return {@code true} if they are equal, {@code false} otherwise
*/
@Override
public boolean equals(Object other) {
return other instanceof ModelNode && equals((ModelNode) other);
}
/**
* Determine whether this object is equal to another.
*
* @param other the other object
*
* @return {@code true} if they are equal, {@code false} otherwise
*/
public boolean equals(ModelNode other) {
return this == other || other != null && other.value.equals(value);
}
/**
* Get the hash code of this node object. Note that unless the value is {@link #protect()}ed, the hash code may change over
* time, thus making unprotected nodes unsuitable for use as hash table keys.
*
* @return the hash code
*/
@Override
public int hashCode() {
return value.hashCode();
}
/**
* Clone this model node.
*
* @return the clone
*/
public ModelNode clone() {
ModelNode clone = new ModelNode();
clone.value = value.copy();
return clone;
}
protected void format(StringBuilder builder, int indent, boolean multiLine) {
value.format(builder, indent, multiLine);
}
void formatAsJSON(StringBuilder builder, int indent, boolean multiLine) {
value.formatAsJSON(builder, indent, multiLine);
}
/**
* Get the current type of this node.
*
* @return the node type
*/
public ModelType getType() {
return value.getType();
}
/**
* Write this node's content in binary format to the given target.
*
* @param out the target to which the content should be written
*/
public void writeExternal(DataOutput out) {
ModelValue value = this.value;
ModelType type = value.getType();
out.writeByte(type.getTypeChar());
value.writeExternal(out);
}
/**
* Read this node's content in binary format from the given source.
*
* @param in the source from which the content should be read
*/
void readExternal(DataInput in) {
checkProtect();
byte[] b; // used by some of these
try {
ModelType type = ModelType.forChar((char) (in.readByte() & 0xff));
switch (type) {
case UNDEFINED:
value = ModelValue.UNDEFINED;
return;
case BIG_DECIMAL:
value = new BigDecimalModelValue(in);
return;
case BIG_INTEGER:
b = new byte[in.readInt()];
in.readFully(b);
value = new BigIntegerModelValue(new BigInteger(b));
return;
case BOOLEAN:
value = BooleanModelValue.valueOf(in.readBoolean());
return;
case BYTES:
b = new byte[in.readInt()];
in.readFully(b);
value = new BytesModelValue(b);
return;
case DOUBLE:
value = new DoubleModelValue(in.readDouble());
return;
case EXPRESSION:
value = new ExpressionValue(in.readUTF());
return;
case INT:
value = new IntModelValue(in.readInt());
return;
case LIST:
value = new ListModelValue(in);
return;
case LONG:
value = new LongModelValue(in.readLong());
return;
case OBJECT:
value = new ObjectModelValue(in);
return;
case PROPERTY:
value = new PropertyModelValue(in);
return;
case STRING:
value = new StringModelValue(in.readUTF());
return;
case TYPE:
value = TypeModelValue.of(ModelType.forChar((char) (in.readByte() & 0xff)));
return;
default:
throw new IllegalStateException("Invalid type read: " + type);
}
} catch (IllegalArgumentException e) {
throw new IllegalStateException(e.getMessage(), e.getCause());
}
}
private void checkProtect() {
if (protect) {
throw new UnsupportedOperationException();
}
}
/** @return {@code true} if this node has an outcome and the outcome does not equal "success" */
public boolean isFailure() {
return hasDefined(OUTCOME) && !get(OUTCOME).asString().equals(SUCCESS);
}
/** @return the failure description or "No failure-description provided" */
public String getFailureDescription() {
if (hasDefined(FAILURE_DESCRIPTION)) {
StringBuilder failure = new StringBuilder();
get(FAILURE_DESCRIPTION).format(failure, 0, true);
return failure.toString();
} else {
return "No failure-description provided";
}
}
}