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