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    *      https://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  import org.apache.commons.lang3.StringUtils;
22  
23  /**
24   * Works with {@link ToStringBuilder} to create a "deep" {@code toString}.
25   * But instead a single line like the {@link RecursiveToStringStyle} this creates a multiline String
26   * similar to the {@link ToStringStyle#MULTI_LINE_STYLE}.
27   *
28   * <p>To use this class write code as follows:</p>
29   *
30   * <pre>
31   * public class Job {
32   *   String title;
33   *   ...
34   * }
35   *
36   * public class Person {
37   *   String name;
38   *   int age;
39   *   boolean smoker;
40   *   Job job;
41   *
42   *   ...
43   *
44   *   public String toString() {
45   *     return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
46   *   }
47   * }
48   * </pre>
49   *
50   * <p>
51   * This will produce a toString of the format:<br>
52   * {@code Person@7f54[ <br>
53   * &nbsp; name=Stephen, <br>
54   * &nbsp; age=29, <br>
55   * &nbsp; smokealse, <br>
56   * &nbsp; job=Job@43cd2[ <br>
57   * &nbsp; &nbsp; title=Manager <br>
58   * &nbsp;  ] <br>
59   * ]
60   * }
61   * </p>
62   *
63   * @since 3.4
64   */
65  public class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
66  
67      /**
68       * Required for serialization support.
69       *
70       * @see java.io.Serializable
71       */
72      private static final long serialVersionUID = 1L;
73  
74      /** Indenting of inner lines. */
75      private static final int INDENT = 2;
76  
77      /** Current indenting. */
78      private int spaces = 2;
79  
80      /**
81       * Constructs a new instance.
82       */
83      public MultilineRecursiveToStringStyle() {
84          resetIndent();
85      }
86  
87      @Override
88      protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
89          spaces += INDENT;
90          resetIndent();
91          super.appendDetail(buffer, fieldName, array);
92          spaces -= INDENT;
93          resetIndent();
94      }
95  
96      @Override
97      protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
98          spaces += INDENT;
99          resetIndent();
100         super.appendDetail(buffer, fieldName, array);
101         spaces -= INDENT;
102         resetIndent();
103     }
104 
105     @Override
106     protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
107         spaces += INDENT;
108         resetIndent();
109         super.appendDetail(buffer, fieldName, array);
110         spaces -= INDENT;
111         resetIndent();
112     }
113 
114     @Override
115     protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
116         spaces += INDENT;
117         resetIndent();
118         super.appendDetail(buffer, fieldName, array);
119         spaces -= INDENT;
120         resetIndent();
121     }
122 
123     @Override
124     protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
125         spaces += INDENT;
126         resetIndent();
127         super.appendDetail(buffer, fieldName, array);
128         spaces -= INDENT;
129         resetIndent();
130     }
131 
132     @Override
133     protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
134         spaces += INDENT;
135         resetIndent();
136         super.appendDetail(buffer, fieldName, array);
137         spaces -= INDENT;
138         resetIndent();
139     }
140 
141     @Override
142     protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
143         spaces += INDENT;
144         resetIndent();
145         super.appendDetail(buffer, fieldName, array);
146         spaces -= INDENT;
147         resetIndent();
148     }
149 
150     @Override
151     public void appendDetail(final StringBuffer buffer, final String fieldName, final Object value) {
152         if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
153                 && accept(value.getClass())) {
154             spaces += INDENT;
155             resetIndent();
156             buffer.append(ReflectionToStringBuilder.toString(value, this));
157             spaces -= INDENT;
158             resetIndent();
159         } else {
160             super.appendDetail(buffer, fieldName, value);
161         }
162     }
163 
164     @Override
165     protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] 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 short[] array) {
175         spaces += INDENT;
176         resetIndent();
177         super.appendDetail(buffer, fieldName, array);
178         spaces -= INDENT;
179         resetIndent();
180     }
181 
182     @Override
183     protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
184         spaces += INDENT;
185         resetIndent();
186         super.reflectionAppendArrayDetail(buffer, fieldName, array);
187         spaces -= INDENT;
188         resetIndent();
189     }
190 
191     /**
192      * Resets the fields responsible for the line breaks and indenting.
193      * Must be invoked after changing the {@link #spaces} value.
194      */
195     private void resetIndent() {
196         setArrayStart("{" + System.lineSeparator() + spacer(spaces));
197         setArraySeparator("," + System.lineSeparator() + spacer(spaces));
198         setArrayEnd(System.lineSeparator() + spacer(spaces - INDENT) + "}");
199 
200         setContentStart("[" + System.lineSeparator() + spacer(spaces));
201         setFieldSeparator("," + System.lineSeparator() + spacer(spaces));
202         setContentEnd(System.lineSeparator() + spacer(spaces - INDENT) + "]");
203     }
204 
205     /**
206      * Creates a StringBuilder responsible for the indenting.
207      *
208      * @param spaces how far to indent
209      * @return a StringBuilder with {spaces} leading space characters.
210      */
211     private String spacer(final int spaces) {
212         return StringUtils.repeat(' ', spaces);
213     }
214 
215 }