// Path: src/main/java/de/galan/verjson/step/ProcessStepException.java
// public class ProcessStepException extends ReadException {
// 
// 	public ProcessStepException(String message) {
// 		super(message);
// 	}
// 
// 
// 	public ProcessStepException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// }
// 
// Path: src/main/java/de/galan/verjson/step/Step.java
// public interface Step {
// 
// 	/** Perform action on wrapped root node */
// 	public void process(JsonNode node) throws ProcessStepException;
// 
// }
// 
// Path: src/main/java/de/galan/verjson/util/MetaWrapper.java
// public class MetaWrapper {
// 
// 	public static final String ID_VERSION = "$v";
// 	public static final String ID_NAMESPACE = "$ns";
// 	public static final String ID_DATA = "$d";
// 	public static final String ID_TIMESTAMP = "$ts";
// 
// 	/** Incremental version */
// 	@JsonProperty(ID_VERSION)
// 	private long version;
// 
// 	/** Namespace for the data object */
// 	@JsonProperty(ID_NAMESPACE)
// 	private String namespace;
// 
// 	/** Timestamp when the object was serialized */
// 	@JsonProperty(ID_TIMESTAMP)
// 	private Date timestamp;
// 
// 	/** Actual payload */
// 	@JsonProperty(ID_DATA)
// 	private Object data;
// 
// 
// 	public MetaWrapper(long version, String namespace, Object data, Date timestamp) {
// 		this.version = version;
// 		this.namespace = namespace;
// 		this.data = data;
// 		this.timestamp = timestamp;
// 	}
// 
// 
// 	/** Returns the data node from a wrapped JsonNode */
// 	public static JsonNode getData(JsonNode node) {
// 		return getObj(obj(node), MetaWrapper.ID_DATA);
// 	}
// 
// 
// 	/** Returns the namespace from a wrapped JsonNode */
// 	public static String getNamespace(JsonNode node) {
// 		JsonNode nodeNs = obj(node).get(ID_NAMESPACE);
// 		return (nodeNs != null) ? nodeNs.asText() : null;
// 	}
// 
// 
// 	/** Returns the source version from a wrapped JsonNode */
// 	public static Long getVersion(JsonNode node) {
// 		return obj(node).get(ID_VERSION).asLong();
// 	}
// 
// 
// 	/** Sets the version on a wrapped JsonNode */
// 	public static void setVersion(JsonNode node, Long version) {
// 		obj(node).put(ID_VERSION, version);
// 	}
// 
// 
// 	/** Returns the timestamp from a wrapped JsonNode */
// 	public static Date getTimestamp(JsonNode node) {
// 		String text = obj(node).get(ID_TIMESTAMP).asText();
// 		return isNotBlank(text) ? from(instantUtc(text)).toDate() : null;
// 	}
// 
// }
// 
// Path: src/main/java/de/galan/verjson/util/ReadException.java
// public abstract class ReadException extends Exception {
// 
// 	public ReadException(String message) {
// 		super(message);
// 	}
// 
// 
// 	public ReadException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// }


import static de.galan.commons.time.Instants.*;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Preconditions;
import de.galan.verjson.step.ProcessStepException;
import de.galan.verjson.step.Step;
import de.galan.verjson.util.MetaWrapper;
import de.galan.verjson.util.ReadException;
		return wrapper;
	}


	public T readPlain(JsonNode node, long version) throws VersionNotSupportedException, NamespaceMismatchException, ProcessStepException, IOReadException {
		return read(wrapPlainNode(node, version));
	}


	public T read(String json) throws VersionNotSupportedException, NamespaceMismatchException, ProcessStepException, IOReadException {
		T result = null;
		try {
			result = read(readTree(json));
		}
		catch (IOException ex) {
			throw new IOReadException("Reading json failed: " + ex.getMessage(), ex);
		}
		return result;
	}


	public T read(JsonNode node) throws VersionNotSupportedException, NamespaceMismatchException, ProcessStepException, IOReadException {
		T result = null;
		try {
			verifyNamespace(node);
			Long jsonVersion = verifyVersion(node);
			steps.get(jsonVersion).process(node);
			JsonNode data = MetaWrapper.getData(node);
			result = getMapper().treeToValue(data, getValueClass());
		}
// Path: src/main/java/de/galan/verjson/step/ProcessStepException.java
// public class ProcessStepException extends ReadException {
// 
// 	public ProcessStepException(String message) {
// 		super(message);
// 	}
// 
// 
// 	public ProcessStepException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// }
// 
// Path: src/main/java/de/galan/verjson/step/Step.java
// public interface Step {
// 
// 	/** Perform action on wrapped root node */
// 	public void process(JsonNode node) throws ProcessStepException;
// 
// }
// 
// Path: src/main/java/de/galan/verjson/util/MetaWrapper.java
// public class MetaWrapper {
// 
// 	public static final String ID_VERSION = "$v";
// 	public static final String ID_NAMESPACE = "$ns";
// 	public static final String ID_DATA = "$d";
// 	public static final String ID_TIMESTAMP = "$ts";
// 
// 	/** Incremental version */
// 	@JsonProperty(ID_VERSION)
// 	private long version;
// 
// 	/** Namespace for the data object */
// 	@JsonProperty(ID_NAMESPACE)
// 	private String namespace;
// 
// 	/** Timestamp when the object was serialized */
// 	@JsonProperty(ID_TIMESTAMP)
// 	private Date timestamp;
// 
// 	/** Actual payload */
// 	@JsonProperty(ID_DATA)
// 	private Object data;
// 
// 
// 	public MetaWrapper(long version, String namespace, Object data, Date timestamp) {
// 		this.version = version;
// 		this.namespace = namespace;
// 		this.data = data;
// 		this.timestamp = timestamp;
// 	}
// 
// 
// 	/** Returns the data node from a wrapped JsonNode */
// 	public static JsonNode getData(JsonNode node) {
// 		return getObj(obj(node), MetaWrapper.ID_DATA);
// 	}
// 
// 
// 	/** Returns the namespace from a wrapped JsonNode */
// 	public static String getNamespace(JsonNode node) {
// 		JsonNode nodeNs = obj(node).get(ID_NAMESPACE);
// 		return (nodeNs != null) ? nodeNs.asText() : null;
// 	}
// 
// 
// 	/** Returns the source version from a wrapped JsonNode */
// 	public static Long getVersion(JsonNode node) {
// 		return obj(node).get(ID_VERSION).asLong();
// 	}
// 
// 
// 	/** Sets the version on a wrapped JsonNode */
// 	public static void setVersion(JsonNode node, Long version) {
// 		obj(node).put(ID_VERSION, version);
// 	}
// 
// 
// 	/** Returns the timestamp from a wrapped JsonNode */
// 	public static Date getTimestamp(JsonNode node) {
// 		String text = obj(node).get(ID_TIMESTAMP).asText();
// 		return isNotBlank(text) ? from(instantUtc(text)).toDate() : null;
// 	}
// 
// }
// 
// Path: src/main/java/de/galan/verjson/util/ReadException.java
// public abstract class ReadException extends Exception {
// 
// 	public ReadException(String message) {
// 		super(message);
// 	}
// 
// 
// 	public ReadException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// }


// Path: src/main/java/de/galan/verjson/core/Verjson.java
import static de.galan.commons.time.Instants.*;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Preconditions;
import de.galan.verjson.step.ProcessStepException;
import de.galan.verjson.step.Step;
import de.galan.verjson.util.MetaWrapper;
import de.galan.verjson.util.ReadException;

		return wrapper;
	}


	public T readPlain(JsonNode node, long version) throws VersionNotSupportedException, NamespaceMismatchException, ProcessStepException, IOReadException {
		return read(wrapPlainNode(node, version));
	}


	public T read(String json) throws VersionNotSupportedException, NamespaceMismatchException, ProcessStepException, IOReadException {
		T result = null;
		try {
			result = read(readTree(json));
		}
		catch (IOException ex) {
			throw new IOReadException("Reading json failed: " + ex.getMessage(), ex);
		}
		return result;
	}


	public T read(JsonNode node) throws VersionNotSupportedException, NamespaceMismatchException, ProcessStepException, IOReadException {
		T result = null;
		try {
			verifyNamespace(node);
			Long jsonVersion = verifyVersion(node);
			steps.get(jsonVersion).process(node);
			JsonNode data = MetaWrapper.getData(node);
			result = getMapper().treeToValue(data, getValueClass());
		}
		catch (ReadException ex) {