org.agiso.core.lang.util.ObjectUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.agiso.core.lang.util.ObjectUtils.java

Source

/* org.agiso.core.lang.util.ObjectUtils (2008-11-05)
 * 
 * ObjectUtils.java
 * 
 * Copyright 2008 agiso.org.
 * 
 * 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.agiso.core.lang.util;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.agiso.core.lang.annotation.InEquals;
import org.agiso.core.lang.annotation.InHashCode;
import org.agiso.core.lang.annotation.InToString;
import org.agiso.core.lang.exception.BaseRuntimeException;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;

/**
 * Klasa dostarczajaca metod narzdziowych pozwalajcych na generowanie
 * reprezentacji tekstowej obiektu, wyznaczanie jego sumy haszujcej oraz
 * porwnywanie obiektw.
 * </br>
 * Obiekty analizowane s za pomoc mechanizmu refleksji poprzez bezporedni
 * analiz pl klasy. Uwzglniany jest stan pl prywatnych.
 * 
 * @author Karol Kopacz
 * @since 1.0
 */
public abstract class ObjectUtils {
    private static final String TOSTRING_COLON = ": ";
    private static final String TOSTRING_HYPHEN = ", ";
    private static final String TOSTRING_PREFIX = "[";
    private static final String TOSTRING_SUFFIX = "]";
    private static final String TOSTRING_EMPTY = "[null]";
    private static final String TOSTRING_REPEATE = "[...]";

    private static final String TOSTRING_TCBUFF = "_toStringTCBuff";
    private static final String TOSTRING_TCATTR = "_toStringTCAttr";

    //   --------------------------------------------------------------------------
    //   Wsparcie implementacji metod hashCode, equals i toString
    //   --------------------------------------------------------------------------
    /**
     * Generuje sum haszujc obiektu wykorzystujc mechamizmy refleksji.
     * Podczas jej wyznaczania wykorzystuje opis dostarczany przez adnotacj
     * {@link InHashCode}. Ignoruje pola finalne oraz statyczne.
     * 
     * @see HashCodeBuilder#reflectionHashCode(Object, String[])
     */
    public static int hashCodeBuilder(Object object) {
        Set<String> excludeFields = new HashSet<String>();
        for (Field field : object.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(InHashCode.class)) {
                InHashCode inHashCode = field.getAnnotation(InHashCode.class);
                if (inHashCode.ignore()) {
                    excludeFields.add(field.getName());
                }
            }
        }
        return HashCodeBuilder.reflectionHashCode(object, excludeFields);
    }

    /**
     * Porwnuje dwa obiekty wykorzystujc mechamizmy refleksji. W trakcie
     * porwnywania wykorzystuje opis dostarczany przez adnotacj {@link
     * InEquals}. Ignoruje pola finalne oraz statyczne.
     * 
     * @see EqualsBuilder#reflectionEquals(Object, Object, String[])
     */
    public static boolean equalsBuilder(Object object1, Object object2) {
        Set<String> excludeFields = new HashSet<String>();
        for (Field field : object1.getClass().getDeclaredFields()) {
            if (field.isAnnotationPresent(InEquals.class)) {
                InEquals inEquals = field.getAnnotation(InEquals.class);
                if (inEquals.ignore()) {
                    excludeFields.add(field.getName());
                }
            }
        }
        return EqualsBuilder.reflectionEquals(object1, object2, excludeFields);
    }

    /**
     * Generuje acuch opisujcy obiekt wykorzystujc mechanizmy refleksji.
     * Podczas generacji acucha wykorzystuje opis dostarczany przez adnotacj
     * {@link InToString}. Ignoruje pola finalne oraz statyczne.
     * 
     * @param object Obiekt, ktrego opis ma zosta wygenerowany.
     * @return Opis obiektu.
     */
    public static String toStringBuilder(Object object) {
        if (ThreadUtils.hasAttribute(TOSTRING_TCBUFF)) {
            // Nastpio rekurencyjne wywoanie metody toStringBuidler(Object).
            // Pobieramy z wtku bufor zawierajcy generowany acuch i zachowujemy
            // go w zmiennej pomocniczej na czas tworzenia reprezentacji podobiektu:
            StringBuffer resultBuffer = (StringBuffer) ThreadUtils.getAttribute(TOSTRING_TCBUFF);

            StringBuffer localBuffer = new StringBuffer();
            ThreadUtils.putAttribute(TOSTRING_TCBUFF, localBuffer);

            toStringObject(object.getClass(), object);

            ThreadUtils.putAttribute(TOSTRING_TCBUFF, resultBuffer);

            localBuffer.append(TOSTRING_SUFFIX);
            return localBuffer.toString();
        } else
            try {
                // Generujemy reprezentacj tekstow obiektu. Tworzymy bufor na t
                // reprezentacj i zbir gromadzcy informacje o obiektach ju
                // przetworzonych (w celu uniknicia cyklicznych wywoa):
                StringBuffer resultBuffer = new StringBuffer();
                ThreadUtils.putAttribute(TOSTRING_TCBUFF, resultBuffer);
                ThreadUtils.putAttribute(TOSTRING_TCATTR, new HashSet<String>());

                toStringObject(object.getClass(), object);

                resultBuffer.append(TOSTRING_SUFFIX);
                return resultBuffer.toString();
            } finally {
                ThreadUtils.removeAttribute(TOSTRING_TCBUFF);
                ThreadUtils.removeAttribute(TOSTRING_TCATTR);
            }
    }

    /**
     * Metoda generujca reprezentacj acuchow obiektu. Przeglda wszystkie
     * pola obiektu i pobiera ich reprezentacj acuchow. Na tej podstawie
     * generuje acuch wynikowy.
     * 
     * @param clazz
     * @param object
     */
    private static void toStringObject(Class<?> clazz, Object object) {
        StringBuffer buffer = (StringBuffer) ThreadUtils.getAttribute(TOSTRING_TCBUFF);

        String hexHash = Integer.toHexString(System.identityHashCode(object));
        if (0 == buffer.length()) {
            buffer.append(clazz.getSimpleName()).append('@').append(hexHash).append(TOSTRING_PREFIX);
        }

        @SuppressWarnings("unchecked")
        Set<String> converted = (Set<String>) ThreadUtils.getAttribute(TOSTRING_TCATTR);
        converted.add(object.getClass().getCanonicalName() + "@" + hexHash);

        // Rekurencyjne przegldanie wszystkich klas nadrzdnych:
        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null) {
            toStringObject(superClass, object);
        }

        String hyphen = "";
        if (TOSTRING_PREFIX.charAt(0) != buffer.charAt(buffer.length() - 1)) {
            hyphen = TOSTRING_HYPHEN;
        }

        for (Field field : clazz.getDeclaredFields()) {
            try {
                field.setAccessible(true);
                if (field.isAnnotationPresent(InToString.class)) {
                    InToString inToString = field.getAnnotation(InToString.class);
                    if (!inToString.ignore()) {
                        String name = inToString.name();
                        buffer.append(hyphen).append((name.length() > 0) ? name : field.getName())
                                .append(TOSTRING_COLON);
                        toStringField(field.get(object));
                        hyphen = TOSTRING_HYPHEN;
                    }
                } else if ((field.getModifiers() & (Modifier.STATIC | Modifier.FINAL)) == 0) {
                    buffer.append(hyphen).append(field.getName()).append(TOSTRING_COLON);
                    toStringField(field.get(object));
                    hyphen = TOSTRING_HYPHEN;
                }
            } catch (Exception e) {
                // TODO: Zaimplementowa logowanie wyjtku
                e.printStackTrace();
            }
        }
    }

    /**
     * Generuje reprezentacj acuchow pola. Sprawdza, czy nie bya ona ju wczeniej
     * wyznaczana. Jeli tak, tworzy wersj skrcon reprezentacji takiego pola.
     * 
     * @param object
     */
    private static void toStringField(Object object) {
        StringBuffer buffer = (StringBuffer) ThreadUtils.getAttribute(TOSTRING_TCBUFF);

        if (object == null) {
            buffer.append(TOSTRING_EMPTY);
        } else {
            Class<?> clazz = object.getClass();
            if (clazz.isPrimitive()) {
                buffer.append(object.toString());
            } else {
                @SuppressWarnings("unchecked")
                Set<String> converted = (Set<String>) ThreadUtils.getAttribute(TOSTRING_TCATTR);
                String hexHash = Integer.toHexString(System.identityHashCode(object));
                if (converted.contains(clazz.getCanonicalName() + "@" + hexHash)) {
                    buffer.append(clazz.getSimpleName()).append('@').append(hexHash).append(TOSTRING_REPEATE);
                } else {
                    buffer.append(object.toString());
                }
            }
        }
    }

    //   --------------------------------------------------------------------------
    //   Porwnywanie obiektw
    //   --------------------------------------------------------------------------
    /**
     * Porwnuje dwa obiekty wykorzystujc standardow metod <code>equals</code>
     * obiektu przekazanego parametrem wywoania <code>object1</code>. Zwraca
     * <code>true</code> jeli oba przekazane do porwnania obiekty nie s
     * okrelone (maj warto <code>null</code>).
     */
    public static boolean equalsChecker(Object object1, Object object2) {
        return null == object1 ? null == object2 : object1.equals(object2);
    }

    /**
     * Porwnuje dwa obiekty typu {@link Comparable} wykorzystujc metod <code>
     * comapreTo</code> obiektu przezkazanego prarametrem wywoania <code>object1</code>.
     * Zwraca <code>true</code> jeli oba przekazane do porwnania obiekty nie s
     * okrelone (maj warto <code>null</code>).
     */
    @SuppressWarnings("unchecked")
    public static final boolean compareChecker(Comparable<?> object1, Comparable<?> object2) {
        if (null == object1 || null == object2) {
            return object1 == object2;
        } else {
            return 0 == ((Comparable<Object>) object1).compareTo((Comparable<Object>) object2);
        }
    }

    /**
     * Porwnuje dwa obiekty <code>T</code> typu {@link Comparable} wykorzystujc
     * metdo {@link Comparable#compareTo(Object)}. Jeli przekazane obiekty <code>
     * val?</code> maj warto <code>null</code>, do do porwnania wykorzystuje
     * odpowiadajce im wartoci <code>def?</code>.
     * 
     * @param <T>
     * @param val1
     * @param def1
     * @param val2
     * @param def2
     * @return
     */
    // TODO: Zweryfikowa dokumentacj
    public static final <T extends Comparable<T>> int comparator(T val1, T def1, T val2, T def2) {
        return comparator(val1 == null ? def1 : val1, val2 == null ? def2 : val2);
        // return (val1 == null? def1 : val1).compareTo(val2 == null? def2 : val2);
    }

    // TODO: Uzupeni dokumentacj
    public static final <T extends Comparable<T>> int comparator(T val1, T val2) {
        if (val1 == null) {
            return val2 == null ? 0 : -1;
        } else {
            return val2 == null ? 1 : val1.compareTo(val2);
        }
    }

    //   --------------------------------------------------------------------------
    //   Operacje na listach. TODO: Do weryfikacji wykorzystania i przeniesienia
    //   --------------------------------------------------------------------------
    public static <E> List<E> subtractList(List<E> minuend, Collection<?> subtrahendList) {
        return subtractList(minuend, null, subtrahendList, null);
    }

    public static <E> List<E> subtractList(List<E> minuend, String mProperty, Collection<?> subtrahendList) {
        return subtractList(minuend, mProperty, subtrahendList, null);
    }

    public static <E> List<E> subtractList(List<E> minuend, Collection<?> subtrahendList, String sProperty) {
        return subtractList(minuend, null, subtrahendList, sProperty);
    }

    public static <E> List<E> subtractList(List<E> minuend, String mProperty, Collection<?> subtrahendList,
            String sProperty) {
        Object mObject, sObject;
        List<E> result = new ArrayList<E>();

        try {
            for (Object element : subtrahendList) {
                for (int index = minuend.size() - 1; index >= 0; index--) {
                    sObject = StringUtils.isEmpty(sProperty) ? element
                            : PropertyUtils.getProperty(element, sProperty);
                    mObject = StringUtils.isEmpty(mProperty) ? minuend.get(index)
                            : PropertyUtils.getProperty(minuend.get(index), mProperty);

                    if (mObject.equals(sObject)) {
                        result.add(minuend.remove(index));
                    }
                }
            }
        } catch (Exception e) {
            // TODO: Zaimplementowa dedykowany wyjtek dla metody QbjectUtils.subtractList()
            throw new BaseRuntimeException(e);
        }

        return result;
    }
}