001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas/ 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see https://www.gnu.org/licenses/. 027 */ 028 029package edu.wisc.ssec.mcidasv.util; 030 031import java.util.Arrays; 032import java.util.Objects; 033 034/** 035 * Utility class for {@code toString()} methods. 036 * 037 * <p>Largely taken from Guava's {@code toStringHelper()}, with some 038 * formatting differences as well as the hash code for the given instance.</p> 039 */ 040public class MakeToString { 041 042 private final ValueHolder head = new ValueHolder(); 043 private final String className; 044 private final int instanceHashCode; 045 046 private boolean omitNullValues = false; 047 private ValueHolder tail = head; 048 049 private MakeToString(Object instance) { 050 Objects.requireNonNull(instance, "Cannot generate toString for a null object"); 051 className = instance.getClass().getSimpleName(); 052 instanceHashCode = instance.hashCode(); 053 } 054 055 private MakeToString(String clazzName) { 056 className = clazzName; 057 instanceHashCode = -1; 058 } 059 060 public static MakeToString fromInstance(Object instance) { 061 return new MakeToString(instance); 062 } 063 064 public static MakeToString fromClass(Class<?> clazz) { 065 return new MakeToString(clazz.getSimpleName()); 066 } 067 068 public MakeToString omitNullValues() { 069 omitNullValues = true; 070 return this; 071 } 072 073 public MakeToString addQuoted(String name, Object value) { 074 return addHolder(name, '"' + String.valueOf(value) + '"'); 075 } 076 077 public MakeToString add(String name, Object value) { 078 return addHolder(name, String.valueOf(value)); 079 } 080 081 public MakeToString add(String name, long value) { 082 return addHolder(name, String.valueOf(value)); 083 } 084 085 public MakeToString add(String name, boolean value) { 086 return addHolder(name, String.valueOf(value)); 087 } 088 089 public MakeToString add(String name, int value) { 090 return addHolder(name, String.valueOf(value)); 091 } 092 093 public MakeToString add(String name, char value) { 094 return addHolder(name, String.valueOf(value)); 095 } 096 097 public MakeToString add(String name, float value) { 098 return addHolder(name, String.valueOf(value)); 099 } 100 101 public MakeToString add(String name, double value) { 102 return addHolder(name, String.valueOf(value)); 103 } 104 105 public MakeToString add(String name, double... arr) { 106 StringBuilder buf = new StringBuilder(512); 107 buf.append("[ "); 108 for (int i = 0; i < arr.length; i++) { 109 buf.append(arr[i]); 110 if ((i+1) < arr.length) { 111 buf.append(','); 112 } 113 } 114 buf.append(" ]"); 115 return addHolder(name, buf.toString()); 116 } 117 118 private ValueHolder addHolder() { 119 ValueHolder valueHolder = new ValueHolder(); 120 tail = tail.next = valueHolder; 121 return valueHolder; 122 } 123 124 private MakeToString addHolder(String name, Object value) { 125 ValueHolder valueHolder = addHolder(); 126 valueHolder.name = Objects.requireNonNull(name); 127 valueHolder.value = value; 128 return this; 129 } 130 131 /** 132 * After calling this method, you can keep adding more properties to 133 * later call toString() again and get a more complete representation of 134 * the same object; but properties cannot be removed, so this only allows 135 * limited reuse of the helper instance. The helper allows duplication of 136 * properties (multiple name/value pairs with the same name can be added). 137 */ 138 @Override public String toString() { 139 boolean omitNullValuesSnapshot = omitNullValues; 140 String hex = Integer.toHexString(instanceHashCode); 141 StringBuilder builder = 142 new StringBuilder((className.length() + hex.length()) * 10); 143 builder.append('[').append(className); 144 if (instanceHashCode != -1) { 145 builder.append('@').append(hex); 146 } 147 builder.append(": "); 148 String nextSeparator = ""; 149 for (ValueHolder valueHolder = head.next; 150 valueHolder != null; 151 valueHolder = valueHolder.next) 152 { 153 Object value = valueHolder.value; 154 if (!omitNullValuesSnapshot || (value != null)) { 155 builder.append(nextSeparator); 156 nextSeparator = ", "; 157 158 if (valueHolder.name != null) { 159 builder.append(valueHolder.name).append('='); 160 } 161 if ((value != null) && value.getClass().isArray()) { 162 Object[] objectArray = { value }; 163 String arrayString = Arrays.deepToString(objectArray); 164 builder.append(arrayString, 1, arrayString.length() - 1); 165 } else { 166 builder.append(value); 167 } 168 } 169 } 170 return builder.append(']').toString(); 171 } 172 173 private static final class ValueHolder { 174 String name; 175 Object value; 176 ValueHolder next; 177 } 178}