org.sculptor.framework.domain.AbstractDomainObject.java Source code

Java tutorial

Introduction

Here is the source code for org.sculptor.framework.domain.AbstractDomainObject.java

Source

/*
 * Copyright 2007 The Fornax Project Team, including the original
 * author or authors.
 *
 * 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.sculptor.framework.domain;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * Base class for all Domain Objects. It provides stuff like toString, equals
 * and hashCode.
 * 
 */
public abstract class AbstractDomainObject implements Serializable, Cloneable {
    private static final long serialVersionUID = -7580523094174795539L;

    // acceptedToStringTypes contains Class objects or String names of types
    // accepted by toString
    private static Set<Object> acceptedToStringTypes = new HashSet<Object>();
    static {
        acceptedToStringTypes.add(int.class);
        acceptedToStringTypes.add(Integer.class);
        acceptedToStringTypes.add(long.class);
        acceptedToStringTypes.add(Long.class);
        acceptedToStringTypes.add(double.class);
        acceptedToStringTypes.add(Double.class);
        acceptedToStringTypes.add(BigDecimal.class);
        acceptedToStringTypes.add(boolean.class);
        acceptedToStringTypes.add(Boolean.class);
        acceptedToStringTypes.add(String.class);
        acceptedToStringTypes.add(Date.class);
        acceptedToStringTypes.add(float.class);
        acceptedToStringTypes.add(Float.class);
        acceptedToStringTypes.add(short.class);
        acceptedToStringTypes.add(Short.class);
        // avoid runtime dependency to joda, since it is optional
        acceptedToStringTypes.add("org.joda.time.LocalDate");
        acceptedToStringTypes.add("org.joda.time.DateTime");
    }

    /**
     * Only "simple" types will be included in toString. Relations to other
     * domain objects can not be included since we don't know if they are
     * fetched and we don't wont toString to fetch them.
     * <p>
     * More intelligent implementation can be done in subclasses if needed.
     * Subclasses may also override {@link #acceptToString(Field)}.
     * <p>
     * The
     */
    @Override
    public String toString() {
        try {
            ReflectionToStringBuilder builder = new ReflectionToStringBuilder(this, toStringStyle()) {
                @Override
                protected boolean accept(Field f) {
                    if (super.accept(f)) {
                        return acceptToString(f);
                    } else {
                        return false;
                    }
                }
            };
            builder.setAppendStatics(false);
            builder.setAppendTransients(true);
            return builder.toString();
        } catch (RuntimeException e) {
            // toString is only used for debug purpose and
            // eventual exceptions should not be "ignored"
            return "RuntimeException in " + getClass().getName() + ".toString():" + e.getMessage();
        }
    }

    /**
     * Subclass may override to define the style to use for toString, e.g.
     * ToStringStyle.SHORT_PREFIX_STYLE.
     */
    protected ToStringStyle toStringStyle() {
        return null;
    }

    /**
     * Subclasses may override this method to include or exclude some properties
     * in toString. By default only "simple" types will be included in toString.
     * Relations to other domain objects can not be included since we don't know
     * if they are fetched and we don't wont toString to fetch them.
     * 
     * @param field
     *            the field to include in toString if this method returns true
     * @return true to include the field in toString, or false to exclude it
     */
    protected boolean acceptToString(Field field) {
        return acceptedToStringTypes.contains(field.getType())
                || acceptedToStringTypes.contains(field.getType().getName());
    }

    /**
     * Subclass will typically override this method and return the real natural
     * key or UUID key. If it is not overridden this default implementation
     * returns null, which means that equals and hashCode will be implemented by
     * java.lang.Object (if those methods are not overridden in subclass).
     */
    protected Object getKey() {
        return null;
    }

    @Override
    public boolean equals(Object obj) {
        if (getKey() == null) {
            return super.equals(obj);
        }
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }

        if (!isSameClassHierarchy(obj.getClass())) {
            return false;
        }

        AbstractDomainObject other = (AbstractDomainObject) obj;

        Object key = getKey();
        if (key instanceof BigDecimal) {
            return ((BigDecimal) key).compareTo((BigDecimal) other.getKey()) == 0;
        }

        return key.equals(other.getKey());
    }

    protected boolean isSameClassHierarchy(Class<?> otherClass) {
        Class<?> clz = getClass();
        if (clz == otherClass) {
            return true;
        }
        Class<?> potentialRoot;
        do {
            potentialRoot = clz;
            clz = clz.getSuperclass();
        } while (clz != AbstractDomainObject.class);

        return potentialRoot.isAssignableFrom(otherClass);
    }

    @Override
    public int hashCode() {
        if (getKey() == null) {
            return super.hashCode();
        }
        return getKey().hashCode();
    }

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError();
        }
    }

}