Code Snippets

The following is a selection of code snippets that may be useful to use and/or learn from.

Sections

License

This software is released under the GNU GPLv3 license

Copyright © 2010 Keang Ltd


String Padding

Before the introduction of the printf(..) and format(..) methods in Java 1.5 padding strings was a little more involved. The following method allows a string to be aligned within a column by padding the string with the given character (most probably ' ').

For those of you who can't get to grips with format strings this method provides a simple means of aligning text for output in tabular format.

/**
 * Pad a string to the given size and alignment requirements. If the
 * string is greater than the max width it is returned unmodified.
 *
 * If the alignment is LEFT all padding is added after the text, if the alignment
 * is RIGHT all padding is added before the text and if the alignment is CENTER
 * the padding is split equally before and after the text. If the padding
 * cannot be split equally the extra padding is added after the text.
 *
 *@param text - the string to pad
 *@param width - the width to pad it to
 *@param align - the alignment, one of the swing constants
 *               SwingConstants.LEFT, SwingConstants.RIGHT or SwingConstants.CENTER
 *@param padChar - the char to use for padding
 *@return String - the padded string
 */
public static String padString(String text, int width, int align, char padChar)
    {
    if ( text == null || text.length() >= width )
        return text;

    StringBuilder sb = new StringBuilder();

    int spaces = width - text.length();

    if ( align == SwingConstants.RIGHT ||
         align == SwingConstants.TRAILING )
        {
        for ( int i = 0; i < spaces; ++i )
            sb.append(padChar);

        sb.append(text);
        }
    else if ( align == SwingConstants.LEFT ||
         align == SwingConstants.LEADING )
        {
        sb.append(text);

        for ( int i = 0; i < spaces; ++i )
            sb.append(padChar);
        }
    else if ( align == SwingConstants.CENTER )
        {
        int pre = spaces / 2;
        int post = spaces - pre;

        for ( int i = 0; i < pre; ++i )
            sb.append(padChar);

        sb.append(text);

        for ( int i = 0; i < post; ++i )
            sb.append(padChar);
        }

    return sb.toString();
    }


String Capitalisation

Ever had some text where the capitalisation is not what you want, or user input that needs correcting before output then these methods could just help you.

Sentence Capitalisation

Convert text to sentence capitalisation ie all lower case except for the first letter of the sentence. This method also handles the special case of English text which requires that the word 'I' is always capitalised, it does not handle proper nouns etc.
/**
 * Set the capitalisation of the text on a sentence basis.
* All text to set to lower case and then the first letter of each sentence * is set to upper case.
* A sentence is a group of characters following a '.' character. * This implementation handles the English special case of 'I' which is * always capitalised when used as a word in a sentence. * *@param text - the text to capitalise *@return String - the captialised text */ public String toSentenceCapitalisation(String text) { char[] c = text.toLowerCase().toCharArray(); // the first char must be upper case boolean caps = true; // find any new sentences for ( int i = 0; i < c.length; ++i ) { if ( c[i] == '.' ) caps = true; else if ( caps && Character.isLetter(c[i]) ) { c[i] = Character.toUpperCase(c[i]); caps = false; } else if ( caps && !Character.isWhitespace(c[i]) ) caps = false; } text = new String(c); // if english set word 'i' to 'I' if ( Locale.getDefault().getLanguage().equals("en") ) { StringBuilder sb = new StringBuilder(); BreakIterator boundary = BreakIterator.getWordInstance(); boundary.setText(text); int start = boundary.first(); int end = 0; while ( (end = boundary.next()) != BreakIterator.DONE ) { String word = text.substring(start, end); start = end; if ( word.equals("i") ) word = "I"; sb.append(word); } text = sb.toString(); } return text; }

Word Capitalisation

Convert text to word capitalisation ie every word is has an upper case first letter and the rest of the word is lower case.
/**
 * Set the capitalisation of the text on a word basis.
 * All text to set to lower case and then the first letter of each word is
 * set to upper case.
 * A word is a group of characters following a whitespace character.
 *
 *@param text - the text to capitalise
 *@param locale - the locale
 *@return String - the captialised text
 */
public String toWordCapitalisation(String text, Locale locale)
    {
    StringBuilder sb = new StringBuilder();

    BreakIterator boundary = BreakIterator.getWordInstance(locale);
    boundary.setText(text);

    int start = boundary.first();
    int end = 0;

    while ( (end = boundary.next()) != BreakIterator.DONE )
        {
        char[] c = text.substring(start, end).toLowerCase().toCharArray();

        start = end;

        // if the first char is a letter
        if ( Character.isLetter(c[0]) )
            c[0] = Character.toUpperCase(c[0]);

        sb.append(c);
        }

    return sb.toString();
    }
    


File Copy

Making a copy of a file sounds easy but handling all the exceptions and ensuring the file streams are always closed makes a simple task very complicated.

Here's a method that creates the destination files directory structure if it doesn't already exist and then copies the source file to the destination file. This method handles binary and text files.

/**
 * Copy a file.
 *
 *@param source - the file to be copied
 *@param dest   - the destination file
 */
public void copyFile(File source, File dest) throws FileNotFoundException, IOException
    {
    if ( source == null || dest == null )
        throw new NullPointerException("The source and/or destination files can not be null");

    // if there's no source
    if ( !source.exists() )
        {
        throw new FileNotFoundException("The source file "+source+" does not exist");
        }

    // if the destination file has a parent directory but it doesn't exist yet then create it
    File dir = dest.getParentFile();

    if ( dir != null && !dir.exists() )
        {
        boolean  ok = dir.mkdirs();

        if ( !ok )
            {
            throw new IOException("Make dirs for '"+dir+"' failed");
            }
        }

    BufferedInputStream bfis;
    BufferedOutputStream bfos;

    // open input and output streams
    bfis = new BufferedInputStream(new FileInputStream(source), BUFSIZE);

    try {
        bfos = new BufferedOutputStream(new FileOutputStream(dest), BUFSIZE);
        }
    catch(IOException e)
        {
        // if there is no output stream close the input stream and exit
        bfis.close();

        throw e;
        }

    // copy the file
    try {
        byte[] b = new byte[BUFSIZE];
        int nb;

        do
            {
            nb = bfis.read(b, 0, b.length);

            if ( nb > 0 )
                bfos.write(b, 0, nb);
            }
        while ( nb != -1 );

        bfos.flush();
        }
    finally
        {
        // close the file streams
        try {
            bfis.close();
            }
        finally
            {
            bfos.close();
            }
        }
    }
    


Alpha-Numeric Tokeniser

If you have a string with a combination and text and numbers with clearly defined boundaries (for example: "Number of Parts = 230") then you can easily tokenise the string using the java.util.scanner class. But if there are no obvious boundaries such as component part numbers which are often a combination of letters and numbers (for example: "AN3498-A") it is harder to tokenise the text.

This class provides a solution, handling embedded integer and optionally floating point numbers in locale specific formats.

/**
 * Copyright (c) 2004, 2010 Keang Ltd
 *
 * The AlphaNumericTokeniser class 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.
 *
 * The AlphaNumericTokeniser class 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 the AlphaNumericTokeniser class.  If not, see www.gnu.org/licenses.
 */
package uk.co.keang.utilities;

import java.text.DecimalFormatSymbols;
import java.util.Iterator;
import java.util.Locale;
import java.util.NoSuchElementException;

/**
 * A tokeniser class that breaks a string into alpha and numeric parts in a
 * locale dependent way.
 * <p>
 * The numeric parts are converted into either int or double types
 * wrapped in their corresponding object type.
* The alpha parts are returned as Strings. * <PRE> * Example: * <CODE> * AlphaNumericTokeniser ant = new AlphaNumericTokeniser(null); * * ant.setText("Mixed 123 alph 0.122 chars"); * * while ( ant.hasNext() ) * logger.info("Found '"+ant.next()+"'"); * </CODE> * </PRE> * <p> * The tokeniser can be configured to adjust the way it handles numeric values. * * *@author Keang Ltd *@version 1.0, 22/06/2004 */ public class AlphaNumericTokeniser implements Iterato<Object> { private Locale locale; private String text; private char[] textArray; private int index; private char decimal, separator; private char minus; private boolean minusEnabled; private boolean floatsEnabled; private boolean separatorsEnabled; private boolean floatsNoLeadingDigitEnabled; /** * Creates an empty tokeniser for this locale * This tokeniser handles negative numbers, floating point numbers and numeric separators. * */ public AlphaNumericTokeniser() { this(null, null); } /** * Creates an empty tokeniser for this locale * This tokeniser handles negative numbers, floating point numbers and numeric separators. * *@param loc - the locale or null to use the default locale */ public AlphaNumericTokeniser(Locale loc) { this(null, loc); } /** * Creates a tokeniser for the given string * This tokeniser handles negative numbers, floating point numbers and numeric separators. * *@param str - the string to tokenise */ public AlphaNumericTokeniser(String str) { this(str, null); } /** * Creates a tokeniser for the given string. * This tokeniser handles negative numbers, floating point numbers and numeric separators. * *@param str - the string to tokenise *@param loc - the locale or null to use the default locale */ public AlphaNumericTokeniser(String str, Locale loc) { this(str, loc, true, true, true, true); } /** * Creates a tokeniser for the given string * * @param str - the string to tokenise * @param loc the locale to use for define numeric symbols such as the decimal point etc, * or null for the default locale * @param minusEnabled true to allow negative numbers * @param floatsEnabled true to allow floating point numbers * @param floatsNoLeadingDigitEnabled true to allow floating point numbers without leading digits * @param separatorsEnabled true to allow numeric separators (ie in UK ',' is a thousands separator) */ public AlphaNumericTokeniser(String str, Locale loc, boolean minusEnabled, boolean floatsEnabled, boolean floatsNoLeadingDigitEnabled, boolean separatorsEnabled) { this.minusEnabled = minusEnabled; this.floatsEnabled = floatsEnabled; this.separatorsEnabled = separatorsEnabled; this.floatsNoLeadingDigitEnabled = floatsNoLeadingDigitEnabled; setLocale(loc); setText(str); } /** * Set the locale * *@param loc - the locale or null to use the default locale */ public void setLocale(Locale loc) { if ( loc == null ) this.locale = Locale.getDefault(); else this.locale = loc; DecimalFormatSymbols sym = new DecimalFormatSymbols(this.locale); decimal = sym.getDecimalSeparator(); separator = sym.getGroupingSeparator(); minus = sym.getMinusSign(); } /** * Gets the locale * *@return - the current locale */ public Locale getLocale() { return locale; } /** * Set the text this tokeniser is to tokenise * *@param str - the string to tokenise */ public void setText(String str) { text = str; // get the char array if there is one if ( text != null ) textArray = text.toCharArray(); // reset the index pointer index = 0; } /** * Returns true if negative numbers are handled. If false the locale dependant * minus sign is treated as an alpha character. * * @return true if negative number are allowed */ public boolean isMinusEnabled() { return minusEnabled; } /** * Sets whether or not to handle negative numbers. * * @param minusEnabled true to handle negative numbers */ public void setMinusEnabled(boolean minusEnabled) { this.minusEnabled = minusEnabled; } /** * Returns true if floating pints numbers are handled. If false the locale dependant * decimal character is treated as an alpha character. * * @return the floatsEnabled */ public boolean isFloatsEnabled() { return floatsEnabled; } /** * Sets whether or not to handle floating point numbers. * * @param floatsEnabled true to handle floating point numbers */ public void setFloatsEnabled(boolean floatsEnabled) { this.floatsEnabled = floatsEnabled; } /** * Returns true if floating pints numbers are handled. If false the locale dependant * decimal character is treated as an alpha character. * * @return the floatsNoLeadingDigitEnabled */ public boolean isFloatsNoLeadingDigitEnabled() { return floatsNoLeadingDigitEnabled; } /** * Sets whether or not to allow floating point numbers without any leading digits * ie where the first character is a decimal character. * * @param floatsNoLeadingDigitEnabled true to allow floating points numbers without any leading digits */ public void setFloatsNoLeadingDigitEnabled(boolean floatsNoLeadingDigitEnabled) { this.floatsNoLeadingDigitEnabled = floatsNoLeadingDigitEnabled; } /** * Returns true if floating point numbers are handled. If false the locale dependant * decimal character is treated as an alpha character. * * @return the separatorsEnabled */ public boolean isSeparatorsEnabled() { return separatorsEnabled; } /** * Sets whether or not to handle separators within a number. * * @param separatorsEnabled true to handle separators within a number */ public void setSeparatorsEnabled(boolean separatorsEnabled) { this.separatorsEnabled = separatorsEnabled; } /** * Checks to see if the char could be part of a numeric value. * This could be a digit or one of the numeric symbols for this locale * eg English would include '-', '.' and ','. * <p> * If the char is one of the * numeric symbols it can only be numeric if it's context is numeric * eg a minus sign must be followed by a digit; a decimal point must be * followed by a digit; a separator must be * proceeded by and followed by a digit * *@param c - the char[] containing the text *@param index - the index in the char array containing the current character *@param firstChar true if testing for the first character in the potential number *@return boolean - true if this is a digit or numeric symbol */ private final boolean isNumeric(char[] c, int index, boolean firstChar) { return ( Character.isDigit(c[index]) || isNumericSymbol(c, index, firstChar) ); } /** * Checks to see if the char could be part of an allowed numeric value. * This is one of the numeric symbols for this locale * eg English could include '-', '.' and ','. * <p> * If the char is one of the numeric symbols it can only be considered * a numeric symbol if it's context is numeric * eg a minus sign must be followed by a digit; a decimal point must be * followed by a digit; a separator must be proceeded by and followed by a digit * *@param c the char[] containing the text *@param index the index in the char array containing the current character *@param firstChar true if testing for the first character in the potential number *@return boolean - true if this is an allowed numeric symbol */ private final boolean isNumericSymbol(char[] c, int index, boolean firstChar) { // if there isn't a next char or it's not a digit then this char can't be a numeric symbol if ( c.length <= index + 1 || !Character.isDigit(c[index+1]) ) return false; // if this is the first char of the digit if ( firstChar ) { // if negative numbers are allowed and this is a minus sign if ( minusEnabled && c[index] == minus ) return true; // if floating point numbers with no leading digits are allowed and this is a decimal point if ( floatsNoLeadingDigitEnabled && floatsEnabled && c[index] == decimal ) return true; } else { // if separators are allowed and this is a separator and the proceeding char is a digit if ( separatorsEnabled && c[index] == separator && index > 0 && Character.isDigit(c[index-1]) ) return true; // if floating point numbers are allowed and this is a decimal point if ( floatsEnabled && c[index] == decimal ) return true; } return false; } /** * Gets the 'n' characters that make up a valid number * *@return the numeric section */ private String getNextNumericPart() { int nSeps = 0; // add the first char as this must be numeric StringBuilder sb = new StringBuilder(); sb.append(textArray[index]); // loop until the end of the string or a non numeric character is found for ( int i = index+1; i < textArray.length; i++ ) { if ( !isNumeric(textArray, i, false) ) { break; } // do not add separator chars to the returned string if ( textArray[i] == separator ) nSeps++; else sb.append(textArray[i]); } index += sb.length() + nSeps; return sb.toString(); } /** * Gets the 'n' characters that are non numeric * *@return the alpha section */ private String getNextAlphaPart() { // add the first char as this must be non numeric StringBuilder sb = new StringBuilder(); sb.append(textArray[index]); // loop until the end of the string or a numeric character is found for ( int i = index+1; i < textArray.length; i++ ) { if ( isNumeric(textArray, i, true) ) { break; } sb.append(textArray[i]); } index += sb.length(); return sb.toString(); } // ------------ methods from the Iterator interface -------------- /** * Checks to see if there are any more tokens in this string * *@return true if there are more tokens available */ public boolean hasNext() { if ( text == null ) return false; return index < textArray.length; } /** * Gets the next token * *@return the next token either a String, Double or an Integer *@exception NoSuchElementException - iteration has no more elements. */ public Object next() { // if the next section is numeric handle it here if ( isNumeric(textArray, index, true) ) { String n = getNextNumericPart(); // if this is a floating point number if ( n.indexOf(decimal) >= 0 ) { return new Double(n); } else { int val = Integer.parseInt(n); return Integer.valueOf(val); } } else { // else extract the alpha part String n = getNextAlphaPart(); return n; } } /** * This method is not supported by this iterator * *@exception UnsupportedOperationException - if the remove operation is not * supported by this Iterator. */ public void remove() { throw new UnsupportedOperationException(); } }


Alpha-Numeric Comparator

When sorting lists of text, such as file names, into alphbetical order problems can arise if the text includes numbers. For example if you have a series of files numbered from myFile1.txt to myFile17.txt then myFile2.txt to myFile9.txt would appear after myFile17.txt when, from a numerical point of view, they should be after myFile1.txt and before myFile10.txt.

This class provides a solution utilising the Alpha-Numeric Tokeniser class to handle embedded integer and optionally floating point numbers in locale specific formats.

Sorting of lists etc is achieved by creating an instance of this comparator and passing it to any sorting method that accepts a comparator for example java.util.Collections.sort(..) or for classes which maintain order of their elements, for example java.util.TreeMap, by passing it as a parameter to the class constructor.

/**
 * Copyright (c) 2006, 2010 Keang Ltd
 *
 * The AlphaNumericComparator class 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.
 *
 * The AlphaNumericComparator class 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 the AlphaNumericComparator class.  If not, see www.gnu.org/licenses.
 */
package uk.co.keang.utilities;

import java.util.Comparator;
import java.util.Locale;

/**
 * A comparator class for comparing strings with a mix of
 * alpha and numeric parts. The strings are split into there alpha and numeric
 * parts which are then compared part by part. In this way numeric parts are
 * handled as complete numbers giving proper ordering.
 *
 *@author Keang Ltd
 *@version 1.0, 28/06/2006
 */
public class AlphaNumericComparator implements Comparator<String>
{
private AlphaNumericTokeniser tok1;
private AlphaNumericTokeniser tok2;

/**
 * Create a Comparator for this locale that does not allow negative number, floating point
 * numbers or numeric separators.
 *
 */
public AlphaNumericComparator()
    {
    this(null, false, false, false, false);
    }

/**
 * Create a Comparator
 *
 * @param locale  the locale to use for define numeric symbols such as the decimal point etc,
 *                or null for the default locale
 * @param minusEnabled  true to allow negative numbers
 * @param floatsEnabled  true to allow floating point numbers
 * @param floatsNoLeadingDigitEnabled  true to allow floating point numbers without leading digits
 * @param separatorsEnabled  true to allow numeric separators (ie in UK ',' is a thousands separator)
 */
public AlphaNumericComparator(Locale locale, boolean minusEnabled, boolean floatsEnabled,
        boolean floatsNoLeadingDigitEnabled, boolean separatorsEnabled)
    {
    tok1 = new AlphaNumericTokeniser(null, locale, minusEnabled, floatsEnabled,
                                floatsNoLeadingDigitEnabled, separatorsEnabled);
    tok2 = new AlphaNumericTokeniser(null, locale, minusEnabled, floatsEnabled,
                                floatsNoLeadingDigitEnabled, separatorsEnabled);
    }

/**
 * Compares its two arguments for order. Returns a negative integer, zero, or a
 * positive integer as the first argument is less than, equal to, or greater
 * than the second.
 * <p>
 * The ordering by type is Integer, Double and then String.
 * The ordering within each type is: Numeric types are in numerical order, String types
 * are in alphabetical order.
 *
 *@param s1 - the first object to be compared.
 *@param s2 - the second object to be compared.
 *@return a negative integer, zero, or a positive integer as the first argument
 *        is less than, equal to, or greater than the second.
 *@exception ClassCastException - if the arguments' types prevent them from
 *                                being compared by this Comparator.
 */
public int compare(String s1, String s2)
    {
    // do a simple check for equality first
    if ( s1.equals(s2) )
        return 0;

    tok1.setText(s1);
    tok2.setText(s2);

    while ( tok1.hasNext() )
        {
        // if the second string doesn't have as many parts: o1 > o2
        if ( !tok2.hasNext() )
            return 1;

        Object obj1 = tok1.next();
        Object obj2 = tok2.next();

        if ( obj1 instanceof String )
            {
            if ( obj2 instanceof String )
                {
                // they're both strings so compare them
                int result = ((String)obj1).compareTo((String)obj2);

                if ( result != 0 )
                    return result;
                }
            else
                {
                // o1 has a string part here and o2 doesn't: o1 > 02
                return 1;
                }
            }
        else if ( obj1 instanceof Integer )
            {
            if ( obj2 instanceof Integer )
                {
                // they're both ints so compare them
                int i1 = ((Integer)obj1).intValue();
                int i2 = ((Integer)obj2).intValue();

                int result = i1 - i2;

                if ( result < 0 )
                    return -1;
                else if ( result > 0 )
                    return 1;
                }
            else
                {
                // o1 has an integer part here and o2 doesn't: o1 < 02
                return -1;
                }
            }
        else if ( obj1 instanceof Double )
            {
            if ( obj2 instanceof Double )
                {
                // they're both ints so compare them
                double i1 = ((Double)obj1).doubleValue();
                double i2 = ((Double)obj2).doubleValue();

                double result = i1 - i2;

                if ( result < 0 )
                    return -1;
                else if ( result > 0 )
                    return 1;
                }
            else if ( obj2 instanceof Integer )
                {
                // o1 has a double part here and o2 has an integer: o1 > 02
                return 1;
                }
            else
                {
                // o1 has a double part here and o2 has a string: o1 < 02
                return -1;
                }
            }
        else
            {
            throw new ClassCastException("Can't compare "+obj1+" to "+obj2);
            }
        }

    if ( tok2.hasNext() )
        return -1;

    return 0;
    }
}
 
Back to the Top