com.conwet.silbops.model.AbstractMapping.java Source code

Java tutorial

Introduction

Here is the source code for com.conwet.silbops.model.AbstractMapping.java

Source

package com.conwet.silbops.model;

/*
 * #%L
 * SilboPS API
 * %%
 * Copyright (C) 2011 - 2014 CoNWeT Lab., Universidad Politcnica de Madrid
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

import org.json.simple.JSONObject;

import com.conwet.silbops.model.basic.Attribute;
import com.conwet.silbops.model.basic.Type;
import com.conwet.silbops.model.basic.Value;
import com.conwet.silbops.util.JSONizable;
import com.conwet.silbops.util.externalizer.AttributeExternalizer;
import com.conwet.silbops.util.externalizer.Externalizer;
import com.conwet.silbops.util.externalizer.ValueExternalizer;

/**
 * Abstract implementation for mapping attribute to values.
 * It uses the "getThis" trick described by Angelika Langer.
 * 
 * @author sergio
 * @see <a href="http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ206"
 * >getThis Trick</a>
 */
public abstract class AbstractMapping<T extends AbstractMapping<T>>
        implements JSONizable, Externalizable, IterableAttribute<Value> {

    private static final long serialVersionUID = 1L;
    private static final String NULL_ATTR = "Attribute is null";
    private static final Externalizer<Attribute> attributeExt;
    private static final ValueExternalizer valueExt;

    static {
        attributeExt = new AttributeExternalizer();
        valueExt = new ValueExternalizer();
    }

    private Map<Attribute, Value> attributes;

    /**
     * Creates a new instance and initialize internal fields
     */
    protected AbstractMapping() {

        this.attributes = new HashMap<>();
    }

    /**
     * @return the "this" object
     */
    protected abstract T getThis();

    /**
     * @return the object type
     */
    protected abstract Class<T> getThisType();

    /**
     * Adds a new value for a given attribute.
     *
     * @param name Attribute name
     * @param value Attribute value. String, Float or Integer value.
     * @return the same object for enabling chaining.
     */
    public T attribute(String name, Type type, Object value) {

        return attribute(new Attribute(name, type), value);
    }

    /**
     * Adds a new value for a given attribute.
     * 
     * @param attribute key
     * @param value value to map
     * @return the same object for enabling chaining.
     * @throws IllegalArgumentException if the attribute type is different
     * from value type.
     */
    public T attribute(Attribute attribute, Object value) {

        Objects.requireNonNull(attribute, NULL_ATTR);
        Value val = Value.valueOf(value);

        if (attribute.getType() != val.getType()) {

            throw new IllegalArgumentException("Type mismatch: " + attribute.getType() + " vs " + val.getType());
        }

        attributes.put(attribute, val);
        return getThis();
    }

    @Override
    public Set<Attribute> getAttributes() {

        return attributes.keySet();
    }

    @Override
    public Set<Entry<Attribute, Value>> entries() {

        return attributes.entrySet();
    }

    /**
     * The value for a given attribute.
     *
     * @param name the attribute name
     * @param type the attribute type
     * @return the mapped value
     */
    public Value getValue(String name, Type type) {

        return getValue(new Attribute(name, type));
    }

    /**
     * @param attribute the key attribute
     * @return the mapped value
     */
    public Value getValue(Attribute attribute) {

        return attributes.get(Objects.requireNonNull(attribute, NULL_ATTR));
    }

    @Override
    public boolean equals(Object obj) {

        if (this == obj) {
            return true;
        }

        Class<T> clazz = getThisType();

        if (clazz.isInstance(obj)) {

            AbstractMapping<T> other = clazz.cast(obj);
            return attributes.equals(other.attributes);
        }

        return false;
    }

    @Override
    public int hashCode() {

        return 67 * getThisType().hashCode() + this.attributes.hashCode();
    }

    @Override
    public void readExternal(ObjectInput input) throws IOException, ClassNotFoundException {

        Map<Attribute, Value> map = new HashMap<>();
        int size = input.readInt();

        for (int i = 0; i < size; i++) {

            Attribute attribute = attributeExt.readExternal(input);
            Value value = valueExt.readExternal(input, attribute.getType());
            map.put(attribute, value);
        }

        attributes = map;
    }

    @Override
    public void writeExternal(ObjectOutput output) throws IOException {

        output.writeInt(attributes.size());

        for (Entry<Attribute, Value> entry : attributes.entrySet()) {

            attributeExt.writeExternal(output, entry.getKey());
            valueExt.writeExternal(output, entry.getValue());
        }
    }

    /**
     * Convert attribute-value mapping from its JSON representation.
     *
     * @param clazz the Class of the new type to instantiate
     * @param json JSON representation of a mapping between attributes and values
     * @return the new instance created
     * @throws IllegalArgumentException if some problem occur parsing JSON or
     * when creating instance class
     */
    @SuppressWarnings("unchecked")
    protected static <U extends AbstractMapping<U>> U fromJSON(Class<U> clazz, JSONObject json) {

        try {
            U instance = clazz.newInstance();

            for (Entry<String, Object> entry : (Set<Entry<String, Object>>) json.entrySet()) {

                instance.attribute(Attribute.fromJSON(entry.getKey()), Value.fromJSON(entry.getValue()));
            }

            return instance;
        } catch (InstantiationException | IllegalAccessException ex) {
            throw new IllegalArgumentException("Unable to load instance of class " + clazz, ex);
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public JSONObject toJSON() {

        JSONObject json = new JSONObject();

        for (Entry<Attribute, Value> entry : attributes.entrySet()) {

            json.put(entry.getKey().toJSON(), entry.getValue().toJSON());
        }

        return json;
    }

    @Override
    public String toJSONString() {

        return toJSON().toJSONString();
    }
}