com.github.haixing_hu.bean.DefaultProperty.java Source code

Java tutorial

Introduction

Here is the source code for com.github.haixing_hu.bean.DefaultProperty.java

Source

/*
 * Copyright (c) 2014  Haixing Hu
 *
 * 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 com.github.haixing_hu.bean;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Nullable;

import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;

import static com.github.haixing_hu.lang.Argument.requireNonNull;

/**
 * A {@link DefaultProperty} represents a descriptor of a bean.
 *
 * @author Haixing Hu
 */
public class DefaultProperty implements Property {

    protected final PropertyDescriptor descriptor;
    protected Object value;

    /**
     * Constructs a {@link DefaultProperty}.
     *
     * @param descriptor
     *          a property descriptor.
     */
    public DefaultProperty(final PropertyDescriptor descriptor) {
        this.descriptor = requireNonNull("descriptor", descriptor);
        switch (descriptor.getKind()) {
        case INDEXED:
            value = new ArrayList<Object>();
            break;
        case MAPPED:
            value = new HashMap<String, Object>();
            break;
        case SIMPLE:
        default:
            value = null;
            break;
        }
    }

    @Override
    public final String getName() {
        return descriptor.getName();
    }

    @Override
    public PropertyKind getKind() {
        return descriptor.getKind();
    }

    @Override
    public Class<?> getType() {
        return descriptor.getType();
    }

    /**
     * Checks the kind of this property.
     *
     * @param expectedKinds
     *          the array of expected kinds of this property.
     * @throws IllegalArgumentException
     *           if the kind of this property is not the required kind.
     */
    protected void checkKind(final PropertyKind... expectedKinds) {
        final PropertyKind actualKind = descriptor.getKind();
        for (final PropertyKind kind : expectedKinds) {
            if (actualKind == kind) {
                return;
            }
        }
        throw new InvalidPropertyKindException(descriptor.getName(), actualKind, expectedKinds);
    }

    /**
     * Checks the type of a value.
     *
     * @param value
     *          the value to be checked, which could be {@code null}, depending on
     *          the implementation.
     * @throws ClassCastException
     *           if the value is not an instance of the type of this property.
     */
    protected void checkType(@Nullable final Object value) {
        if (value == null) {
            return;
        }
        final Class<?> type = descriptor.getType();
        if (!type.isAssignableFrom(value.getClass())) {
            throw new ClassCastException("Cannot cast the value of type " + value.getClass().getName()
                    + " to the value of type " + type.getName());
        }
    }

    @Override
    public final PropertyDescriptor getDescriptor() {
        return descriptor;
    }

    @Override
    public final Object getRawValue() {
        return value;
    }

    @SuppressWarnings("unchecked")
    @Override
    public final void setRawValue(@Nullable final Object value) {
        switch (descriptor.getKind()) {
        case INDEXED:
            if (value == null) {
                throw new NullPointerException("value is null.");
            }
            if (!(value instanceof ArrayList)) {
                throw new ClassCastException("The value of an indexed property must be a java.util.ArrayList.");
            }
            //  FIXME: check the generic argument type of the ArrayList
            setIndexedValue((ArrayList<Object>) value);
            return;
        case MAPPED:
            if (value == null) {
                throw new NullPointerException("value is null.");
            }
            if (!(value instanceof HashMap)) {
                throw new ClassCastException("The value of a mapped property must be a java.util.HashMap.");
            }
            //  FIXME: check the generic argument type of the HashMap
            setMappedValue((HashMap<String, Object>) value);
            return;
        case SIMPLE:
        default:
            this.value = value;
            return;
        }
    }

    @Override
    public final int getSize() {
        final PropertyKind kind = descriptor.getKind();
        switch (kind) {
        case INDEXED:
            return ((List<?>) value).size();
        case MAPPED:
            return ((Map<?, ?>) value).size();
        case SIMPLE:
        default:
            throw new InvalidPropertyKindException(descriptor.getName(), kind, PropertyKind.INDEXED,
                    PropertyKind.MAPPED);
        }
    }

    @Override
    public final Object getSimpleValue() {
        checkKind(PropertyKind.SIMPLE);
        return value;
    }

    @Override
    public final void setSimpleValue(@Nullable final Object object) {
        checkKind(PropertyKind.SIMPLE);
        checkType(object);
        value = object;
    }

    @SuppressWarnings("unchecked")
    @Override
    public final ArrayList<Object> getIndexedValue() {
        checkKind(PropertyKind.INDEXED);
        return (ArrayList<Object>) value;
    }

    @Override
    public final void setIndexedValue(final ArrayList<Object> list) {
        checkKind(PropertyKind.INDEXED);
        requireNonNull("list", list);
        for (final Object obj : list) {
            checkType(obj);
        }
        @SuppressWarnings("unchecked")
        final ArrayList<Object> valueList = (ArrayList<Object>) value;
        valueList.clear();
        valueList.addAll(list);
    }

    @Override
    public final Object getIndexedValue(final int index) {
        final ArrayList<Object> list = getIndexedValue();
        return list.get(index);
    }

    @Override
    public final void setIndexedValue(final int index, final Object value) {
        final ArrayList<Object> list = getIndexedValue();
        checkType(value);
        list.set(index, value);
    }

    @Override
    public final void addIndexedValue(final int index, final Object value) {
        final ArrayList<Object> list = getIndexedValue();
        checkType(value);
        list.add(index, value);
    }

    @Override
    public final void addIndexedValue(final Object value) {
        final ArrayList<Object> list = getIndexedValue();
        checkType(value);
        list.add(value);
    }

    @Override
    public final Object removeIndexedValue(final int index) {
        final ArrayList<Object> list = getIndexedValue();
        return list.remove(index);
    }

    @SuppressWarnings("unchecked")
    @Override
    public final HashMap<String, Object> getMappedValue() {
        checkKind(PropertyKind.MAPPED);
        return (HashMap<String, Object>) value;
    }

    @Override
    public final void setMappedValue(final HashMap<String, Object> map) {
        checkKind(PropertyKind.MAPPED);
        requireNonNull("map", map);
        for (final Object obj : map.values()) {
            checkType(obj);
        }
        @SuppressWarnings("unchecked")
        final HashMap<String, Object> valueMap = (HashMap<String, Object>) value;
        valueMap.clear();
        valueMap.putAll(map);
    }

    @Override
    public final Set<String> getKeySet() {
        final HashMap<String, Object> map = getMappedValue();
        return map.keySet();
    }

    @Override
    public final boolean containsKey(final String key) {
        final HashMap<String, Object> map = getMappedValue();
        return map.containsKey(key);
    }

    @Override
    public final Object getMappedValue(final String key) {
        final HashMap<String, Object> map = getMappedValue();
        return map.get(key);
    }

    @Override
    public final void setMappedValue(final String key, final Object value) {
        final HashMap<String, Object> map = getMappedValue();
        checkType(value);
        map.put(key, value);
    }

    @Override
    public final Object removeMappedValue(final String key) {
        final HashMap<String, Object> map = getMappedValue();
        return map.remove(key);
    }

    @Override
    public final void clear() {
        final PropertyKind kind = descriptor.getKind();
        switch (kind) {
        case INDEXED:
            ((List<?>) value).clear();
            break;
        case MAPPED:
            ((Map<?, ?>) value).clear();
            break;
        case SIMPLE:
        default:
            throw new InvalidPropertyKindException(descriptor.getName(), kind, PropertyKind.INDEXED,
                    PropertyKind.MAPPED);
        }
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(11, 7).append(descriptor).append(value).build();
    }

    @Override
    public boolean equals(final Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj.getClass() != getClass()) {
            return false;
        }
        final DefaultProperty rhs = (DefaultProperty) obj;
        return new EqualsBuilder().append(descriptor, rhs.descriptor).append(value, rhs.value).build();
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("descriptor", descriptor).append("value", value).build();
    }

}