Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.sonar.api.utils.KeyValueFormat Maven / Gradle / Ivy
/*
* SonarQube, open source software quality management tool.
* Copyright (C) 2008-2014 SonarSource
* mailto:contact AT sonarsource DOT com
*
* SonarQube is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* SonarQube is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.api.utils;
import com.google.common.collect.Maps;
import com.google.common.collect.Multiset;
import org.apache.commons.collections.Bag;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.sonar.api.rules.RulePriority;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.Set;
/**
* Formats and parses key/value pairs with the text representation : "key1=value1;key2=value2". Field typing
* is supported, to make conversion from/to primitive types easier for example.
*
* Since version 4.5.1, text keys and values are escaped if they contain the separator characters '=' or ';'.
*
* Parsing examples
*
* Map<String,String> mapOfStrings = KeyValueFormat.parse("hello=world;foo=bar");
* Map<String,Integer> mapOfStringInts = KeyValueFormat.parseStringInt("one=1;two=2");
* Map<Integer,String> mapOfIntStrings = KeyValueFormat.parseIntString("1=one;2=two");
* Map<String,Date> mapOfStringDates = KeyValueFormat.parseStringDate("d1=2014-01-14;d2=2015-07-28");
*
* // custom conversion
* Map<String,MyClass> mapOfStringMyClass = KeyValueFormat.parse("foo=xxx;bar=yyy",
* KeyValueFormat.newStringConverter(), new MyClassConverter());
*
*
* Formatting examples
*
* String output = KeyValueFormat.format(map);
*
* Map<Integer,String> mapIntString;
* KeyValueFormat.formatIntString(mapIntString);
*
* @since 1.10
*/
public final class KeyValueFormat {
public static final String PAIR_SEPARATOR = ";";
public static final String FIELD_SEPARATOR = "=";
private KeyValueFormat() {
// only static methods
}
private static class FieldParserContext {
private final StringBuilder result = new StringBuilder();
private boolean escaped = false;
private char firstChar;
private char previous = (char) -1;
}
static class FieldParser {
private static final char DOUBLE_QUOTE = '"';
private final String csv;
private int position = 0;
FieldParser(String csv) {
this.csv = csv;
}
@CheckForNull
String nextKey() {
return next('=');
}
@CheckForNull
String nextVal() {
return next(';');
}
@CheckForNull
private String next(char separator) {
if (position >= csv.length()) {
return null;
}
FieldParserContext context = new FieldParserContext();
context.firstChar = csv.charAt(position);
// check if value is escaped by analyzing first character
checkEscaped(context);
boolean isEnd = false;
while (position < csv.length() && !isEnd) {
isEnd = advance(separator, context);
}
return context.result.toString();
}
private boolean advance(char separator, FieldParserContext context) {
boolean end = false;
char c = csv.charAt(position);
if (c == separator && !context.escaped) {
end = true;
position++;
} else if (c == '\\' && context.escaped && position < csv.length() + 1 && csv.charAt(position + 1) == DOUBLE_QUOTE) {
// on a backslash that escapes double-quotes -> keep double-quotes and jump after
context.previous = DOUBLE_QUOTE;
context.result.append(context.previous);
position += 2;
} else if (c == '"' && context.escaped && context.previous != '\\') {
// on unescaped double-quotes -> end of escaping.
// assume that next character is a separator (= or ;). This could be
// improved to enforce check.
end = true;
position += 2;
} else {
context.result.append(c);
context.previous = c;
position++;
}
return end;
}
private void checkEscaped(FieldParserContext context) {
if (context.firstChar == DOUBLE_QUOTE) {
context.escaped = true;
position++;
context.previous = context.firstChar;
}
}
}
public abstract static class Converter {
abstract String format(T type);
@CheckForNull
abstract T parse(String s);
String escape(String s) {
if (s.contains(FIELD_SEPARATOR) || s.contains(PAIR_SEPARATOR)) {
return new StringBuilder()
.append(FieldParser.DOUBLE_QUOTE)
.append(s.replace("\"", "\\\""))
.append(FieldParser.DOUBLE_QUOTE).toString();
}
return s;
}
}
public static final class StringConverter extends Converter {
private static final StringConverter INSTANCE = new StringConverter();
private StringConverter() {
}
@Override
String format(String s) {
return escape(s);
}
@Override
String parse(String s) {
return s;
}
}
public static StringConverter newStringConverter() {
return StringConverter.INSTANCE;
}
public static final class ToStringConverter extends Converter {
private static final ToStringConverter INSTANCE = new ToStringConverter();
private ToStringConverter() {
}
@Override
String format(Object o) {
return escape(o.toString());
}
@Override
String parse(String s) {
throw new UnsupportedOperationException("Can not parse with ToStringConverter: " + s);
}
}
public static ToStringConverter newToStringConverter() {
return ToStringConverter.INSTANCE;
}
public static final class IntegerConverter extends Converter {
private static final IntegerConverter INSTANCE = new IntegerConverter();
private IntegerConverter() {
}
@Override
String format(Integer s) {
return s == null ? "" : String.valueOf(s);
}
@Override
Integer parse(String s) {
return StringUtils.isBlank(s) ? null : NumberUtils.toInt(s);
}
}
public static IntegerConverter newIntegerConverter() {
return IntegerConverter.INSTANCE;
}
public static final class PriorityConverter extends Converter {
private static final PriorityConverter INSTANCE = new PriorityConverter();
private PriorityConverter() {
}
@Override
String format(RulePriority s) {
return s == null ? "" : s.toString();
}
@Override
RulePriority parse(String s) {
return StringUtils.isBlank(s) ? null : RulePriority.valueOf(s);
}
}
public static PriorityConverter newPriorityConverter() {
return PriorityConverter.INSTANCE;
}
public static final class DoubleConverter extends Converter {
private static final DoubleConverter INSTANCE = new DoubleConverter();
private DoubleConverter() {
}
@Override
String format(Double d) {
return d == null ? "" : String.valueOf(d);
}
@Override
Double parse(String s) {
return StringUtils.isBlank(s) ? null : NumberUtils.toDouble(s);
}
}
public static DoubleConverter newDoubleConverter() {
return DoubleConverter.INSTANCE;
}
public static class DateConverter extends Converter {
private SimpleDateFormat dateFormat;
private DateConverter(String format) {
this.dateFormat = new SimpleDateFormat(format);
}
@Override
String format(Date d) {
return d == null ? "" : dateFormat.format(d);
}
@Override
Date parse(String s) {
try {
return StringUtils.isBlank(s) ? null : dateFormat.parse(s);
} catch (ParseException e) {
throw new IllegalArgumentException("Not a date with format: " + dateFormat.toPattern(), e);
}
}
}
public static DateConverter newDateConverter() {
return newDateConverter(DateUtils.DATE_FORMAT);
}
public static DateConverter newDateTimeConverter() {
return newDateConverter(DateUtils.DATETIME_FORMAT);
}
public static DateConverter newDateConverter(String format) {
return new DateConverter(format);
}
/**
* If input is null, then an empty map is returned.
*/
public static Map parse(@Nullable String input, Converter keyConverter, Converter valueConverter) {
Map map = Maps.newLinkedHashMap();
if (input != null) {
FieldParser reader = new FieldParser(input);
boolean end = false;
while (!end) {
String key = reader.nextKey();
if (key == null) {
end = true;
} else {
String val = StringUtils.defaultString(reader.nextVal(), "");
map.put(keyConverter.parse(key), valueConverter.parse(val));
}
}
}
return map;
}
public static Map parse(@Nullable String data) {
return parse(data, newStringConverter(), newStringConverter());
}
/**
* @since 2.7
*/
public static Map parseStringInt(@Nullable String data) {
return parse(data, newStringConverter(), newIntegerConverter());
}
/**
* @since 2.7
*/
public static Map parseStringDouble(@Nullable String data) {
return parse(data, newStringConverter(), newDoubleConverter());
}
/**
* @since 2.7
*/
public static Map parseIntString(@Nullable String data) {
return parse(data, newIntegerConverter(), newStringConverter());
}
/**
* @since 2.7
*/
public static Map parseIntDouble(@Nullable String data) {
return parse(data, newIntegerConverter(), newDoubleConverter());
}
/**
* @since 2.7
*/
public static Map parseIntDate(@Nullable String data) {
return parse(data, newIntegerConverter(), newDateConverter());
}
/**
* @since 2.7
*/
public static Map parseIntInt(@Nullable String data) {
return parse(data, newIntegerConverter(), newIntegerConverter());
}
/**
* @since 2.7
*/
public static Map parseIntDateTime(@Nullable String data) {
return parse(data, newIntegerConverter(), newDateTimeConverter());
}
private static String formatEntries(Collection> entries, Converter keyConverter, Converter valueConverter) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Map.Entry entry : entries) {
if (!first) {
sb.append(PAIR_SEPARATOR);
}
sb.append(keyConverter.format(entry.getKey()));
sb.append(FIELD_SEPARATOR);
if (entry.getValue() != null) {
sb.append(valueConverter.format(entry.getValue()));
}
first = false;
}
return sb.toString();
}
private static String formatEntries(Set> entries, Converter keyConverter) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Multiset.Entry entry : entries) {
if (!first) {
sb.append(PAIR_SEPARATOR);
}
sb.append(keyConverter.format(entry.getElement()));
sb.append(FIELD_SEPARATOR);
sb.append(newIntegerConverter().format(entry.getCount()));
first = false;
}
return sb.toString();
}
/**
* @since 2.7
*/
public static String format(Map map, Converter keyConverter, Converter valueConverter) {
return formatEntries(map.entrySet(), keyConverter, valueConverter);
}
/**
* @since 2.7
*/
public static String format(Map map) {
return format(map, newToStringConverter(), newToStringConverter());
}
/**
* @since 2.7
*/
public static String formatIntString(Map map) {
return format(map, newIntegerConverter(), newStringConverter());
}
/**
* @since 2.7
*/
public static String formatIntDouble(Map map) {
return format(map, newIntegerConverter(), newDoubleConverter());
}
/**
* @since 2.7
*/
public static String formatIntDate(Map map) {
return format(map, newIntegerConverter(), newDateConverter());
}
/**
* @since 2.7
*/
public static String formatIntDateTime(Map map) {
return format(map, newIntegerConverter(), newDateTimeConverter());
}
/**
* @since 2.7
*/
public static String formatStringInt(Map map) {
return format(map, newStringConverter(), newIntegerConverter());
}
/**
* @since 2.7
*/
public static String format(Multiset multiset, Converter keyConverter) {
return formatEntries(multiset.entrySet(), keyConverter);
}
public static String format(Multiset multiset) {
return format(multiset, newToStringConverter());
}
/**
* @since 1.11
* @deprecated use Multiset from google collections instead of commons-collections bags
*/
@Deprecated
public static String format(Bag bag, int var) {
StringBuilder sb = new StringBuilder();
if (bag != null) {
boolean first = true;
for (Object obj : bag.uniqueSet()) {
if (!first) {
sb.append(PAIR_SEPARATOR);
}
sb.append(obj.toString());
sb.append(FIELD_SEPARATOR);
sb.append(bag.getCount(obj) + var);
first = false;
}
}
return sb.toString();
}
}