View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang3.builder;
19  
20  import org.apache.commons.lang3.ClassUtils;
21  
22  /**
23   * Works with {@link ToStringBuilder} to create a "deep" {@code toString}.
24   * But instead a single line like the {@link RecursiveToStringStyle} this creates a multiline String
25   * similar to the {@link ToStringStyle#MULTI_LINE_STYLE}.
26   *
27   * <p>To use this class write code as follows:</p>
28   *
29   * <pre>
30   * public class Job {
31   *   String title;
32   *   ...
33   * }
34   *
35   * public class Person {
36   *   String name;
37   *   int age;
38   *   boolean smoker;
39   *   Job job;
40   *
41   *   ...
42   *
43   *   public String toString() {
44   *     return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
45   *   }
46   * }
47   * </pre>
48   *
49   * <p>
50   * This will produce a toString of the format:<br>
51   * <code>Person@7f54[ <br>
52   * &nbsp; name=Stephen, <br>
53   * &nbsp; age=29, <br>
54   * &nbsp; smoker=false, <br>
55   * &nbsp; job=Job@43cd2[ <br>
56   * &nbsp; &nbsp; title=Manager <br>
57   * &nbsp;  ] <br>
58   * ]
59   * </code>
60   * </p>
61   *
62   * @since 3.4
63   */
64  public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
65  
66      /**
67       * Required for serialization support.
68       * @see java.io.Serializable
69       */
70      private static final long serialVersionUID = 1L;
71  
72      /** Indenting of inner lines. */
73      private static final int INDENT = 2;
74  
75      /** Current indenting. */
76      private int spaces = 2;
77  
78      /**
79       * Constructor.
80       */
81      public MultilineRecursiveToStringStyle() {
82          resetIndent();
83      }
84  
85      @Override
86      protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
87          spaces += INDENT;
88          resetIndent();
89          super.appendDetail(buffer, fieldName, array);
90          spaces -= INDENT;
91          resetIndent();
92      }
93  
94      @Override
95      protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
96          spaces += INDENT;
97          resetIndent();
98          super.appendDetail(buffer, fieldName, array);
99          spaces -= INDENT;
100         resetIndent();
101     }
102 
103     @Override
104     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
105         spaces += INDENT;
106         resetIndent();
107         super.appendDetail(buffer, fieldName, array);
108         spaces -= INDENT;
109         resetIndent();
110     }
111 
112     @Override
113     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
114         spaces += INDENT;
115         resetIndent();
116         super.appendDetail(buffer, fieldName, array);
117         spaces -= INDENT;
118         resetIndent();
119     }
120 
121     @Override
122     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
123         spaces += INDENT;
124         resetIndent();
125         super.appendDetail(buffer, fieldName, array);
126         spaces -= INDENT;
127         resetIndent();
128     }
129 
130     @Override
131     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
132         spaces += INDENT;
133         resetIndent();
134         super.appendDetail(buffer, fieldName, array);
135         spaces -= INDENT;
136         resetIndent();
137     }
138 
139     @Override
140     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
141         spaces += INDENT;
142         resetIndent();
143         super.appendDetail(buffer, fieldName, array);
144         spaces -= INDENT;
145         resetIndent();
146     }
147 
148     @Override
149     public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
150         if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
151                 && accept(value.getClass())) {
152             spaces += INDENT;
153             resetIndent();
154             buffer.append(ReflectionToStringBuilder.toString(value, this));
155             spaces -= INDENT;
156             resetIndent();
157         } else {
158             super.appendDetail(buffer, fieldName, value);
159         }
160     }
161 
162     @Override
163     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
164         spaces += INDENT;
165         resetIndent();
166         super.appendDetail(buffer, fieldName, array);
167         spaces -= INDENT;
168         resetIndent();
169     }
170 
171     @Override
172     protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
173         spaces += INDENT;
174         resetIndent();
175         super.appendDetail(buffer, fieldName, array);
176         spaces -= INDENT;
177         resetIndent();
178     }
179 
180     @Override
181     protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
182         spaces += INDENT;
183         resetIndent();
184         super.reflectionAppendArrayDetail(buffer, fieldName, array);
185         spaces -= INDENT;
186         resetIndent();
187     }
188 
189     /**
190      * Resets the fields responsible for the line breaks and indenting.
191      * Must be invoked after changing the {@link #spaces} value.
192      */
193     private void resetIndent() {
194         setArrayStart("{" + System.lineSeparator() + spacer(spaces));
195         setArraySeparator("," + System.lineSeparator() + spacer(spaces));
196         setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
197 
198         setContentStart("[" + System.lineSeparator() + spacer(spaces));
199         setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
200         setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
201     }
202 
203     /**
204      * Creates a StringBuilder responsible for the indenting.
205      *
206      * @param spaces how far to indent
207      * @return a StringBuilder with {spaces} leading space characters.
208      */
209     private StringBuilder spacer(final int spaces) {
210         final StringBuilder sb = new StringBuilder();
211         for (int i = 0; i < spaces; i++) {
212             sb.append(" ");
213         }
214         return sb;
215     }
216 
217 }