001package com.hfg.graphics; 002 003import com.hfg.css.CSS; 004import com.hfg.html.HTMLTag; 005import com.hfg.html.Span; 006import com.hfg.math.Range; 007import com.hfg.util.CompareUtil; 008import com.hfg.util.StringUtil; 009import com.hfg.util.Value; 010import com.hfg.util.ValueImpl; 011import com.hfg.util.collection.CollectionUtil; 012import com.hfg.util.collection.OrderedMap; 013import com.hfg.xml.XMLName; 014import com.hfg.xml.XMLTag; 015 016import java.util.ArrayList; 017import java.util.Collections; 018import java.util.List; 019import java.util.Map; 020 021 022//------------------------------------------------------------------------------ 023/** 024 Colorist implementation that uses ranges. 025 <div> 026 @author J. Alex Taylor, hairyfatguy.com 027 </div> 028 */ 029//------------------------------------------------------------------------------ 030// com.hfg Library 031// 032// This library is free software; you can redistribute it and/or 033// modify it under the terms of the GNU Lesser General Public 034// License as published by the Free Software Foundation; either 035// version 2.1 of the License, or (at your option) any later version. 036// 037// This library is distributed in the hope that it will be useful, 038// but WITHOUT ANY WARRANTY; without even the implied warranty of 039// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 040// Lesser General Public License for more details. 041// 042// You should have received a copy of the GNU Lesser General Public 043// License along with this library; if not, write to the Free Software 044// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 045// 046// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 047// jataylor@hairyfatguy.com 048//------------------------------------------------------------------------------ 049public class RangeColorist<T extends Number> implements Colorist 050{ 051 private Map<Range<T>, ColorSpec> mRangeMap; 052 053 private static final XMLName XML_RANGE = new XMLName("Range"); 054 private static final XMLName XML_TYPE_ATT = new XMLName("type"); 055 private static final XMLName XML_START_ATT = new XMLName("start"); 056 private static final XMLName XML_END_ATT = new XMLName("end"); 057 058 //########################################################################### 059 // CONSTRUCTORS 060 //########################################################################### 061 062 //--------------------------------------------------------------------------- 063 public RangeColorist() 064 { 065 066 } 067 068 //--------------------------------------------------------------------------- 069 public RangeColorist(XMLTag inXMLTag) 070 { 071 inXMLTag.verifyTagName(XML_COLORIST); 072 073 List<XMLTag> rangeTags = inXMLTag.getSubtagsByName(XML_RANGE); 074 if (CollectionUtil.hasValues(rangeTags)) 075 { 076 for (XMLTag rangeTag : rangeTags) 077 { 078 addRange(extractRange(rangeTag), new ColorSpec((XMLTag) rangeTag.getSubtags().get(0))); 079 } 080 } 081 } 082 083 //--------------------------------------------------------------------------- 084 private Range extractRange(XMLTag inRangeTag) 085 { 086 Range range = null; 087 088 String type = inRangeTag.getAttributeValue(XML_TYPE_ATT); 089 090 if (type.equals(Integer.class.getSimpleName())) 091 { 092 range = new Range<Integer>(); 093 094 String start = inRangeTag.getAttributeValue(XML_START_ATT); 095 if (StringUtil.isSet(start)) 096 { 097 range.setStart(Integer.parseInt(start)); 098 } 099 100 String end = inRangeTag.getAttributeValue(XML_END_ATT); 101 if (StringUtil.isSet(end)) 102 { 103 range.setEnd(Integer.parseInt(end)); 104 } 105 } 106 else if (type.equals(Long.class.getSimpleName())) 107 { 108 range = new Range<Long>(); 109 110 String start = inRangeTag.getAttributeValue(XML_START_ATT); 111 if (StringUtil.isSet(start)) 112 { 113 range.setStart(Long.parseLong(start)); 114 } 115 116 String end = inRangeTag.getAttributeValue(XML_END_ATT); 117 if (StringUtil.isSet(end)) 118 { 119 range.setEnd(Long.parseLong(end)); 120 } 121 } 122 else if (type.equals(Float.class.getSimpleName())) 123 { 124 range = new Range<Float>(); 125 126 String start = inRangeTag.getAttributeValue(XML_START_ATT); 127 if (StringUtil.isSet(start)) 128 { 129 range.setStart(Float.parseFloat(start)); 130 } 131 132 String end = inRangeTag.getAttributeValue(XML_END_ATT); 133 if (StringUtil.isSet(end)) 134 { 135 range.setEnd(Float.parseFloat(end)); 136 } 137 } 138 else // Use Double as the catch-all 139 { 140 range = new Range<Double>(); 141 142 String start = inRangeTag.getAttributeValue(XML_START_ATT); 143 if (StringUtil.isSet(start)) 144 { 145 range.setStart(Double.parseDouble(start)); 146 } 147 148 String end = inRangeTag.getAttributeValue(XML_END_ATT); 149 if (StringUtil.isSet(end)) 150 { 151 range.setEnd(Double.parseDouble(end)); 152 } 153 } 154 155 return range; 156 } 157 158 //########################################################################### 159 // PUBLIC METHODS 160 //########################################################################### 161 162 //--------------------------------------------------------------------------- 163 public Map<Range<T>, ColorSpec> getRangeMap() 164 { 165 return mRangeMap != null ? Collections.unmodifiableMap(mRangeMap) : null; 166 } 167 168 //--------------------------------------------------------------------------- 169 public RangeColorist addRange(Range<T> inRange, ColorSpec inColorSpec) 170 { 171 if (null == mRangeMap) 172 { 173 mRangeMap = new OrderedMap<>(); 174 } 175 176 mRangeMap.put(inRange, inColorSpec); 177 178 return this; 179 } 180 181 //--------------------------------------------------------------------------- 182 public XMLTag toXMLTag() 183 { 184 XMLTag tag = new XMLTag(XML_COLORIST); 185 tag.setAttribute(XML_CLASS_ATT, getClass().getName()); 186 187 if (CollectionUtil.hasValues(mRangeMap)) 188 { 189 for (Range<T> range : mRangeMap.keySet()) 190 { 191 XMLTag rangeTag = new XMLTag(XML_RANGE); 192 tag.addSubtag(rangeTag); 193 194 Class typeClass = null; 195 196 if (range.getStart() != null) 197 { 198 rangeTag.setAttribute(XML_START_ATT, range.getStart()); 199 typeClass = range.getStart().getClass(); 200 } 201 202 if (range.getEnd() != null) 203 { 204 rangeTag.setAttribute(XML_END_ATT, range.getEnd()); 205 typeClass = range.getEnd().getClass(); 206 } 207 208 if (typeClass != null) 209 { 210 rangeTag.setAttribute(XML_TYPE_ATT, typeClass.getSimpleName()); 211 } 212 213 rangeTag.addSubtag(mRangeMap.get(range).toXMLTag()); 214 } 215 } 216 217 return tag; 218 } 219 220 //--------------------------------------------------------------------------- 221 @Override 222 public HTMLTag toDisplayHTML() 223 { 224 Span span = new Span(); 225 226 if (CollectionUtil.hasValues(mRangeMap)) 227 { 228 List<Range> orderedRanges = new ArrayList<>(mRangeMap.keySet()); 229 Collections.sort(orderedRanges); 230 231 for (Range<T> range : orderedRanges) 232 { 233 String rangeText = (range.getStart() != null ? range.getStart() + "" : (range.getEnd() != null ? "-" : "") + "∞") // Entity is for the infinity symbol 234 + (CompareUtil.compare(range.getStart(), range.getEnd()) != 0 ? " - " + (range.getEnd() != null ? range.getEnd() + "" : "∞") : ""); 235 236 Span rangeSpan = span.addSpan(); 237 rangeSpan.addContentWithoutEscaping(rangeText); 238 rangeSpan.addStyle("padding: 2px 8px"); 239 240 ColorSpec colorSpec = mRangeMap.get(range); 241 if (colorSpec != null) 242 { 243 if (colorSpec.getBackgroundColor() != null) 244 { 245 rangeSpan.addStyle(CSS.bgColor(colorSpec.getBackgroundColor())); 246 } 247 248 if (colorSpec.getForegroundColor() != null) 249 { 250 rangeSpan.addStyle(CSS.color(colorSpec.getForegroundColor())); 251 } 252 } 253 } 254 } 255 256 return span; 257 } 258 259 //--------------------------------------------------------------------------- 260 @Override 261 public int hashCode() 262 { 263 int hashcode = 1; 264 265 if (mRangeMap != null) 266 { 267 hashcode += 31 * mRangeMap.hashCode(); 268 } 269 270 return hashcode; 271 } 272 273 //--------------------------------------------------------------------------- 274 @Override 275 public boolean equals(Object inObj2) 276 { 277 return (inObj2 != null 278 && inObj2 instanceof RangeColorist 279 && 0 == compareTo((RangeColorist) inObj2)); 280 } 281 282 //--------------------------------------------------------------------------- 283 @Override 284 public int compareTo(Colorist inObj2) 285 { 286 int result = -1; 287 288 if (this == inObj2) 289 { 290 result = 0; 291 } 292 else if (inObj2 instanceof RangeColorist) 293 { 294 if (inObj2 != null) 295 { 296 result = CompareUtil.compare(mRangeMap, ((RangeColorist)inObj2).mRangeMap); 297 } 298 } 299 300 return result; 301 } 302 303 //--------------------------------------------------------------------------- 304 @Override 305 public ColorSpec colorByNumber(Value inValue) 306 { 307 ColorSpec result = null; 308 309 if (CollectionUtil.hasValues(mRangeMap)) 310 { 311 for (Range<T> range : mRangeMap.keySet()) 312 { 313 if (range.contains(inValue != null && ! inValue.isNull() ? inValue.doubleValue() : null)) 314 { 315 result = mRangeMap.get(range); 316 break; 317 } 318 } 319 } 320 321 return result; 322 } 323 324 //--------------------------------------------------------------------------- 325 @Override 326 public ColorSpec colorByNumber(Number inValue) 327 { 328 return colorByNumber(new ValueImpl(inValue)); 329 } 330}