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.Multiset;
023    import org.apache.commons.collections.Bag;
024    import org.apache.commons.lang.StringUtils;
025    import org.apache.commons.lang.math.NumberUtils;
026    import org.slf4j.LoggerFactory;
027    import org.sonar.api.rules.RulePriority;
028    
029    import java.util.HashMap;
030    import java.util.Map;
031    
032    /**
033     * Util class to format key/value data. Output is a string representation ready to be
034     * injected into the database
035     * 
036     * @since 1.10
037     */
038    public final class KeyValueFormat {
039    
040      private KeyValueFormat() {
041      }
042    
043      /**
044       * Transforms a string with the following format : "key1=value1;key2=value2..."
045       * into a Map<KEY, VALUE>. Requires to implement the transform(key,value) method
046       * 
047       * @param data the input string
048       * @param transformer the interface to implement
049       * @return a Map of <key, value>
050       */
051      public static <KEY, VALUE> Map<KEY, VALUE> parse(String data, Transformer<KEY, VALUE> transformer) {
052        Map<String, String> rawData = parse(data);
053        Map<KEY, VALUE> map = new HashMap<KEY, VALUE>();
054        for (Map.Entry<String, String> entry : rawData.entrySet()) {
055          KeyValue<KEY, VALUE> keyVal = transformer.transform(entry.getKey(), entry.getValue());
056          if (keyVal != null) {
057            map.put(keyVal.getKey(), keyVal.getValue());
058          }
059        }
060        return map;
061      }
062    
063      /**
064       * Transforms a string with the following format : "key1=value1;key2=value2..."
065       * into a Map<String,String>
066       * 
067       * @param data the string to parse
068       * @return a map
069       */
070      public static Map<String, String> parse(String data) {
071        Map<String, String> map = new HashMap<String, String>();
072        String[] pairs = StringUtils.split(data, ";");
073        for (String pair : pairs) {
074          String[] keyValue = StringUtils.split(pair, "=");
075          String key = keyValue[0];
076          String value = (keyValue.length == 2 ? keyValue[1] : "");
077          map.put(key, value);
078        }
079        return map;
080      }
081    
082      /**
083       * Transforms a map<KEY,VALUE> into a string with the format : "key1=value1;key2=value2..."
084       * 
085       * @param map the map to transform
086       * @return the formatted string
087       */
088      public static <KEY, VALUE> String format(Map<KEY, VALUE> map) {
089        StringBuilder sb = new StringBuilder();
090        boolean first = true;
091        for (Map.Entry<?, ?> entry : map.entrySet()) {
092          if (!first) {
093            sb.append(";");
094          }
095          sb.append(entry.getKey().toString());
096          sb.append("=");
097          if (entry.getValue() != null) {
098            sb.append(entry.getValue());
099          }
100          first = false;
101        }
102    
103        return sb.toString();
104      }
105    
106      /**
107       * @since 1.11
108       * @deprecated use Multiset from google collections instead of commons-collections bags
109       */
110      @Deprecated
111      public static String format(Bag bag) {
112        return format(bag, 0);
113      }
114    
115      /**
116       * @since 1.11
117       * @deprecated use Multiset from google collections instead of commons-collections bags
118       */
119      @Deprecated
120      public static String format(Bag bag, int var) {
121        StringBuilder sb = new StringBuilder();
122        if (bag != null) {
123          boolean first = true;
124          for (Object obj : bag.uniqueSet()) {
125            if (!first) {
126              sb.append(";");
127            }
128            sb.append(obj.toString());
129            sb.append("=");
130            sb.append(bag.getCount(obj) + var);
131            first = false;
132          }
133        }
134        return sb.toString();
135      }
136    
137      /**
138       * Transforms a Multiset<?> into a string with the format : "key1=count1;key2=count2..."
139       * 
140       * @param set the set to transform
141       * @return the formatted string
142       */
143      public static String format(Multiset<?> set) {
144        StringBuilder sb = new StringBuilder();
145        if (set != null) {
146          boolean first = true;
147          for (Multiset.Entry<?> entry : set.entrySet()) {
148            if (!first) {
149              sb.append(";");
150            }
151            sb.append(entry.getElement().toString());
152            sb.append("=");
153            sb.append(entry.getCount());
154            first = false;
155          }
156        }
157        return sb.toString();
158      }
159    
160      /**
161       * Transforms a Object... into a string with the format : "object1=object2;object3=object4..."
162       * 
163       * @param objects the object list to transform
164       * @return the formatted string
165       */
166      public static String format(Object... objects) {
167        StringBuilder sb = new StringBuilder();
168        boolean first = true;
169        if (objects != null) {
170          for (int i = 0; i < objects.length; i++) {
171            if (!first) {
172              sb.append(";");
173            }
174            sb.append(objects[i++].toString());
175            sb.append("=");
176            sb.append(objects[i]);
177            first = false;
178          }
179        }
180        return sb.toString();
181      }
182    
183      public interface Transformer<KEY, VALUE> {
184        KeyValue<KEY, VALUE> transform(String key, String value);
185      }
186    
187      /**
188       * Implementation of Transformer<String, Double>
189       */
190      public static class StringNumberPairTransformer implements Transformer<String, Double> {
191    
192        public KeyValue<String, Double> transform(String key, String value) {
193          return new KeyValue<String, Double>(key, toDouble(value));
194        }
195      }
196    
197      /**
198       * Implementation of Transformer<Double, Double>
199       */
200      public static class DoubleNumbersPairTransformer implements Transformer<Double, Double> {
201    
202        public KeyValue<Double, Double> transform(String key, String value) {
203          return new KeyValue<Double, Double>(toDouble(key), toDouble(value));
204        }
205      }
206    
207      /**
208       * Implementation of Transformer<Integer, Integer>
209       */
210      public static class IntegerNumbersPairTransformer implements Transformer<Integer, Integer> {
211    
212        public KeyValue<Integer, Integer> transform(String key, String value) {
213          return new KeyValue<Integer, Integer>(toInteger(key), toInteger(value));
214        }
215      }
216    
217      /**
218       * Implementation of Transformer<RulePriority, Integer>
219       */
220      public static class RulePriorityNumbersPairTransformer implements Transformer<RulePriority, Integer> {
221    
222        public KeyValue<RulePriority, Integer> transform(String key, String value) {
223          try {
224            if (StringUtils.isBlank(value)) {
225              value = "0";
226            }
227            return new KeyValue<RulePriority, Integer>(RulePriority.valueOf(key.toUpperCase()), Integer.parseInt(value));
228          } catch (Exception e) {
229            LoggerFactory.getLogger(RulePriorityNumbersPairTransformer.class).warn("Property " + key + " has invalid value: " + value, e);
230            return null;
231          }
232        }
233      }
234    
235      private static Double toDouble(String value) {
236        return StringUtils.isBlank(value) ? null : NumberUtils.toDouble(value);
237      }
238    
239      private static Integer toInteger(String value) {
240        return StringUtils.isBlank(value) ? null : NumberUtils.toInt(value);
241      }
242    }