package org.xmlsh.types;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.xmlsh.core.CoreException;
import org.xmlsh.core.InvalidArgumentException;
import org.xmlsh.core.XValue;
import org.xmlsh.json.JSONUtils;
import org.xmlsh.sh.shell.SerializeOpts;
import org.xmlsh.util.JavaUtils;
import org.xmlsh.util.Util;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.POJONode;
public class JSONTypeFamily extends AbstractTypeFamily implements ITypeFamily
{
private static final JSONTypeFamily _instance = new JSONTypeFamily();
private static Logger mLogger = LogManager.getLogger();
private final static Object _nullValue = JSONUtils.nullValue();
@Override
public boolean isClassOfFamily(Class<?> cls)
{
return JsonNode.class.isAssignableFrom(cls);
}
@Override
public boolean isInstanceOfFamily(Object obj)
{
return obj instanceof JsonNode;
}
@Override
public TypeFamily typeFamily()
{
return TypeFamily.JSON;
}
@Override
public String asString(Object value)
{
try {
return JSONUtils.jsonToString((JsonNode) value,false);
} catch (JsonProcessingException e1) {
mLogger.warn("Exception serializing Json value", e1);
}
return "";
}
@Override
public int getSize(Object obj)
{
assert (obj instanceof JsonNode);
if( obj == null )
return 0;
return ((JsonNode) obj).size();
}
@Override
public XValue getXValue(Object obj, String ind) throws CoreException
{
assert (!Util.isBlank(ind));
if(obj == null)
return nullXValue();
assert (obj instanceof JsonNode);
if( Util.isEmpty(ind))
return getXValue(obj);
JsonNode node = (JsonNode) obj;
switch (node.getNodeType()) {
case ARRAY:
return getXValue(node.get(Util.parseInt(ind, 0)));
case OBJECT:
return getXValue(node.get(ind));
case POJO:
return XValue.newXValue(node.get(ind)); // not JSON type
default:
return getXValue(obj);
}
}
@Override
public void serialize(Object value, OutputStream out, SerializeOpts opts) throws IOException
{
if(value == null)
return;
assert (value instanceof JsonNode);
JsonNode node = (JsonNode) value;
try {
if( ! opts.getSerializeJson() && node.isValueNode() )
out.write(node.asText().getBytes(opts.getOutput_text_encoding()) );
else
JSONUtils.writeJsonNode(node, out, opts);
} catch (JsonGenerationException | JsonMappingException e) {
Util.wrapIOException(e);
}
}
/*
* (non-Javadoc)
*
* @see org.xmlsh.types.AbstractMethods#simpleTypeName(java.lang.Object)
*/
@Override
public String simpleTypeName(Object obj)
{
if( obj == null )
return "null";
if(obj instanceof JsonNode) {
JsonNodeType nt = ((JsonNode) obj).getNodeType();
return nt.toString().toLowerCase();
}
return JSONUtils.getJavaType(obj).getClass().getSimpleName();
}
@Override
public boolean isEmpty(Object value)
{
assert (value != null);
if(value instanceof JsonNode)
return JSONUtils.isEmpty((JsonNode) value);
return false;
}
// Set a named index
@Override
public XValue setXValue(XValue xobj, String ind, XValue value) throws CoreException
{
assert (xobj != null && !xobj.isNull());
if(xobj == null || xobj.isNull())
throw new CoreException("Cannot set indexed value to null object");
assert (!Util.isBlank(ind));
Object obj = xobj.asObject();
assert (obj instanceof JsonNode);
JsonNode node = (JsonNode) obj;
switch (node.getNodeType()) {
case ARRAY:
((ArrayNode) node).set(Util.parseInt(ind, 0), JSONUtils.toJsonType(value));
break;
case OBJECT:
((ObjectNode) node).set(ind, JSONUtils.toJsonType(value));
break;
case POJO:
try {
JavaUtils.setNameIndexedValue(((POJONode) node).getPojo(), ind, JSONUtils.toJsonType(value));
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
Util.wrapCoreException("Exception setting indexed value", e);
}
break;
default:
assert (false);
}
return xobj;
}
@Override
public List<XValue> getXValues(Object obj) throws InvalidArgumentException
{
if(obj == null)
return Collections.emptyList();
assert (obj instanceof JsonNode);
JsonNode node = (JsonNode) obj;
switch (node.getNodeType()) {
case ARRAY:
case OBJECT:
return JSONUtils.asXList(node.elements());
case POJO:
return Collections.singletonList(getXValue(((POJONode) node).getPojo()));
default:
return Collections.singletonList(getXValue(obj));
}
}
@Override
public XValue getXValue(Object obj, int index) throws CoreException
{
assert( obj != null );
assert (obj instanceof JsonNode);
JsonNode node = (JsonNode) obj;
Object ret = null ;
switch (node.getNodeType()) {
case ARRAY:
ret = ((ArrayNode) node).get(index);
break;
case POJO:
try {
ret = JavaUtils.getIndexValue( ((POJONode) node).getPojo(), index);
} catch (SecurityException | IllegalArgumentException e) {
Util.wrapCoreException("Exception setting indexed value", e);
}
break;
default:
assert (false);
}
return getXValue(ret);
}
// Set a positional index value
@Override
public XValue setXValue(XValue xobj, int index, XValue value) throws CoreException
{
assert (xobj != null && !xobj.isNull());
if(xobj == null || xobj.isNull())
throw new CoreException("Cannot set indexed value to null object");
assert (index >= 0);
JsonNode node = getJsonNode(xobj);
switch (node.getNodeType()) {
case OBJECT:
((ArrayNode) node).set(index, JSONUtils.toJsonType(value));
break;
case POJO:
JavaUtils.setNamePositionalValue(((POJONode) node).getPojo(), index, JSONUtils.toJsonType(value));
break;
default:
throw new InvalidArgumentException("Cannot set indexed value to non Array class: " + describeClass(node));
}
return xobj;
}
@Override
public XValue getXValue(Object obj) throws InvalidArgumentException
{
if(obj== null )
return nullXValue() ;
if( obj instanceof XValue )
return( (XValue) obj );
if( obj instanceof JsonNode || isInstanceOfFamily(obj) )
return XValue.newXValue( this , obj , false );
return XValue.newXValue(obj);
}
@Override
public boolean isAtomic(Object value)
{
return JSONUtils.isAtomic(value);
}
private static JsonNode getJsonNode(XValue xobj) throws InvalidArgumentException
{
if(xobj == null)
return JSONUtils.nullValue();
Object obj = xobj.asObject();
return getJsonNode(obj);
}
private static JsonNode getJsonNode(Object obj) throws InvalidArgumentException
{
if(obj == null)
return JSONUtils.nullValue();
assert (obj instanceof JsonNode);
if(!(obj instanceof JsonNode))
throw new InvalidArgumentException("Not a Json Node type: " + describeClass(obj));
return (JsonNode) obj;
}
@Override
public XValue nullXValue()
{
try {
return XValue.newXValue(this , _nullValue , false );
} catch (InvalidArgumentException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public Object nullValue()
{
// TODO Auto-generated method stub
return _nullValue;
}
public static JSONTypeFamily getInstance()
{
return _instance;
}
@Override
public boolean hasKey(Object obj, String key) {
if( obj instanceof ObjectNode )
return ((ObjectNode)obj).has(key);
return false ;
}
@Override
public XValue append(Object value, XValue v) throws InvalidArgumentException {
assert( value instanceof JsonNode );
ArrayNode a = JSONUtils.newJsonArray();
JsonNode node = (JsonNode) value ;
switch (node.getNodeType()) {
case ARRAY:
a.addAll( (ArrayNode) v.asJson() );
break;
case OBJECT:
default :
a.add( v.asJson() );
}
return XValue.newXValue(TypeFamily.JSON, a) ;
}
@Override
public boolean isContainer(Object obj) {
JsonNode node = (JsonNode) obj;
switch (node.getNodeType()) {
case ARRAY:
case OBJECT:
return true ;
default:
return false ;
}
}
}