All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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