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