
com.addthis.codec.config.ConfigNodeCursor Maven / Gradle / Ivy
/*
* 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
*
* 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 com.addthis.codec.config;
import javax.annotation.Nullable;
import java.util.Iterator;
import java.util.Map;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.JsonToken;
import com.typesafe.config.ConfigList;
import com.typesafe.config.ConfigObject;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueType;
import static com.typesafe.config.ConfigValueType.LIST;
import static com.typesafe.config.ConfigValueType.OBJECT;
abstract class ConfigNodeCursor extends JsonStreamContext {
/**
* Parent cursor of this cursor, if any; null for root
* cursors.
*/
protected final ConfigNodeCursor _parent;
/**
* Current field name
*/
@Nullable protected String _currentName;
public ConfigNodeCursor(int contextType, ConfigNodeCursor p) {
super();
_type = contextType;
_index = -1;
_parent = p;
}
/*
/**********************************************************
/* JsonStreamContext impl
/**********************************************************
*/
// note: co-variant return type
@Override
public final ConfigNodeCursor getParent() { return _parent; }
@Override
@Nullable
public final String getCurrentName() {
return _currentName;
}
/**
* @since 2.0
*/
public void overrideCurrentName(String name) {
_currentName = name;
}
/*
/**********************************************************
/* Extended API
/**********************************************************
*/
public abstract JsonToken nextToken();
public abstract JsonToken nextValue();
public abstract JsonToken endToken();
public abstract ConfigValue currentNode();
public abstract boolean currentHasChildren();
/**
* Method called to create a new context for iterating all
* contents of the current structured value (JSON array or object)
*/
public final ConfigNodeCursor iterateChildren() {
ConfigValue n = currentNode();
if (n == null) throw new IllegalStateException("No current node");
if (n.valueType() == LIST) { // false since we have already returned START_ARRAY
return new Array((ConfigList) n, this);
}
if (n.valueType() == OBJECT) {
return new Object((ConfigObject) n, this);
}
throw new IllegalStateException("Current node of type " + n.getClass().getName());
}
static JsonToken forConfigValue(ConfigValue configValue) {
ConfigValueType valueType = configValue.valueType();
switch (valueType) {
case NUMBER:
if (configValue.unwrapped() instanceof Double) {
return JsonToken.VALUE_NUMBER_FLOAT;
} else {
return JsonToken.VALUE_NUMBER_INT;
}
case BOOLEAN:
if (configValue.unwrapped().equals(Boolean.TRUE)) {
return JsonToken.VALUE_TRUE;
} else {
return JsonToken.VALUE_FALSE;
}
case NULL:
return JsonToken.VALUE_NULL;
case STRING:
return JsonToken.VALUE_STRING;
case OBJECT:
return JsonToken.START_OBJECT;
case LIST:
return JsonToken.START_ARRAY;
default:
// not possible unless the set of enums changes on us later
throw new IllegalArgumentException(valueType.name() + " is not a supported ConfigValueType");
}
}
/*
/**********************************************************
/* Concrete implementations
/**********************************************************
*/
/**
* Context matching root-level value nodes (i.e. anything other
* than JSON Object and Array).
* Note that context is NOT created for leaf values.
*/
protected static final class RootValue extends ConfigNodeCursor {
@Nullable private ConfigValue _node;
private boolean _done = false;
public RootValue(ConfigValue n, ConfigNodeCursor p) {
super(JsonStreamContext.TYPE_ROOT, p);
_node = n;
}
@Override
public void overrideCurrentName(String name) {}
@Override
@Nullable
public JsonToken nextToken() {
if (!_done) {
_done = true;
return forConfigValue(_node);
}
_node = null;
return null;
}
@Override
@Nullable
public JsonToken nextValue() { return nextToken(); }
@Override
@Nullable
public JsonToken endToken() { return null; }
@Override
public ConfigValue currentNode() { return _node; }
@Override
public boolean currentHasChildren() { return false; }
}
/**
* Cursor used for traversing non-empty JSON Array nodes
*/
protected static final class Array extends ConfigNodeCursor {
private final Iterator _contents;
@Nullable private ConfigValue _currentNode;
public Array(ConfigList n, ConfigNodeCursor p) {
super(JsonStreamContext.TYPE_ARRAY, p);
_contents = n.iterator();
}
@Override
@Nullable
public JsonToken nextToken() {
if (!_contents.hasNext()) {
_currentNode = null;
return null;
}
_currentNode = _contents.next();
return forConfigValue(_currentNode);
}
@Override
public JsonToken nextValue() { return nextToken(); }
@Override
public JsonToken endToken() { return JsonToken.END_ARRAY; }
@Override
public ConfigValue currentNode() { return _currentNode; }
@Override
public boolean currentHasChildren() {
// note: ONLY to be called for container nodes
ConfigValue currentValue = currentNode();
if (currentValue.valueType() == ConfigValueType.LIST) {
return !((ConfigList) currentValue).isEmpty();
} else {
return !((ConfigObject) currentValue).isEmpty();
}
}
}
/**
* Cursor used for traversing non-empty JSON Object nodes
*/
protected static final class Object extends ConfigNodeCursor {
private final Iterator> _contents;
@Nullable private Map.Entry _current;
private boolean _needEntry;
public Object(ConfigObject n, ConfigNodeCursor p) {
super(JsonStreamContext.TYPE_OBJECT, p);
_contents = n.entrySet().iterator();
_needEntry = true;
}
@Override
@Nullable
public JsonToken nextToken() {
// Need a new entry?
if (_needEntry) {
if (!_contents.hasNext()) {
_currentName = null;
_current = null;
return null;
}
_needEntry = false;
_current = _contents.next();
_currentName = (_current == null) ? null : _current.getKey();
return JsonToken.FIELD_NAME;
}
_needEntry = true;
return forConfigValue(_current.getValue());
}
@Override
public JsonToken nextValue() {
JsonToken t = nextToken();
if (t == JsonToken.FIELD_NAME) {
t = nextToken();
}
return t;
}
@Override
public JsonToken endToken() { return JsonToken.END_OBJECT; }
@Override
public ConfigValue currentNode() {
if (_current == null) {
return null;
} else {
return _current.getValue();
}
}
@Override
public boolean currentHasChildren() {
// note: ONLY to be called for container nodes
ConfigValue currentValue = currentNode();
if (currentValue.valueType() == ConfigValueType.LIST) {
return !((ConfigList) currentValue).isEmpty();
} else {
return !((ConfigObject) currentValue).isEmpty();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy