001 /* 002 * Sonar, open source software quality management tool. 003 * Copyright (C) 2008-2011 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * Sonar is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * Sonar is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with Sonar; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 019 */ 020 package org.sonar.api.utils; 021 022 import com.google.common.collect.LinkedHashMultiset; 023 import com.google.common.collect.Maps; 024 import com.google.common.collect.Multimap; 025 import com.google.common.collect.Multiset; 026 import org.apache.commons.collections.Bag; 027 import org.apache.commons.lang.StringUtils; 028 import org.apache.commons.lang.math.NumberUtils; 029 import org.slf4j.LoggerFactory; 030 import org.sonar.api.rules.RulePriority; 031 032 import java.text.ParseException; 033 import java.text.SimpleDateFormat; 034 import java.util.*; 035 036 /** 037 * Formats and parses key/value pairs with the string representation : "key1=value1;key2=value2". Conversion 038 * of fields is supported and can be extended. 039 * 040 * @since 1.10 041 */ 042 public final class KeyValueFormat { 043 044 public static final String PAIR_SEPARATOR = ";"; 045 public static final String FIELD_SEPARATOR = "="; 046 047 private KeyValueFormat() { 048 // only static methods 049 } 050 051 public static abstract class Converter<TYPE> { 052 abstract String format(TYPE type); 053 054 abstract TYPE parse(String s); 055 } 056 057 public static final class StringConverter extends Converter<String> { 058 private static final StringConverter INSTANCE = new StringConverter(); 059 060 private StringConverter() { 061 } 062 063 @Override 064 String format(String s) { 065 return s; 066 } 067 068 @Override 069 String parse(String s) { 070 return s; 071 } 072 } 073 074 public static StringConverter newStringConverter() { 075 return StringConverter.INSTANCE; 076 } 077 078 public static final class ToStringConverter extends Converter<Object> { 079 private static final ToStringConverter INSTANCE = new ToStringConverter(); 080 081 private ToStringConverter() { 082 } 083 084 @Override 085 String format(Object o) { 086 return o.toString(); 087 } 088 089 @Override 090 String parse(String s) { 091 throw new IllegalStateException("Can not parse with ToStringConverter: " + s); 092 } 093 } 094 095 public static ToStringConverter newToStringConverter() { 096 return ToStringConverter.INSTANCE; 097 } 098 099 public static final class IntegerConverter extends Converter<Integer> { 100 private static final IntegerConverter INSTANCE = new IntegerConverter(); 101 102 private IntegerConverter() { 103 } 104 105 @Override 106 String format(Integer s) { 107 return (s == null ? "" : String.valueOf(s)); 108 } 109 110 @Override 111 Integer parse(String s) { 112 return StringUtils.isBlank(s) ? null : NumberUtils.toInt(s); 113 } 114 } 115 116 public static IntegerConverter newIntegerConverter() { 117 return IntegerConverter.INSTANCE; 118 } 119 120 public static final class PriorityConverter extends Converter<RulePriority> { 121 private static final PriorityConverter INSTANCE = new PriorityConverter(); 122 123 private PriorityConverter() { 124 } 125 126 @Override 127 String format(RulePriority s) { 128 return (s == null ? "" : s.toString()); 129 } 130 131 @Override 132 RulePriority parse(String s) { 133 return StringUtils.isBlank(s) ? null : RulePriority.valueOf(s); 134 } 135 } 136 137 public static PriorityConverter newPriorityConverter() { 138 return PriorityConverter.INSTANCE; 139 } 140 141 public static final class DoubleConverter extends Converter<Double> { 142 private static final DoubleConverter INSTANCE = new DoubleConverter(); 143 144 private DoubleConverter() { 145 } 146 147 @Override 148 String format(Double d) { 149 return (d == null ? "" : String.valueOf(d)); 150 } 151 152 @Override 153 Double parse(String s) { 154 return StringUtils.isBlank(s) ? null : NumberUtils.toDouble(s); 155 } 156 } 157 158 public static DoubleConverter newDoubleConverter() { 159 return DoubleConverter.INSTANCE; 160 } 161 162 public static class DateConverter extends Converter<Date> { 163 private SimpleDateFormat dateFormat; 164 165 /** 166 * @deprecated in version 2.13. Replaced by {@link org.sonar.api.utils.KeyValueFormat#newDateConverter()} 167 */ 168 @Deprecated 169 public DateConverter() { 170 this(DateUtils.DATE_FORMAT); 171 } 172 173 private DateConverter(String format) { 174 this.dateFormat = new SimpleDateFormat(format); 175 } 176 177 @Override 178 String format(Date d) { 179 return (d == null ? "" : dateFormat.format(d)); 180 } 181 182 @Override 183 Date parse(String s) { 184 try { 185 return StringUtils.isBlank(s) ? null : dateFormat.parse(s); 186 } catch (ParseException e) { 187 throw new SonarException("Not a date with format: " + dateFormat.toPattern(), e); 188 } 189 } 190 } 191 192 public static DateConverter newDateConverter() { 193 return new DateConverter(DateUtils.DATE_FORMAT); 194 } 195 196 public static DateConverter newDateTimeConverter() { 197 return new DateConverter(DateUtils.DATETIME_FORMAT); 198 } 199 200 public static DateConverter newDateConverter(String format) { 201 return new DateConverter(format); 202 } 203 204 /** 205 * @deprecated in version 2.13. Replaced by {@link org.sonar.api.utils.KeyValueFormat#newDateTimeConverter()} 206 */ 207 @Deprecated 208 public static class DateTimeConverter extends DateConverter { 209 public DateTimeConverter() { 210 super(DateUtils.DATETIME_FORMAT); 211 } 212 } 213 214 public static <K, V> Map<K, V> parse(String data, Converter<K> keyConverter, Converter<V> valueConverter) { 215 Map<K, V> map = Maps.newLinkedHashMap(); 216 if (data != null) { 217 String[] pairs = StringUtils.split(data, PAIR_SEPARATOR); 218 for (String pair : pairs) { 219 String[] keyValue = StringUtils.split(pair, FIELD_SEPARATOR); 220 String key = keyValue[0]; 221 String value = (keyValue.length == 2 ? keyValue[1] : ""); 222 map.put(keyConverter.parse(key), valueConverter.parse(value)); 223 } 224 } 225 return map; 226 } 227 228 public static Map parse(String data) { 229 return parse(data, newStringConverter(), newStringConverter()); 230 } 231 232 /** 233 * @since 2.7 234 */ 235 public static Map<String, Integer> parseStringInt(String data) { 236 return parse(data, newStringConverter(), newIntegerConverter()); 237 } 238 239 /** 240 * @since 2.7 241 */ 242 public static Map<String, Double> parseStringDouble(String data) { 243 return parse(data, newStringConverter(), newDoubleConverter()); 244 } 245 246 /** 247 * @since 2.7 248 */ 249 public static Map<Integer, String> parseIntString(String data) { 250 return parse(data, newIntegerConverter(), newStringConverter()); 251 } 252 253 /** 254 * @since 2.7 255 */ 256 public static Map<Integer, Double> parseIntDouble(String data) { 257 return parse(data, newIntegerConverter(), newDoubleConverter()); 258 } 259 260 /** 261 * @since 2.7 262 */ 263 public static Map<Integer, Date> parseIntDate(String data) { 264 return parse(data, newIntegerConverter(), newDateConverter()); 265 } 266 267 /** 268 * @since 2.7 269 */ 270 public static Map<Integer, Integer> parseIntInt(String data) { 271 return parse(data, newIntegerConverter(), newIntegerConverter()); 272 } 273 274 /** 275 * @since 2.7 276 */ 277 public static Map<Integer, Date> parseIntDateTime(String data) { 278 return parse(data, newIntegerConverter(), newDateTimeConverter()); 279 } 280 281 /** 282 * Value of pairs is the occurrences of the same single key. A multiset is sometimes called a bag. 283 * For example parsing "foo=2;bar=1" creates a multiset with 3 elements : foo, foo and bar. 284 */ 285 /** 286 * @since 2.7 287 */ 288 public static <K> Multiset<K> parseMultiset(String data, Converter<K> keyConverter) { 289 Multiset<K> multiset = LinkedHashMultiset.create();// to keep the same order 290 if (data != null) { 291 String[] pairs = StringUtils.split(data, PAIR_SEPARATOR); 292 for (String pair : pairs) { 293 String[] keyValue = StringUtils.split(pair, FIELD_SEPARATOR); 294 String key = keyValue[0]; 295 String value = (keyValue.length == 2 ? keyValue[1] : "0"); 296 multiset.add(keyConverter.parse(key), new IntegerConverter().parse(value)); 297 } 298 } 299 return multiset; 300 } 301 302 303 /** 304 * @since 2.7 305 */ 306 public static Multiset<Integer> parseIntegerMultiset(String data) { 307 return parseMultiset(data, newIntegerConverter()); 308 } 309 310 /** 311 * @since 2.7 312 */ 313 public static Multiset<String> parseMultiset(String data) { 314 return parseMultiset(data, newStringConverter()); 315 } 316 317 /** 318 * Transforms a string with the following format : "key1=value1;key2=value2..." 319 * into a Map<KEY, VALUE>. Requires to implement the transform(key,value) method 320 * 321 * @param data the input string 322 * @param transformer the interface to implement 323 * @return a Map of <key, value> 324 * @deprecated since 2.7 325 */ 326 @Deprecated 327 public static <KEY, VALUE> Map<KEY, VALUE> parse(String data, Transformer<KEY, VALUE> transformer) { 328 Map<String, String> rawData = parse(data); 329 Map<KEY, VALUE> map = new HashMap<KEY, VALUE>(); 330 for (Map.Entry<String, String> entry : rawData.entrySet()) { 331 KeyValue<KEY, VALUE> keyVal = transformer.transform(entry.getKey(), entry.getValue()); 332 if (keyVal != null) { 333 map.put(keyVal.getKey(), keyVal.getValue()); 334 } 335 } 336 return map; 337 } 338 339 private static <K, V> String formatEntries(Collection<Map.Entry<K, V>> entries, Converter<K> keyConverter, Converter<V> valueConverter) { 340 StringBuilder sb = new StringBuilder(); 341 boolean first = true; 342 for (Map.Entry<K, V> entry : entries) { 343 if (!first) { 344 sb.append(PAIR_SEPARATOR); 345 } 346 sb.append(keyConverter.format(entry.getKey())); 347 sb.append(FIELD_SEPARATOR); 348 if (entry.getValue() != null) { 349 sb.append(valueConverter.format(entry.getValue())); 350 } 351 first = false; 352 } 353 return sb.toString(); 354 } 355 356 private static <K> String formatEntries(Set<Multiset.Entry<K>> entries, Converter<K> keyConverter) { 357 StringBuilder sb = new StringBuilder(); 358 boolean first = true; 359 for (Multiset.Entry<K> entry : entries) { 360 if (!first) { 361 sb.append(PAIR_SEPARATOR); 362 } 363 sb.append(keyConverter.format(entry.getElement())); 364 sb.append(FIELD_SEPARATOR); 365 sb.append(new IntegerConverter().format(entry.getCount())); 366 first = false; 367 } 368 return sb.toString(); 369 } 370 371 372 /** 373 * @since 2.7 374 */ 375 public static <K, V> String format(Map<K, V> map, Converter<K> keyConverter, Converter<V> valueConverter) { 376 return formatEntries(map.entrySet(), keyConverter, valueConverter); 377 } 378 379 /** 380 * @since 2.7 381 */ 382 public static String format(Map map) { 383 return format(map, newToStringConverter(), newToStringConverter()); 384 } 385 386 /** 387 * @since 2.7 388 */ 389 public static String formatIntString(Map<Integer, String> map) { 390 return format(map, newIntegerConverter(), newStringConverter()); 391 } 392 393 /** 394 * @since 2.7 395 */ 396 public static String formatIntDouble(Map<Integer, Double> map) { 397 return format(map, newIntegerConverter(), newDoubleConverter()); 398 } 399 400 /** 401 * @since 2.7 402 */ 403 public static String formatIntDate(Map<Integer, Date> map) { 404 return format(map, newIntegerConverter(), newDateConverter()); 405 } 406 407 /** 408 * @since 2.7 409 */ 410 public static String formatIntDateTime(Map<Integer, Date> map) { 411 return format(map, newIntegerConverter(), newDateTimeConverter()); 412 } 413 414 /** 415 * @since 2.7 416 */ 417 public static String formatStringInt(Map<String, Integer> map) { 418 return format(map, newStringConverter(), newIntegerConverter()); 419 } 420 421 /** 422 * Limitation: there's currently no methods to parse into Multimap. 423 * 424 * @since 2.7 425 */ 426 public static <K, V> String format(Multimap<K, V> map, Converter<K> keyConverter, Converter<V> valueConverter) { 427 return formatEntries(map.entries(), keyConverter, valueConverter); 428 } 429 430 /** 431 * @since 2.7 432 */ 433 public static <K> String format(Multiset<K> multiset, Converter<K> keyConverter) { 434 return formatEntries(multiset.entrySet(), keyConverter); 435 } 436 437 public static String format(Multiset multiset) { 438 return formatEntries(multiset.entrySet(), newToStringConverter()); 439 } 440 441 442 /** 443 * @since 1.11 444 * @deprecated use Multiset from google collections instead of commons-collections bags 445 */ 446 @Deprecated 447 public static String format(Bag bag) { 448 return format(bag, 0); 449 } 450 451 /** 452 * @since 1.11 453 * @deprecated use Multiset from google collections instead of commons-collections bags 454 */ 455 @Deprecated 456 public static String format(Bag bag, int var) { 457 StringBuilder sb = new StringBuilder(); 458 if (bag != null) { 459 boolean first = true; 460 for (Object obj : bag.uniqueSet()) { 461 if (!first) { 462 sb.append(PAIR_SEPARATOR); 463 } 464 sb.append(obj.toString()); 465 sb.append(FIELD_SEPARATOR); 466 sb.append(bag.getCount(obj) + var); 467 first = false; 468 } 469 } 470 return sb.toString(); 471 } 472 473 474 /** 475 * @deprecated since 2.7. Replaced by Converter 476 */ 477 @Deprecated 478 public interface Transformer<KEY, VALUE> { 479 KeyValue<KEY, VALUE> transform(String key, String value); 480 } 481 482 /** 483 * Implementation of Transformer<String, Double> 484 * 485 * @deprecated since 2.7 replaced by Converter 486 */ 487 @Deprecated 488 public static class StringNumberPairTransformer implements Transformer<String, Double> { 489 public KeyValue<String, Double> transform(String key, String value) { 490 return new KeyValue<String, Double>(key, toDouble(value)); 491 } 492 } 493 494 /** 495 * Implementation of Transformer<Double, Double> 496 * 497 * @deprecated since 2.7. Replaced by Converter 498 */ 499 @Deprecated 500 public static class DoubleNumbersPairTransformer implements Transformer<Double, Double> { 501 public KeyValue<Double, Double> transform(String key, String value) { 502 return new KeyValue<Double, Double>(toDouble(key), toDouble(value)); 503 } 504 } 505 506 /** 507 * Implementation of Transformer<Integer, Integer> 508 * 509 * @deprecated since 2.7. Replaced by Converter 510 */ 511 @Deprecated 512 public static class IntegerNumbersPairTransformer implements Transformer<Integer, Integer> { 513 public KeyValue<Integer, Integer> transform(String key, String value) { 514 return new KeyValue<Integer, Integer>(toInteger(key), toInteger(value)); 515 } 516 } 517 518 519 /** 520 * Implementation of Transformer<RulePriority, Integer> 521 * 522 * @deprecated since 2.7. Replaced by Converter 523 */ 524 @Deprecated 525 public static class RulePriorityNumbersPairTransformer implements Transformer<RulePriority, Integer> { 526 527 public KeyValue<RulePriority, Integer> transform(String key, String value) { 528 try { 529 if (StringUtils.isBlank(value)) { 530 value = "0"; 531 } 532 return new KeyValue<RulePriority, Integer>(RulePriority.valueOf(key.toUpperCase()), Integer.parseInt(value)); 533 } catch (Exception e) { 534 LoggerFactory.getLogger(RulePriorityNumbersPairTransformer.class).warn("Property " + key + " has invalid value: " + value, e); 535 return null; 536 } 537 } 538 } 539 540 private static Double toDouble(String value) { 541 return StringUtils.isBlank(value) ? null : NumberUtils.toDouble(value); 542 } 543 544 private static Integer toInteger(String value) { 545 return StringUtils.isBlank(value) ? null : NumberUtils.toInt(value); 546 } 547 }