/* * Copyright (c) 2003, KNOPFLERFISH project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * - Neither the name of the KNOPFLERFISH project nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.knopflerfish.util; import java.util.StringTokenizer; import java.util.Vector; import java.util.Iterator; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import java.util.Comparator; /** * Misc static text utility methods. */ public class Text { /** * Retrieves a parameter value from a parameter string. If the parameter is * not found the default value is returned. * * @param s * Parameter string, format * '<param1>=data1::<param2>=data2' * @param param * Parameter to retrieve. * @param def * Default value to return, if the parameter is not found. * @return parameter value or default value. */ public static String getParam(String s, String param, String def) { if (s == null || param == null) return def; int ix = s.indexOf(param + "="); if (ix < 0) return def; int startIdx = ix + param.length() + 1; int endIdx = s.indexOf("::", startIdx); if (endIdx < 0) endIdx = s.length(); try { return s.substring(startIdx, endIdx); } catch (Exception e) { return def; } } // String replace functions below by Erik Wistrand /** * Replace all occurances of a substring with another string. * *
* The returned string will shrink or grow as necessary, depending on the * lengths of v1 and v2. *
* ** Implementation note: This method avoids using the standard String * manipulation methods to increase execution speed. Using the * replace method does however include two new * operations in the case when matches are found. *
* * * @param s * Source string. * @param v1 * String to be replaced withv2.
* @param v2
* String replacing v1.
* @return Modified string. If any of the input strings are null,
* the source string s will be returned unmodified. If
* v1.length == 0, v1.equals(v2) or no
* occurances of v1 is found, also return s
* unmodified.
*/
public static String replace(final String s, final String v1,
final String v2) {
// return quick when nothing to do
if (s == null || v1 == null || v2 == null || v1.length() == 0
|| v1.equals(v2)) {
return s;
}
int ix = 0;
int v1Len = v1.length();
int n = 0;
// count number of occurances to be able to correctly size
// the resulting output char array
while (-1 != (ix = s.indexOf(v1, ix))) {
n++;
ix += v1Len;
}
// No occurances at all, just return source string
if (n == 0) {
return s;
}
// Set up an output char array of correct size
int start = 0;
int v2Len = v2.length();
char[] r = new char[s.length() + n * (v2Len - v1Len)];
int rPos = 0;
// for each occurance, copy v2 where v1 used to be
while (-1 != (ix = s.indexOf(v1, start))) {
while (start < ix)
r[rPos++] = s.charAt(start++);
for (int j = 0; j < v2Len; j++) {
r[rPos++] = v2.charAt(j);
}
start += v1Len;
}
// ...and add all remaining chars
ix = s.length();
while (start < ix)
r[rPos++] = s.charAt(start++);
// ..ouch. this hurts.
return new String(r);
}
/**
* Utility method for replacing substrings with an integer.
*
* * Equivalent to replace(s, v1, Integer.toString(v2)) *
*/ public static String replace(String s, String v1, int v2) { return replace(s, v1, Integer.toString(v2)); } /** * Utility method for replacing substrings with a boolean. * ** Equivalent to replace(s, v1, v2 ? "true" : "false") *
*/ public static String replace(String s, String v1, boolean v2) { return replace(s, v1, v2 ? "true" : "false"); } /** * Expand all tabs in a string. Tab stop positions are placed at the columns * which are multiples oftabSize.
*
* @param s
* String to untabify.
* @param tabSize
* Tab stop interval.
* @return String with expanded tabs.
*/
public static String untabify(String s, int tabSize) {
StringBuffer sb = new StringBuffer(s);
for (int i = 0; i < sb.length(); i++) {
if (sb.charAt(i) == '\t') {
String toinsert = " ".substring(0, tabSize
- (i % tabSize));
if (toinsert.length() == 0) {
sb = new StringBuffer(sb.toString().substring(0, i)
+ sb.toString().substring(i + 1));
} else {
sb.setCharAt(i, ' ');
if (toinsert.length() > 1) {
sb.insert(i, toinsert.substring(1));
}
}
}
}
return sb.toString();
}
/**
* Format a java type in human readable way.
*
* @param s
* Type string to format.
* @param prefixIgnore
* Prefix strings to ignore in output.
* @return Nice-to-read string.
*/
public static String formatJavaType(String s, String[] prefixIgnore) {
for (int i = 0; i < prefixIgnore.length; i++) {
if (s.startsWith(prefixIgnore[i])) {
return s.substring(prefixIgnore[i].length());
}
}
// Handle array types
if (s.startsWith("[L") && s.endsWith(";")) {
return formatJavaType(s.substring(2, s.length() - 1), prefixIgnore)
+ "[]";
}
return s;
}
/**
* Make first (and only) character in string upper case.
*
* @param s
* String to capitalize.
* @return Capitalized string. If s is null or equals
* the empty string, return s.
*/
public static String capitalize(String s) {
return (s.equals("") || s == null) ? s : s.substring(0, 1)
.toUpperCase()
+ s.substring(1).toLowerCase();
}
/**
* Default whitespace string for splitwords(). Value is " \t\n\r")
*/
protected static String WHITESPACE = " \t\n\r";
/**
* Default citation char for splitwords(). Value is '"'
*/
protected static char CITCHAR = '"';
/**
* Utility method to split a string into words separated by whitespace.
*
* * Equivalent to splitwords(s, Text.WHITESPACE) *
*/ public static String[] splitwords(String s) { return splitwords(s, Text.WHITESPACE); } /** * Utility method to split a string into words separated by whitespace. * ** Equivalent to splitwords(s, Text.WHITESPACE, Text.CITCHAR) *
*/ public static String[] splitwords(String s, String whiteSpace) { return splitwords(s, Text.WHITESPACE, Text.CITCHAR); } /** * Split a string into words separated by whitespace. ** Citation chars may be used to group words with embedded whitespace. *
* * @param s * String to split. * @param whiteSpace * whitespace to use for splitting. Any of the characters in the * whiteSpace string are considered whitespace between words and * will be removed from the result. If no words are found, return * an array of length zero. * @param citChar * Citation character used for grouping words with embedded * whitespace. Typically '"' */ public static String[] splitwords(String s, String whiteSpace, char citChar) { boolean bCit = false; // true when inside citation chars. Vector v = new Vector(); // (String) individual words after splitting StringBuffer buf = null; int i = 0; while (i < s.length()) { char c = s.charAt(i); if (bCit || whiteSpace.indexOf(c) == -1) { // Build up word until we breaks on either a citation char or // whitespace if (c == citChar) { bCit = !bCit; } else { if (buf == null) { buf = new StringBuffer(); } buf.append(c); } i++; } else { // found whitespace or end of citation, append word if we have // one if (buf != null) { v.addElement(buf.toString()); buf = null; } // and skip whitespace so we start clean on a word or citation // char while ((i < s.length()) && (-1 != whiteSpace.indexOf(s.charAt(i)))) { i++; } } } // Add possible remaining word if (buf != null) { v.addElement(buf.toString()); } // Copy back into an array String[] r = new String[v.size()]; v.copyInto(r); return r; } /** * Splits a string into words, using theStringTokenizer
* class.
*/
public static String[] split(String s, String sep) {
StringTokenizer st = new StringTokenizer(s, sep);
int ntok = st.countTokens();
String[] res = new String[ntok];
for (int i = 0; i < ntok; i++) {
res[i] = st.nextToken();
}
return res;
}
/**
* Join an array into a single string with a given separator.
*/
public static String join(Object[] s, String sep) {
StringBuffer buf = new StringBuffer();
int l = s.length;
if (l > 0) {
buf.append(s[0].toString());
}
for (int i = 1; i < l; i++) {
buf.append(sep);
buf.append(s[i].toString());
}
return buf.toString();
}
public static Object[] toArray(Vector v) {
int size = v.size();
Object[] o = new Object[size];
// Why not v.copyInto(o) ??
for (int i = 0; i < size; i++) {
o[i] = v.elementAt(i);
}
return o;
}
/*
* // Testing purposes static public void main(String[] argv) { String[] s =
* splitwords(argv[0], " ,"); for(int i = 0; i < s.length; i++) {
* System.out.println(s[i]); }
*
* int n = 100000; { Hashtable h = new Hashtable(); long start =
* System.currentTimeMillis(); for(int i = 0; i < n; i++) { String ss =
* replace(argv[0], argv[1], argv[2]); h.put(ss, ss); } long stop =
* System.currentTimeMillis(); double delta = ((double)stop - start) / 1000;
*
* System.out.println("total = " + delta + ", replace/sec=" + (n / delta)); } }
*/
static final Comparator strComp = new Comparator() {
/**
* String compare
*
* @param oa Object to compare.
* @param ob Object to compare.
* @return Return 0 if equals, negative if first object is less than second
* object and positive if first object is larger then second object.
* @exception ClassCastException if objects are not a String objects.
*/
public int compare(Object oa, Object ob) throws ClassCastException {
String a = (String)oa;
String b = (String)ob;
return a.compareTo(b);
}
};
/**
* Parse strings of format:
*
* ENTRY (, ENTRY)*
*
* @param d Directive being parsed
* @param s String to parse
* @return A sorted ArrayList with enumeration or null if enumeration string was null.
* @exception IllegalArgumentException If syntax error in input string.
*/
public static ArrayList parseEnumeration(String d, String s) {
ArrayList result = new ArrayList();
if (s != null) {
AttributeTokenizer at = new AttributeTokenizer(s);
do {
String key = at.getKey();
if (key == null) {
throw new IllegalArgumentException("Directive " + d + ", unexpected character at: "
+ at.getRest());
}
if (!at.getEntryEnd()) {
throw new IllegalArgumentException("Directive " + d + ", expected end of entry at: "
+ at.getRest());
}
int i = Math.abs(binarySearch(result, strComp, key) + 1);
result.add(i, key);
} while (!at.getEnd());
return result;
} else {
return null;
}
}
/**
* Do binary search for a package entry in the list with the same
* version number add the specifies package entry.
*
* @param pl Sorted list of package entries to search.
* @param p Package entry to search for.
* @return index of the found entry. If no entry is found, return
* (-(insertion point) - 1). The insertion
* point is defined as the point at which the
* key would be inserted into the list.
*/
public static int binarySearch(List pl, Comparator c, Object p) {
int l = 0;
int u = pl.size()-1;
while (l <= u) {
int m = (l + u)/2;
int v = c.compare(pl.get(m), p);
if (v > 0) {
l = m + 1;
} else if (v < 0) {
u = m - 1;
} else {
return m;
}
}
return -(l + 1); // key not found.
}
/**
* Parse strings of format:
*
* ENTRY (, ENTRY)*
* ENTRY = key (; key)* (; PARAM)*
* PARAM = attribute '=' value
* PARAM = directive ':=' value
*
* @param a Attribute being parsed
* @param s String to parse
* @param single If true, only allow one key per ENTRY
* @param unique Only allow unique parameters for each ENTRY.
* @param single_entry If true, only allow one ENTRY is allowed.
* @return Iterator(Map(param -> value)) or null if input string is null.
* @exception IllegalArgumentException If syntax error in input string.
*/
public static Iterator parseEntries(String a, String s, boolean single, boolean unique, boolean single_entry) {
ArrayList result = new ArrayList();
if (s != null) {
AttributeTokenizer at = new AttributeTokenizer(s);
do {
ArrayList keys = new ArrayList();
HashMap params = new HashMap();
String key = at.getKey();
if (key == null) {
throw new IllegalArgumentException("Definition, " + a + ", expected key at: " + at.getRest());
}
if (!single) {
keys.add(key);
while ((key = at.getKey()) != null) {
keys.add(key);
}
}
String param;
while ((param = at.getParam()) != null) {
List old = (List)params.get(param);
boolean is_directive = at.isDirective();
if (old != null && unique) {
throw new IllegalArgumentException("Definition, " + a + ", duplicate " +
(is_directive ? "directive" : "attribute") +
": " + param);
}
String value = at.getValue();
if (value == null) {
throw new IllegalArgumentException("Definition, " + a + ", expected value at: " + at.getRest());
}
if (is_directive) {
// NYI Handle directives and check them
// This method has become very ugly, please rewrite.
}
if (unique) {
params.put(param, value);
} else {
if (old == null) {
old = new ArrayList();
params.put(param, old);
}
old.add(value);
}
}
if (at.getEntryEnd()) {
if (single) {
params.put("key", key);
} else {
params.put("keys", keys);
}
result.add(params);
} else {
throw new IllegalArgumentException("Definition, " + a + ", expected end of entry at: " + at.getRest());
}
if (single_entry && !at.getEnd()) {
throw new IllegalArgumentException("Definition, " + a + ", expected end of single entry at: " + at.getRest());
}
} while (!at.getEnd());
}
return result.iterator();
}
/**
* Check if a string exists in a list. Ignore case when comparing.
*/
public static boolean containsIgnoreCase(List l, List l2) {
for (Iterator i = l.iterator(); i.hasNext(); ) {
String s = (String)i.next();
for (Iterator j = l2.iterator(); j.hasNext(); ) {
if (s.equalsIgnoreCase((String)j.next())) {
return true;
}
}
}
return false;
}
/**
* Class for tokenize an attribute string.
*/
public static class AttributeTokenizer {
String s;
int length;
int pos = 0;
AttributeTokenizer(String input) {
s = input;
length = s.length();
}
String getWord() {
skipWhite();
boolean backslash = false;
boolean quote = false;
StringBuffer val = new StringBuffer();
int end = 0;
loop:
for (; pos < length; pos++) {
if (backslash) {
backslash = false;
val.append(s.charAt(pos));
} else {
char c = s.charAt(pos);
switch (c) {
case '"':
quote = !quote;
end = val.length();
break;
case '\\':
backslash = true;
break;
case ',': case ':': case ';': case '=':
if (!quote) {
break loop;
}
// Fall through
default:
val.append(c);
if (!Character.isWhitespace(c)) {
end = val.length();
}
break;
}
}
}
if (quote || backslash || end == 0) {
return null;
}
char [] res = new char [end];
val.getChars(0, end, res, 0);
return new String(res);
}
String getKey() {
if (pos >= length) {
return null;
}
int save = pos;
if (s.charAt(pos) == ';') {
pos++;
}
String res = getWord();
if (res != null) {
if (pos == length) {
return res;
}
char c = s.charAt(pos);
if (c == ';' || c == ',') {
return res;
}
}
pos = save;
return null;
}
String getParam() {
if (pos == length || s.charAt(pos) != ';') {
return null;
}
int save = pos++;
String res = getWord();
if (res != null) {
if (pos < length && s.charAt(pos) == '=') {
return res;
} if (pos + 1 < length && s.charAt(pos) == ':' && s.charAt(pos+1) == '=') {
return res;
}
}
pos = save;
return null;
}
boolean isDirective() {
if (pos + 1 < length && s.charAt(pos) == ':') {
pos++;
return true;
} else {
return false;
}
}
String getValue() {
if (s.charAt(pos) != '=') {
return null;
}
int save = pos++;
skipWhite();
String val = getWord();
if (val == null) {
pos = save;
return null;
}
return val;
}
boolean getEntryEnd() {
int save = pos;
skipWhite();
if (pos == length) {
return true;
} else if (s.charAt(pos) == ',') {
pos++;
return true;
} else {
pos = save;
return false;
}
}
boolean getEnd() {
int save = pos;
skipWhite();
if (pos == length) {
return true;
} else {
pos = save;
return false;
}
}
String getRest() {
String res = s.substring(pos).trim();
return res.length() == 0 ? "