001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.mappaint; 003 004 import static org.openstreetmap.josm.tools.Utils.equal; 005 006 import java.awt.Color; 007 import java.util.Arrays; 008 import java.util.HashMap; 009 import java.util.List; 010 import java.util.Map; 011 012 import org.openstreetmap.josm.gui.mappaint.mapcss.CSSColors; 013 import org.openstreetmap.josm.tools.Utils; 014 015 /** 016 * Simple map of properties with dynamic typing. 017 */ 018 public class Cascade implements Cloneable { 019 020 public static final Cascade EMPTY_CASCADE = new Cascade(); 021 022 protected Map<String, Object> prop = new HashMap<String, Object>(); 023 024 public <T> T get(String key, T def, Class<T> klass) { 025 return get(key, def, klass, false); 026 } 027 028 /** 029 * Get value for the given key 030 * @param key the key 031 * @param def default value, can be null 032 * @param klass the same as T 033 * @param suppressWarnings show or don't show a warning when some value is 034 * found, but cannot be converted to the requested type 035 * @return if a value with class klass has been mapped to key, returns this 036 * value, def otherwise 037 */ 038 public <T> T get(String key, T def, Class<T> klass, boolean suppressWarnings) { 039 if (def != null && !klass.isInstance(def)) 040 throw new IllegalArgumentException(); 041 Object o = prop.get(key); 042 if (o == null) 043 return def; 044 T res = convertTo(o, klass); 045 if (res == null) { 046 if (!suppressWarnings) { 047 System.err.println(String.format("Warning: unable to convert property %s to type %s: found %s of type %s!", key, klass, o, o.getClass())); 048 } 049 return def; 050 } else 051 return res; 052 } 053 054 public Object get(String key) { 055 return prop.get(key); 056 } 057 058 public void put(String key, Object val) { 059 prop.put(key, val); 060 } 061 062 public void putOrClear(String key, Object val) { 063 if (val != null) { 064 prop.put(key, val); 065 } else { 066 prop.remove(key); 067 } 068 } 069 070 public void remove(String key) { 071 prop.remove(key); 072 } 073 074 @SuppressWarnings("unchecked") 075 public static <T> T convertTo(Object o, Class<T> klass) { 076 if (o == null) 077 return null; 078 if (klass.isInstance(o)) 079 return (T) o; 080 081 if (klass == float.class || klass == Float.class) 082 return (T) toFloat(o); 083 084 if (klass == double.class || klass == Double.class) { 085 o = toFloat(o); 086 if (o != null) { 087 o = new Double((Float) o); 088 } 089 return (T) o; 090 } 091 092 if (klass == boolean.class || klass == Boolean.class) 093 return (T) toBool(o); 094 095 if (klass == float[].class) 096 return (T) toFloatArray(o); 097 098 if (klass == Color.class) 099 return (T) toColor(o); 100 101 if (klass == String.class) { 102 if (o instanceof Keyword) 103 return (T) ((Keyword) o).val; 104 105 return (T) o.toString(); 106 } 107 108 return null; 109 } 110 111 private static Float toFloat(Object o) { 112 if (o instanceof Float) 113 return (Float) o; 114 if (o instanceof Double) 115 return new Float((Double) o); 116 if (o instanceof Integer) 117 return new Float((Integer) o); 118 if (o instanceof String) { 119 try { 120 float f = Float.parseFloat((String) o); 121 return f; 122 } catch (NumberFormatException e) { 123 } 124 } 125 return null; 126 } 127 128 private static Boolean toBool(Object o) { 129 if (o instanceof Boolean) 130 return (Boolean) o; 131 if (o instanceof Keyword) { 132 String s = ((Keyword) o).val; 133 if (equal(s, "true") || equal(s, "yes")) 134 return true; 135 if (equal(s, "false") || equal(s, "no")) 136 return false; 137 } 138 return null; 139 } 140 141 private static float[] toFloatArray(Object o) { 142 if (o instanceof float[]) 143 return (float[]) o; 144 if (o instanceof List) { 145 List l = (List) o; 146 float[] a = new float[l.size()]; 147 for (int i=0; i<l.size(); ++i) { 148 Float f = toFloat(l.get(i)); 149 if (f == null) 150 return null; 151 else 152 a[i] = f; 153 } 154 return a; 155 } 156 Float f = toFloat(o); 157 if (f != null) 158 return new float[] { f }; 159 return null; 160 } 161 162 private static Color toColor(Object o) { 163 if (o instanceof Color) 164 return (Color) o; 165 if (o instanceof Keyword) 166 return CSSColors.get(((Keyword) o).val); 167 return null; 168 } 169 170 @Override 171 public Cascade clone() { 172 @SuppressWarnings("unchecked") 173 HashMap<String, Object> clonedProp = (HashMap) ((HashMap) this.prop).clone(); 174 Cascade c = new Cascade(); 175 c.prop = clonedProp; 176 return c; 177 } 178 179 @Override 180 public String toString() { 181 StringBuilder res = new StringBuilder("Cascade{ "); 182 for (String key : prop.keySet()) { 183 res.append(key+":"); 184 Object val = prop.get(key); 185 if (val instanceof float[]) { 186 res.append(Arrays.toString((float[]) val)); 187 } else if (val instanceof Color) { 188 res.append(Utils.toString((Color)val)); 189 } else { 190 res.append(val+""); 191 } 192 res.append("; "); 193 } 194 return res.append("}").toString(); 195 } 196 197 public boolean containsKey(String key) { 198 return prop.containsKey(key); 199 } 200 }