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