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

com.arangodb.velocypack.VPackParser Maven / Gradle / Ivy

There is a newer version: 7.15.0
Show newest version
/*
 * DISCLAIMER
 *
 * Copyright 2016 ArangoDB GmbH, Cologne, Germany
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Copyright holder is ArangoDB GmbH, Cologne, Germany
 */

package com.arangodb.velocypack;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.json.simple.JSONValue;
import org.json.simple.parser.ContentHandler;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;

import com.arangodb.velocypack.exception.VPackBuilderException;
import com.arangodb.velocypack.exception.VPackException;

/**
 * @author Mark - mark at arangodb.com
 *
 */
public class VPackParser {

	private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");// ISO 8601
	private static final char OBJECT_OPEN = '{';
	private static final char OBJECT_CLOSE = '}';
	private static final char ARRAY_OPEN = '[';
	private static final char ARRAY_CLOSE = ']';
	private static final char FIELD = ':';
	private static final char SEPARATOR = ',';
	private static final String NULL = "null";
	private static final String NON_REPRESENTABLE_TYPE = "(non-representable type)";
	private final Map deserializers;
	private final Map> deserializersByName;
	private final Map, VPackJsonSerializer> serializers;
	private final Map, VPackJsonSerializer>> serializersByName;

	public VPackParser() {
		super();
		deserializers = new HashMap();
		deserializersByName = new HashMap>();
		serializers = new HashMap, VPackJsonSerializer>();
		serializersByName = new HashMap, VPackJsonSerializer>>();
	}

	public String toJson(final VPackSlice vpack) throws VPackException {
		return toJson(vpack, false);
	}

	public String toJson(final VPackSlice vpack, final boolean includeNullValues) throws VPackException {
		final StringBuilder json = new StringBuilder();
		parse(null, null, vpack, json, includeNullValues);
		return json.toString();
	}

	public VPackParser registerDeserializer(
		final String attribute,
		final ValueType type,
		final VPackJsonDeserializer deserializer) {
		Map byName = deserializersByName.get(attribute);
		if (byName == null) {
			byName = new HashMap();
			deserializersByName.put(attribute, byName);
		}
		byName.put(type, deserializer);
		return this;
	}

	public VPackParser registerDeserializer(final ValueType type, final VPackJsonDeserializer deserializer) {
		deserializers.put(type, deserializer);
		return this;
	}

	private VPackJsonDeserializer getDeserializer(final String attribute, final ValueType type) {
		VPackJsonDeserializer deserializer = null;
		final Map byName = deserializersByName.get(attribute);
		if (byName != null) {
			deserializer = byName.get(type);
		}
		if (deserializer == null) {
			deserializer = deserializers.get(type);
		}
		return deserializer;
	}

	public  VPackParser registerSerializer(
		final String attribute,
		final Class type,
		final VPackJsonSerializer serializer) {
		Map, VPackJsonSerializer> byName = serializersByName.get(attribute);
		if (byName == null) {
			byName = new HashMap, VPackJsonSerializer>();
			serializersByName.put(attribute, byName);
		}
		byName.put(type, serializer);
		return this;
	}

	public  VPackParser registerSerializer(final Class type, final VPackJsonSerializer serializer) {
		serializers.put(type, serializer);
		return this;
	}

	private VPackJsonSerializer getSerializer(final String attribute, final Class type) {
		VPackJsonSerializer serializer = null;
		final Map, VPackJsonSerializer> byName = serializersByName.get(attribute);
		if (byName != null) {
			serializer = byName.get(type);
		}
		if (serializer == null) {
			serializer = serializers.get(type);
		}
		return serializer;
	}

	private void parse(
		final VPackSlice parent,
		final String attribute,
		final VPackSlice value,
		final StringBuilder json,
		final boolean includeNullValues) throws VPackException {

		VPackJsonDeserializer deserializer = null;
		if (attribute != null) {
			appendField(attribute, json);
			deserializer = getDeserializer(attribute, value.type());
		}
		if (deserializer != null) {
			deserializer.deserialize(parent, attribute, value, json);
		} else {
			if (value.isObject()) {
				parseObject(value, json, includeNullValues);
			} else if (value.isArray()) {
				parseArray(value, json, includeNullValues);
			} else if (value.isBoolean()) {
				json.append(value.getAsBoolean());
			} else if (value.isString()) {
				json.append(JSONValue.toJSONString(value.getAsString()));
			} else if (value.isNumber()) {
				json.append(value.getAsNumber());
			} else if (value.isDate()) {
				json.append(JSONValue.toJSONString(DATE_FORMAT.format(value.getAsDate())));
			} else if (value.isNull()) {
				json.append(NULL);
			} else {
				json.append(JSONValue.toJSONString(NON_REPRESENTABLE_TYPE));
			}
		}
	}

	private static void appendField(final String attribute, final StringBuilder json) {
		json.append(JSONValue.toJSONString(attribute));
		json.append(FIELD);
	}

	private void parseObject(final VPackSlice value, final StringBuilder json, final boolean includeNullValues)
			throws VPackException {
		json.append(OBJECT_OPEN);
		int added = 0;
		for (final Iterator> iterator = value.objectIterator(); iterator.hasNext();) {
			final Entry next = iterator.next();
			final VPackSlice nextValue = next.getValue();
			if (!nextValue.isNull() || includeNullValues) {
				if (added++ > 0) {
					json.append(SEPARATOR);
				}
				parse(value, next.getKey(), nextValue, json, includeNullValues);
			}
		}
		json.append(OBJECT_CLOSE);
	}

	private void parseArray(final VPackSlice value, final StringBuilder json, final boolean includeNullValues)
			throws VPackException {
		json.append(ARRAY_OPEN);
		int added = 0;
		for (int i = 0; i < value.getLength(); i++) {
			final VPackSlice valueAt = value.get(i);
			if (!valueAt.isNull() || includeNullValues) {
				if (added++ > 0) {
					json.append(SEPARATOR);
				}
				parse(value, null, valueAt, json, includeNullValues);
			}
		}
		json.append(ARRAY_CLOSE);
	}

	public VPackSlice fromJson(final String json) throws VPackException {
		return fromJson(json, false);
	}

	public VPackSlice fromJson(final String json, final boolean includeNullValues) throws VPackException {
		final VPackBuilder builder = new VPackBuilder();
		final JSONParser parser = new JSONParser();
		final ContentHandler contentHandler = new VPackContentHandler(builder, includeNullValues, this);
		try {
			parser.parse(json, contentHandler);
		} catch (final ParseException e) {
			throw new VPackBuilderException(e);
		}
		return builder.slice();
	}

	public VPackSlice fromJson(final Iterable jsons) throws VPackException {
		return fromJson(jsons, false);
	}

	public VPackSlice fromJson(final Iterable jsons, final boolean includeNullValues) throws VPackException {
		final VPackBuilder builder = new VPackBuilder();
		final JSONParser parser = new JSONParser();
		final ContentHandler contentHandler = new VPackContentHandler(builder, includeNullValues, this);
		try {
			builder.add(ValueType.ARRAY);
			for (final String json : jsons) {
				parser.parse(json, contentHandler);
			}
			builder.close();
		} catch (final ParseException e) {
			throw new VPackBuilderException(e);
		}
		return builder.slice();
	}

	private static class VPackContentHandler implements ContentHandler {

		private final VPackBuilder builder;
		private String attribute;
		private final boolean includeNullValues;
		private final VPackParser parser;

		public VPackContentHandler(final VPackBuilder builder, final boolean includeNullValues,
			final VPackParser parser) {
			this.builder = builder;
			this.includeNullValues = includeNullValues;
			this.parser = parser;
			attribute = null;
		}

		private void add(final ValueType value) throws ParseException {
			try {
				builder.add(attribute, value);
				attribute = null;
			} catch (final VPackBuilderException e) {
				throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
			}
		}

		private void add(final String value) throws ParseException {
			try {
				builder.add(attribute, value);
				attribute = null;
			} catch (final VPackBuilderException e) {
				throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
			}
		}

		private void add(final Boolean value) throws ParseException {
			try {
				builder.add(attribute, value);
				attribute = null;
			} catch (final VPackBuilderException e) {
				throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
			}
		}

		private void add(final Double value) throws ParseException {
			try {
				builder.add(attribute, value);
				attribute = null;
			} catch (final VPackBuilderException e) {
				throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
			}
		}

		private void add(final Long value) throws ParseException {
			try {
				builder.add(attribute, value);
				attribute = null;
			} catch (final VPackBuilderException e) {
				throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
			}
		}

		private void close() throws ParseException {
			try {
				builder.close();
			} catch (final VPackBuilderException e) {
				throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
			}
		}

		@Override
		public void startJSON() throws ParseException, IOException {
		}

		@Override
		public void endJSON() throws ParseException, IOException {
		}

		@Override
		public boolean startObject() throws ParseException, IOException {
			add(ValueType.OBJECT);
			return true;
		}

		@Override
		public boolean endObject() throws ParseException, IOException {
			close();
			return true;
		}

		@Override
		public boolean startObjectEntry(final String key) throws ParseException, IOException {
			attribute = key;
			return true;
		}

		@Override
		public boolean endObjectEntry() throws ParseException, IOException {
			return true;
		}

		@Override
		public boolean startArray() throws ParseException, IOException {
			add(ValueType.ARRAY);
			return true;
		}

		@Override
		public boolean endArray() throws ParseException, IOException {
			close();
			return true;
		}

		@SuppressWarnings("unchecked")
		@Override
		public boolean primitive(final Object value) throws ParseException, IOException {
			if (value == null) {
				if (includeNullValues) {
					add(ValueType.NULL);
				}
			} else {
				final VPackJsonSerializer serializer = parser.getSerializer(attribute, value.getClass());
				if (serializer != null) {
					try {
						((VPackJsonSerializer) serializer).serialize(builder, attribute, value);
						attribute = null;
					} catch (final VPackBuilderException e) {
						throw new ParseException(ParseException.ERROR_UNEXPECTED_EXCEPTION);
					}
				} else if (String.class.isAssignableFrom(value.getClass())) {
					add(String.class.cast(value));
				} else if (Boolean.class.isAssignableFrom(value.getClass())) {
					add(Boolean.class.cast(value));
				} else if (Double.class.isAssignableFrom(value.getClass())) {
					add(Double.class.cast(value));
				} else if (Number.class.isAssignableFrom(value.getClass())) {
					add(Long.class.cast(value));
				}
			}
			return true;
		}

	}

}