Package com.bazaarvoice.commons.data.dao.json.schema

Source Code of com.bazaarvoice.commons.data.dao.json.schema.JSONSchemaMarshaller$JSONSchemaPatternPropertyMarshaller

package com.bazaarvoice.commons.data.dao.json.schema;

import com.bazaarvoice.commons.data.dao.json.AbstractJSONMarshaller;
import com.bazaarvoice.commons.data.dao.json.AbstractMappingJSONMarshaller;
import com.bazaarvoice.commons.data.model.json.GenericJSONArrayList;
import com.bazaarvoice.commons.data.model.json.schema.JSONSchema;
import com.bazaarvoice.commons.data.model.json.schema.JSONSchemaNamedProperty;
import com.bazaarvoice.commons.data.model.json.schema.JSONSchemaPatternProperty;
import com.bazaarvoice.commons.data.model.json.schema.JSONSchemaType;
import com.bazaarvoice.commons.data.model.json.schema.JSONSchemaTypes;
import com.bazaarvoice.commons.data.model.json.schema.types.AbstractJSONSchemaNumberType;
import com.bazaarvoice.commons.data.model.json.schema.types.AbstractJSONSchemaSimpleType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaAnyType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaArrayType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaBooleanType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaIntegerType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaNullType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaNumberType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaObjectType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaStringType;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaTextFormat;
import com.bazaarvoice.commons.data.model.json.schema.types.JSONSchemaUnionType;
import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.base.Strings;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.BiMap;
import com.google.common.collect.Collections2;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import javax.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JSONSchemaMarshaller extends AbstractJSONMarshaller<JSONSchema> {
    private static final String DEFAULT_SCHEMA_VERSION = "http://json-schema.org/draft-03/schema";

    private static final Pattern HTTP_CONTENT_TYPE_CHARSET = Pattern.compile("\\bcharset=([-a-zA-Z0-9]+)");

    private final JSONSchemaNamedPropertyMarshaller _namedPropertyMarshaller = new JSONSchemaNamedPropertyMarshaller();
    private final JSONSchemaPatternPropertyMarshaller _patternPropertyMarshaller = new JSONSchemaPatternPropertyMarshaller();

    private final BiMap<JSONSchemaTextFormat, String> _schemaTextFormatJSONValues = buildSchemaTextFormatJSONValues();
    private final BiMap<Class<? extends JSONSchemaType<?>>, String> _schemaTypeJSONTypeValues = buildSchemaTypeJSONTypeValues();

    private final LoadingCache<String, JSONSchema> _schemaCache = CacheBuilder.newBuilder().maximumSize(1000).concurrencyLevel(16).build(new JSONSchemaURLCacheLoader());

    private final SchemaJSONRetriever _schemaJSONRetriever;

    public JSONSchemaMarshaller() {
        this(new DefaultSchemaJSONRetriever());
    }

    public JSONSchemaMarshaller(SchemaJSONRetriever schemaJSONRetriever) {
        _schemaJSONRetriever = schemaJSONRetriever;
    }

    @Override
    public JSONObject toJSONObject(JSONSchema schema)
            throws JSONException {
        return toJSONObject(schema, true);
    }

    public JSONObject toJSONObject(JSONSchema schema, boolean includeSchemaVersion)
            throws JSONException {
        JSONObject jsonObject = new JSONObject().
                putOpt(JSONSchema.SCHEMA_KEY, includeSchemaVersion ? DEFAULT_SCHEMA_VERSION : null).
                putOpt("$ref", schema.getReferencesSchemaID()).
                putOpt("id", schema.getReferencesSchemaID() == null ? schema.getID() : null).
                putOpt("extends", schema.getExtendsSchemaID()).
                putOpt("type", schemaTypesToJSON(schema)).
                putOpt("required", schema.getRequired()).
                putOpt("title", schema.getTitle()).
                putOpt("description", schema.getDescription());

        List<Object> enums = Lists.newArrayList();
        for (final JSONSchemaType<?> type : schema.getTypes()) {
            if (type instanceof AbstractJSONSchemaSimpleType<?,?>) {
                AbstractJSONSchemaSimpleType<?,?> simpleType = (AbstractJSONSchemaSimpleType<?,?>) type;
                jsonObject.putOpt("default", simpleType.getDefaultValue());
                enums.addAll(simpleType.getEnumValues());
            }

            if (type instanceof AbstractJSONSchemaNumberType<?,?>) {
                AbstractJSONSchemaNumberType<?,?> numberType = (AbstractJSONSchemaNumberType<?, ?>) type;
                jsonObject.putOpt("minimum", numberType.getMinimum()).
                        putOpt("exclusiveMinimum", numberType.getMinimumExclusive()).
                        putOpt("maximum", numberType.getMaximum()).
                        putOpt("exclusiveMaximum", numberType.getMaximumExclusive()).
                        putOpt("divisibleBy", numberType.getDivisibleBy());
            }

            if (type instanceof JSONSchemaStringType) {
                JSONSchemaStringType stringType = (JSONSchemaStringType) type;
                jsonObject.putOpt("minLength", stringType.getMinimumLength()).
                        putOpt("maxLength", stringType.getMaximumLength()).
                        putOpt("pattern", stringType.getPattern()).
                        putOpt("format", convertSchemaTextFormatToJSONValue(stringType.getFormat(), stringType.getCustomFormat()));

            } else if (type instanceof JSONSchemaObjectType) {
                JSONSchemaObjectType objectType = (JSONSchemaObjectType) type;

                jsonObject.put("properties", _namedPropertyMarshaller.toMappedJSONObject(objectType.getNamedProperties()));

                if (!objectType.getPatternProperties().isEmpty()) {
                    jsonObject.put("patternProperties", _patternPropertyMarshaller.toMappedJSONObject(objectType.getPatternProperties()));
                }

                JSONSchema additionalPropertiesSchema = objectType.getAdditionalPropertiesSchema();
                if (additionalPropertiesSchema == null) {
                    jsonObject.put("additionalProperties", false);
                } else if (!additionalPropertiesSchema.isEmpty()) {
                    jsonObject.put("additionalProperties", toJSONObject(additionalPropertiesSchema, false));
                }
            } else if (type instanceof JSONSchemaArrayType) {
                JSONSchemaArrayType arrayType = (JSONSchemaArrayType) type;

                if (arrayType.isTuple()) {
                    jsonObject.put("items", toJSONArray(arrayType.getItems(), false));

                    JSONSchema additionalItemsSchema = arrayType.getAdditionalItemsSchema();
                    if (additionalItemsSchema == null) {
                        jsonObject.put("additionalItems", false);
                    } else if (!additionalItemsSchema.isEmpty()) {
                        jsonObject.put("additionalItems", toJSONObject(additionalItemsSchema, false));
                    }
                } else {
                    jsonObject.put("items", toJSONObject(arrayType.getItem(), false));
                }
            }
        }

        if (!enums.isEmpty()) {
            jsonObject.put("enum", enums);
        }

        return jsonObject;
    }

    @Override
    public JSONArray toJSONArray(Collection<? extends JSONSchema> objects) {
        return toJSONArray(objects, true);
    }

    protected JSONArray toJSONArray(Collection<? extends JSONSchema> objects, final boolean includeSchemaVersion) {
        return new JSONArray(toJSONList(objects, includeSchemaVersion));
    }

    private Collection<JSONObject> toJSONList(Collection<? extends JSONSchema> objects, final boolean includeSchemaVersion) {
        return Collections2.transform(objects, new Function<JSONSchema, JSONObject>() {
            @Override
            public JSONObject apply(JSONSchema schema) {
                try {
                    return toJSONObject(schema, includeSchemaVersion);
                } catch (JSONException e) {
                    throw Throwables.propagate(e);
                }
            }
        });
    }

    @Override
    public JSONSchema fromJSONObject(JSONObject jsonObject)
            throws JSONException {
        if (!DEFAULT_SCHEMA_VERSION.equals(jsonObject.optString(JSONSchema.SCHEMA_KEY, DEFAULT_SCHEMA_VERSION))) {
            throw new IllegalArgumentException("Unrecognized schema version: " + jsonObject.optString(JSONSchema.SCHEMA_KEY));
        }

        JSONSchema jsonSchema = new JSONSchema();
        String id = jsonObject.optString("id", null);
        if (!Strings.isNullOrEmpty(id)) {
            jsonSchema.setID(id);
            _schemaCache.put(id, jsonSchema);
        }

        String referencesSchemaID = jsonObject.optString("$ref", null);
        if (!Strings.isNullOrEmpty(referencesSchemaID)) {
            jsonSchema.setReferencesSchema(fromSchemaID(referencesSchemaID));
        }

        String extendsSchemaID = jsonObject.optString("extends", null);
        if (!Strings.isNullOrEmpty(extendsSchemaID)) {
            jsonSchema.setExtendsSchema(fromSchemaID(extendsSchemaID));
        }

        jsonSchema.setRequired((Boolean) jsonObject.opt("required"));
        jsonSchema.setTitle(jsonObject.optString("title", null));
        jsonSchema.setDescription(jsonObject.optString("description", null));

        Object typeValue = jsonObject.opt("type");
        if (typeValue instanceof JSONArray) {
            jsonSchema.setTypes(convertSchemaTypesFromJSONValues(new GenericJSONArrayList((JSONArray) typeValue)));
        } else if (typeValue instanceof Collection) {
            //noinspection unchecked
            jsonSchema.setTypes(convertSchemaTypesFromJSONValues((Collection) typeValue));
        } else if (typeValue != null) {
            jsonSchema.setTypes(convertSchemaTypesFromJSONValues(Collections.singleton(typeValue)));
        }

        for (final JSONSchemaType<?> type : jsonSchema.getTypes()) {
            Object defaultValue = jsonObject.opt("default");
            Number minimum = (Number) jsonObject.opt("minimum");
            Boolean minimumExclusive = (Boolean) jsonObject.opt("exclusiveMinimum");
            Number maximum = (Number) jsonObject.opt("maximum");
            Boolean maximumExclusive = (Boolean) jsonObject.opt("exclusiveMaximum");
            Number divisibleBy = (Number) jsonObject.opt("divisibleBy");

            List<Object> enums = new GenericJSONArrayList(jsonObject.optJSONArray("enum"));

            if (type instanceof JSONSchemaBooleanType) {
                JSONSchemaBooleanType booleanType = (JSONSchemaBooleanType) type;
                if (defaultValue instanceof Boolean) {
                    booleanType.setDefaultValue((Boolean) defaultValue);
                }
            } else if (type instanceof JSONSchemaIntegerType) {
                JSONSchemaIntegerType intType = (JSONSchemaIntegerType) type;
                if (defaultValue instanceof Number) {
                    intType.setDefaultValue(((Number) defaultValue).intValue());
                }

                if (minimum instanceof Number) {
                    intType.setMinimum(minimum.intValue());
                }

                if (minimumExclusive instanceof Boolean) {
                    intType.setMinimumExclusive(minimumExclusive);
                }

                if (maximum instanceof Number) {
                    intType.setMaximum(maximum.intValue());
                }

                if (maximumExclusive instanceof Boolean) {
                    intType.setMaximumExclusive(maximumExclusive);
                }

                if (divisibleBy instanceof Number) {
                    intType.setDivisibleBy(divisibleBy.intValue());
                }

                intType.setEnumValues(Collections2.filter(Lists.transform(enums, new Function<Object, Integer>() {
                    @Override
                    public Integer apply(@Nullable Object input) {
                        return input instanceof Number ? ((Number) input).intValue() : null;
                    }
                }), Predicates.notNull()));
            } else if (type instanceof JSONSchemaNumberType) {
                JSONSchemaNumberType numberType = (JSONSchemaNumberType) type;
                if (defaultValue instanceof Number) {
                    numberType.setDefaultValue((Number) defaultValue);
                }

                if (minimum instanceof Number) {
                    numberType.setMinimum(minimum);
                }

                if (minimumExclusive instanceof Boolean) {
                    numberType.setMinimumExclusive(minimumExclusive);
                }

                if (maximum instanceof Number) {
                    numberType.setMaximum(maximum);
                }

                if (maximumExclusive instanceof Boolean) {
                    numberType.setMaximumExclusive(maximumExclusive);
                }

                if (divisibleBy instanceof Number) {
                    numberType.setDivisibleBy(divisibleBy);
                }

                numberType.setEnumValues(Collections2.filter(Lists.transform(enums, new Function<Object, Number>() {
                    @Override
                    public Number apply(@Nullable Object input) {
                        return input instanceof Number ? (Number) input : null;
                    }
                }), Predicates.notNull()));
            } else if (type instanceof JSONSchemaStringType) {
                JSONSchemaStringType stringType = (JSONSchemaStringType) type;

                if (defaultValue instanceof String) {
                    stringType.setDefaultValue((String) defaultValue);
                }

                Number minimumLength = (Number) jsonObject.opt("minLength");
                if (minimumLength != null) {
                    stringType.setMinimumLength(minimumLength.intValue());
                }

                Number maximumLength = (Number) jsonObject.opt("maxLength");
                if (maximumLength != null) {
                    stringType.setMaximumLength(maximumLength.intValue());
                }

                stringType.setPattern(jsonObject.optString("pattern", null));

                String format = jsonObject.optString("format");
                stringType.setFormat(convertSchemaTextFormatFromJSONValue(format));
                if (stringType.getFormat() == JSONSchemaTextFormat.CUSTOM) {
                    stringType.setCustomFormat(format);
                }

                stringType.setEnumValues(Collections2.filter(Lists.transform(enums, new Function<Object, String>() {
                    @Override
                    public String apply(@Nullable Object input) {
                        return input instanceof String ? (String) input : null;
                    }
                }), Predicates.notNull()));
            } else if (type instanceof JSONSchemaObjectType) {
                JSONSchemaObjectType objectType = (JSONSchemaObjectType) type;

                JSONObject propertiesJSONObject = jsonObject.optJSONObject("properties");
                if (propertiesJSONObject != null) {
                    objectType.setNamedProperties(_namedPropertyMarshaller.fromMappedJSONObject(propertiesJSONObject, null));
                }

                JSONObject patternPropertiesJSONObject = jsonObject.optJSONObject("patternProperties");
                if (patternPropertiesJSONObject != null) {
                    objectType.setPatternProperties(_patternPropertyMarshaller.fromMappedJSONObject(patternPropertiesJSONObject, null));
                }

                Object additionalProperties = jsonObject.opt("additionalProperties");
                if (additionalProperties instanceof JSONObject) {
                    objectType.setAdditionalPropertiesSchema(fromJSONObject((JSONObject) additionalProperties));
                } else if (additionalProperties instanceof Boolean && !((Boolean) additionalProperties)) {
                    objectType.setAdditionalPropertiesSchema(null);
                } else if (additionalProperties != null && additionalProperties != JSONObject.NULL) {
                    throw new IllegalArgumentException("Unsupported additional properties: " + additionalProperties);
                }
            } else if (type instanceof JSONSchemaArrayType) {
                JSONSchemaArrayType arrayType = (JSONSchemaArrayType) type;

                Object items = jsonObject.opt("items");

                if (items instanceof JSONObject) {
                    arrayType.setItem(fromJSONObject((JSONObject) items));
                } else if (items instanceof JSONArray || items instanceof Collection) {
                    arrayType.setItems(fromJSONArray((JSONArray) items));

                    Object additionalItems = jsonObject.opt("additionalItems");
                    if (additionalItems instanceof JSONObject) {
                        arrayType.additionalItemsSchema(fromJSONObject((JSONObject) additionalItems));
                    } else if (additionalItems instanceof Boolean && !((Boolean) additionalItems)) {
                        arrayType.additionalItemsSchema(null);
                    } else if (additionalItems != null && additionalItems != JSONObject.NULL) {
                        throw new IllegalArgumentException("Unsupported additional items: " + additionalItems);
                    } else {
                        arrayType.additionalItemsSchema(new JSONSchema());
                    }
                }
            }
        }

        return jsonSchema;
    }

    private JSONSchema fromSchemaID(String schemaID) {
        try {
            return _schemaCache.get(schemaID).clone();
        } catch (ExecutionException e) {
            throw Throwables.propagate(e.getCause());
        }
    }

    private String convertSchemaTextFormatToJSONValue(@Nullable JSONSchemaTextFormat format, @Nullable String customFormat) {
        if (format == null) {
            return null;
        }

        if (format == JSONSchemaTextFormat.CUSTOM) {
            return customFormat;
        }

        String jsonValue = _schemaTextFormatJSONValues.get(format);
        if (jsonValue == null) {
            throw new IllegalArgumentException("Unknown string format: " + format);
        }
        return jsonValue;
    }

    private JSONSchemaTextFormat convertSchemaTextFormatFromJSONValue(@Nullable String formatJSONValue) {
        if (Strings.isNullOrEmpty(formatJSONValue)) {
            return null;
        }

        JSONSchemaTextFormat format = _schemaTextFormatJSONValues.inverse().get(formatJSONValue);
        if (format == null) {
            return JSONSchemaTextFormat.CUSTOM;
        }

        return format;
    }

    private BiMap<JSONSchemaTextFormat, String> buildSchemaTextFormatJSONValues() {
        BiMap<JSONSchemaTextFormat, String> jsonSchemaTextFormatJSONValues = HashBiMap.create();

        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.DATE_TIME, "date-time");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.DATE, "date");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.TIME, "time");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.UTC_MILLISECONDS, "utc-millisec");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.REGEX, "regex");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.CSS_COLOR, "color");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.CSS_STYLE, "style");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.PHONE_NUMBER, "phone");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.URI, "uri");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.EMAIL_ADDRESS, "email");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.IPV4_ADDRESS, "ip-address");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.IPV6_ADDRESS, "ipv6");
        jsonSchemaTextFormatJSONValues.put(JSONSchemaTextFormat.HOSTNAME, "host-name");

        return jsonSchemaTextFormatJSONValues;
    }

    @Nullable
    private Object schemaTypesToJSON(JSONSchema schema) {
        return schema.isSingleType() ? (schema.getType() != null ? convertSchemaTypeToJSONTypeValue(schema.getType()) : null) : convertSchemaTypesToJSONValues(schema.getTypes());
    }

    private Collection<Object> convertSchemaTypesToJSONValues(Collection<JSONSchemaType<?>> types) {
        List<Object> typeValues = Lists.newArrayList();
        for (final JSONSchemaType<?> type : types) {
            if (type instanceof JSONSchemaUnionType) {
                JSONSchemaUnionType unionType = (JSONSchemaUnionType) type;
                typeValues.addAll(toJSONList(unionType.getSchemas(), false));
            } else {
                typeValues.add(convertSchemaTypeToJSONTypeValue(type));
            }
        }
        return typeValues;
    }

    private Collection<JSONSchemaType<?>> convertSchemaTypesFromJSONValues(Collection<Object> jsonValues) {
        List<JSONSchemaType<?>> types = Lists.newArrayList();
        JSONSchemaUnionType unionType = null;
        for (Object jsonValue : jsonValues) {
            if (jsonValue instanceof JSONObject) {
                if (unionType == null) {
                    unionType = new JSONSchemaUnionType();
                    types.add(unionType);
                }

                unionType.addSchema(fromJSONObject((JSONObject) jsonValue));
            } else {
                types.add(convertSchemaTypeFromJSONTypeValue(jsonValue.toString()));
            }
        }
        return types;
    }

    private String convertSchemaTypeToJSONTypeValue(JSONSchemaType<?> type) {
        String jsonTypeValue = _schemaTypeJSONTypeValues.get(type.getClass());
        if (jsonTypeValue == null) {
            throw new IllegalArgumentException("Illegal JSONSchemaType: " + type);
        }
        return jsonTypeValue;
    }

    private JSONSchemaType<?> convertSchemaTypeFromJSONTypeValue(String jsonTypeValue) {
        Class<? extends JSONSchemaType<?>> typeClass = _schemaTypeJSONTypeValues.inverse().get(jsonTypeValue);
        if (typeClass == null) {
            throw new IllegalArgumentException("Unknown JSONSchemaType: " + jsonTypeValue);
        }

        if (typeClass == JSONSchemaAnyType.class) {
            return JSONSchemaTypes.ANY;
        }

        if (typeClass == JSONSchemaNullType.class) {
            return JSONSchemaTypes.NULL;
        }

        try {
            return typeClass.newInstance();
        } catch (InstantiationException e) {
            throw Throwables.propagate(e);
        } catch (IllegalAccessException e) {
            throw Throwables.propagate(e);
        }
    }

    private BiMap<Class<? extends JSONSchemaType<?>>, String> buildSchemaTypeJSONTypeValues() {
        BiMap<Class<? extends JSONSchemaType<?>>, String> jsonSchemaTypeJSONTypeValues = HashBiMap.create();

        jsonSchemaTypeJSONTypeValues.put(JSONSchemaAnyType.class, "any");
        jsonSchemaTypeJSONTypeValues.put(JSONSchemaNullType.class, "null");
        jsonSchemaTypeJSONTypeValues.put(JSONSchemaArrayType.class, "array");
        jsonSchemaTypeJSONTypeValues.put(JSONSchemaBooleanType.class, "boolean");
        jsonSchemaTypeJSONTypeValues.put(JSONSchemaIntegerType.class, "integer");
        jsonSchemaTypeJSONTypeValues.put(JSONSchemaNumberType.class, "number");
        jsonSchemaTypeJSONTypeValues.put(JSONSchemaObjectType.class, "object");
        jsonSchemaTypeJSONTypeValues.put(JSONSchemaStringType.class, "string");

        return jsonSchemaTypeJSONTypeValues;
    }

    private class JSONSchemaNamedPropertyMarshaller extends AbstractMappingJSONMarshaller<JSONSchemaNamedProperty> {
        @Override
        public JSONObject toJSONObject(JSONSchemaNamedProperty namedProperty)
                throws JSONException {
            return JSONSchemaMarshaller.this.toJSONObject(namedProperty.getValueSchema(), false);
        }

        @Override
        protected String getKey(JSONSchemaNamedProperty namedProperty) {
            return namedProperty.getName();
        }

        @Override
        public JSONSchemaNamedProperty fromJSONObject(JSONObject jsonObject)
                throws JSONException {
            return new JSONSchemaNamedProperty().valueSchema(JSONSchemaMarshaller.this.fromJSONObject(jsonObject));
        }

        @Override
        protected JSONSchemaNamedProperty setKey(JSONSchemaNamedProperty namedProperty, String key) {
            return namedProperty.name(key);
        }
    }

    private class JSONSchemaPatternPropertyMarshaller extends AbstractMappingJSONMarshaller<JSONSchemaPatternProperty> {
        @Override
        public JSONObject toJSONObject(JSONSchemaPatternProperty patternProperty)
                throws JSONException {
            return JSONSchemaMarshaller.this.toJSONObject(patternProperty.getValueSchema(), false);
        }

        @Override
        protected String getKey(JSONSchemaPatternProperty patternProperty) {
            return patternProperty.getPattern();
        }

        @Override
        public JSONSchemaPatternProperty fromJSONObject(JSONObject jsonObject)
                throws JSONException {
            return new JSONSchemaPatternProperty().valueSchema(JSONSchemaMarshaller.this.fromJSONObject(jsonObject));
        }

        @Override
        protected JSONSchemaPatternProperty setKey(JSONSchemaPatternProperty patternProperty, String key) {
            return patternProperty.pattern(key);
        }
    }

    private class JSONSchemaURLCacheLoader extends CacheLoader<String, JSONSchema> {
        @Override
        public JSONSchema load(String schemaID) throws Exception {
            return fromJSONObject(_schemaJSONRetriever.retrieveSchemaJSON(schemaID));
        }
    }

    private static class DefaultSchemaJSONRetriever implements SchemaJSONRetriever {
        @Override
        public JSONObject retrieveSchemaJSON(String schemaID) throws Exception {
            // enhance: support other types of URLs besides http
            URL url = new URL(schemaID);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            String schemaText = readUrlResponseString(urlConnection);
            return new JSONObject(schemaText);
        }
    }

    private static String readUrlResponseString(HttpURLConnection cxn) throws IOException {
        try {
            // try to extract the character set from the Content-Type header
            String characterEncoding = null;
            String contentType = cxn.getContentType();
            if (contentType != null) {
                Matcher matcher = HTTP_CONTENT_TYPE_CHARSET.matcher(contentType);
                if (matcher.find()) {
                    characterEncoding = StringUtils.trimToNull(matcher.group(1));
                }
            }
            if (characterEncoding == null) {
                characterEncoding = "UTF-8";
            }

            InputStream in = cxn.getInputStream();
            try {
                return IOUtils.toString(in, characterEncoding);
            } finally {
                in.close();
            }
        } catch (IOException e) {
            InputStream err = cxn.getErrorStream();
            if (err != null) {
                throw new IOException(IOUtils.toString(err), e);
            } else {
                throw e;
            }
        } finally {
            IOUtils.closeQuietly(cxn.getErrorStream());
        }
    }

    public interface SchemaJSONRetriever {
        /**
         * Retrieves the JSONObject for the given schema ID
         */
        JSONObject retrieveSchemaJSON(String schemaID) throws Exception;
    }
}
TOP

Related Classes of com.bazaarvoice.commons.data.dao.json.schema.JSONSchemaMarshaller$JSONSchemaPatternPropertyMarshaller

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.