/* * $Id$ * * Copyright 2006, The jCoderZ.org 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 jCoderZ.org 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 REGENTS 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 REGENTS AND 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.jcoderz.commons.logging; import java.io.ObjectStreamException; import java.io.Serializable; import java.nio.CharBuffer; import java.text.FieldPosition; import java.text.Format; import java.text.ParsePosition; import java.util.Arrays; /** * This is used to format a String to a fixed length String. Strings, which are * are longer than the configured length, are split after the appropriate number * of chars; Strings which are shorter, are left padded with the configured * padding char. When parsing a String, the configured number of chars is read * from the source String and the padding char is left trimmed from the * resulting String. * */ public class FixLengthFormat extends Format { /** * The string is left padded with the padding char to get the full sized * string. */ public static final Padding LEFT_PADDING = new Padding(); /** * The string is right padded with the padding char to get the full sized * string. */ public static final Padding RIGHT_PADDING = new Padding(); /** * The string is right padded with the padding char to get the full sized * string or cut on the left side if it dues not fit the length. */ public static final Padding LEFT_CUT_RIGHT_PADDING = new Padding(); private static final Padding[] PADDINGS = {LEFT_PADDING, RIGHT_PADDING, LEFT_CUT_RIGHT_PADDING}; private static final long serialVersionUID = 3256720688978278194L; private static final char DEFAULT_PADDING_CHAR = ' '; private final char[] mAllPadding; private final Padding mType; /** * This class implements a typesafe enumeration for the padding type. * */ private static final class Padding implements Comparable, Serializable { private static final long serialVersionUID = 3258410642577831730L; private static int sOrdinal = 0; private final int mOrdinal; private Padding () { mOrdinal = sOrdinal++; } private Object readResolve () throws ObjectStreamException { return PADDINGS[mOrdinal]; } /** {@inheritDoc} */ public int compareTo (Object o) { return mOrdinal - ((Padding) o).mOrdinal; } } /** * Generates an instance of this with the default padding character, which * is a space. * * @param fixedLength all formatted strings will have this length, either * they are cut or padded with spaces. * @param type the type of padding, either {@linkplain #LEFT_PADDING} or * {@linkplain #RIGHT_PADDING} * * @throws IllegalArgumentException if <code>fixedLength <= 0</code> */ public FixLengthFormat (final int fixedLength, final Padding type) { this(fixedLength, type, DEFAULT_PADDING_CHAR); } /** * Generates an instance of this with <code>paddingChar</code> as padding * character. * * @param fixedLength all formatted strings will have this length, either * they are cut or padded with <code>paddingChar</code> * @param type the type of padding, either {@linkplain #LEFT_PADDING} or * {@linkplain #RIGHT_PADDING} * @param paddingChar the character to use for padding. * * @throws IllegalArgumentException if <code>fixedLength <= 0</code> * */ public FixLengthFormat ( final int fixedLength, final Padding type, final char paddingChar) { super(); if (fixedLength <= 0) { throw new IllegalArgumentException("Fixed length must be >= 0, but is " + fixedLength); } mType = type; mAllPadding = new char[fixedLength]; Arrays.fill(mAllPadding, paddingChar); } /** * Parses the source string for a padded string from the position given by * <code>pos</code>. It reads the configured number of chars and trims * all chars equal to the padding character, either from left or from right, * according to the configured type of this. * If the given source string is not long enough, the index of * <code>pos</code> is not modified, but the error index is set to its * current index and null is returned. * * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) * * @param source the string to parse * @param pos the parse position where to start parsing within * <code>source</code>. This will be updated after parsing. * * @return null if not enough chars to read from <code>source</code>; * else parsed String, one-side trimmed from padding char. */ public Object parseObject (String source, ParsePosition pos) { String rc = null; if (pos.getIndex() + mAllPadding.length > source.length()) { pos.setErrorIndex(pos.getIndex()); } else { final CharBuffer paddedString = CharBuffer.wrap(source, pos.getIndex(), pos.getIndex() + mAllPadding.length); if (paddedString.length() != mAllPadding.length) { pos.setErrorIndex(pos.getIndex()); } else { rc = parse(paddedString, pos).toString(); } } return rc; } /** * Formats a string to be of a certain length. If the string is longer than * the configured length; it is cut and the first part being appended to the * StringBuffer, if it is shorter, this call pads it with the configured * char. * * @param obj the String to be formatted, must not be null. * @param toAppendTo the StringBuffer to which to append the formatted String * @param pos field position * * @return StringBuffer where the formatted object has been appended to. * * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition) */ public StringBuffer format (Object obj, StringBuffer toAppendTo, FieldPosition pos) { if (obj == null) { throw new NullPointerException( "Supplied object to format must not be null"); } else if (! (obj instanceof String)) { throw new IllegalArgumentException("Supplied object to be formatted " + "must be a String but is " + obj.getClass().getName() + ": " + obj); } else { pos.setBeginIndex(0); pos.setEndIndex(0); final String toBeFormatted = (String) obj; final int len = toBeFormatted.length(); if (len > mAllPadding.length && mType == LEFT_CUT_RIGHT_PADDING) { toAppendTo.append(toBeFormatted.substring( len - mAllPadding.length)); } else if (len > mAllPadding.length) { toAppendTo.append(toBeFormatted.substring(0, mAllPadding.length)); } else if (len < mAllPadding.length) { if (mType == LEFT_PADDING) { toAppendTo.append(mAllPadding, 0, mAllPadding.length - len); toAppendTo.append(toBeFormatted); } else { toAppendTo.append(toBeFormatted); toAppendTo.append(mAllPadding, 0, mAllPadding.length - len); } } else { toAppendTo.append(toBeFormatted); } } return toAppendTo; } private CharSequence parse ( final CharBuffer paddedString, final ParsePosition pos) { CharSequence rc = null; pos.setIndex(pos.getIndex() + mAllPadding.length); final char pad = mAllPadding[0]; if (mType == LEFT_PADDING) { int i = 0; while ((i < mAllPadding.length) && (paddedString.charAt(i) == pad)) { ++i; } rc = paddedString.subSequence(i, mAllPadding.length); } else { int i = mAllPadding.length - 1; while ((i >= 0) && (paddedString.charAt(i) == pad)) { --i; } if (i < 0) { rc = ""; } else { rc = paddedString.subSequence(0, i + 1); } } return rc; } }