    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 */
    017
    018package org.apache.commons.lang3.builder;
    019
    020import org.apache.commons.lang3.ClassUtils;
    021
    022/**
    023 * Works with {@link ToStringBuilder} to create a "deep" {@code toString}.
    024 * But instead a single line like the {@link RecursiveToStringStyle} this creates a multiline String
    025 * similar to the {@link ToStringStyle#MULTI_LINE_STYLE}.
    026 *
    027 * <p>To use this class write code as follows:</p>
    028 *
    029 * <pre>
    030 * public class Job {
    031 *   String title;
    032 *   ...
    033 * }
    034 *
    035 * public class Person {
    036 *   String name;
    037 *   int age;
    038 *   boolean smoker;
    039 *   Job job;
    040 *
    041 *   ...
    042 *
    043 *   public String toString() {
    044 *     return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
    045 *   }
    046 * }
    047 * </pre>
    048 *
    049 * <p>
    050 * This will produce a toString of the format:<br>
    051 * <code>Person@7f54[ <br>
    052 * &nbsp; name=Stephen, <br>
    053 * &nbsp; age=29, <br>
    054 * &nbsp; smoker=false, <br>
    055 * &nbsp; job=Job@43cd2[ <br>
    056 * &nbsp; &nbsp; title=Manager <br>
    057 * &nbsp;  ] <br>
    058 * ]
    059 * </code>
    060 * </p>
    061 *
    062 * @since 3.4
    063 */
    064public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
    065
    066    /**
    067     * Required for serialization support.
    068     * @see java.io.Serializable
    069     */
    070    private static final long serialVersionUID = 1L;
    071
    072    /** Indenting of inner lines. */
    073    private static final int INDENT = 2;
    074
    075    /** Current indenting. */
    076    private int spaces = 2;
    077
    078    /**
    079     * Constructor.
    080     */
    081    public MultilineRecursiveToStringStyle() {
    082        resetIndent();
    083    }
    084
    085    @Override
    086    protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
    087        spaces += INDENT;
    088        resetIndent();
    089        super.appendDetail(buffer, fieldName, array);
    090        spaces -= INDENT;
    091        resetIndent();
    092    }
    093
    094    @Override
    095    protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
    096        spaces += INDENT;
    097        resetIndent();
    098        super.appendDetail(buffer, fieldName, array);
    099        spaces -= INDENT;
    100        resetIndent();
    101    }
    102
    103    @Override
    104    protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
    105        spaces += INDENT;
    106        resetIndent();
    107        super.appendDetail(buffer, fieldName, array);
    108        spaces -= INDENT;
    109        resetIndent();
    110    }
    111
    112    @Override
    113    protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
    114        spaces += INDENT;
    115        resetIndent();
    116        super.appendDetail(buffer, fieldName, array);
    117        spaces -= INDENT;
    118        resetIndent();
    119    }
    120
    121    @Override
    122    protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
    123        spaces += INDENT;
    124        resetIndent();
    125        super.appendDetail(buffer, fieldName, array);
    126        spaces -= INDENT;
    127        resetIndent();
    128    }
    129
    130    @Override
    131    protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
    132        spaces += INDENT;
    133        resetIndent();
    134        super.appendDetail(buffer, fieldName, array);
    135        spaces -= INDENT;
    136        resetIndent();
    137    }
    138
    139    @Override
    140    protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
    141        spaces += INDENT;
    142        resetIndent();
    143        super.appendDetail(buffer, fieldName, array);
    144        spaces -= INDENT;
    145        resetIndent();
    146    }
    147
    148    @Override
    149    public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
    150        if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
    151                && accept(value.getClass())) {
    152            spaces += INDENT;
    153            resetIndent();
    154            buffer.append(ReflectionToStringBuilder.toString(value, this));
    155            spaces -= INDENT;
    156            resetIndent();
    157        } else {
    158            super.appendDetail(buffer, fieldName, value);
    159        }
    160    }
    161
    162    @Override
    163    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
    164        spaces += INDENT;
    165        resetIndent();
    166        super.appendDetail(buffer, fieldName, array);
    167        spaces -= INDENT;
    168        resetIndent();
    169    }
    170
    171    @Override
    172    protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
    173        spaces += INDENT;
    174        resetIndent();
    175        super.appendDetail(buffer, fieldName, array);
    176        spaces -= INDENT;
    177        resetIndent();
    178    }
    179
    180    @Override
    181    protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
    182        spaces += INDENT;
    183        resetIndent();
    184        super.reflectionAppendArrayDetail(buffer, fieldName, array);
    185        spaces -= INDENT;
    186        resetIndent();
    187    }
    188
    189    /**
    190     * Resets the fields responsible for the line breaks and indenting.
    191     * Must be invoked after changing the {@link #spaces} value.
    192     */
    193    private void resetIndent() {
    194        setArrayStart("{" + System.lineSeparator() + spacer(spaces));
    195        setArraySeparator("," + System.lineSeparator() + spacer(spaces));
    196        setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
    197
    198        setContentStart("[" + System.lineSeparator() + spacer(spaces));
    199        setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
    200        setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
    201    }
    202
    203    /**
    204     * Creates a StringBuilder responsible for the indenting.
    205     *
    206     * @param spaces how far to indent
    207     * @return a StringBuilder with {spaces} leading space characters.
    208     */
    209    private StringBuilder spacer(final int spaces) {
    210        final StringBuilder sb = new StringBuilder();
    211        for (int i = 0; i < spaces; i++) {
    212            sb.append(" ");
    213        }
    214        return sb;
    215    }
    216
    217}