/**
* ZipSnap 2.1
* Copyright 2007 Zach Scrivena
* 2007-08-26
* zachscrivena@gmail.com
* http://zipsnap.sourceforge.net/
*
* ZipSnap is a simple command-line incremental backup tool for directories.
*
* TERMS AND CONDITIONS:
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package zipsnap;
import java.text.NumberFormat;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.ArrayList;
import java.util.List;
/**
* Simple class for manipulating strings.
*/
public class StringManipulator
{
/** default regex delimiter pattern */
private static final String defaultRegexDelim = "[\\s]++";
/**
* Tokenize the given string, using the specified regex delimiter pattern.
*
* @param in
* String to be tokenized
* @param regexDelim
* Regex delimiter pattern
* @param includeDelim
* If true, then delimiter tokens are returned too;
* otherwise, only non-delimiter tokens are returned
* @return
* Tokens in an array of strings;
* null, if the string to be tokenized is null
*/
public static Token[] tokenize(
final String in,
final String regexDelim,
final boolean includeDelimiters)
{
/* null input string */
if (in == null)
return null;
/* regex matcher for delimiter */
final Matcher delimiterMatcher = (regexDelim == null) ?
Pattern.compile(defaultRegexDelim).matcher(in) :
Pattern.compile(regexDelim).matcher(in);
/* return value */
final List<Token> tokens = new ArrayList<Token>();
/* initialize buffer string */
StringBuilder buffer = new StringBuilder();
/* parse each character in input string */
for (int i = 0; i < in.length(); i++)
{
delimiterMatcher.region(i, in.length());
if (delimiterMatcher.lookingAt())
{
/* found delimiter match starting at this index */
/* add buffer string to tokens if nonempty */
if (buffer.length() > 0)
{
tokens.add(new Token(buffer.toString(), false));
buffer = new StringBuilder();
}
if (includeDelimiters)
tokens.add(new Token(delimiterMatcher.group(), true));
/* advance index by length of the delimiter match */
i += delimiterMatcher.group().length() - 1;
}
else
{
/* not a match at this index, so we add the char */
/* to the buffer string */
buffer.append(in.charAt(i));
}
}
/* flush buffer string if nonempty */
if (buffer.length() > 0)
tokens.add(new Token(buffer.toString(), false));
/* return value */
return tokens.toArray(new Token[tokens.size()]);
}
/**
* Inner class for representing a token
*/
public static class Token
{
public String val;
public boolean isDelimiter;
public Token(String val, boolean isDelimiter)
{
this.val = val;
this.isDelimiter = isDelimiter;
}
}
/**
* Extract a substring from a given string, according to the specified
* format.
*
* @param in
* String from which the substring is to be extracted
* @param format
* Format string describing the sequence of characters in the substring
* @param rangeChar
* Range character to be used
* @param delimChar
* Delimiter character to be used
* @return
* Substring of the given string
*/
public static String substring(
final String in,
final String format,
final char rangeChar,
final char delimChar)
{
/* length of input string */
final int len = in.length();
/* nothing to do for empty string */
if (len == 0) return in;
/* tokenize format string by delimiter character, e.g. ',' */
final String[] tokens = format.split("[\\" + delimChar + "]++");
/* return value */
final StringBuilder out = new StringBuilder();
/* Regex pattern for nonzero integers */
final Pattern nonZeroIntegerPattern =
Pattern.compile("\\s*([\\+\\-]?[1-9][0-9]*)\\s*");
/* process each token */
for (int i = 0; i < tokens.length; i++)
{
/* split betwen range characters, e.g. ':' */
final String[] entries = tokens[i].split("[\\" + rangeChar + "]++");
int[] indices = new int[entries.length];
/* check if entries are all non-zero integers */
for (int j = 0; j < entries.length; j++)
{
final String entry = entries[j];
if (nonZeroIntegerPattern.matcher(entry).matches())
{
/* convert to int */
indices[j] = Integer.parseInt(entry);
}
else
{
/* error; not a nonzero integer */
return null;
}
}
if (indices.length == 1)
{
/* format "a" */
indices[0] = normalizeIndex(len, indices[0]);
out.append(in.charAt(indices[0] - 1));
}
else if (indices.length == 2)
{
/* format "a:b" */
indices[0] = normalizeIndex(len, indices[0]);
indices[1] = normalizeIndex(len, indices[1]);
final int delta = (indices[0] <= indices[1]) ? 1 : -1;
for (int k = indices[0]; ; k += delta)
{
if (delta > 0)
{
if (k > indices[1]) break;
}
else
{
if (k < indices[1]) break;
}
out.append(in.charAt(k - 1));
}
}
else if (indices.length == 3)
{
/* format "a:b:c" */
indices[0] = normalizeIndex(len, indices[0]);
indices[2] = normalizeIndex(len, indices[2]);
if ((indices[2] - indices[0]) * indices[1] < 0) continue;
for (int k = indices[0]; ; k += indices[1])
{
if (indices[1] > 0)
{
if (k > indices[2]) break;
}
else
{
if (k < indices[2]) break;
}
out.append(in.charAt(k - 1));
}
}
else
{
/* invalid format string */
return null;
}
}
return out.toString();
}
/**
* Normalize user-specified index value.
*
* @param len
* Length of the source string
* @param index
* User-specified index value
* @return
* Normalized index value
*/
private static int normalizeIndex(
final int len,
final int index)
{
/* clip index to [1,len] */
int newIndex = index;
if (newIndex < 0) newIndex += (len + 1);
if (newIndex < 1) newIndex = 1;
if (newIndex > len) newIndex = len;
return newIndex;
}
/**
* Return a formatted string representation of a given long number
* (format is locale-sensitive).
*
* @param n
* Long number to be formatted
* @return
* Formatted string representation of the given long number
*/
public static String formattedLong(
final long n)
{
final NumberFormat nf = NumberFormat.getNumberInstance();
nf.setGroupingUsed(true);
try
{
return nf.format(n);
}
catch (Exception e)
{
return (n + "");
}
}
/**
* Return a formatted string representation of a given double number
* (format is locale-sensitive).
*
* @param n
* Double number to be formatted
* @return
* Formatted string representation of the given double number
*/
public static String formattedDouble(
final double n)
{
final NumberFormat nf = NumberFormat.getNumberInstance();
nf.setGroupingUsed(true);
try
{
return nf.format(n);
}
catch (Exception e)
{
return (n + "");
}
}
/**
* Center-justify the string representation of a given object, padding with
* leading and trailing spaces so that its length is at least the specified
* width.
*
* @param o
* Object to be center-justified
* @param width
* Width of the resulting center-justified string
* @return
* Center-justified string representation
*/
public static String centerJustify(
final Object o,
final int width)
{
final String s = o + "";
final int len = s.length();
final int totalSpace = width - len;
if (totalSpace <= 0)
return s;
final StringBuilder t = new StringBuilder();
final int leadingSpace = totalSpace / 2;
for (int i = 0; i < leadingSpace; i++)
t.append(' ');
t.append(s);
for (int i = 0; i < totalSpace - leadingSpace; i++)
t.append(' ');
return t.toString();
}
/**
* Left-justify the string representation of a given object, padding with
* trailing spaces so that its length is at least the specified width.
*
* @param o
* Object for to be left-justified
* @param width
* Width of the resulting left-justified string
* @return
* Left-justified string representation
*/
public static String leftJustify(
final Object o,
final int width)
{
final String s = o + "";
final int len = s.length();
final int totalSpace = width - len;
if (totalSpace <= 0)
return s;
final StringBuilder t = new StringBuilder();
for (int i = 0; i < totalSpace; i++)
t.append(' ');
t.append(s);
return t.toString();
}
/**
* Right-justify the string representation of a given object, padding with
* leading spaces so that its length is at least the specified width.
*
* @param o
* Object to be right-justified
* @param width
* Width of the resulting right-justified string
* @return
* Right-justified string representation
*/
public static String rightJustify(
final Object o,
final int width)
{
final String s = o + "";
final int len = s.length();
final int totalSpace = width - len;
if (totalSpace <= 0)
return s;
final StringBuilder t = new StringBuilder(s);
for (int i = 0; i < totalSpace; i++)
t.append(' ');
return t.toString();
}
/**
* Repeat the string representation of a given object, a specified number
* of times.
*
* @param o
* Object to be repeated
* @param n
* Number of times to repeat
* @return
* Repeated string representation
*/
public static String repeat(
final Object o,
final int n)
{
final String s = o + "";
final StringBuilder t = new StringBuilder();
for (int i = 0; i < n; i++)
t.append(s);
return t.toString();
}
/**
* Convert a specified string to initial case, by capitalizing only the
* first letter of each word.
*
* @param s
* Input string
* @return
* Output string
*/
public static String initialCase(
final String s)
{
final Token[] tokens = tokenize(s, "[\\s\\p{Punct}]++", true);
final StringBuilder t = new StringBuilder();
for (Token token : tokens)
{
if (token.val.isEmpty()) continue;
if (token.isDelimiter)
{
t.append(token.val);
}
else
{
t.append(token.val.toUpperCase().charAt(0));
t.append(token.val.toLowerCase().substring(1));
}
}
return t.toString();
}
/**
* Abbreviate a specified string, by keeping only the first letter
* of each word.
*
* @param s
* Input string
* @return
* Output string
*/
public static String abbreviate(
final String s)
{
final Token[] tokens = tokenize(s, "[\\s\\p{Punct}]++", true);
final StringBuilder t = new StringBuilder();
for (Token token : tokens)
{
if (token.val.isEmpty()) continue;
if (token.isDelimiter)
{
t.append(token.val);
}
else
{
t.append(token.val.charAt(0));
}
}
return t.toString();
}
/**
* Remove extra whitespace in a specified string, by replacing contiguous
* whitespace characters with a single space.
*
* @param s
* Input string
* @return
* Output string
*/
public static String removeExtraWhitespace(
final String s)
{
final Token[] tokens = tokenize(s, "[\\s]++", true);
final StringBuilder t = new StringBuilder();
for (Token token : tokens)
{
if (token.val.isEmpty()) continue;
if (token.isDelimiter)
{
t.append(' ');
}
else
{
t.append(token.val);
}
}
return t.toString();
}
/**
* Remove whitespace in a specified string, by deleting all whitespace
* characters.
*
* @param s
* Input string
* @return
* Output string
*/
public static String removeWhitespace(
final String s)
{
final String[] tokens = s.split("[\\s]++");
final StringBuilder t = new StringBuilder();
for (String token : tokens)
t.append(token);
return t.toString();
}
/**
* Remove punctuation in a specified string, by deleting all punctuation
* characters.
*
* @param s
* Input string
* @return
* Output string
*/
public static String removePunctuation(
final String s)
{
final String[] tokens = s.split("[\\p{Punct}]++");
final StringBuilder t = new StringBuilder();
for (String token : tokens)
t.append(token);
return t.toString();
}
/**
* Space out words in a specified string, by inserting a single space
* between concatenated words.
*
* @param s
* Input string
* @return
* Output string
*/
public static String spaceOutWords(
final String s)
{
final StringBuilder t = new StringBuilder();
for (int i = 0; i < s.length(); i++)
{
final char c = s.charAt(i);
if (Character.isLowerCase(c) &&
(i + 1 < s.length()) &&
Character.isUpperCase(s.charAt(i + 1)))
{
t.append(c);
t.append(' ');
}
else if (Character.isUpperCase(c) &&
(i - 1 >= 0) &&
Character.isUpperCase(s.charAt(i - 1)) &&
(i + 1 < s.length()) &&
Character.isLowerCase(s.charAt(i + 1)))
{
t.append(' ');
t.append(c);
}
else
{
t.append(c);
}
}
return t.toString();
}
/**
* Invert the case of a specified string, by converting lower case
* characters to upper case and vice versa.
*
* @param s
* Input string
* @return
* Output string
*/
public static String invertCase(
final String s)
{
final StringBuilder t = new StringBuilder();
for (int i = 0; i < s.length(); i++)
{
final char c = s.charAt(i);
if (Character.isLowerCase(c))
{
t.append(Character.toUpperCase(c));
}
else if (Character.isUpperCase(c))
{
t.append(Character.toLowerCase(c));
}
else
{
t.append(c);
}
}
return t.toString();
}
}