org.jboss.dmr.ModelNode Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2011, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.dmr;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InvalidObjectException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.OutputStream;
import java.io.PrintWriter;
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;
/**
* A dynamic model representation node object.
*
* A node can be of any type specified in the {@link ModelType} enumeration. The type can
* be queried via {@link #getType()} and updated via any of the {@code set*()} methods. The
* value of the node can be acquired via the {@code as()} methods, where {@code } is
* the desired value type. If the type is not the same as the node type, a conversion is attempted between
* the types.
* A node can be made read-only by way of its {@link #protect()} method, which will prevent
* any further changes to the node or its sub-nodes.
*
Instances of this class are not thread-safe and need to be synchronized externally.
*
* @author David M. Lloyd
*/
public class ModelNode implements Externalizable, Cloneable {
private static final long serialVersionUID = 2030456323088551487L;
private boolean protect = false;
private ModelValue value = ModelValue.UNDEFINED;
/**
* Creates a new {@code ModelNode} with an undefined value.
*/
public ModelNode() {
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value. Cannot be {@code null}
*
* @throws IllegalArgumentException if {@code value} is {@code null}
*/
public ModelNode(final BigDecimal value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
this.value = new BigDecimalModelValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value. Cannot be {@code null}
*
* @throws IllegalArgumentException if {@code value} is {@code null}
*/
public ModelNode(final BigInteger value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
this.value = new BigIntegerModelValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value.
*/
public ModelNode(final boolean value) {
this(value ? BooleanModelValue.TRUE : BooleanModelValue.FALSE);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value. Cannot be {@code null}
*
* @throws IllegalArgumentException if {@code value} is {@code null}
*/
public ModelNode(final byte[] value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
this.value = new BytesModelValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value.
*/
public ModelNode(final double value) {
this.value = new DoubleModelValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value.
*/
public ModelNode(final int value) {
this.value = new IntModelValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value.
*/
public ModelNode(final long value) {
this.value = new LongModelValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value. Cannot be {@code null}
*
* @throws IllegalArgumentException if {@code value} is {@code null}
*/
public ModelNode(final String value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
this.value = new StringModelValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value. Cannot be {@code null}
*
* @throws IllegalArgumentException if {@code value} is {@code null}
*/
public ModelNode(final ValueExpression value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
this.value = new ExpressionValue(value);
}
/**
* Creates a new {@code ModelNode} with the given {@code value}.
*
* @param value the value. Cannot be {@code null}
*
* @throws IllegalArgumentException if {@code value} is {@code null}
*/
public ModelNode(final ModelType value) {
if (value == null) {
throw new IllegalArgumentException("value is null");
}
this.value = TypeModelValue.of(value);
}
ModelNode(final 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(final long defVal) {
return value.asLong(defVal);
}
/**
* Get the value of this node as an {@code int}. Collection types will return the size
* of the collection for this value. Other types may attempt a string conversion.
*
* @return the int value
* @throws IllegalArgumentException if no conversion is possible
*/
public int asInt() throws IllegalArgumentException {
return value.asInt();
}
/**
* Get the value of this node as an {@code int}. 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(final int defVal) {
return value.asInt(defVal);
}
/**
* 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.
*
* @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(final 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.
*
* @throws IllegalArgumentException if no conversion is possible
* @return the double value
*/
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(final 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.
*
* @throws IllegalArgumentException if no conversion is possible
* @return the {@code BigDecimal} value
*/
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 an expression.
*
* @return the expression
* @throws IllegalArgumentException if no conversion is possible
*/
public ValueExpression asExpression() throws IllegalArgumentException {
return value.asExpression();
}
/**
* 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 as by the {@link #clone()} method.
* 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();
}
/**
* Determine whether this node is defined. Equivalent to the expression: {@code getType() != ModelType.UNDEFINED}.
*
* @return {@code true} if this node's value is defined
*/
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(final 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(final 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(final 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(final 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
* @deprecated Use {@link #set(ValueExpression)} instead.
*/
@Deprecated
public ModelNode setExpression(final String newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue 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(final ValueExpression newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue 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(final String newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue is null");
}
checkProtect();
value = new StringModelValue(newValue);
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
* @return this node
*/
public ModelNode set(final BigDecimal newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue 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(final BigInteger newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue 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(final ModelNode newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue is null");
}
checkProtect();
value = newValue.value.copy();
return this;
}
void setNoCopy(final ModelNode child) {
value = child.value;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
* @return this node
*/
public ModelNode set(final byte[] newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue is null");
}
checkProtect();
value = new BytesModelValue(newValue.length == 0 ? newValue : newValue.clone());
return this;
}
/**
* Change this node's value to the given value.
*
* @param newValue the new value
* @return this node
*/
public ModelNode set(final ModelType newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue 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(final Property newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue 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(final String propertyName, final ModelNode propertyValue) {
checkProtect();
value = new PropertyModelValue(propertyName, propertyValue, true);
return this;
}
ModelNode setNoCopy(final String propertyName, final ModelNode propertyValue) {
value = new PropertyModelValue(propertyName, propertyValue, false);
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(final String propertyName, final int propertyValue) {
checkProtect();
final 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(final String propertyName, final long propertyValue) {
checkProtect();
final 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(final String propertyName, final double propertyValue) {
checkProtect();
final 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(final String propertyName, final boolean propertyValue) {
checkProtect();
final 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(final String propertyName, final String propertyValue) {
checkProtect();
final 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
* @deprecated Use {@link #set(String,ValueExpression)} instead.
*/
@Deprecated
public ModelNode setExpression(final String propertyName, final String propertyValue) {
checkProtect();
final 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(final String propertyName, final ValueExpression propertyValue) {
checkProtect();
final 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(final String propertyName, final BigDecimal propertyValue) {
checkProtect();
final 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(final String propertyName, final BigInteger propertyValue) {
checkProtect();
final 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(final String propertyName, final byte[] propertyValue) {
checkProtect();
final 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(final String propertyName, final ModelType propertyValue) {
checkProtect();
final 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(final Collection newValue) {
if (newValue == null) {
throw new IllegalArgumentException("newValue is null");
}
checkProtect();
final ArrayList list = new ArrayList(newValue.size());
for (final 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 {@link ModelType#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 {@link ModelType#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(final String name) {
ModelValue value = this.value;
if (value == ModelValue.UNDEFINED) {
checkProtect();
return (this.value = new ObjectModelValue()).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(final 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(final String name) throws NoSuchElementException {
return value.removeChild(name);
}
/**
* Remove a child of this list, 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 index the child index
* @return the child
* @throws NoSuchElementException if the element does not exist
*/
public ModelNode remove(final int index) throws NoSuchElementException {
return value.removeChild(index);
}
/**
* 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(final int index) {
final 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(final 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(final 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(final 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(final 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(final 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
* @deprecated Use {@link #add(ValueExpression)} instead.
*/
@Deprecated
public ModelNode addExpression(final 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(final ValueExpression 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(final 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(final 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(final 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(final ModelNode newValue) {
add().set(newValue);
return this;
}
/**
* insert copy of the given value to provided index 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 insert(final ModelNode newValue, int index) {
insert(index).set(newValue);
return this;
}
ModelNode addNoCopy(final ModelNode child) {
add().value = child.value;
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(final byte[] newValue) {
add().set(newValue);
return this;
}
/**
* Add a property 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 property the property
* @return this node
*/
public ModelNode add(final Property property) {
add().set(property);
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(final String propertyName, final 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(final String propertyName, final 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(final String propertyName, final 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(final String propertyName, final 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(final String propertyName, final ValueExpression 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(final String propertyName, final 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(final String propertyName, final 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(final String propertyName, final 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(final String propertyName, final 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(final String propertyName, final 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 {@link ModelType#LIST}.
*
* @return the new node
*/
public ModelNode add() {
checkProtect();
ModelValue value = this.value;
if (value == ModelValue.UNDEFINED) {
return (this.value = new ListModelValue()).addChild();
}
return value.addChild();
}
/**
* Insert a node at provided index of this node's value list and return it. If the node is undefined, it
* will be initialized to be of type {@link ModelType#LIST}.
*
* @param index where in list to put it
* @return the new node
*/
public ModelNode insert(final int index) {
checkProtect();
ModelValue value = this.value;
if (value == ModelValue.UNDEFINED) {
return (this.value = new ListModelValue()).insertChild(index);
}
return value.insertChild(index);
}
/**
* 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() {
final 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() {
final 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(final 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 {@code true} if there is a (possibly undefined) node at the given key
*/
public boolean has(final String key) {
return value.has(key);
}
/**
* Recursively determine whether this node has children with the given names. If any child along the path does not
* exist, return {@code false}.
*
* @param names the child names
* @return {@code true} if a call to {@link #get(String...)} with the given {@code names} would succeed without
* needing to create any new nodes; {@code false} otherwise
*/
public boolean has(final String... names) {
ModelNode current = this;
for (final String part : names) {
if (current.has(part)) {
current = current.get(part);
} else {
return false;
}
}
return true;
}
/**
* 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 {@code true} if there is a node at the given index and its {@link #getType() type} is not {@link ModelType#UNDEFINED}
*/
public boolean hasDefined(String key) {
return value.has(key) && get(key).isDefined();
}
/**
* Recursively determine whether this node has defined children with the given names. If any child along the path does not
* exist or is not defined, return {@code false}.
*
* @param names the child names
* @return {@code true} if a call to {@link #get(String...)} with the given {@code names} would succeed without
* needing to create any new nodes and without traversing any undefined nodes; {@code false} otherwise
*/
public boolean hasDefined(final String... names) {
ModelNode current = this;
for (final String part : names) {
if (current.hasDefined(part)) {
current = current.get(part);
} else {
return false;
}
}
return true;
}
/**
* 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(final String... names) {
ModelNode current = this;
for (final 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();
}
/**
* Output the DMR string representation of this model node, formatted nicely, if requested to the supplied PrintWriter
* instance.
*
* @param writer A PrintWriter instance used to output the DMR string.
* @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}).
*/
public void writeString(final PrintWriter writer, final boolean compact) {
value.writeString(writer, compact);
}
/**
* 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(final boolean compact) {
return value.toJSONString(compact);
}
/**
* Output the JSON string representation of this model node, formatted nicely, if requested to the supplied PrintWriter
* instance.
*
* @param writer A PrintWriter instance used to output the JSON string.
* @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}).
*/
public void writeJSONString(final PrintWriter writer, final boolean compact) {
value.writeJSONString(writer, compact);
}
/**
* Get a model node from a string representation of the model node.
*
* @param input the input string
* @return the model node
*/
public static ModelNode fromString(final String input) {
final ModelNodeParser parser = new ModelNodeParser();
try {
parser.setInput(new ByteArrayInputStream(input.getBytes("US-ASCII")));
if (parser.yyParse() > 0) {
throw new IllegalArgumentException("DMR parser error");
}
return parser.getResult();
} catch (final IOException e) {
final IllegalArgumentException n = new IllegalArgumentException(e.getMessage());
n.setStackTrace(e.getStackTrace());
throw n;
}
}
public static ModelNode fromJSONString(final String input) {
final JSONParserImpl parser = new JSONParserImpl();
try {
parser.setInput(new ByteArrayInputStream(input.getBytes("UTF-8")));
if(parser.yyParse() > 0) {
throw new IllegalArgumentException("JSON parser error");
}
return parser.getResult();
} catch (final IOException e) {
final IllegalArgumentException n = new IllegalArgumentException(e.getMessage());
n.setStackTrace(e.getStackTrace());
throw n;
}
}
/**
* Get a model node from a text representation of the model node. The stream must be encoded in
* US-ASCII encoding.
*
* @param stream the source stream
* @return the model node
*/
public static ModelNode fromStream(final InputStream stream) throws IOException {
final ModelNodeParser parser = new ModelNodeParser();
parser.setInput(stream);
if (parser.yyParse() > 0) {
throw new IOException("Parser error");
}
return parser.getResult();
}
/**
* Get a model node from a JSON text representation of the model node. The stream should be encoded in UTF-8.
*
* @param stream the source stream
* @return the model node
*/
public static ModelNode fromJSONStream(final InputStream stream) throws IOException {
final JSONParserImpl parser = new JSONParserImpl();
parser.setInput(stream);
if (parser.yyParse() > 0) {
throw new IOException("Parser error");
}
return parser.getResult();
}
/**
* Reads base64 data from the passed stream,
* and deserializes the decoded result.
*
* @see #writeBase64(OutputStream)
* @return the decoded model node
* @throws IOException if the passed stream has an issue
*/
public static ModelNode fromBase64(InputStream stream) throws IOException {
Base64.InputStream bstream = new Base64.InputStream(stream);
ModelNode node = new ModelNode();
node.readExternal(bstream);
bstream.close();
return node;
}
/**
* Return a copy of this model node, with all values of type {@link ModelType#EXPRESSION} locally resolved.
* The caller must have permission to access all of the system properties named in the node tree. If an expression
* begins with {@code ${env.} then a system property named {@code env.@lt;remainder of expression@gt;} will be
* checked, and if not present a {@link System#getenv(String) system environment variable named @lt;remainder of expression@gt;}
* will be checked. In that case the caller must have permission to access the environment variable.
*
* @return the resolved copy
*
* @throws IllegalStateException if there is a value of type {@link ModelType#EXPRESSION} in the node tree and
* there is no system property or environment variable that matches the expression
* @throws SecurityException
* if a security manager exists and its
* {@link SecurityManager#checkPermission checkPermission}
* method doesn't allow access to the relevant system property or environment variable
*/
public ModelNode resolve() {
final 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(final 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(final 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() {
//noinspection NonFinalFieldReferencedInHashCode
return value.hashCode();
}
/**
* Clone this model node.
*
* @return the clone
*/
@Override
public ModelNode clone() {
final ModelNode clone = new ModelNode();
clone.value = value.copy();
return clone;
}
void format(final PrintWriter writer, final int indent, final boolean multiLine) {
value.format(writer, indent, multiLine);
}
void formatAsJSON(final PrintWriter writer, final int indent, final boolean multiLine) {
value.formatAsJSON(writer, 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
* @throws IOException if an I/O error occurs
*/
@Override
public void writeExternal(final ObjectOutput out) throws IOException {
writeExternal((DataOutput) out);
}
/**
* Write this node's content in binary format to the given target.
*
* @param out the target to which the content should be written
* @throws IOException if an I/O error occurs
*/
public void writeExternal(final OutputStream out) throws IOException {
writeExternal((DataOutput) new DataOutputStream(out));
}
/**
* Write this node's content in binary format to the given target.
*
* @param out the target to which the content should be written
* @throws IOException if an I/O error occurs
*/
public void writeExternal(final DataOutputStream out) throws IOException {
writeExternal((DataOutput) out);
}
/**
* Write this node's content in binary format to the given target.
*
* @param out the target to which the content should be written
* @throws IOException if an I/O error occurs
*/
public void writeExternal(final DataOutput out) throws IOException {
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
* @throws IOException if an I/O error occurs
*/
@Override
public void readExternal(final ObjectInput in) throws IOException {
readExternal((DataInput) in);
}
/**
* Read this node's content in binary format from the given source.
*
* @param in the source from which the content should be read
* @throws IOException if an I/O error occurs
*/
public void readExternal(final DataInputStream in) throws IOException {
readExternal((DataInput) in);
}
/**
* Read this node's content in binary format from the given source.
*
* @param in the source from which the content should be read
* @throws IOException if an I/O error occurs
*/
public void readExternal(final InputStream in) throws IOException {
readExternal((DataInput) new DataInputStream(in));
}
/**
* Read this node's content in binary format from the given source.
*
* @param in the source from which the content should be read
* @throws IOException if an I/O error occurs
*/
public void readExternal(final DataInput in) throws IOException {
checkProtect();
try {
final char c = (char) (in.readByte() & 0xff);
final ModelType type = ModelType.forChar(c);
switch (type) {
case UNDEFINED: value = ModelValue.UNDEFINED; return;
case BIG_DECIMAL: value = new BigDecimalModelValue(in); return;
case BIG_INTEGER: value = new BigIntegerModelValue(in); return;
case BOOLEAN: value = BooleanModelValue.valueOf(in.readBoolean()); return;
case BYTES: value = new BytesModelValue(in); 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(c, in); return;
case TYPE: value = TypeModelValue.of(ModelType.forChar((char) (in.readByte() & 0xff))); return;
default: throw new InvalidObjectException("Invalid type read: " + type);
}
} catch (final IllegalArgumentException e) {
final InvalidObjectException ne = new InvalidObjectException(e.getMessage());
ne.initCause(e.getCause());
throw ne;
}
}
/**
* Encodes the serialized representation in base64 form
* and writes it to the specified output stream.
*
* @param stream the stream to write to
* @throws IOException if the specified stream has an issue
*/
public void writeBase64(OutputStream stream) throws IOException {
Base64.OutputStream bstream = new Base64.OutputStream(stream);
writeExternal(bstream);
bstream.flushBase64(); // Required to ensure last block is written to stream.
}
private void checkProtect() {
if (protect) {
throw new UnsupportedOperationException();
}
}
}