/* * $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.nio.CharBuffer; import java.text.FieldPosition; import java.text.Format; import java.text.ParsePosition; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * This format is used for formatting a collection. The begin/end pair can be * set as well as the separator for the collection items and the formatter for * the items. * */ public class CollectionFormat extends Format { private static final long serialVersionUID = 3258126947170400564L; /** The separator character between collection items. */ private static final String SEPARATOR = ","; private static final String LIST_BEGIN = "["; private static final String LIST_END = "]"; private final String mListBegin; private final String mListEnd; private final Format mListElementFormat; private final String mSeparator; private final int mLengthListBegin; private final int mLengthListEnd; private final int mLengthSeparator; /** * Creates a new instance of this and sets default values for the Strings * denoting the begin or end of the collection as well as the separator for * the collection items. * * @param elementFormat the Format to use for formatting each collection * element. */ public CollectionFormat (final Format elementFormat) { this(elementFormat, LIST_BEGIN, LIST_END, SEPARATOR); } /** * Creates a new instance of this and configures values for the Strings * denoting the begin or end of the collection as well as the separator for * the collection items. If teh begin and empty markers are both empty or * null, then this might fail to parse empty lists. * * @param elementFormat the Format to use for formatting each collection * element. Must not be null. * @param begin The string to set as start of the collection, might be null, * then there will be no marker for the start of the collection. * @param end The string to set as end of the collection, might be null, * then there will be no marker for the end of the collection. * @param separator The string to set as separator between collection * elements, might be null, then ther will be no separator between the * elements. */ public CollectionFormat ( final Format elementFormat, final String begin, final String end, final String separator) { super(); mListBegin = (begin != null) ? begin : ""; mListEnd = (end != null) ? end : ""; mListElementFormat = elementFormat; mSeparator = (separator != null) ? separator : ""; mLengthListBegin = mListBegin.length(); mLengthListEnd = mListEnd.length(); mLengthSeparator = mSeparator.length(); } /** * @see java.text.Format#parseObject(java.lang.String, java.text.ParsePosition) * * Parses the supplied string for creating a list of object. If an error * occurs the position is unchanged, the error position is set to the * position where it occurred and null is returrned. * This expects the configured list begin sequence to start at the current * position, the list item to be delimited with the configured delimiter, * the latter not being parsed by the list item parser, and the list being * terminated by the list end sequence. If the list end sequence is set * to null or empty string, then empty lists are not detected. * * @param source The string to parse. * @param pos The current position within <code>source</code> * * @return null if an error occurs, the collection being parsed from the * source string else. */ public Object parseObject (String source, ParsePosition pos) { List rc = null; final int len = source.length(); boolean errorOccurred = false; final CharBuffer sourceBuffer = CharBuffer.wrap(source, pos.getIndex(), source.length()); final int currentPos = pos.getIndex(); // the string has to start with the configured list begin char, if it is // set if (beginOfList(sourceBuffer, pos)) { rc = new ArrayList(); if (! (mLengthListEnd != 0 && endOfList(sourceBuffer, pos))) { errorOccurred = parseListElements( sourceBuffer, source, pos, len, rc); if (! (errorOccurred || endOfList(sourceBuffer, pos))) { pos.setErrorIndex(pos.getIndex()); errorOccurred = true; } } if (errorOccurred) { rc.clear(); rc = null; pos.setIndex(currentPos); } } else { pos.setErrorIndex(pos.getIndex()); } return rc; } private boolean parseListElements ( final CharBuffer sourceBuffer, final String source, final ParsePosition pos, final int len, final List elements) { boolean rc = false; // not an empty list do { final Object obj = mListElementFormat.parseObject(source, pos); if (obj == null) { rc = true; } else { elements.add(obj); } } while (separator(sourceBuffer, pos) && (pos.getIndex() < len) && ! rc); return rc; } /** * Formats a collection of strings. * * @see java.text.Format#format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition) * * @param obj the Object to format. Must be a Collection * @param toAppendTo the StringBuffer where to append to the formatted * Collection. * @param pos the field position. * * @return StringBuffer where the formatted collection has been appended to. * * @throws IllegalArgumentException if <code>obj</code> is not a Collection. */ public StringBuffer format ( Object obj, StringBuffer toAppendTo, FieldPosition pos) { if (! (obj instanceof Collection)) { throw new IllegalArgumentException("The object to format must be " + " a java.util.Collection, but is: " + obj); } pos.setBeginIndex(0); pos.setEndIndex(0); final Collection elements = (Collection) obj; final StringBuffer tempBuffer = new StringBuffer(mListBegin); boolean first = true; for (final Iterator iter = elements.iterator(); iter.hasNext(); ) { if (first) { first = false; } else { tempBuffer.append(mSeparator); } mListElementFormat.format(iter.next(), tempBuffer, pos); } tempBuffer.append(mListEnd); toAppendTo.append(tempBuffer); return toAppendTo; } /** * Sets the buffers position to the current parse position and checks whether * the buffer contains an begin-of-list at the current position and if so * updates its position and the index of the supplied parse position. * * @param buffer The buffer storing the line to parse. * @param pos The current parse postion. * * @return true if at end of list or no end of list specifier set; * false, else. */ private boolean beginOfList ( final CharBuffer buffer, final ParsePosition pos) { boolean rc = false; buffer.position(pos.getIndex()); if (mLengthListBegin == 0) { rc = true; } else { if (buffer.remaining() >= mLengthListBegin) { boolean allOk = true; for (int i = 0; i < mLengthListBegin && allOk; ++i) { if (buffer.charAt(i) != mListBegin.charAt(i)) { allOk = false; } } rc = allOk; if (rc) { pos.setIndex(pos.getIndex() + mLengthListBegin); buffer.position(pos.getIndex()); } } } return rc; } /** * Sets the buffers position to the current parse position and checks whether * the buffer contains an end-of-list at the current position and if so * updates its position and the index of the supplied parse position. * * @param buffer The buffer storing the line to parse. * @param pos The current parse postion. * * @return true if at end of list or no end of list specifier set; * false, else. */ private boolean endOfList ( final CharBuffer buffer, final ParsePosition pos) { boolean rc = false; buffer.position(pos.getIndex()); if (mLengthListEnd == 0) { rc = true; } else { if (buffer.remaining() >= mLengthListEnd) { boolean allOk = true; for (int i = 0; i < mLengthListEnd && allOk; ++i) { if (buffer.charAt(i) != mListEnd.charAt(i)) { allOk = false; } } rc = allOk; if (rc) { pos.setIndex(pos.getIndex() + mLengthListEnd); buffer.position(pos.getIndex()); } } } return rc; } /** * Sets the buffers position to the current parse position and checks whether * the buffer contains a separator at the current position and if so updates * its position and the index of the supplied parse position. * * @param buffer The buffer storing the line to parse. * @param pos The current parse postion. * * @return true if a separator exists at the current position; false, else. */ private boolean separator (final CharBuffer buffer, final ParsePosition pos) { boolean rc = false; boolean allOk = true; buffer.position(pos.getIndex()); if (buffer.remaining() >= mLengthSeparator) { for (int i = 0; i < mLengthSeparator && allOk; ++i) { if (buffer.charAt(i) != mSeparator.charAt(i)) { allOk = false; } } rc = allOk; if (rc) { pos.setIndex(pos.getIndex() + mLengthSeparator); buffer.position(pos.getIndex()); } } return rc; } }