eu.trentorise.opendata.semtext.jackson.SemTextModule.java Source code

Java tutorial

Introduction

Here is the source code for eu.trentorise.opendata.semtext.jackson.SemTextModule.java

Source

/*
 * Copyright 2015 Trento Rise.
 *
 * 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 eu.trentorise.opendata.semtext.jackson;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.module.SimpleModule;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableSet;
import eu.trentorise.opendata.commons.Dict;
import eu.trentorise.opendata.commons.NotFoundException;
import static eu.trentorise.opendata.commons.OdtUtils.checkNotEmpty;
import eu.trentorise.opendata.commons.jackson.OdtCommonsModule;
import eu.trentorise.opendata.semtext.HasMetadata;
import eu.trentorise.opendata.semtext.Meaning;
import eu.trentorise.opendata.semtext.MeaningKind;
import eu.trentorise.opendata.semtext.MeaningStatus;
import eu.trentorise.opendata.semtext.SemText;
import eu.trentorise.opendata.semtext.Sentence;
import eu.trentorise.opendata.semtext.Term;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import javax.annotation.Nullable;

/**
 * A module for handling semtext objects with Jackson JSON serialization
 * framework. In order to work properly the module needs you to register also
 * OdtCommonModule and GuavaModule (both are in separate maven packages). For
 * properly deserializing metadata you also need to register it by calling {@link #registerMetadata(java.lang.Class, java.lang.String, java.lang.Class)
 * }
 *
 * @author David Leoni <david.leoni@unitn.it>
 */
public final class SemTextModule extends SimpleModule {

    private static final long serialVersionUID = 1L;

    /**
     * Map for Class name, namespace, and jacksonizable class
     */
    private static final Map<String, Map<String, TypeReference>> METADATA_NAMESPACES = new HashMap();

    private static final class MeaningMetadataDeserializer extends MetadataDeserializer {

        private MeaningMetadataDeserializer() {
            super(Meaning.class);
        }
    }

    private abstract static class JacksonMeaning {

        @JsonCreator
        public static Meaning of(@JsonProperty("id") String id, @JsonProperty("kind") MeaningKind kind,
                @JsonProperty("probability") double probability, @JsonProperty("name") Dict name,
                @JsonProperty("description") Dict description,
                @JsonDeserialize(using = MeaningMetadataDeserializer.class) @JsonProperty("metadata") Map<String, ?> metadata) {
            return null;
        }
    }

    private static final class TermMetadataDeserializer extends MetadataDeserializer {

        private TermMetadataDeserializer() {
            super(Term.class);
        }
    }

    private abstract static class JacksonTerm {

        @JsonCreator
        public static Term of(@JsonProperty("start") int start, @JsonProperty("end") int end,
                @JsonProperty("meaningStatus") MeaningStatus meaningStatus,
                @JsonProperty("selectedMeaning") @Nullable Meaning selectedMeaning,
                @JsonProperty("meanings") Iterable<Meaning> meanings,
                @JsonDeserialize(using = TermMetadataDeserializer.class) @JsonProperty("metadata") Map<String, ?> metadata) {
            return null;

        }
    }

    private static final class SentenceMetadataDeserializer extends MetadataDeserializer {

        private SentenceMetadataDeserializer() {
            super(Sentence.class);
        }
    }

    private abstract static class JacksonSentence {

        @JsonCreator
        public static Sentence of(@JsonProperty("start") int start, @JsonProperty("end") int end,
                @JsonProperty("terms") Iterable<Term> terms,
                @JsonDeserialize(using = SentenceMetadataDeserializer.class) @JsonProperty("metadata") Map<String, ?> metadata) {
            return null;
        }
    }

    private static final class SemTextMetadataDeserializer extends MetadataDeserializer {

        private SemTextMetadataDeserializer() {
            super(SemText.class);
        }
    }

    private abstract static class JacksonSemText {

        @JsonCreator
        public static SemText ofSentences(@JsonProperty("locale") Locale locale, @JsonProperty("text") String text,
                @JsonProperty("sentences") Iterable<Sentence> sentences,
                @JsonDeserialize(using = SemTextMetadataDeserializer.class) @JsonProperty("metadata") Map<String, ?> metadata) {
            return null;
        }
    }

    /**
     * Creates the module and registers all the needed serializaers and
     * deserializers
     */
    public SemTextModule() {
        super("odt-commons-jackson", OdtCommonsModule.readJacksonVersion(SemTextModule.class));

        setMixInAnnotation(Meaning.class, JacksonMeaning.class);
        setMixInAnnotation(Term.class, JacksonTerm.class);
        setMixInAnnotation(Sentence.class, JacksonSentence.class);
        setMixInAnnotation(SemText.class, JacksonSemText.class);

    }

    @Override
    public int hashCode() {
        return getClass().hashCode(); // it's like this in Guava module!
    }

    @Override
    public boolean equals(Object o) {
        return this == o; // it's like this in Guava module!
    }

    /**
     * Registers in the provided object mapper the jackson semtext module and
     * also the required odt commons and guava modules.
     */
    public static void registerModulesInto(ObjectMapper om) {
        OdtCommonsModule.registerModulesInto(om);
        om.registerModule(new SemTextModule());
    }

    /**
     * Registers the provided namespace to the corresponding metadata class, so
     * Jackson will know how to deserialize objects under that namespace. The
     * mapping will be valid only for the specified metadata holder class.
     *
     * NOTE: If your class has generics (like List<String>) , use
     * {@link #registerMetadata(java.lang.Class, java.lang.String, com.fasterxml.jackson.core.type.TypeReference)}
     * instead.
     *
     * @param hasMetadataClass Either {@link eu.trentorise.opendata.semtext.SemText}, {@link eu.trentorise.opendata.semtext.Sentence},
     * {@link eu.trentorise.opendata.semtext.Term} or
     * {@link eu.trentorise.opendata.semtext.Meaning}
     * @param namespace a non-empty string
     * @param metadataClass any class that Jackson can serialize and
     * deserialize.
     */
    public static <T> void registerMetadata(Class<? extends HasMetadata> hasMetadataClass, String namespace,
            final Class<T> metadataClass) {
        registerMetadata(hasMetadataClass, namespace, new TypeReference<T>() {
            @Override
            public Type getType() {
                return metadataClass;
            }
        });

    }

    /**
     * Registers the provided namespace to the corresponding metadata type
     * reference, so Jackson will know how to deserialize objects under that
     * namespace. The mapping will be valid only for the specified metadata
     * holder class.
     *
     * @param hasMetadataClass Either {@link eu.trentorise.opendata.semtext.SemText}, {@link eu.trentorise.opendata.semtext.Sentence},
     * {@link eu.trentorise.opendata.semtext.Term} or
     * {@link eu.trentorise.opendata.semtext.Meaning}
     * @param namespace a non-empty string
     * @param metadataClass any class that Jackson can serialize and
     * deserialize.
     */
    public static void registerMetadata(Class<? extends HasMetadata> hasMetadataClass, String namespace,
            TypeReference metadataClass) {
        checkNotNull(hasMetadataClass);
        checkNotEmpty(namespace, "Invalid metadata namespace!");
        checkNotNull(metadataClass);

        Map<String, TypeReference> candidateMapping = METADATA_NAMESPACES.get(hasMetadataClass.getName());
        Map<String, TypeReference> namespaceMapping;
        if (candidateMapping == null) {
            namespaceMapping = new HashMap();
        } else {
            namespaceMapping = candidateMapping;
        }

        namespaceMapping.put(namespace, metadataClass);
        METADATA_NAMESPACES.put(hasMetadataClass.getName(), namespaceMapping);
    }

    /**
     * Unregisters all the previously registered metadata namespaces.
     */
    public static void clearMetadata() {
        METADATA_NAMESPACES.clear();
    }

    /**
     * Returns the namespaces registered for the given class that can hold
     * metadata. If nothing is found an empty set is returned.
     *
     * @param hasMetadataClass Either {@link eu.trentorise.opendata.semtext.SemText}, {@link eu.trentorise.opendata.semtext.Sentence},
     * {@link eu.trentorise.opendata.semtext.Term} or
     * {@link eu.trentorise.opendata.semtext.Meaning}
     */
    public static ImmutableSet<String> getMetadataNamespaces(Class<? extends HasMetadata> hasMetadataClass) {
        Map<String, TypeReference> map = METADATA_NAMESPACES.get(hasMetadataClass.getName());
        if (map == null) {
            return ImmutableSet.of();
        } else {
            return ImmutableSet.copyOf(map.keySet());
        }
    }

    /**
     * Returns the jacksonizable class associated to the provided class that can
     * hold metadata and namespace
     *
     * @param hasMetadataClass Either {@link eu.trentorise.opendata.semtext.SemText}, {@link eu.trentorise.opendata.semtext.Sentence},
     * {@link eu.trentorise.opendata.semtext.Term} or
     * {@link eu.trentorise.opendata.semtext.Meaning}
     * @param namespace a registered namespace
     * @throws eu.trentorise.opendata.commons.NotFoundException if the namespace
     * was not registered
     */
    public static TypeReference getMetadataTypeReference(Class<? extends HasMetadata> hasMetadataClass,
            String namespace) {
        checkNotNull(hasMetadataClass);
        checkNotEmpty(namespace, "Invalid metadata namespace!");
        Map<String, TypeReference> mapping = METADATA_NAMESPACES.get(hasMetadataClass.getName());
        if (mapping != null) {
            TypeReference clazz = mapping.get(namespace);
            if (clazz != null) {
                return clazz;
            }
        }
        throw new NotFoundException("Couldn't find any class registered under class " + hasMetadataClass.getName()
                + " and namespace " + namespace);
    }

}