org.etlunit.parser.ETLTestValueObjectImpl Maven / Gradle / Ivy
package org.etlunit.parser;
import net.sf.json.util.JSONUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.etlunit.util.NeedsTest;
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
public class ETLTestValueObjectImpl extends ETLTestDebugTraceableImpl implements ETLTestValueObject
{
private final String strValue;
private final value_type vt;
private final List listValue;
private final Map mapValue;
private final Object pojoValue;
public ETLTestValueObjectImpl(Object value)
{
this(value, null);
}
public ETLTestValueObjectImpl(Object value, Token t)
{
pojoValue = value;
vt = value_type.pojo;
listValue = null;
mapValue = null;
strValue = null;
}
public ETLTestValueObjectImpl(String value)
{
this(value, false);
}
public ETLTestValueObjectImpl(String value, Token t)
{
this(value, false, t);
}
public ETLTestValueObjectImpl(String value, boolean literal)
{
strValue = value;
listValue = null;
mapValue = null;
pojoValue = null;
vt = literal ? value_type.literal : value_type.quoted_string;
}
public ETLTestValueObjectImpl(boolean value, Token t)
{
super(t);
strValue = Boolean.toString(value);
listValue = null;
mapValue = null;
pojoValue = null;
vt = value_type.literal;
}
public ETLTestValueObjectImpl(String value, boolean literal, Token t)
{
super(t);
strValue = value;
listValue = null;
mapValue = null;
pojoValue = null;
vt = literal ? value_type.literal : value_type.quoted_string;
}
public ETLTestValueObjectImpl(List value)
{
strValue = null;
listValue = value;
mapValue = null;
pojoValue = null;
vt = value_type.list;
}
public ETLTestValueObjectImpl(Map value)
{
strValue = null;
listValue = null;
mapValue = value;
pojoValue = null;
vt = value_type.object;
}
@NeedsTest
@Override
public String getValueAsString()
{
throwNull(strValue);
return strValue;
}
@Override
public boolean getValueAsBoolean()
{
String val = getValueAsString();
String valLower = val.toLowerCase();
if (valLower.equals("true"))
{
return true;
}
else if (valLower.equals("false"))
{
return false;
}
throw new IllegalArgumentException("Value cannot be represented as boolean true or false: " + val);
}
@NeedsTest
@Override
public List getValueAsList()
{
throwNull(listValue);
return listValue;
}
@NeedsTest
@Override
public List getValueAsListOfStrings()
{
List slist = new ArrayList();
List list = getValueAsList();
Iterator it = list.iterator();
while (it.hasNext())
{
ETLTestValueObject next = it.next();
slist.add(next != null ? next.getValueAsString() : null);
}
return slist;
}
@NeedsTest
@Override
public Map getValueAsMap()
{
throwNull(mapValue);
return mapValue;
}
@Override
public Object getValueAsPojo()
{
return pojoValue;
}
private void throwNull(Object v)
{
if (v == null)
{
throw new UnsupportedOperationException("Value Object cannot be coerced to this type");
}
}
@Override
public value_type getValueType()
{
return vt;
}
@Override
public ETLTestValueObject queryRequired(String path)
{
ETLTestValueObject res = query(path);
if (res == null)
{
throw new IllegalArgumentException("Path '" + path + "' not found");
}
return res;
}
@Override
public ETLTestValueObject query(String path)
{
return queryImpl(path);
}
@NeedsTest
private ETLTestValueObject queryImpl(String path)
{
ETLTestValueObject val = this;
StringTokenizer st = new StringTokenizer(path, ".");
while (st.hasMoreTokens())
{
String token = st.nextToken();
switch (val.getValueType())
{
case literal:
case list:
case quoted_string:
throw new UnsupportedOperationException("Path queries are only valid on object types");
case object:
Map map = val.getValueAsMap();
if (!map.containsKey(token))
{
return null;
}
val = map.get(token);
break;
case pojo:
try
{
BeanInfo info = Introspector.getBeanInfo(pojoValue.getClass());
for (PropertyDescriptor pd : info.getPropertyDescriptors())
{
if (!pd.getName().equals(token))
{
continue;
}
Method m = pd.getReadMethod();
if (m != null)
{
Object invoke = m.invoke(pojoValue);
if (invoke instanceof String)
{
val = new ETLTestValueObjectImpl((String) invoke);
}
else
{
val = new ETLTestValueObjectImpl(invoke);
}
}
}
}
catch (Exception e)
{
throw new RuntimeException(e);
}
break;
}
}
return val;
}
@Override
public ETLTestValueObject merge(ETLTestValueObject obj)
{
return merge(obj, merge_type.distinct_merge);
}
@Override
public ETLTestValueObject merge(ETLTestValueObject obj, merge_type type)
{
if (getValueType() != value_type.object || obj.getValueType() != value_type.object)
{
throw new UnsupportedOperationException("Merge operations only supported on object types");
}
ETLTestValueObjectImpl impl = new ETLTestValueObjectImpl(new HashMap(mapValue));
Map valueOther = obj.getValueAsMap();
// create a unified key set
Set setCombined = new TreeSet(getValueAsMap().keySet());
setCombined.addAll(obj.getValueAsMap().keySet());
// iterate through the keys, and handle l/r insert / merge as required
for (String s : setCombined)
{
// check the lvalue (me)
ETLTestValueObject lvalue = mapValue.get(s);
ETLTestValueObject rvalue = valueOther.get(s);
if (lvalue == null && rvalue == null)
{
throw new IllegalStateException("What??!?");
}
else if (lvalue == null)
{
// insert into impl
impl.mapValue.put(s, rvalue);
}
else if (rvalue == null)
{
// impl already has
}
else
{
// both sides contain, need to merge.
if (lvalue.getValueType() != rvalue.getValueType() && type == merge_type.distinct_merge)
{
throw new IllegalArgumentException("Object types not compatible: [lvalue = '"
+ lvalue.getValueType()
+ "', rvalue = '"
+ rvalue.getValueType()
+ "']");
}
else if (lvalue.getValueType() != value_type.object)
{
switch (type)
{
case left_merge:
// leave the left value in place
break;
case right_merge:
// copy the rvalue to the lvalue
impl.mapValue.put(s, rvalue);
break;
case distinct_merge:
throw new IllegalArgumentException("Non object types can not be merged: [lvalue = '"
+ lvalue.getValueType()
+ "', rvalue = '"
+ rvalue.getValueType()
+ "']");
}
}
else
{
// do a recursive merge (Ryan loves this!)
// revisit this as it may cause unnecessary object copies
impl.mapValue.put(s, lvalue.merge(rvalue, type));
}
}
}
return impl;
}
@Override
public ETLTestValueObject copy()
{
switch (vt)
{
case quoted_string:
return new ETLTestValueObjectImpl(strValue, false);
case object:
return new ETLTestValueObjectImpl(deepCopy(mapValue));
case list:
return new ETLTestValueObjectImpl(deepCopy(listValue));
case literal:
return new ETLTestValueObjectImpl(strValue, true);
case pojo:
return new ETLTestValueObjectImpl(pojoValue);
default:
throw new Error("JVM fault");
}
}
private Map deepCopy(Map mapValue)
{
Map map = new HashMap();
for (Map.Entry entry : mapValue.entrySet())
{
map.put(entry.getKey(), entry.getValue().copy());
}
return map;
}
private List deepCopy(List listValue)
{
List list = new ArrayList();
for (ETLTestValueObject value : listValue)
{
list.add(value.copy());
}
return list;
}
@Override
public JsonNode getJsonNode()
{
ObjectMapper mapper = new ObjectMapper();
try
{
switch (getValueType())
{
case quoted_string:
return mapper.readValue(JSONUtils.valueToString(getValueAsString()), JsonNode.class);
case object:
{
ObjectNode node = mapper.createObjectNode();
Map map = getValueAsMap();
for (Map.Entry enode : map.entrySet())
{
String key = enode.getKey();
ETLTestValueObject value = enode.getValue();
node.put(key, value.getJsonNode());
}
return node;
}
case list:
{
ArrayNode node = mapper.createArrayNode();
List list = getValueAsList();
for (ETLTestValueObject enode : list)
{
node.add(enode != null ? enode.getJsonNode() : null);
}
return node;
}
case literal:
return mapper.readValue(getValueAsString(), JsonNode.class);
case pojo:
return mapper.readValue(getValueAsString(), JsonNode.class);
default:
throw new IllegalArgumentException("JVM Fault");
}
}
catch (IOException e)
{
throw new IllegalArgumentException(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy