LANG-1052: Multiline recursive to string style. This fixes #34 from github. Thanks to Jan Matèrne.

git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1637671 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Benedikt Ritter 2014-11-09 12:47:12 +00:00
parent 85c18d0ee8
commit 635e41ea18
3 changed files with 484 additions and 0 deletions

View File

@ -22,6 +22,7 @@
<body>
<release version="3.4" date="tba" description="tba">
<action issue="LANG-1052" type="add" dev="britter" due-to="Jan Matèrne">Multiline recursive to string style</action>
<action issue="LANG-536" type="add" dev="djones" due-to="James Sawle">Add isSorted() to ArrayUtils</action>
<action issue="LANG-1041" type="fix" dev="britter" due-to="Alexandre Bartel">Fix MethodUtilsTest so it does not depend on JDK method ordering</action>
<action issue="LANG-827" type="update" dev="djones">CompareToBuilder's doc doesn't specify precedence of fields it uses in performing comparisons</action>

View File

@ -0,0 +1,219 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.builder;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.SystemUtils;
/**
* <p>Works with {@link ToStringBuilder} to create a "deep" <code>toString</code>.
* But instead a single line like the {@link RecursiveToStringStyle} this creates a multiline String
* similar to the {@link ToStringStyle.MultiLineToStringStyle}.</p>
*
* <p>To use this class write code as follows:</p>
*
* <pre>
* public class Job {
* String title;
* ...
* }
*
* public class Person {
* String name;
* int age;
* boolean smoker;
* Job job;
*
* ...
*
* public String toString() {
* return new ReflectionToStringBuilder(this, new MultilineRecursiveToStringStyle()).toString();
* }
* }
* </pre>
*
* <p>
* This will produce a toString of the format:<br>
* <code>Person@7f54[ <br>
* &nbsp; name=Stephen, <br>
* &nbsp; age=29, <br>
* &nbsp; smoker=false, <br>
* &nbsp; job=Job@43cd2[ <br>
* &nbsp; &nbsp; title=Manager <br>
* &nbsp; ] <br>
* ]
* </code>
* </p>
*
* @since 3.4
* @version $Id$
*/
class MultilineRecursiveToStringStyle extends RecursiveToStringStyle {
/**
* Required for serialization support.
* @see java.io.Serializable
*/
private static final long serialVersionUID = 1L;
/** Indenting of inner lines. */
private int indent = 2;
/** Current indenting. */
private int spaces = 2;
/**
* Constructor.
*/
public MultilineRecursiveToStringStyle() {
super();
resetIndent();
}
/**
* Resets the fields responsible for the line breaks and indenting.
* Must be invoked after changing the {@link #spaces} value.
*/
private void resetIndent() {
setArrayStart("{" + SystemUtils.LINE_SEPARATOR + spacer(spaces));
setArraySeparator("," + SystemUtils.LINE_SEPARATOR + spacer(spaces));
setArrayEnd(SystemUtils.LINE_SEPARATOR + spacer(spaces - indent) + "}");
setContentStart("[" + SystemUtils.LINE_SEPARATOR + spacer(spaces));
setFieldSeparator("," + SystemUtils.LINE_SEPARATOR + spacer(spaces));
setContentEnd(SystemUtils.LINE_SEPARATOR + spacer(spaces - indent) + "]");
}
/**
* Creates a string(buffer) responsible for the indenting.
* @param spaces how far to indent
* @return
*/
private StringBuilder spacer(int spaces) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < spaces; i++) {
sb.append(" ");
}
return sb;
}
@Override
public void appendDetail(StringBuffer buffer, String fieldName, Object value) {
if (!ClassUtils.isPrimitiveWrapper(value.getClass()) && !String.class.equals(value.getClass())
&& accept(value.getClass())) {
spaces += indent;
resetIndent();
buffer.append(ReflectionToStringBuilder.toString(value, this));
spaces -= indent;
resetIndent();
} else {
super.appendDetail(buffer, fieldName, value);
}
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final Object[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void reflectionAppendArrayDetail(final StringBuffer buffer, final String fieldName, final Object array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final long[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final int[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final short[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final byte[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final char[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final double[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final float[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
@Override
protected void appendDetail(final StringBuffer buffer, final String fieldName, final boolean[] array) {
spaces += indent;
resetIndent();
super.appendDetail(buffer, fieldName, array);
spaces -= indent;
resetIndent();
}
}

View File

@ -0,0 +1,264 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.lang3.builder;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang3.SystemUtils;
import org.junit.Test;
/**
* @version $Id$
*/
public class MultilineRecursiveToStringStyleTest {
private final String BR = SystemUtils.LINE_SEPARATOR;
@Test
public void simpleObject() {
Transaction tx = new Transaction("2014.10.15", 100);
String expected = getClassPrefix(tx) + "[" + BR
+ " amount=100.0," + BR
+ " date=2014.10.15" + BR
+ "]";
assertEquals(expected, toString(tx));
}
@Test
public void nestedElements() {
Customer customer = new Customer("Douglas Adams");
Bank bank = new Bank("ASF Bank");
customer.bank = bank;
String exp = getClassPrefix(customer) + "[" + BR
+ " name=Douglas Adams," + BR
+ " bank=" + getClassPrefix(bank) + "[" + BR
+ " name=ASF Bank" + BR
+ " ]," + BR
+ " accounts=<null>" + BR
+ "]";
assertEquals(exp, toString(customer));
}
@Test
public void nestedAndArray() {
Account acc = new Account();
Transaction tx1 = new Transaction("2014.10.14", 100);
Transaction tx2 = new Transaction("2014.10.15", 50);
acc.transactions.add(tx1);
acc.transactions.add(tx2);
String expected = getClassPrefix(acc) + "[" + BR
+ " owner=<null>," + BR
+ " transactions=" + getClassPrefix(acc.transactions) + "{" + BR
+ " " + getClassPrefix(tx1) + "[" + BR
+ " amount=100.0," + BR
+ " date=2014.10.14" + BR
+ " ]," + BR
+ " " + getClassPrefix(tx2) + "[" + BR
+ " amount=50.0," + BR
+ " date=2014.10.15" + BR
+ " ]" + BR
+ " }" + BR
+ "]";
assertEquals(expected, toString(acc));
}
@Test
public void noArray() {
WithArrays wa = new WithArrays();
String exp = getClassPrefix(wa) + "[" + BR
+ " boolArray=<null>," + BR
+ " charArray=<null>," + BR
+ " intArray=<null>," + BR
+ " doubleArray=<null>," + BR
+ " longArray=<null>," + BR
+ " stringArray=<null>" + BR
+ "]";
assertEquals(exp, toString(wa));
}
@Test
public void boolArray() {
WithArrays wa = new WithArrays();
wa.boolArray = new boolean[] { true, false, true };
String exp = getClassPrefix(wa) + "[" + BR
+ " boolArray={" + BR
+ " true," + BR
+ " false," + BR
+ " true" + BR
+ " }," + BR
+ " charArray=<null>," + BR
+ " intArray=<null>," + BR
+ " doubleArray=<null>," + BR
+ " longArray=<null>," + BR
+ " stringArray=<null>" + BR
+ "]";
assertEquals(exp, toString(wa));
}
@Test
public void charArray() {
WithArrays wa = new WithArrays();
wa.charArray = new char[] { 'a', 'A' };
String exp = getClassPrefix(wa) + "[" + BR
+ " boolArray=<null>," + BR
+ " charArray={" + BR
+ " a," + BR
+ " A" + BR
+ " }," + BR
+ " intArray=<null>," + BR
+ " doubleArray=<null>," + BR
+ " longArray=<null>," + BR
+ " stringArray=<null>" + BR
+ "]";
assertEquals(exp, toString(wa));
}
@Test
public void intArray() {
WithArrays wa = new WithArrays();
wa.intArray = new int[] { 1, 2 };
String exp = getClassPrefix(wa) + "[" + BR
+ " boolArray=<null>," + BR
+ " charArray=<null>," + BR
+ " intArray={" + BR
+ " 1," + BR
+ " 2" + BR
+ " }," + BR
+ " doubleArray=<null>," + BR
+ " longArray=<null>," + BR
+ " stringArray=<null>" + BR
+ "]";
assertEquals(exp, toString(wa));
}
@Test
public void doubleArray() {
WithArrays wa = new WithArrays();
wa.doubleArray = new double[] { 1, 2 };
String exp = getClassPrefix(wa) + "[" + BR
+ " boolArray=<null>," + BR
+ " charArray=<null>," + BR
+ " intArray=<null>," + BR
+ " doubleArray={" + BR
+ " 1.0," + BR
+ " 2.0" + BR
+ " }," + BR
+ " longArray=<null>," + BR
+ " stringArray=<null>" + BR
+ "]";
assertEquals(exp, toString(wa));
}
@Test
public void longArray() {
WithArrays wa = new WithArrays();
wa.longArray = new long[] { 1L, 2L };
String exp = getClassPrefix(wa) + "[" + BR
+ " boolArray=<null>," + BR
+ " charArray=<null>," + BR
+ " intArray=<null>," + BR
+ " doubleArray=<null>," + BR
+ " longArray={" + BR
+ " 1," + BR
+ " 2" + BR
+ " }," + BR
+ " stringArray=<null>" + BR
+ "]";
assertEquals(exp, toString(wa));
}
@Test
public void stringArray() {
WithArrays wa = new WithArrays();
wa.stringArray = new String[] { "a", "A" };
String exp = getClassPrefix(wa) + "[" + BR
+ " boolArray=<null>," + BR
+ " charArray=<null>," + BR
+ " intArray=<null>," + BR
+ " doubleArray=<null>," + BR
+ " longArray=<null>," + BR
+ " stringArray={" + BR
+ " a," + BR
+ " A" + BR
+ " }" + BR
+ "]";
assertEquals(exp, toString(wa));
}
private String getClassPrefix(Object object) {
return object.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(object));
}
private String toString(Object object) {
return new ReflectionToStringBuilder(object, new MultilineRecursiveToStringStyle()).toString();
}
static class WithArrays {
boolean[] boolArray;
char[] charArray;
int[] intArray;
double[] doubleArray;
long[] longArray;
String[] stringArray;
}
static class Bank {
String name;
public Bank(String name) {
this.name = name;
}
}
static class Customer {
String name;
Bank bank;
List<Account> accounts;
public Customer(String name) {
this.name = name;
}
}
static class Account {
Customer owner;
List<Transaction> transactions = new ArrayList<Transaction>();
public double getBalance() {
double balance = 0;
for (Transaction tx : transactions) {
balance += tx.amount;
}
return balance;
}
}
static class Transaction {
double amount;
String date;
public Transaction(String datum, double betrag) {
this.date = datum;
this.amount = betrag;
}
}
}