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}