    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 * <p>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}.</p>
    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        super();
    083        resetIndent();
    084    }
    085
    086    /**
    087     * Resets the fields responsible for the line breaks and indenting.
    088     * Must be invoked after changing the {@link #spaces} value.
    089     */
    090    private void resetIndent() {
    091        setArrayStart("{" + System.lineSeparator() + spacer(spaces));
    092        setArraySeparator("," + System.lineSeparator() + spacer(spaces));
    093        setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
    094
    095        setContentStart("[" + System.lineSeparator() + spacer(spaces));
    096        setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
    097        setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
    098    }
    099
    100    /**
    101     * Creates a StringBuilder responsible for the indenting.
    102     *
    103     * @param spaces how far to indent
    104     * @return a StringBuilder with {spaces} leading space characters.
    105     */
    106    private StringBuilder spacer(final int spaces) {
    107        final StringBuilder sb = new StringBuilder();
    108        for (int i = 0; i < spaces; i++) {
    109            sb.append(" ");
    110        }
    111        return sb;
    112    }
    113
    114    @Override
    115    public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
    116        if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
    117                && accept(value.getClass())) {
    118            spaces += INDENT;
    119            resetIndent();
    120            buffer.append(ReflectionToStringBuilder.toString(value, this));
    121            spaces -= INDENT;
    122            resetIndent();
    123        } else {
    124            super.appendDetail(buffer, fieldName, value);
    125        }
    126    }
    127
    128    @Override
    129    protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
    130        spaces += INDENT;
    131        resetIndent();
    132        super.appendDetail(buffer, fieldName, array);
    133        spaces -= INDENT;
    134        resetIndent();
    135    }
    136
    137    @Override
    138    protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
    139        spaces += INDENT;
    140        resetIndent();
    141        super.reflectionAppendArrayDetail(buffer, fieldName, array);
    142        spaces -= INDENT;
    143        resetIndent();
    144    }
    145
    146    @Override
    147    protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
    148        spaces += INDENT;
    149        resetIndent();
    150        super.appendDetail(buffer, fieldName, array);
    151        spaces -= INDENT;
    152        resetIndent();
    153    }
    154
    155    @Override
    156    protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
    157        spaces += INDENT;
    158        resetIndent();
    159        super.appendDetail(buffer, fieldName, array);
    160        spaces -= INDENT;
    161        resetIndent();
    162    }
    163
    164    @Override
    165    protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
    166        spaces += INDENT;
    167        resetIndent();
    168        super.appendDetail(buffer, fieldName, array);
    169        spaces -= INDENT;
    170        resetIndent();
    171    }
    172
    173    @Override
    174    protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
    175        spaces += INDENT;
    176        resetIndent();
    177        super.appendDetail(buffer, fieldName, array);
    178        spaces -= INDENT;
    179        resetIndent();
    180    }
    181
    182    @Override
    183    protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
    184        spaces += INDENT;
    185        resetIndent();
    186        super.appendDetail(buffer, fieldName, array);
    187        spaces -= INDENT;
    188        resetIndent();
    189    }
    190
    191    @Override
    192    protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
    193        spaces += INDENT;
    194        resetIndent();
    195        super.appendDetail(buffer, fieldName, array);
    196        spaces -= INDENT;
    197        resetIndent();
    198    }
    199
    200    @Override
    201    protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
    202        spaces += INDENT;
    203        resetIndent();
    204        super.appendDetail(buffer, fieldName, array);
    205        spaces -= INDENT;
    206        resetIndent();
    207    }
    208
    209    @Override
    210    protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
    211        spaces += INDENT;
    212        resetIndent();
    213        super.appendDetail(buffer, fieldName, array);
    214        spaces -= INDENT;
    215        resetIndent();
    216    }
    217
    218}