1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2 *
3 * This program and the accompanying materials are made available under
4 * the terms of the Common Public License v1.0 which accompanies this distribution,
5 * and is available at http://www.eclipse.org/legal/cpl-v10.html
6 *
7 * $Id: Strings.java,v 1.1.1.1 2004/05/09 16:57:55 vlad_r Exp $
8 */
9package com.vladium.util;
10
11import java.io.File;
12import java.io.IOException;
13import java.util.ArrayList;
14import java.util.HashSet;
15import java.util.List;
16import java.util.Set;
17import java.util.StringTokenizer;
18
19// ----------------------------------------------------------------------------
20/**
21 * @author Vlad Roubtsov, (C) 2003
22 */
23public
24abstract class Strings
25{
26    // public: ................................................................
27
28
29    public static final String WHITE_SPACE = " \t\r\n";
30
31
32    //TODO: add duplicate removal
33    public static String toListForm (final String [] strings, final char delimiter)
34    {
35        if (strings == null) return null;
36        if (strings.length == 0) return "";
37
38        final StringBuffer s = new StringBuffer ();
39        for (int i = 0, iLimit = strings.length; i < iLimit; ++ i)
40        {
41            if (i != 0) s.append (delimiter);
42            s.append (strings [i]);
43        }
44
45        return s.toString ();
46    }
47
48    public static String [] removeDuplicates (final String [] strings, final boolean removeNull)
49    {
50        if (strings == null) return strings;
51
52        final int length = strings.length;
53        if (length == 0) return strings;
54
55        final Set /* String */ _strings = new HashSet (length);
56        final List /* String */ _result = new ArrayList (length);
57
58        for (int i = 0; i < length; ++ i)
59        {
60            final String s = strings [i];
61            if (removeNull && (s == null)) continue;
62
63            if (_strings.add (s)) _result.add (s);
64        }
65
66        final int resultLength = _result.size ();
67        if (resultLength == length)
68            return strings;
69        else
70        {
71            final String [] result = new String [resultLength];
72            _result.toArray (result);
73
74            return result;
75        }
76    }
77
78    /**
79     * Also removes duplicates.
80     *
81     * @param strings
82     * @param delimiters
83     * @param removeNull
84     * @return
85     */
86    public static String [] merge (final String [] strings, final String delimiters, final boolean removeNull)
87    {
88        if (strings == null) return strings;
89
90        final int length = strings.length;
91        if (length == 0) return strings;
92
93        if ((delimiters == null) || (delimiters.length () == 0))
94            throw new IllegalArgumentException ("null/empty input: delimiters");
95
96        final Set /* String */ _strings = new HashSet (length);
97        final List /* String */ _result = new ArrayList (length);
98
99        for (int i = 0; i < length; ++ i)
100        {
101            final String s = strings [i];
102            if (removeNull && (s == null)) continue;
103
104            final StringTokenizer tokenizer = new StringTokenizer (s, delimiters);
105            while (tokenizer.hasMoreTokens ())
106            {
107                final String ss = tokenizer.nextToken ();
108                if (_strings.add (ss)) _result.add (ss);
109            }
110        }
111
112        final String [] result = new String [_result.size ()];
113        _result.toArray (result);
114
115        return result;
116    }
117
118    /**
119     * Removes duplicates.
120     *
121     * @param delimiters
122     * @param processAtFiles
123     * @return
124     * @throws IOException
125     */
126    public static String [] mergeAT (final String [] strings, final String delimiters, final boolean processAtFiles)
127        throws IOException
128    {
129        if (! processAtFiles)
130            return merge (strings, delimiters, true);
131        else
132        {
133            if (strings == null) return strings;
134
135            final int length = strings.length;
136            if (length == 0) return strings;
137
138            if ((delimiters == null) || (delimiters.length () == 0))
139                throw new IllegalArgumentException ("null/empty input: delimiters");
140
141            final Set /* String */ _strings = new HashSet (length);
142            final List /* String */ _result = new ArrayList (length);
143
144            for (int i = 0; i < length; ++ i)
145            {
146                final String s = strings [i];
147                if (s == null) continue;
148
149                final StringTokenizer tokenizer = new StringTokenizer (s, delimiters);
150                while (tokenizer.hasMoreTokens ())
151                {
152                    final String ss = tokenizer.nextToken ();
153
154                    if (ss.startsWith ("@"))
155                    {
156                        final String [] fileList = Files.readFileList (new File (ss.substring (1)));
157                        for (int j = 0; j < fileList.length; ++ j)
158                        {
159                            final String sss = fileList [j];
160                            if (_strings.add (sss)) _result.add (sss);
161                        }
162                    }
163                    else if (_strings.add (ss)) _result.add (ss);
164                }
165            }
166
167            final String [] result = new String [_result.size ()];
168            _result.toArray (result);
169
170            return result;
171        }
172    }
173
174    /**
175     * HTML attribute values can be quoted using either double or single quotes.
176     * Depending on the type of quote used, the other kind can be used unescaped
177     * within the attribute value. This method assumes that only double quotes
178     * are used for delimiting, hence this is the only kind that is escaped.
179     */
180    public static void HTMLEscape (final String s, final StringBuffer append)
181    {
182        if (s == null) throw new IllegalArgumentException ("null input: s");
183        if (append == null) throw new IllegalArgumentException ("null input: append");
184
185        final char [] chars;
186        if (USE_GET_CHARS) chars = s.toCharArray ();
187
188        for (int i = 0, iLimit = s.length (); i < iLimit; ++ i)
189        {
190            final char c = USE_GET_CHARS ? chars [i] : s.charAt (i);
191
192            switch (c)
193            {
194                case '<':
195                    append.append ("&lt;");
196                    break;
197
198                case '>':
199                    append.append ("&gt;");
200                    break;
201
202                case '"':
203                    append.append ("&#34;");
204                    break;
205
206                case '&':
207                    append.append ("&amp;");
208                    break;
209
210                default:
211                    append.append (c);
212
213            } // end of switch
214        }
215    }
216
217    /**
218     * Same as {@link #HTMLEscape(String, StringBuffer)} but also replaces spaces
219     * with "&nbsp;"'s, which is handy for escaping code.
220     */
221    public static void HTMLEscapeNB (final String s, final StringBuffer append)
222    {
223        if (s == null) throw new IllegalArgumentException ("null input: s");
224        if (append == null) throw new IllegalArgumentException ("null input: append");
225
226        final char [] chars;
227        if (USE_GET_CHARS) chars = s.toCharArray ();
228
229        for (int i = 0, iLimit = s.length (); i < iLimit; ++ i)
230        {
231            final char c = USE_GET_CHARS ? chars [i] : s.charAt (i);
232
233            switch (c)
234            {
235                case ' ':
236                    append.append ('\u00A0'); // don't use "&#160;": a waste of space
237                    break;
238
239                case '\t':
240                    append.append ("\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0"); // TODO: define a prop for this
241                    break;
242
243//                case '-':
244//                    append.append ((char) 0x8209);
245//                    break;
246
247                case '<':
248                    append.append ("&lt;");
249                    break;
250
251                case '>':
252                    append.append ("&gt;");
253                    break;
254
255                case '"':
256                    append.append ("&#34;");
257                    break;
258
259                case '&':
260                    append.append ("&amp;");
261                    break;
262
263                default:
264                    append.append (c);
265
266            } // end of switch
267        }
268    }
269
270    public static String HTMLEscape (final String s)
271    {
272        final StringBuffer buf = new StringBuffer ();
273        HTMLEscape (s, buf);
274
275        return buf.toString ();
276    }
277
278    public static String HTMLEscapeSP (final String s)
279    {
280        final StringBuffer buf = new StringBuffer ();
281        HTMLEscapeNB (s, buf);
282
283        return buf.toString ();
284    }
285
286    // protected: .............................................................
287
288    // package: ...............................................................
289
290    // private: ...............................................................
291
292
293    private Strings () {} // prevent subclassing
294
295
296    private static final boolean USE_GET_CHARS = true;
297
298} // end of class
299// ----------------------------------------------------------------------------