package com.lordofthejars.nosqlunit.objects;
import com.lordofthejars.nosqlunit.util.ReflectionUtil;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class KeyValueObjectMapper {
private static final String NO_IMPLEMENTATION_PROVIDED = "";
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public Map<Object, Object> readValues(InputStream dataStream) {
Map<Object, Object> objects = new HashMap<Object, Object>();
try {
Iterator<JsonNode> elements = dataElements(dataStream);
objects.putAll(readElements(elements));
} catch (JsonParseException e) {
throw new IllegalArgumentException(e);
} catch (JsonMappingException e) {
throw new IllegalArgumentException(e);
} catch (IOException e) {
throw new IllegalArgumentException(e);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Implementation class is not found in classpath", e);
}
return objects;
}
private Map<Object, Object> readElements(Iterator<JsonNode> elements) throws IOException,
JsonParseException, JsonMappingException, ClassNotFoundException {
Map<Object, Object> objects = new HashMap<Object, Object>();
while (elements.hasNext()) {
JsonNode element = elements.next();
Object keyValue = keyValue(element);
Object readValue = readValue(element);
objects.put(keyValue, readValue);
}
return objects;
}
private Object readValue(JsonNode element) throws IOException, JsonParseException, JsonMappingException,
ClassNotFoundException {
JsonNode value = valueNode(element);
String implementationValue = implementationClass(element);
Object readObject = readElement(value, implementationValue);
return readObject;
}
private Object readElement(JsonNode value, String implementationValue) throws IOException, JsonParseException,
JsonMappingException, ClassNotFoundException {
Object readObject = null;
if (value.isArray()) {
readObject = readArray(value, implementationValue);
} else {
readObject = readObject(value, implementationValue);
}
return readObject;
}
private Object readObject(JsonNode value, String implementationValue) throws IOException, JsonParseException,
JsonMappingException, ClassNotFoundException {
Object readObject;
if (value.isObject()) {
readObject = unmarshallObject(value, implementationValue);
} else {
readObject = simpleValue(value);
}
return readObject;
}
private Object unmarshallObject(JsonNode value, String implementationValue) throws IOException, JsonParseException,
JsonMappingException, ClassNotFoundException {
if(NO_IMPLEMENTATION_PROVIDED.equals(implementationValue)) {
throw new IllegalArgumentException("No implementation class has been provided.");
}
return OBJECT_MAPPER.readValue(value, Class.forName(implementationValue));
}
private Object readArray(JsonNode value, String implementationValue) throws IOException, JsonParseException,
JsonMappingException, ClassNotFoundException {
Object readObject;
Iterator<JsonNode> elements = value.getElements();
Collection<Object> objects = collection(implementationValue);
while (elements.hasNext()) {
JsonNode newElement = elements.next();
objects.add(readValue(newElement));
}
readObject = objects;
return readObject;
}
private String implementationKeyClass(JsonNode element) {
String implementationValue = NO_IMPLEMENTATION_PROVIDED;
if (element.has(KeyValueTokens.IMPLEMENTATION_KEY_TOKEN)) {
implementationValue = element.path(KeyValueTokens.IMPLEMENTATION_KEY_TOKEN).getTextValue();
}
return implementationValue.trim();
}
private String implementationClass(JsonNode element) {
String implementationValue = NO_IMPLEMENTATION_PROVIDED;
if (element.has(KeyValueTokens.IMPLEMENTATION_TOKEN)) {
implementationValue = element.path(KeyValueTokens.IMPLEMENTATION_TOKEN).getTextValue();
}
return implementationValue.trim();
}
private JsonNode valueNode(JsonNode element) {
if (element.has(KeyValueTokens.VALUE_TOKEN)) {
return element.path(KeyValueTokens.VALUE_TOKEN);
} else {
throw new IllegalArgumentException("Given dataset does not contain "+KeyValueTokens.VALUE_TOKEN+" token.");
}
}
private Collection<Object> collection(String implementation) {
if(implementation.equals(NO_IMPLEMENTATION_PROVIDED)) {
return new ArrayList<Object>();
} else {
return ReflectionUtil.createInstance(implementation);
}
}
private Object simpleValue(JsonNode simpleValue) {
if (simpleValue.isNumber()) {
switch (simpleValue.getNumberType()) {
case BIG_DECIMAL:
return simpleValue.getDecimalValue();
case BIG_INTEGER:
return simpleValue.getBigIntegerValue();
case DOUBLE:
return simpleValue.getDoubleValue();
case FLOAT:
return simpleValue.getDoubleValue();
case INT:
return simpleValue.getIntValue();
case LONG:
return simpleValue.getLongValue();
default:
return simpleValue.getTextValue();
}
} else {
return simpleValue.getTextValue();
}
}
private Object keyValue(JsonNode element) throws JsonParseException, JsonMappingException, IOException, ClassNotFoundException {
Object keyValue;
if (element.has(KeyValueTokens.KEY_TOKEN)) {
String implementationKey = implementationKeyClass(element);
keyValue = readElement(element.path(KeyValueTokens.KEY_TOKEN), implementationKey);
} else {
throw new IllegalArgumentException("Given dataset does not contain "+KeyValueTokens.KEY_TOKEN+" token.");
}
return keyValue;
}
private Iterator<JsonNode> dataElements(InputStream dataStream) throws IOException, JsonParseException,
JsonMappingException {
JsonNode rootNode = OBJECT_MAPPER.readValue(dataStream, JsonNode.class);
JsonNode dataNode = rootNode.path(KeyValueTokens.DATA_TOKEN);
Iterator<JsonNode> elements = dataNode.getElements();
return elements;
}
}