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    /**
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}