Java tutorial
/* 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; } }