001package org.javasimon.console.text; 002 003import java.util.HashMap; 004import java.util.Map; 005 006/** 007 * Dictionary (Class type,String subType)→Stringifier. 008 * 009 * @author gquintana 010 */ 011public class CompositeStringifier implements Stringifier<Object> { 012 /** 013 * Key of the dictionary is the couple (Class type,String subType). 014 * subType can be null 015 */ 016 private static final class StringifierKey { 017 018 private final Class<?> type; 019 private final String subType; 020 021 /** 022 * Constructor 023 */ 024 public StringifierKey(Class<?> type, String subType) { 025 this.type = type; 026 this.subType = subType; 027 } 028 029 /** 030 * Compares 2 objects including the case one of them is null. 031 */ 032 private boolean equalsTo(Object o1, Object o2) { 033 if (o1 == o2) { 034 return true; 035 } else { 036 if ((o1 == null) || (o2 == null)) { 037 return false; 038 } else { 039 return o1.equals(o2); 040 } 041 } 042 } 043 044 /** 045 * Generate an hashCode, including the case null object 046 */ 047 private int hashCode(Object o) { 048 return o == null ? 0 : o.hashCode(); 049 } 050 051 @Override 052 public boolean equals(Object obj) { 053 if (obj == null) { 054 return false; 055 } 056 if (getClass() != obj.getClass()) { 057 return false; 058 } 059 final StringifierKey other = (StringifierKey) obj; 060 061 return equalsTo(this.type, other.type) && equalsTo(this.subType, other.subType); 062 } 063 064 @Override 065 public int hashCode() { 066 return 3 + 5 * hashCode(this.type) + 7 * hashCode(this.subType); 067 } 068 069 @Override 070 public String toString() { 071 return "StringifierKey[" + type.getName() + "," + subType + "]"; 072 } 073 } 074 075 /** 076 * Main attribute of this class as it contains the dictionnary 077 */ 078 private final Map<StringifierKey, Stringifier> stringifiers = new HashMap<>(); 079 /** 080 * Null stringifier used to format null values 081 */ 082 private Stringifier nullStringifier; 083 /** 084 * Default Stringier used when no value is found in the dictionnary 085 */ 086 private Stringifier defaultStringifier; 087 088 /** 089 * Adds a stringifier to the dictionnary 090 * 091 * @param type Type (null sub-type) 092 * @param stringifier Stringifier 093 */ 094 public final <T> void add(Class<? extends T> type, Stringifier<T> stringifier) { 095 add(type, null, stringifier); 096 } 097 098 /** 099 * Adds a stringifier to the dictionnary. 100 * 101 * @param type Type 102 * @param stringifier Stringifier 103 */ 104 public final <T> void add(Class<? extends T> type, String name, Stringifier<T> stringifier) { 105 stringifiers.put(new StringifierKey(type, name), stringifier); 106 } 107 108 /** 109 * Look for a stringifier in the dictionnary 110 * 111 * @param type Type (null sub-type) 112 * @return Stringifier 113 */ 114 public final <T> Stringifier<T> getForType(Class<? extends T> type) { 115 return getForType(type, null); 116 } 117 118 /** 119 * Look for a stringifier in the dictionary.<ol> 120 * <li>First look with type+subtype</li> 121 * <li>If not found, try with type alone</li> 122 * </ol> 123 * 124 * @param type Type 125 * @param subType Sub type 126 * @return Stringifier 127 */ 128 @SuppressWarnings("unchecked") 129 private <T> Stringifier<T> get(Class<? extends T> type, String subType) { 130 Stringifier<T> stringifier = null; 131 if (subType != null) { 132 stringifier = stringifiers.get(new StringifierKey(type, subType)); 133 } 134 if (stringifier == null) { 135 stringifier = stringifiers.get(new StringifierKey(type, null)); 136 } 137 return stringifier; 138 } 139 140 /** 141 * Look for a stringifier in the dictionary.<ol> 142 * <li>First look with type+subtype</li> 143 * <li>If not found, try with type alone</li> 144 * </ol> 145 * 146 * @param type Type 147 * @param subType Sub type 148 * @return Stringifier 149 */ 150 @SuppressWarnings("unchecked") 151 public final <T> Stringifier<T> getForType(Class<? extends T> type, String subType) { 152 return NoneStringifier.checkInstance(get(type, subType)); 153 } 154 155 /** 156 * Get stringifier for an instance: 157 * <ul> 158 * <li>If instance is null, return null stringifier</li> 159 * <li>Else look in the dictionary with instance's class, if found return it</li> 160 * <li>Else return default strinfigier</li> 161 * </ul> 162 * 163 * @param object Object instance 164 */ 165 @SuppressWarnings("unchecked") 166 private <T> Stringifier<T> getForInstance(T object, String subType) { 167 Stringifier<T> stringifier; 168 if (object == null) { 169 stringifier = nullStringifier; 170 } else { 171 stringifier = (Stringifier<T>) get(object.getClass(), subType); 172 if (stringifier == null) { 173 stringifier = defaultStringifier; 174 } 175 } 176 return NoneStringifier.checkInstance(stringifier); 177 } 178 179 /** 180 * Converts an object into a String looking for appropriate 181 * stringfier among dictionary 182 * 183 * @param object Object 184 * @return String representing the object 185 */ 186 public String toString(Object object) { 187 return getForInstance(object, null).toString(object); 188 } 189 190 /** 191 * Converts an object into a String looking for appropriate 192 * stringfier among dictionary 193 * 194 * @param object Object 195 * @param subType Sub type 196 * @return String representing the object 197 */ 198 public String toString(Object object, String subType) { 199 return getForInstance(object, subType).toString(object); 200 } 201 202 public Stringifier getDefaultStringifier() { 203 return defaultStringifier; 204 } 205 206 public void setDefaultStringifier(Stringifier defaultStringifier) { 207 this.defaultStringifier = defaultStringifier; 208 } 209 210 public Stringifier getNullStringifier() { 211 return nullStringifier; 212 } 213 214 public void setNullStringifier(Stringifier nullStringifier) { 215 this.nullStringifier = nullStringifier; 216 } 217 218}