org.jsonschema2pojo.integration.MediaIT.java Source code

Java tutorial

Introduction

Here is the source code for org.jsonschema2pojo.integration.MediaIT.java

Source

/**
 * Copyright  2010-2014 Nokia
 *
 * 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.
 */

package org.jsonschema2pojo.integration;

import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.config;
import static org.jsonschema2pojo.integration.util.CodeGenerationHelper.generateAndCompile;
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
import static java.lang.String.format;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URI;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.net.QuotedPrintableCodec;
import org.hamcrest.Matcher;
import org.jsonschema2pojo.AbstractAnnotator;
import org.junit.BeforeClass;
import org.junit.Test;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JFieldVar;

public class MediaIT {
    private static Class<?> classWithMediaProperties;
    private static Class<byte[]> BYTE_ARRAY = byte[].class;

    @BeforeClass
    public static void generateAndCompileClass() throws ClassNotFoundException {

        ClassLoader resultsClassLoader = generateAndCompile("/schema/media/mediaProperties.json", "com.example",
                config("customAnnotator", QuotedPrintableAnnotator.class.getName()));

        classWithMediaProperties = resultsClassLoader.loadClass("com.example.MediaProperties");
    }

    @Test
    public void shouldCreateByteArrayField() throws SecurityException, NoSuchFieldException {
        Field field = classWithMediaProperties.getDeclaredField("minimalBinary");

        assertThat("the minimal binary field has type byte[]", field.getType(), equalToType(BYTE_ARRAY));
    }

    @Test
    public void shouldCreateByteArrayGetter() throws SecurityException, NoSuchMethodException {
        Method getter = classWithMediaProperties.getDeclaredMethod("getMinimalBinary");

        assertThat("the minimal binary getter has return type byte[]", getter.getReturnType(),
                equalToType(BYTE_ARRAY));
    }

    @Test
    public void shouldCreateByteArraySetter() throws SecurityException, NoSuchMethodException {
        Method setter = classWithMediaProperties.getDeclaredMethod("setMinimalBinary", BYTE_ARRAY);

        assertThat("the minimal binary setter has return type void", setter.getReturnType(),
                equalToType(Void.TYPE));
    }

    @Test
    public void shouldCreateByteArrayFieldWithAnyEncoding() throws SecurityException, NoSuchFieldException {
        Field field = classWithMediaProperties.getDeclaredField("anyBinaryEncoding");
        JsonSerialize serAnnotation = field.getAnnotation(JsonSerialize.class);
        JsonDeserialize deserAnnotation = field.getAnnotation(JsonDeserialize.class);

        assertThat("any binary encoding field has type byte[]", field.getType(), equalToType(BYTE_ARRAY));
        assertThat("any binary encoding has a serializer", serAnnotation, notNullValue());
        assertThat("any binary encoding has a deserializer", deserAnnotation, notNullValue());
    }

    @Test
    public void shouldCreateByteArrayGetterWithAnyEncoding() throws SecurityException, NoSuchMethodException {
        Method getter = classWithMediaProperties.getDeclaredMethod("getAnyBinaryEncoding");

        assertThat("any binary encoding getter has return type byte[]", getter.getReturnType(),
                equalToType(BYTE_ARRAY));
    }

    @Test
    public void shouldCreateByteArraySetterWithAnyEncoding() throws SecurityException, NoSuchMethodException {
        Method setter = classWithMediaProperties.getDeclaredMethod("setAnyBinaryEncoding", BYTE_ARRAY);

        assertThat("any binary encoding setter has return type void", setter.getReturnType(),
                equalToType(Void.TYPE));
    }

    @Test
    public void shouldCreateStringFieldWithoutEncoding() throws SecurityException, NoSuchFieldException {
        Field field = classWithMediaProperties.getDeclaredField("unencoded");

        assertThat("unencoded field has type String", field.getType(), equalToType(String.class));
    }

    @Test
    public void shouldCreateStringGetterWithoutEncoding() throws SecurityException, NoSuchMethodException {
        Method getter = classWithMediaProperties.getDeclaredMethod("getUnencoded");

        assertThat("unencoded getter has return type String", getter.getReturnType(), equalToType(String.class));
    }

    @Test
    public void shouldCreateStringSetterWithoutEncoding() throws SecurityException, NoSuchMethodException {
        Method setter = classWithMediaProperties.getDeclaredMethod("setUnencoded", String.class);

        assertThat("unencoded setter has return type void", setter.getReturnType(), equalToType(Void.TYPE));
    }

    @Test
    public void shouldCreateUriFieldWithUriFormat() throws SecurityException, NoSuchFieldException {
        Field field = classWithMediaProperties.getDeclaredField("withUriFormat");

        assertThat("withUriFormat field has type URI", field.getType(), equalToType(URI.class));
    }

    @Test
    public void shouldHandleUnencodedDefault() throws Exception {
        Method getter = classWithMediaProperties.getDeclaredMethod("getUnencodedWithDefault");

        Object object = new ObjectMapper().readValue("{}", classWithMediaProperties);
        String value = (String) getter.invoke(object);

        assertThat("unencodedWithDefault has the default value", value, equalTo("default value"));
    }

    @Test
    @SuppressWarnings("unchecked")
    public void shouldReasonablyHandleBase64Default() throws Exception {
        Method getter = classWithMediaProperties.getDeclaredMethod("getBase64WithDefault");

        Object object = new ObjectMapper().readValue("{}", classWithMediaProperties);
        byte[] value = (byte[]) getter.invoke(object);

        // if we got here, then at least defaults do not blow up the code.  Make sure
        // we get null or the default.  Users should not depend on the functionality in
        // this situation, as it is unsupported.
        assertThat("base64WithDefault is null or the default value", value,
                anyOf(nullValue(), equalTo(new byte[] { (byte) 0xFF, (byte) 0xF0, (byte) 0x0F, (byte) 0x00 })));
    }

    @Test
    public void shouldRoundTripBase64Field() throws Exception {
        roundTripAssertions(new ObjectMapper(), "minimalBinary", "//APAA==",
                new byte[] { (byte) 0xFF, (byte) 0xF0, (byte) 0x0F, (byte) 0x00 });
    }

    @Test
    public void shouldRoundTripUnencodedField() throws Exception {
        roundTripAssertions(new ObjectMapper(), "unencoded", "some text", "some text");
    }

    @Test
    public void shouldRoundTripQuotedPrintableField() throws Exception {
        roundTripAssertions(new ObjectMapper(), "anyBinaryEncoding",
                "\"=E3=82=A8=E3=83=B3=E3=82=B3=E3=83=BC=E3=83=89=E3=81=95=E3=82=8C=E3=81=9F=E6=96=87=E5=AD=97=E5=88=97\" is Japanese for \"encoded string\"",
                "\"??\" is Japanese for \"encoded string\"".getBytes("UTF-8"));
    }

    @Test
    public void shouldRoundTripQuotedPrintableFieldWithNoFieldVisibility() throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibilityChecker(mapper.getVisibilityChecker().withFieldVisibility(Visibility.NONE));

        roundTripAssertions(new ObjectMapper(), "anyBinaryEncoding",
                "\"=E3=82=A8=E3=83=B3=E3=82=B3=E3=83=BC=E3=83=89=E3=81=95=E3=82=8C=E3=81=9F=E6=96=87=E5=AD=97=E5=88=97\" is Japanese for \"encoded string\"",
                "\"??\" is Japanese for \"encoded string\"".getBytes("UTF-8"));
    }

    /**
     * Returns a matcher that tests for equality to the specified type.
     * @param type the type to check.
     * @return a matcher that tests for equality to the specified type.
     */
    @SuppressWarnings("rawtypes")
    public static Matcher<Class> equalToType(Class<?> type) {
        return equalTo((Class) type);
    }

    public static void roundTripAssertions(ObjectMapper objectMapper, String propertyName, String jsonValue,
            Object javaValue) throws Exception {

        ObjectNode node = objectMapper.createObjectNode();
        node.put(propertyName, jsonValue);

        Object pojo = objectMapper.treeToValue(node, classWithMediaProperties);

        Method getter = new PropertyDescriptor(propertyName, classWithMediaProperties).getReadMethod();

        assertThat(getter.invoke(pojo), is(equalTo(javaValue)));

        JsonNode jsonVersion = objectMapper.valueToTree(pojo);

        assertThat(jsonVersion.get(propertyName).asText(), is(equalTo(jsonValue)));
    }

    /**
     * An example annotator that supports the quoted printable encoding, from RFC 2045.
     * 
     * @author Christian Trimble
     * @see <a href="http://tools.ietf.org/html/rfc2045#section-6.7">Quoted-Printable Content-Transfer-Encoding, Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</a>
     */
    public static class QuotedPrintableAnnotator extends AbstractAnnotator {
        public static final String TYPE = "type";
        public static final String STRING = "string";
        public static final String MEDIA = "media";
        public static final String BINARY_ENCODING = "binaryEncoding";
        public static final String QUOTED_PRINTABLE = "quoted-printable";
        public static final String USING = "using";
        public static final String INCLUDE = "include";

        @Override
        public void propertyField(JFieldVar field, JDefinedClass clazz, String propertyName,
                JsonNode propertyNode) {
            if (isQuotedPrintableProperty(propertyNode)) {
                field.annotate(JsonSerialize.class).param(USING, QuotedPrintableSerializer.class).param(INCLUDE,
                        JsonSerialize.Inclusion.NON_NULL);
                field.annotate(JsonDeserialize.class).param(USING, QuotedPrintableDeserializer.class);
            }
        }

        private static boolean isQuotedPrintableProperty(JsonNode propertyNode) {
            return propertyNode.has(TYPE) && STRING.equals(propertyNode.get(TYPE).asText())
                    && propertyNode.has(MEDIA) && isQuotedPrintable(propertyNode.get(MEDIA));

        }

        private static boolean isQuotedPrintable(JsonNode mediaNode) {
            return mediaNode.has(BINARY_ENCODING)
                    && QUOTED_PRINTABLE.equalsIgnoreCase(mediaNode.get(BINARY_ENCODING).asText());
        }

    }

    public static class QuotedPrintableSerializer extends StdSerializer<byte[]> {
        private static QuotedPrintableCodec codec = new QuotedPrintableCodec();

        public QuotedPrintableSerializer() {
            super(byte[].class);
        }

        @Override
        public void serialize(byte[] value, JsonGenerator jgen, SerializerProvider provider)
                throws IOException, JsonGenerationException {
            jgen.writeString(new String(codec.encode(value), "UTF-8"));
        }

    }

    @SuppressWarnings("serial")
    public static class QuotedPrintableDeserializer extends StdDeserializer<byte[]> {
        private static QuotedPrintableCodec codec = new QuotedPrintableCodec();

        public QuotedPrintableDeserializer() {
            super(byte[].class);
        }

        @Override
        public byte[] deserialize(JsonParser jp, DeserializationContext ctxt)
                throws IOException, JsonProcessingException {
            try {
                return codec.decode(jp.getText().getBytes("UTF-8"));
            } catch (DecoderException e) {
                throw new IOException(format("could not decode quoted string in %s", jp.getCurrentName()), e);
            }
        }

    }

}