View Javadoc

1   /*
2    * Copyright (C) 2006  Tom Gibara
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17   */
18  package com.tomgibara.pronto.util;
19  
20  import java.util.ArrayList;
21  import java.util.LinkedHashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.regex.Matcher;
25  import java.util.regex.Pattern;
26  
27  /**
28   * A collection of static utility methods for processing strings.
29   * 
30   * @author Tom Gibara
31   */
32  
33  public final class Strings {
34  
35      /** Pattern for splitting at commas. */
36      private static final Pattern COMMA_SPLIT = Pattern.compile("\\s*,\\s*");
37  
38      /** Pattern for matching colon separated key-value pairs. */
39      private static final Pattern KEY_VALUE = Pattern.compile("\\s*(\\S+)\\s*:(.*)");
40  
41      /** Pattern for splitting values at a semi colon. */
42      private static final Pattern SEMI_SPLIT = Pattern.compile(";");
43  
44      /**
45       * Splits a single string into a list of strings as follows:
46       * 
47       * 1) The string is split at each comma. 2) Each resulting substring is
48       * trimmed and added to the list
49       * 
50       * Note that there is no way of escaping a comma within a value.
51       * 
52       * @param str
53       *            the string to be parsed
54       * @return a list of substrings
55       */
56  
57      public static List<String> splitCommas(final String str) {
58          ArrayList<String> list = new ArrayList<String>();
59          Matcher matcher = COMMA_SPLIT.matcher(str);
60          int last = 0;
61          while (true) {
62              if (matcher.find()) {
63                  String value = str.substring(last, matcher.start());
64                  if (last == 0) value = value.trim(); // may be leading spaces
65                  // on first value
66                  list.add(value);
67                  last = matcher.end();
68              } else {
69                  String value = str.substring(last).trim(); // may be trailing
70                  // spaces on last
71                  // value
72                  list.add(value);
73                  break;
74              }
75  
76          }
77          return list;
78      }
79  
80      /**
81       * Constructs a map of strings from a single string as follows:
82       * 
83       * 1) The string is split at each semicolon. 2) Each component is split at a
84       * colon into a key and a value. 3) Each key-value pair is trimmed and added
85       * to the map
86       * 
87       * This encoding should be superficially familiar to those familiar with CSS
88       * style properties. Note that there are no mechanisms for escaping colons
89       * and semicolons. Keys may not be duplicated.
90       * 
91       * @param propsStr
92       *            the string to be parsed
93       * @return a map of string properties
94       */
95  
96      public static Map<String, String> parseProperties(final String propsStr) {
97          LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();
98          String[] propArr = SEMI_SPLIT.split(propsStr);
99          for (String propStr : propArr) {
100             Matcher kvMatcher = KEY_VALUE.matcher(propStr);
101             if (!kvMatcher.matches()) {
102                 // we ignore empty pairs otherwise throw an exception
103                 if (propStr.trim().length() == 0) continue;
104                 throw new IllegalArgumentException(String.format("Illegal key/value pair: %s", propStr));
105             }
106             String key = kvMatcher.group(1);
107             if (map.containsKey(key)) throw new IllegalArgumentException(String.format("Duplicate key: %s", key));
108             String value = kvMatcher.group(2).trim();
109             map.put(key, value);
110         }
111         return map;
112     }
113 
114     private Strings() {
115     }
116 
117     /**
118      * Joins the string representations of an array of objects into a single
119      * string, separating them with the supplied delimiter. A zero length array
120      * results in an empty string.
121      * 
122      * @param array
123      *            the array of objects to be joined
124      * @param delimiter
125      *            string to be inserted between array element
126      * 
127      * @return all the elements of the array concatenated into a single string,
128      *         never null
129      * 
130      */
131 
132     public static String join(final Object[] array, final String delimiter) {
133         Arguments.notNull(array, "array");
134         Arguments.notNull(delimiter, "delimiter");
135 
136         switch (array.length) {
137         case 0:
138             return "";
139         case 1:
140             return array[0] == null ? "null" : array[0].toString();
141         case 2:
142             return array[0] + delimiter + array[1];
143         default:
144             StringBuilder sb = new StringBuilder();
145             for (Object obj : array) {
146                 if (sb.length() != 0) sb.append(delimiter);
147                 sb.append(obj);
148             }
149             return sb.toString();
150         }
151     }
152 
153 }