    001/**
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *     http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.lang3.builder;
    018
    019import static org.apache.commons.lang3.reflect.FieldUtils.readField;
    020
    021import java.lang.reflect.Field;
    022import java.lang.reflect.Modifier;
    023
    024import org.apache.commons.lang3.ClassUtils;
    025import org.apache.commons.lang3.reflect.FieldUtils;
    026
    027/**
    028 * <p>
    029 * Assists in implementing {@link Diffable#diff(Object)} methods.
    030 * </p>
    031 * <p>
    032 * All non-static, non-transient fields (including inherited fields)
    033 * of the objects to diff are discovered using reflection and compared
    034 * for differences.
    035 * </p>
    036 *
    037 * <p>
    038 * To use this class, write code as follows:
    039 * </p>
    040 *
    041 * <pre>
    042 * public class Person implements Diffable&lt;Person&gt; {
    043 *   String name;
    044 *   int age;
    045 *   boolean smoker;
    046 *   ...
    047 *
    048 *   public DiffResult diff(Person obj) {
    049 *     // No need for null check, as NullPointerException correct if obj is null
    050 *     return new ReflectionDiffBuilder(this, obj, ToStringStyle.SHORT_PREFIX_STYLE)
    051 *       .build();
    052 *   }
    053 * }
    054 * </pre>
    055 *
    056 * <p>
    057 * The {@code ToStringStyle} passed to the constructor is embedded in the
    058 * returned {@code DiffResult} and influences the style of the
    059 * {@code DiffResult.toString()} method. This style choice can be overridden by
    060 * calling {@link DiffResult#toString(ToStringStyle)}.
    061 * </p>
    062 * @see Diffable
    063 * @see Diff
    064 * @see DiffResult
    065 * @see ToStringStyle
    066 * @since 3.6
    067 */
    068public class ReflectionDiffBuilder implements Builder<DiffResult> {
    069
    070    private final Object left;
    071    private final Object right;
    072    private final DiffBuilder diffBuilder;
    073
    074    /**
    075     * <p>
    076     * Constructs a builder for the specified objects with the specified style.
    077     * </p>
    078     *
    079     * <p>
    080     * If {@code lhs == rhs} or {@code lhs.equals(rhs)} then the builder will
    081     * not evaluate any calls to {@code append(...)} and will return an empty
    082     * {@link DiffResult} when {@link #build()} is executed.
    083     * </p>
    084     * @param <T>
    085     *            type of the objects to diff
    086     * @param lhs
    087     *            {@code this} object
    088     * @param rhs
    089     *            the object to diff against
    090     * @param style
    091     *            the style will use when outputting the objects, {@code null}
    092     *            uses the default
    093     * @throws IllegalArgumentException
    094     *             if {@code lhs} or {@code rhs} is {@code null}
    095     */
    096    public <T> ReflectionDiffBuilder(final T lhs, final T rhs, final ToStringStyle style) {
    097        this.left = lhs;
    098        this.right = rhs;
    099        diffBuilder = new DiffBuilder(lhs, rhs, style);
    100    }
    101
    102    @Override
    103    public DiffResult build() {
    104        if (left.equals(right)) {
    105            return diffBuilder.build();
    106        }
    107
    108        appendFields(left.getClass());
    109        return diffBuilder.build();
    110    }
    111
    112    private void appendFields(final Class<?> clazz) {
    113        for (final Field field : FieldUtils.getAllFields(clazz)) {
    114            if (accept(field)) {
    115                try {
    116                    diffBuilder.append(field.getName(), readField(field, left, true),
    117                            readField(field, right, true));
    118                } catch (final IllegalAccessException ex) {
    119                    //this can't happen. Would get a Security exception instead
    120                    //throw a runtime exception in case the impossible happens.
    121                    throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
    122                }
    123            }
    124        }
    125    }
    126
    127    private boolean accept(final Field field) {
    128        if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
    129            return false;
    130        }
    131        if (Modifier.isTransient(field.getModifiers())) {
    132            return false;
    133        }
    134        return !Modifier.isStatic(field.getModifiers());
    135    }
    136
    137}