Fixed JavaDoc errors to enable Java 8 build.

This commit is contained in:
Harald Kuhr
2019-08-10 00:41:36 +02:00
parent 7d2c692663
commit 9e23413456
168 changed files with 34586 additions and 34396 deletions

View File

@@ -1,204 +1,203 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.lang;
import java.util.Date;
import java.util.TimeZone;
/**
* A utility class with useful date manipulation methods and constants.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/DateUtil.java#1 $
*/
public final class DateUtil {
/** One second: 1000 milliseconds. */
public static final long SECOND = 1000l;
/** One minute: 60 seconds (60 000 milliseconds). */
public static final long MINUTE = 60l * SECOND;
/**
* One hour: 60 minutes (3 600 000 milliseconds).
* 60 minutes = 3 600 seconds = 3 600 000 milliseconds
*/
public static final long HOUR = 60l * MINUTE;
/**
* One day: 24 hours (86 400 000 milliseconds).
* 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
*/
public static final long DAY = 24l * HOUR;
/**
* One calendar year: 365.2425 days (31556952000 milliseconds).
* 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
* = 31556952000 milliseconds.
*/
public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
private DateUtil() {
}
/**
* Returns the time between the given start time and now (as defined by
* {@link System#currentTimeMillis()}).
*
* @param pStart the start time
*
* @return the time between the given start time and now.
*/
public static long delta(long pStart) {
return System.currentTimeMillis() - pStart;
}
/**
* Returns the time between the given start time and now (as defined by
* {@link System#currentTimeMillis()}).
*
* @param pStart the start time
*
* @return the time between the given start time and now.
*/
public static long delta(Date pStart) {
return System.currentTimeMillis() - pStart.getTime();
}
/**
* Gets the current time, rounded down to the closest second.
* Equivalent to invoking
* {@code roundToSecond(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest second.
*/
public static long currentTimeSecond() {
return roundToSecond(System.currentTimeMillis());
}
/**
* Gets the current time, rounded down to the closest minute.
* Equivalent to invoking
* {@code roundToMinute(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest minute.
*/
public static long currentTimeMinute() {
return roundToMinute(System.currentTimeMillis());
}
/**
* Gets the current time, rounded down to the closest hour.
* Equivalent to invoking
* {@code roundToHour(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest hour.
*/
public static long currentTimeHour() {
return roundToHour(System.currentTimeMillis());
}
/**
* Gets the current time, rounded down to the closest day.
* Equivalent to invoking
* {@code roundToDay(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest day.
*/
public static long currentTimeDay() {
return roundToDay(System.currentTimeMillis());
}
/**
* Rounds the given time down to the closest second.
*
* @param pTime time
* @return the time rounded to the closest second.
*/
public static long roundToSecond(final long pTime) {
return (pTime / SECOND) * SECOND;
}
/**
* Rounds the given time down to the closest minute.
*
* @param pTime time
* @return the time rounded to the closest minute.
*/
public static long roundToMinute(final long pTime) {
return (pTime / MINUTE) * MINUTE;
}
/**
* Rounds the given time down to the closest hour, using the default timezone.
*
* @param pTime time
* @return the time rounded to the closest hour.
*/
public static long roundToHour(final long pTime) {
return roundToHour(pTime, TimeZone.getDefault());
}
/**
* Rounds the given time down to the closest hour, using the given timezone.
*
* @param pTime time
* @param pTimeZone the timezone to use when rounding
* @return the time rounded to the closest hour.
*/
public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
int offset = pTimeZone.getOffset(pTime);
return ((pTime / HOUR) * HOUR) - offset;
}
/**
* Rounds the given time down to the closest day, using the default timezone.
*
* @param pTime time
* @return the time rounded to the closest day.
*/
public static long roundToDay(final long pTime) {
return roundToDay(pTime, TimeZone.getDefault());
}
/**
* Rounds the given time down to the closest day, using the given timezone.
*
* @param pTime time
* @param pTimeZone the timezone to use when rounding
* @return the time rounded to the closest day.
*/
public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
int offset = pTimeZone.getOffset(pTime);
return (((pTime + offset) / DAY) * DAY) - offset;
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.lang;
import java.util.Date;
import java.util.TimeZone;
/**
* A utility class with useful date manipulation methods and constants.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/DateUtil.java#1 $
*/
public final class DateUtil {
/** One second: 1000 milliseconds. */
public static final long SECOND = 1000l;
/** One minute: 60 seconds (60 000 milliseconds). */
public static final long MINUTE = 60l * SECOND;
/**
* One hour: 60 minutes (3 600 000 milliseconds).
* 60 minutes = 3 600 seconds = 3 600 000 milliseconds
*/
public static final long HOUR = 60l * MINUTE;
/**
* One day: 24 hours (86 400 000 milliseconds).
* 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
*/
public static final long DAY = 24l * HOUR;
/**
* One calendar year: 365.2425 days (31556952000 milliseconds).
* 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
* = 31556952000 milliseconds.
*/
public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
private DateUtil() {
}
/**
* Returns the time between the given start time and now (as defined by
* {@link System#currentTimeMillis()}).
*
* @param pStart the start time
*
* @return the time between the given start time and now.
*/
public static long delta(long pStart) {
return System.currentTimeMillis() - pStart;
}
/**
* Returns the time between the given start time and now (as defined by
* {@link System#currentTimeMillis()}).
*
* @param pStart the start time
*
* @return the time between the given start time and now.
*/
public static long delta(Date pStart) {
return System.currentTimeMillis() - pStart.getTime();
}
/**
* Gets the current time, rounded down to the closest second.
* Equivalent to invoking
* {@code roundToSecond(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest second.
*/
public static long currentTimeSecond() {
return roundToSecond(System.currentTimeMillis());
}
/**
* Gets the current time, rounded down to the closest minute.
* Equivalent to invoking
* {@code roundToMinute(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest minute.
*/
public static long currentTimeMinute() {
return roundToMinute(System.currentTimeMillis());
}
/**
* Gets the current time, rounded down to the closest hour.
* Equivalent to invoking
* {@code roundToHour(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest hour.
*/
public static long currentTimeHour() {
return roundToHour(System.currentTimeMillis());
}
/**
* Gets the current time, rounded down to the closest day.
* Equivalent to invoking
* {@code roundToDay(System.currentTimeMillis())}.
*
* @return the current time, rounded to the closest day.
*/
public static long currentTimeDay() {
return roundToDay(System.currentTimeMillis());
}
/**
* Rounds the given time down to the closest second.
*
* @param pTime time
* @return the time rounded to the closest second.
*/
public static long roundToSecond(final long pTime) {
return (pTime / SECOND) * SECOND;
}
/**
* Rounds the given time down to the closest minute.
*
* @param pTime time
* @return the time rounded to the closest minute.
*/
public static long roundToMinute(final long pTime) {
return (pTime / MINUTE) * MINUTE;
}
/**
* Rounds the given time down to the closest hour, using the default timezone.
*
* @param pTime time
* @return the time rounded to the closest hour.
*/
public static long roundToHour(final long pTime) {
return roundToHour(pTime, TimeZone.getDefault());
}
/**
* Rounds the given time down to the closest hour, using the given timezone.
*
* @param pTime time
* @param pTimeZone the timezone to use when rounding
* @return the time rounded to the closest hour.
*/
public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
int offset = pTimeZone.getOffset(pTime);
return ((pTime / HOUR) * HOUR) - offset;
}
/**
* Rounds the given time down to the closest day, using the default timezone.
*
* @param pTime time
* @return the time rounded to the closest day.
*/
public static long roundToDay(final long pTime) {
return roundToDay(pTime, TimeZone.getDefault());
}
/**
* Rounds the given time down to the closest day, using the given timezone.
*
* @param pTime time
* @param pTimeZone the timezone to use when rounding
* @return the time rounded to the closest day.
*/
public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
int offset = pTimeZone.getOffset(pTime);
return (((pTime + offset) / DAY) * DAY) - offset;
}
}

View File

@@ -199,9 +199,10 @@ public final class Platform {
/**
* Enumeration of common System {@code Architecture}s.
* <p/>
* <p>
* For {@link #Unknown unknown architectures}, {@code toString()} will return
* the the same value as {@code System.getProperty("os.arch")}.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $
@@ -228,9 +229,10 @@ public final class Platform {
/**
* Enumeration of common {@code OperatingSystem}s.
* <p/>
* <p>
* For {@link #Unknown unknown operating systems}, {@code getName()} will return
* the the same value as {@code System.getProperty("os.name")}.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $

View File

@@ -1,139 +1,140 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.lang;
/**
* Util class for various reflection-based operations.
* <p/>
* <em>NOTE: This class is not considered part of the public API and may be
* changed without notice</em>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java#1 $
*/
public final class ReflectUtil {
/** Don't allow instances */
private ReflectUtil() {}
/**
* Returns the primitive type for the given wrapper type.
*
* @param pType the wrapper type
*
* @return the primitive type
*
* @throws IllegalArgumentException if {@code pType} is not a primitive
* wrapper
*/
public static Class unwrapType(Class pType) {
if (pType == Boolean.class) {
return Boolean.TYPE;
}
else if (pType == Byte.class) {
return Byte.TYPE;
}
else if (pType == Character.class) {
return Character.TYPE;
}
else if (pType == Double.class) {
return Double.TYPE;
}
else if (pType == Float.class) {
return Float.TYPE;
}
else if (pType == Integer.class) {
return Integer.TYPE;
}
else if (pType == Long.class) {
return Long.TYPE;
}
else if (pType == Short.class) {
return Short.TYPE;
}
throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
}
/**
* Returns the wrapper type for the given primitive type.
*
* @param pType the primitive tpye
*
* @return the wrapper type
*
* @throws IllegalArgumentException if {@code pType} is not a primitive
* type
*/
public static Class wrapType(Class pType) {
if (pType == Boolean.TYPE) {
return Boolean.class;
}
else if (pType == Byte.TYPE) {
return Byte.class;
}
else if (pType == Character.TYPE) {
return Character.class;
}
else if (pType == Double.TYPE) {
return Double.class;
}
else if (pType == Float.TYPE) {
return Float.class;
}
else if (pType == Integer.TYPE) {
return Integer.class;
}
else if (pType == Long.TYPE) {
return Long.class;
}
else if (pType == Short.TYPE) {
return Short.class;
}
throw new IllegalArgumentException("Not a primitive type: " + pType);
}
/**
* Returns {@code true} if the given type is a primitive wrapper.
*
* @param pType
*
* @return {@code true} if the given type is a primitive wrapper, otherwise
* {@code false}
*/
public static boolean isPrimitiveWrapper(Class pType) {
return pType == Boolean.class || pType == Byte.class
|| pType == Character.class || pType == Double.class
|| pType == Float.class || pType == Integer.class
|| pType == Long.class || pType == Short.class;
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.lang;
/**
* Util class for various reflection-based operations.
* <p>
* <em>NOTE: This class is not considered part of the public API and may be
* changed without notice</em>
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/ReflectUtil.java#1 $
*/
public final class ReflectUtil {
/** Don't allow instances */
private ReflectUtil() {}
/**
* Returns the primitive type for the given wrapper type.
*
* @param pType the wrapper type
*
* @return the primitive type
*
* @throws IllegalArgumentException if {@code pType} is not a primitive
* wrapper
*/
public static Class unwrapType(Class pType) {
if (pType == Boolean.class) {
return Boolean.TYPE;
}
else if (pType == Byte.class) {
return Byte.TYPE;
}
else if (pType == Character.class) {
return Character.TYPE;
}
else if (pType == Double.class) {
return Double.TYPE;
}
else if (pType == Float.class) {
return Float.TYPE;
}
else if (pType == Integer.class) {
return Integer.TYPE;
}
else if (pType == Long.class) {
return Long.TYPE;
}
else if (pType == Short.class) {
return Short.TYPE;
}
throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
}
/**
* Returns the wrapper type for the given primitive type.
*
* @param pType the primitive tpye
*
* @return the wrapper type
*
* @throws IllegalArgumentException if {@code pType} is not a primitive
* type
*/
public static Class wrapType(Class pType) {
if (pType == Boolean.TYPE) {
return Boolean.class;
}
else if (pType == Byte.TYPE) {
return Byte.class;
}
else if (pType == Character.TYPE) {
return Character.class;
}
else if (pType == Double.TYPE) {
return Double.class;
}
else if (pType == Float.TYPE) {
return Float.class;
}
else if (pType == Integer.TYPE) {
return Integer.class;
}
else if (pType == Long.TYPE) {
return Long.class;
}
else if (pType == Short.TYPE) {
return Short.class;
}
throw new IllegalArgumentException("Not a primitive type: " + pType);
}
/**
* Returns {@code true} if the given type is a primitive wrapper.
*
* @param pType
*
* @return {@code true} if the given type is a primitive wrapper, otherwise
* {@code false}
*/
public static boolean isPrimitiveWrapper(Class pType) {
return pType == Boolean.class || pType == Byte.class
|| pType == Character.class || pType == Double.class
|| pType == Float.class || pType == Integer.class
|| pType == Long.class || pType == Short.class;
}
}

View File

@@ -36,10 +36,11 @@ import java.util.Map;
/**
* Kind of like {@code org.apache.commons.lang.Validate}. Just smarter. ;-)
* <p/>
* <p>
* Uses type parameterized return values, thus making it possible to check
* constructor arguments before
* they are passed on to {@code super} or {@code this} type constructors.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $

View File

@@ -1,402 +1,404 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* AbstractDecoratedMap
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java#2 $
*/
// TODO: The generics in this class looks suspicious..
abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable {
protected Map<K, Entry<K, V>> entries;
protected transient volatile int modCount;
private transient volatile Set<Entry<K, V>> entrySet = null;
private transient volatile Set<K> keySet = null;
private transient volatile Collection<V> values = null;
/**
* Creates a {@code Map} backed by a {@code HashMap}.
*/
public AbstractDecoratedMap() {
this(new HashMap<K, Entry<K, V>>(), null);
}
/**
* Creates a {@code Map} backed by a {@code HashMap}, containing all
* key/value mappings from the given {@code Map}.
* <p/>
* <small>This is constructor is here to comply with the reccomendations for
* "standard" constructors in the {@code Map} interface.</small>
*
* @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*/
public AbstractDecoratedMap(Map<? extends K, ? extends V> pContents) {
this(new HashMap<K, Entry<K, V>>(), pContents);
}
/**
* Creates a {@code Map} backed by the given backing-{@code Map},
* containing all key/value mappings from the given contents-{@code Map}.
* <p/>
* NOTE: The backing map is structuraly cahnged, and it should NOT be
* accessed directly, after the wrapped map is created.
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*
* @throws IllegalArgumentException if {@code pBacking} is {@code null}
* or if {@code pBacking} differs from {@code pContent} and is not empty.
*/
public AbstractDecoratedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
if (pBacking == null) {
throw new IllegalArgumentException("backing == null");
}
Entry<? extends K, ? extends V>[] entries = null;
if (pBacking == pContents) {
// NOTE: Special treatment to avoid ClassCastExceptions
Set<? extends Entry<? extends K, ? extends V>> es = pContents.entrySet();
//noinspection unchecked
entries = new Entry[es.size()];
entries = es.toArray(entries);
pContents = null;
pBacking.clear();
}
else if (!pBacking.isEmpty()) {
throw new IllegalArgumentException("backing must be empty");
}
this.entries = pBacking;
init();
if (pContents != null) {
putAll(pContents);
}
else if (entries != null) {
// Reinsert entries, this time wrapped
for (Entry<? extends K, ? extends V> entry : entries) {
put(entry.getKey(), entry.getValue());
}
}
}
/**
* Default implementation, does nothing.
*/
protected void init() {
}
public int size() {
return entries.size();
}
public void clear() {
entries.clear();
modCount++;
init();
}
public boolean isEmpty() {
return entries.isEmpty();
}
public boolean containsKey(Object pKey) {
return entries.containsKey(pKey);
}
/**
* Returns {@code true} if this map maps one or more keys to the
* specified pValue. More formally, returns {@code true} if and only if
* this map contains at least one mapping to a pValue {@code v} such that
* {@code (pValue==null ? v==null : pValue.equals(v))}.
* <p/>
* This implementation requires time linear in the map size for this
* operation.
*
* @param pValue pValue whose presence in this map is to be tested.
* @return {@code true} if this map maps one or more keys to the
* specified pValue.
*/
public boolean containsValue(Object pValue) {
for (V value : values()) {
if (value == pValue || (value != null && value.equals(pValue))) {
return true;
}
}
return false;
}
public Collection<V> values() {
Collection<V> values = this.values;
return values != null ? values : (this.values = new Values());
}
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
public Set<K> keySet() {
Set<K> ks = keySet;
return ks != null ? ks : (keySet = new KeySet());
}
/**
* Returns a shallow copy of this {@code AbstractMap} instance: the keys
* and values themselves are not cloned.
*
* @return a shallow copy of this map.
*/
protected Object clone() throws CloneNotSupportedException {
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
map.values = null;
map.entrySet = null;
map.keySet = null;
// TODO: Implement: Need to clone the backing map...
return map;
}
// Subclass overrides these to alter behavior of views' iterator() method
protected abstract Iterator<K> newKeyIterator();
protected abstract Iterator<V> newValueIterator();
protected abstract Iterator<Entry<K, V>> newEntryIterator();
// TODO: Implement these (get/put/remove)?
public abstract V get(Object pKey);
public abstract V remove(Object pKey);
public abstract V put(K pKey, V pValue);
/*protected*/ Entry<K, V> createEntry(K pKey, V pValue) {
return new BasicEntry<K, V>(pKey, pValue);
}
/*protected*/ Entry<K, V> getEntry(K pKey) {
return entries.get(pKey);
}
/**
* Removes the given entry from the Map.
*
* @param pEntry the entry to be removed
*
* @return the removed entry, or {@code null} if nothing was removed.
*/
protected Entry<K, V> removeEntry(Entry<K, V> pEntry) {
if (pEntry == null) {
return null;
}
// Find candidate entry for this key
Entry<K, V> candidate = getEntry(pEntry.getKey());
if (candidate == pEntry || (candidate != null && candidate.equals(pEntry))) {
// Remove
remove(pEntry.getKey());
return pEntry;
}
return null;
}
protected class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
protected class EntrySet extends AbstractSet<Entry<K, V>> {
public Iterator<Entry<K, V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Entry))
return false;
Entry e = (Entry) o;
//noinspection SuspiciousMethodCalls
Entry<K, V> candidate = entries.get(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
if (!(o instanceof Entry)) {
return false;
}
/*
// NOTE: Extra cautions is taken, to only remove the entry if it
// equals the entry in the map
Object key = ((Entry) o).getKey();
Entry entry = (Entry) entries.get(key);
// Same entry?
if (entry != null && entry.equals(o)) {
return AbstractWrappedMap.this.remove(key) != null;
}
return false;
*/
//noinspection unchecked
return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
protected class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return AbstractDecoratedMap.this.remove(o) != null;
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
/**
* A simple Map.Entry implementaton.
*/
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
K mKey;
V mValue;
BasicEntry(K pKey, V pValue) {
mKey = pKey;
mValue = pValue;
}
/**
* Default implementation does nothing.
*
* @param pMap the map that is accessed
*/
protected void recordAccess(Map<K, V> pMap) {
}
/**
* Default implementation does nothing.
* @param pMap the map that is removed from
*/
protected void recordRemoval(Map<K, V> pMap) {
}
public V getValue() {
return mValue;
}
public V setValue(V pValue) {
V oldValue = mValue;
mValue = pValue;
return oldValue;
}
public K getKey() {
return mKey;
}
public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) {
return false;
}
Map.Entry entry = (Map.Entry) pOther;
Object k1 = mKey;
Object k2 = entry.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = mValue;
Object v2 = entry.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
return true;
}
}
return false;
}
public int hashCode() {
return (mKey == null ? 0 : mKey.hashCode()) ^
(mValue == null ? 0 : mValue.hashCode());
}
public String toString() {
return getKey() + "=" + getValue();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* AbstractDecoratedMap
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java#2 $
*/
// TODO: The generics in this class looks suspicious..
abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable {
protected Map<K, Entry<K, V>> entries;
protected transient volatile int modCount;
private transient volatile Set<Entry<K, V>> entrySet = null;
private transient volatile Set<K> keySet = null;
private transient volatile Collection<V> values = null;
/**
* Creates a {@code Map} backed by a {@code HashMap}.
*/
public AbstractDecoratedMap() {
this(new HashMap<K, Entry<K, V>>(), null);
}
/**
* Creates a {@code Map} backed by a {@code HashMap}, containing all
* key/value mappings from the given {@code Map}.
* <p>
* <small>This is constructor is here to comply with the reccomendations for
* "standard" constructors in the {@code Map} interface.</small>
* </p>
*
* @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*/
public AbstractDecoratedMap(Map<? extends K, ? extends V> pContents) {
this(new HashMap<K, Entry<K, V>>(), pContents);
}
/**
* Creates a {@code Map} backed by the given backing-{@code Map},
* containing all key/value mappings from the given contents-{@code Map}.
* <p>
* NOTE: The backing map is structuraly cahnged, and it should NOT be
* accessed directly, after the wrapped map is created.
* </p>
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*
* @throws IllegalArgumentException if {@code pBacking} is {@code null}
* or if {@code pBacking} differs from {@code pContent} and is not empty.
*/
public AbstractDecoratedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
if (pBacking == null) {
throw new IllegalArgumentException("backing == null");
}
Entry<? extends K, ? extends V>[] entries = null;
if (pBacking == pContents) {
// NOTE: Special treatment to avoid ClassCastExceptions
Set<? extends Entry<? extends K, ? extends V>> es = pContents.entrySet();
//noinspection unchecked
entries = new Entry[es.size()];
entries = es.toArray(entries);
pContents = null;
pBacking.clear();
}
else if (!pBacking.isEmpty()) {
throw new IllegalArgumentException("backing must be empty");
}
this.entries = pBacking;
init();
if (pContents != null) {
putAll(pContents);
}
else if (entries != null) {
// Reinsert entries, this time wrapped
for (Entry<? extends K, ? extends V> entry : entries) {
put(entry.getKey(), entry.getValue());
}
}
}
/**
* Default implementation, does nothing.
*/
protected void init() {
}
public int size() {
return entries.size();
}
public void clear() {
entries.clear();
modCount++;
init();
}
public boolean isEmpty() {
return entries.isEmpty();
}
public boolean containsKey(Object pKey) {
return entries.containsKey(pKey);
}
/**
* Returns {@code true} if this map maps one or more keys to the
* specified pValue. More formally, returns {@code true} if and only if
* this map contains at least one mapping to a pValue {@code v} such that
* {@code (pValue==null ? v==null : pValue.equals(v))}.
* <p>
* This implementation requires time linear in the map size for this
* operation.
* </p>
*
* @param pValue pValue whose presence in this map is to be tested.
* @return {@code true} if this map maps one or more keys to the
* specified pValue.
*/
public boolean containsValue(Object pValue) {
for (V value : values()) {
if (value == pValue || (value != null && value.equals(pValue))) {
return true;
}
}
return false;
}
public Collection<V> values() {
Collection<V> values = this.values;
return values != null ? values : (this.values = new Values());
}
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
public Set<K> keySet() {
Set<K> ks = keySet;
return ks != null ? ks : (keySet = new KeySet());
}
/**
* Returns a shallow copy of this {@code AbstractMap} instance: the keys
* and values themselves are not cloned.
*
* @return a shallow copy of this map.
*/
protected Object clone() throws CloneNotSupportedException {
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
map.values = null;
map.entrySet = null;
map.keySet = null;
// TODO: Implement: Need to clone the backing map...
return map;
}
// Subclass overrides these to alter behavior of views' iterator() method
protected abstract Iterator<K> newKeyIterator();
protected abstract Iterator<V> newValueIterator();
protected abstract Iterator<Entry<K, V>> newEntryIterator();
// TODO: Implement these (get/put/remove)?
public abstract V get(Object pKey);
public abstract V remove(Object pKey);
public abstract V put(K pKey, V pValue);
/*protected*/ Entry<K, V> createEntry(K pKey, V pValue) {
return new BasicEntry<K, V>(pKey, pValue);
}
/*protected*/ Entry<K, V> getEntry(K pKey) {
return entries.get(pKey);
}
/**
* Removes the given entry from the Map.
*
* @param pEntry the entry to be removed
*
* @return the removed entry, or {@code null} if nothing was removed.
*/
protected Entry<K, V> removeEntry(Entry<K, V> pEntry) {
if (pEntry == null) {
return null;
}
// Find candidate entry for this key
Entry<K, V> candidate = getEntry(pEntry.getKey());
if (candidate == pEntry || (candidate != null && candidate.equals(pEntry))) {
// Remove
remove(pEntry.getKey());
return pEntry;
}
return null;
}
protected class Values extends AbstractCollection<V> {
public Iterator<V> iterator() {
return newValueIterator();
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public boolean contains(Object o) {
return containsValue(o);
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
protected class EntrySet extends AbstractSet<Entry<K, V>> {
public Iterator<Entry<K, V>> iterator() {
return newEntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Entry))
return false;
Entry e = (Entry) o;
//noinspection SuspiciousMethodCalls
Entry<K, V> candidate = entries.get(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
if (!(o instanceof Entry)) {
return false;
}
/*
// NOTE: Extra cautions is taken, to only remove the entry if it
// equals the entry in the map
Object key = ((Entry) o).getKey();
Entry entry = (Entry) entries.get(key);
// Same entry?
if (entry != null && entry.equals(o)) {
return AbstractWrappedMap.this.remove(key) != null;
}
return false;
*/
//noinspection unchecked
return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
protected class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return newKeyIterator();
}
public int size() {
return AbstractDecoratedMap.this.size();
}
public boolean contains(Object o) {
return containsKey(o);
}
public boolean remove(Object o) {
return AbstractDecoratedMap.this.remove(o) != null;
}
public void clear() {
AbstractDecoratedMap.this.clear();
}
}
/**
* A simple Map.Entry implementaton.
*/
static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
K mKey;
V mValue;
BasicEntry(K pKey, V pValue) {
mKey = pKey;
mValue = pValue;
}
/**
* Default implementation does nothing.
*
* @param pMap the map that is accessed
*/
protected void recordAccess(Map<K, V> pMap) {
}
/**
* Default implementation does nothing.
* @param pMap the map that is removed from
*/
protected void recordRemoval(Map<K, V> pMap) {
}
public V getValue() {
return mValue;
}
public V setValue(V pValue) {
V oldValue = mValue;
mValue = pValue;
return oldValue;
}
public K getKey() {
return mKey;
}
public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) {
return false;
}
Map.Entry entry = (Map.Entry) pOther;
Object k1 = mKey;
Object k2 = entry.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = mValue;
Object v2 = entry.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
return true;
}
}
return false;
}
public int hashCode() {
return (mKey == null ? 0 : mKey.hashCode()) ^
(mValue == null ? 0 : mValue.hashCode());
}
public String toString() {
return getKey() + "=" + getValue();
}
}
}

View File

@@ -1,89 +1,88 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
/**
* Abstract base class for {@code TokenIterator}s to extend.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java#1 $
*/
public abstract class AbstractTokenIterator implements TokenIterator {
/**
* Not supported.
*
* @throws UnsupportedOperationException {@code remove} is not supported by
* this Iterator.
*/
public void remove() {
// TODO: This is not difficult:
// - Convert String to StringBuilder in constructor
// - delete(pos, next.lenght())
// - Add toString() method
// BUT: Would it ever be useful? :-)
throw new UnsupportedOperationException("remove");
}
public final boolean hasMoreTokens() {
return hasNext();
}
/**
* Returns the next element in the iteration as a {@code String}.
* This implementation simply returns {@code (String) next()}.
*
* @return the next element in the iteration.
* @exception java.util.NoSuchElementException iteration has no more elements.
* @see #next()
*/
public final String nextToken() {
return next();
}
/**
* This implementation simply returns {@code hasNext()}.
* @see #hasNext()
*/
public final boolean hasMoreElements() {
return hasNext();
}
/**
* This implementation simply returns {@code next()}.
* @see #next()
*/
public final String nextElement() {
return next();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
/**
* Abstract base class for {@code TokenIterator}s to extend.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java#1 $
*/
public abstract class AbstractTokenIterator implements TokenIterator {
/**
* Not supported.
*
* @throws UnsupportedOperationException {@code remove} is not supported by
* this Iterator.
*/
public void remove() {
// TODO: This is not difficult:
// - Convert String to StringBuilder in constructor
// - delete(pos, next.lenght())
// - Add toString() method
// BUT: Would it ever be useful? :-)
throw new UnsupportedOperationException("remove");
}
public final boolean hasMoreTokens() {
return hasNext();
}
/**
* Returns the next element in the iteration as a {@code String}.
* This implementation simply returns {@code (String) next()}.
*
* @return the next element in the iteration.
* @exception java.util.NoSuchElementException iteration has no more elements.
* @see #next()
*/
public final String nextToken() {
return next();
}
/**
* This implementation simply returns {@code hasNext()}.
* @see #hasNext()
*/
public final boolean hasMoreElements() {
return hasNext();
}
/**
* This implementation simply returns {@code next()}.
* @see #next()
*/
public final String nextElement() {
return next();
}
}

View File

@@ -1,247 +1,248 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* A {@code Map} adapter for a Java Bean.
* <p/>
* Ruthlessly stolen from
* <a href="http://binkley.blogspot.com/2006/08/mapping-java-bean.html>Binkley's Blog</a>
*/
public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable {
private final Object bean;
private transient Set<PropertyDescriptor> descriptors;
public BeanMap(Object pBean) throws IntrospectionException {
if (pBean == null) {
throw new IllegalArgumentException("bean == null");
}
bean = pBean;
descriptors = initDescriptors(pBean);
}
private static Set<PropertyDescriptor> initDescriptors(Object pBean) throws IntrospectionException {
final Set<PropertyDescriptor> descriptors = new HashSet<PropertyDescriptor>();
PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(pBean.getClass()).getPropertyDescriptors();
for (PropertyDescriptor descriptor : propertyDescriptors) {
// Skip Object.getClass(), as you probably never want it
if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) {
continue;
}
// Only support simple setter/getters.
if (!(descriptor instanceof IndexedPropertyDescriptor)) {
descriptors.add(descriptor);
}
}
return Collections.unmodifiableSet(descriptors);
}
public Set<Entry<String, Object>> entrySet() {
return new BeanSet();
}
public Object get(final Object pKey) {
return super.get(pKey);
}
public Object put(final String pKey, final Object pValue) {
checkKey(pKey);
for (Entry<String, Object> entry : entrySet()) {
if (entry.getKey().equals(pKey)) {
return entry.setValue(pValue);
}
}
return null;
}
public Object remove(final Object pKey) {
return super.remove(checkKey(pKey));
}
public int size() {
return descriptors.size();
}
private String checkKey(final Object pKey) {
if (pKey == null) {
throw new IllegalArgumentException("key == null");
}
// NB - the cast forces CCE if key is the wrong type.
final String name = (String) pKey;
if (!containsKey(name)) {
throw new IllegalArgumentException("Bad key: " + pKey);
}
return name;
}
private Object readResolve() throws IntrospectionException {
// Initialize the property descriptors
descriptors = initDescriptors(bean);
return this;
}
private class BeanSet extends AbstractSet<Entry<String, Object>> {
public Iterator<Entry<String, Object>> iterator() {
return new BeanIterator(descriptors.iterator());
}
public int size() {
return descriptors.size();
}
}
private class BeanIterator implements Iterator<Entry<String, Object>> {
private final Iterator<PropertyDescriptor> mIterator;
public BeanIterator(final Iterator<PropertyDescriptor> pIterator) {
mIterator = pIterator;
}
public boolean hasNext() {
return mIterator.hasNext();
}
public BeanEntry next() {
return new BeanEntry(mIterator.next());
}
public void remove() {
mIterator.remove();
}
}
private class BeanEntry implements Entry<String, Object> {
private final PropertyDescriptor mDescriptor;
public BeanEntry(final PropertyDescriptor pDescriptor) {
this.mDescriptor = pDescriptor;
}
public String getKey() {
return mDescriptor.getName();
}
public Object getValue() {
return unwrap(new Wrapped() {
public Object run() throws IllegalAccessException, InvocationTargetException {
final Method method = mDescriptor.getReadMethod();
// A write-only bean.
if (method == null) {
throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
}
return method.invoke(bean);
}
});
}
public Object setValue(final Object pValue) {
return unwrap(new Wrapped() {
public Object run() throws IllegalAccessException, InvocationTargetException {
final Method method = mDescriptor.getWriteMethod();
// A read-only bean.
if (method == null) {
throw new UnsupportedOperationException("No write method for property: " + mDescriptor.getName());
}
final Object old = getValue();
method.invoke(bean, pValue);
return old;
}
});
}
public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) {
return false;
}
Map.Entry entry = (Map.Entry) pOther;
Object k1 = getKey();
Object k2 = entry.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = entry.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
return true;
}
}
return false;
}
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode()) ^
(getValue() == null ? 0 : getValue().hashCode());
}
public String toString() {
return getKey() + "=" + getValue();
}
}
private static interface Wrapped {
Object run() throws IllegalAccessException, InvocationTargetException;
}
private static Object unwrap(final Wrapped wrapped) {
try {
return wrapped.run();
}
catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
catch (final InvocationTargetException e) {
// Javadocs for setValue indicate cast is ok.
throw (RuntimeException) e.getCause();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
/**
* A {@code Map} adapter for a Java Bean.
* <p>
* Ruthlessly stolen from
* <a href="http://binkley.blogspot.com/2006/08/mapping-java-bean.html">Binkley's Blog</a>
* </p>
*/
public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable {
private final Object bean;
private transient Set<PropertyDescriptor> descriptors;
public BeanMap(Object pBean) throws IntrospectionException {
if (pBean == null) {
throw new IllegalArgumentException("bean == null");
}
bean = pBean;
descriptors = initDescriptors(pBean);
}
private static Set<PropertyDescriptor> initDescriptors(Object pBean) throws IntrospectionException {
final Set<PropertyDescriptor> descriptors = new HashSet<PropertyDescriptor>();
PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(pBean.getClass()).getPropertyDescriptors();
for (PropertyDescriptor descriptor : propertyDescriptors) {
// Skip Object.getClass(), as you probably never want it
if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) {
continue;
}
// Only support simple setter/getters.
if (!(descriptor instanceof IndexedPropertyDescriptor)) {
descriptors.add(descriptor);
}
}
return Collections.unmodifiableSet(descriptors);
}
public Set<Entry<String, Object>> entrySet() {
return new BeanSet();
}
public Object get(final Object pKey) {
return super.get(pKey);
}
public Object put(final String pKey, final Object pValue) {
checkKey(pKey);
for (Entry<String, Object> entry : entrySet()) {
if (entry.getKey().equals(pKey)) {
return entry.setValue(pValue);
}
}
return null;
}
public Object remove(final Object pKey) {
return super.remove(checkKey(pKey));
}
public int size() {
return descriptors.size();
}
private String checkKey(final Object pKey) {
if (pKey == null) {
throw new IllegalArgumentException("key == null");
}
// NB - the cast forces CCE if key is the wrong type.
final String name = (String) pKey;
if (!containsKey(name)) {
throw new IllegalArgumentException("Bad key: " + pKey);
}
return name;
}
private Object readResolve() throws IntrospectionException {
// Initialize the property descriptors
descriptors = initDescriptors(bean);
return this;
}
private class BeanSet extends AbstractSet<Entry<String, Object>> {
public Iterator<Entry<String, Object>> iterator() {
return new BeanIterator(descriptors.iterator());
}
public int size() {
return descriptors.size();
}
}
private class BeanIterator implements Iterator<Entry<String, Object>> {
private final Iterator<PropertyDescriptor> mIterator;
public BeanIterator(final Iterator<PropertyDescriptor> pIterator) {
mIterator = pIterator;
}
public boolean hasNext() {
return mIterator.hasNext();
}
public BeanEntry next() {
return new BeanEntry(mIterator.next());
}
public void remove() {
mIterator.remove();
}
}
private class BeanEntry implements Entry<String, Object> {
private final PropertyDescriptor mDescriptor;
public BeanEntry(final PropertyDescriptor pDescriptor) {
this.mDescriptor = pDescriptor;
}
public String getKey() {
return mDescriptor.getName();
}
public Object getValue() {
return unwrap(new Wrapped() {
public Object run() throws IllegalAccessException, InvocationTargetException {
final Method method = mDescriptor.getReadMethod();
// A write-only bean.
if (method == null) {
throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
}
return method.invoke(bean);
}
});
}
public Object setValue(final Object pValue) {
return unwrap(new Wrapped() {
public Object run() throws IllegalAccessException, InvocationTargetException {
final Method method = mDescriptor.getWriteMethod();
// A read-only bean.
if (method == null) {
throw new UnsupportedOperationException("No write method for property: " + mDescriptor.getName());
}
final Object old = getValue();
method.invoke(bean, pValue);
return old;
}
});
}
public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) {
return false;
}
Map.Entry entry = (Map.Entry) pOther;
Object k1 = getKey();
Object k2 = entry.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = entry.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
return true;
}
}
return false;
}
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode()) ^
(getValue() == null ? 0 : getValue().hashCode());
}
public String toString() {
return getKey() + "=" + getValue();
}
}
private static interface Wrapped {
Object run() throws IllegalAccessException, InvocationTargetException;
}
private static Object unwrap(final Wrapped wrapped) {
try {
return wrapped.run();
}
catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
catch (final InvocationTargetException e) {
// Javadocs for setValue indicate cast is ok.
throw (RuntimeException) e.getCause();
}
}
}

View File

@@ -1,153 +1,152 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
/**
* DuplicateHandler
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/DuplicateHandler.java#2 $
*/
public interface DuplicateHandler<T> {
/**
* Resolves duplicates according to a certain strategy.
*
* @param pOld the old value
* @param pNew the new value
*
* @return the resolved value.
*
* @throws IllegalArgumentException is the arguments cannot be resolved for
* some reason.
*/
public T resolve(T pOld, T pNew);
/**
* Will use the first (old) value. Any new values will be discarded.
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<?> USE_FIRST_VALUE = new DuplicateHandler() {
/**
* Returns {@code pOld}.
*
* @param pOld the old value
* @param pNew the new value
*
* @return {@code pOld}
*/
public Object resolve(Object pOld, Object pNew) {
return pOld;
}
};
/**
* Will use the last (new) value. Any old values will be discarded
* (overwritten).
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<?> USE_LAST_VALUE = new DuplicateHandler() {
/**
* Returns {@code pNew}.
*
* @param pOld the old value
* @param pNew the new value
*
* @return {@code pNew}
*/
public Object resolve(Object pOld, Object pNew) {
return pNew;
}
};
/**
* Converts duplicats to an {@code Object} array.
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<?> DUPLICATES_AS_ARRAY = new DuplicateHandler() {
/**
* Returns an {@code Object} array, containing {@code pNew} as its
* last element.
*
* @param pOld the old value
* @param pNew the new value
*
* @return an {@code Object} array, containing {@code pNew} as its
* last element.
*/
public Object resolve(Object pOld, Object pNew) {
Object[] result;
if (pOld instanceof Object[]) {
Object[] old = ((Object[]) pOld);
result = new Object[old.length + 1];
System.arraycopy(old, 0, result, 0, old.length);
result[old.length] = pNew;
}
else {
result = new Object[] {pOld, pNew};
}
return result;
}
};
/**
* Converts duplicates to a comma-separated {@code String}.
* Note that all values should allready be {@code String}s if using this
* handler.
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<String> DUPLICATES_AS_CSV = new DuplicateHandler<String>() {
/**
* Returns a comma-separated {@code String}, with the string
* representation of {@code pNew} as the last element.
*
* @param pOld the old value
* @param pNew the new value
*
* @return a comma-separated {@code String}, with the string
* representation of {@code pNew} as the last element.
*/
public String resolve(String pOld, String pNew) {
StringBuilder result = new StringBuilder(String.valueOf(pOld));
result.append(',');
result.append(pNew);
return result.toString();
}
};
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
/**
* DuplicateHandler
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/DuplicateHandler.java#2 $
*/
public interface DuplicateHandler<T> {
/**
* Resolves duplicates according to a certain strategy.
*
* @param pOld the old value
* @param pNew the new value
*
* @return the resolved value.
*
* @throws IllegalArgumentException is the arguments cannot be resolved for
* some reason.
*/
public T resolve(T pOld, T pNew);
/**
* Will use the first (old) value. Any new values will be discarded.
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<?> USE_FIRST_VALUE = new DuplicateHandler() {
/**
* Returns {@code pOld}.
*
* @param pOld the old value
* @param pNew the new value
*
* @return {@code pOld}
*/
public Object resolve(Object pOld, Object pNew) {
return pOld;
}
};
/**
* Will use the last (new) value. Any old values will be discarded
* (overwritten).
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<?> USE_LAST_VALUE = new DuplicateHandler() {
/**
* Returns {@code pNew}.
*
* @param pOld the old value
* @param pNew the new value
*
* @return {@code pNew}
*/
public Object resolve(Object pOld, Object pNew) {
return pNew;
}
};
/**
* Converts duplicats to an {@code Object} array.
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<?> DUPLICATES_AS_ARRAY = new DuplicateHandler() {
/**
* Returns an {@code Object} array, containing {@code pNew} as its
* last element.
*
* @param pOld the old value
* @param pNew the new value
*
* @return an {@code Object} array, containing {@code pNew} as its
* last element.
*/
public Object resolve(Object pOld, Object pNew) {
Object[] result;
if (pOld instanceof Object[]) {
Object[] old = ((Object[]) pOld);
result = new Object[old.length + 1];
System.arraycopy(old, 0, result, 0, old.length);
result[old.length] = pNew;
}
else {
result = new Object[] {pOld, pNew};
}
return result;
}
};
/**
* Converts duplicates to a comma-separated {@code String}.
* Note that all values should allready be {@code String}s if using this
* handler.
*
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
*/
public final static DuplicateHandler<String> DUPLICATES_AS_CSV = new DuplicateHandler<String>() {
/**
* Returns a comma-separated {@code String}, with the string
* representation of {@code pNew} as the last element.
*
* @param pOld the old value
* @param pNew the new value
*
* @return a comma-separated {@code String}, with the string
* representation of {@code pNew} as the last element.
*/
public String resolve(String pOld, String pNew) {
StringBuilder result = new StringBuilder(String.valueOf(pOld));
result.append(',');
result.append(pNew);
return result.toString();
}
};
}

View File

@@ -1,153 +1,154 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Wraps (decorates) an {@code Iterator} with extra functionality, to allow
* element filtering. Each
* element is filtered against the given {@code Filter}, and only elements
* that are {@code accept}ed are returned by the {@code next} method.
* <p/>
* The optional {@code remove} operation is implemented, but may throw
* {@code UnsupportedOperationException} if the underlying iterator does not
* support the remove operation.
*
* @see FilterIterator.Filter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/FilterIterator.java#1 $
*/
public class FilterIterator<E> implements Iterator<E> {
protected final Filter<E> filter;
protected final Iterator<E> iterator;
private E next = null;
private E current = null;
/**
* Creates a {@code FilterIterator} that wraps the {@code Iterator}. Each
* element is filtered against the given {@code Filter}, and only elements
* that are {@code accept}ed are returned by the {@code next} method.
*
* @param pIterator the iterator to filter
* @param pFilter the filter
* @see FilterIterator.Filter
*/
public FilterIterator(final Iterator<E> pIterator, final Filter<E> pFilter) {
if (pIterator == null) {
throw new IllegalArgumentException("iterator == null");
}
if (pFilter == null) {
throw new IllegalArgumentException("filter == null");
}
iterator = pIterator;
filter = pFilter;
}
/**
* Returns {@code true} if the iteration has more elements. (In other
* words, returns {@code true} if {@code next} would return an element
* rather than throwing an exception.)
*
* @return {@code true} if the iterator has more elements.
* @see FilterIterator.Filter#accept
*/
public boolean hasNext() {
while (next == null && iterator.hasNext()) {
E element = iterator.next();
if (filter.accept(element)) {
next = element;
break;
}
}
return next != null;
}
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration.
* @see FilterIterator.Filter#accept
*/
public E next() {
if (hasNext()) {
current = next;
// Make sure we advance next time
next = null;
return current;
}
else {
throw new NoSuchElementException("Iteration has no more elements.");
}
}
/**
* Removes from the underlying collection the last element returned by the
* iterator (optional operation). This method can be called only once per
* call to {@code next}. The behavior of an iterator is unspecified if
* the underlying collection is modified while the iteration is in
* progress in any way other than by calling this method.
*/
public void remove() {
if (current != null) {
iterator.remove();
}
else {
throw new IllegalStateException("Iteration has no current element.");
}
}
/**
* Used to tests whether or not an element fulfills certain criteria, and
* hence should be accepted by the FilterIterator instance.
*/
public static interface Filter<E> {
/**
* Tests whether or not the element fulfills certain criteria, and hence
* should be accepted.
*
* @param pElement the element to test
* @return {@code true} if the object is accepted, otherwise
* {@code false}
*/
public boolean accept(E pElement);
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* Wraps (decorates) an {@code Iterator} with extra functionality, to allow
* element filtering. Each
* element is filtered against the given {@code Filter}, and only elements
* that are {@code accept}ed are returned by the {@code next} method.
* <p>
* The optional {@code remove} operation is implemented, but may throw
* {@code UnsupportedOperationException} if the underlying iterator does not
* support the remove operation.
* </p>
*
* @see FilterIterator.Filter
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/FilterIterator.java#1 $
*/
public class FilterIterator<E> implements Iterator<E> {
protected final Filter<E> filter;
protected final Iterator<E> iterator;
private E next = null;
private E current = null;
/**
* Creates a {@code FilterIterator} that wraps the {@code Iterator}. Each
* element is filtered against the given {@code Filter}, and only elements
* that are {@code accept}ed are returned by the {@code next} method.
*
* @param pIterator the iterator to filter
* @param pFilter the filter
* @see FilterIterator.Filter
*/
public FilterIterator(final Iterator<E> pIterator, final Filter<E> pFilter) {
if (pIterator == null) {
throw new IllegalArgumentException("iterator == null");
}
if (pFilter == null) {
throw new IllegalArgumentException("filter == null");
}
iterator = pIterator;
filter = pFilter;
}
/**
* Returns {@code true} if the iteration has more elements. (In other
* words, returns {@code true} if {@code next} would return an element
* rather than throwing an exception.)
*
* @return {@code true} if the iterator has more elements.
* @see FilterIterator.Filter#accept
*/
public boolean hasNext() {
while (next == null && iterator.hasNext()) {
E element = iterator.next();
if (filter.accept(element)) {
next = element;
break;
}
}
return next != null;
}
/**
* Returns the next element in the iteration.
*
* @return the next element in the iteration.
* @see FilterIterator.Filter#accept
*/
public E next() {
if (hasNext()) {
current = next;
// Make sure we advance next time
next = null;
return current;
}
else {
throw new NoSuchElementException("Iteration has no more elements.");
}
}
/**
* Removes from the underlying collection the last element returned by the
* iterator (optional operation). This method can be called only once per
* call to {@code next}. The behavior of an iterator is unspecified if
* the underlying collection is modified while the iteration is in
* progress in any way other than by calling this method.
*/
public void remove() {
if (current != null) {
iterator.remove();
}
else {
throw new IllegalStateException("Iteration has no current element.");
}
}
/**
* Used to tests whether or not an element fulfills certain criteria, and
* hence should be accepted by the FilterIterator instance.
*/
public static interface Filter<E> {
/**
* Tests whether or not the element fulfills certain criteria, and hence
* should be accepted.
*
* @param pElement the element to test
* @return {@code true} if the object is accepted, otherwise
* {@code false}
*/
public boolean accept(E pElement);
}
}

View File

@@ -1,178 +1,180 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
/**
* A {@code Map} decorator that makes the mappings in the backing map
* case insensitive
* (this is implemented by converting all keys to uppercase),
* if the keys used are {@code Strings}. If the keys
* used are not {@code String}s, it wil work as a normal
* {@code java.util.Map}.
* <p/>
*
* @see java.util.Map
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/
public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements Serializable, Cloneable {
/**
* Constructs a new empty {@code Map}.
* The backing map will be a {@link java.util.HashMap}
*/
public IgnoreCaseMap() {
super();
}
/**
* Constructs a new {@code Map} with the same key-value mappings as the
* given {@code Map}.
* The backing map will be a {@link java.util.HashMap}
* <p/>
* NOTE: As the keys in the given map parameter will be converted to
* uppercase (if they are strings), any duplicate key/value pair where
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
* is true, will be lost.
*
* @param pMap the map whose mappings are to be placed in this map.
*/
public IgnoreCaseMap(Map<String, ? extends V> pMap) {
super(pMap);
}
/**
* Constructs a new {@code Map} with the same key-value mappings as the
* given {@code Map}.
* <p/>
* NOTE: The backing map is structuraly cahnged, and it should NOT be
* accessed directly, after the wrapped map is created.
* <p/>
* NOTE: As the keys in the given map parameter will be converted to
* uppercase (if they are strings), any duplicate key/value pair where
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
* is true, will be lost.
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}
*
* @throws IllegalArgumentException if {@code pBacking} is {@code null}
* @throws IllegalArgumentException if {@code pBacking} differs from
* {@code pContent} and is not empty.
*/
public IgnoreCaseMap(Map pBacking, Map<String, ? extends V> pContents) {
super(pBacking, pContents);
}
/**
* Maps the specified key to the specified value in this map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey the map key.
* @param pValue the value.
* @return the previous value of the specified key in this map,
* or null if it did not have one.
*/
public V put(String pKey, V pValue) {
String key = (String) toUpper(pKey);
return unwrap(entries.put(key, new BasicEntry<String, V>(key, pValue)));
}
private V unwrap(Entry<String, V> pEntry) {
return pEntry != null ? pEntry.getValue() : null;
}
/**
* Returns the value to which the specified key is mapped in this
* map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey a key in the map
* @return the value to which the key is mapped in this map; null if
* the key is not mapped to any value in this map.
*/
public V get(Object pKey) {
return unwrap(entries.get(toUpper(pKey)));
}
/**
* Removes the key (and its corresponding value) from this map. This
* method does nothing if the key is not in the map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey the key that needs to be removed.
* @return the value to which the key had been mapped in this map,
* or null if the key did not have a mapping.
*/
public V remove(Object pKey) {
return unwrap(entries.remove(toUpper(pKey)));
}
/**
* Tests if the specified object is a key in this map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey possible key.
* @return true if and only if the specified object is a key in this
* map, as determined by the equals method; false otherwise.
*/
public boolean containsKey(Object pKey) {
return entries.containsKey(toUpper(pKey));
}
/**
* Converts the parameter to uppercase, if it's a String.
*/
protected static Object toUpper(final Object pObject) {
if (pObject instanceof String) {
return ((String) pObject).toUpperCase();
}
return pObject;
}
protected Iterator<Entry<String, V>> newEntryIterator() {
return (Iterator) entries.entrySet().iterator();
}
protected Iterator<String> newKeyIterator() {
return entries.keySet().iterator();
}
protected Iterator<V> newValueIterator() {
return (Iterator<V>) entries.values().iterator();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.Iterator;
import java.util.Map;
/**
* A {@code Map} decorator that makes the mappings in the backing map
* case insensitive
* (this is implemented by converting all keys to uppercase),
* if the keys used are {@code Strings}. If the keys
* used are not {@code String}s, it wil work as a normal
* {@code java.util.Map}.
*
* @see java.util.Map
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/
public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements Serializable, Cloneable {
/**
* Constructs a new empty {@code Map}.
* The backing map will be a {@link java.util.HashMap}
*/
public IgnoreCaseMap() {
super();
}
/**
* Constructs a new {@code Map} with the same key-value mappings as the
* given {@code Map}.
* The backing map will be a {@link java.util.HashMap}
* <p>
* NOTE: As the keys in the given map parameter will be converted to
* uppercase (if they are strings), any duplicate key/value pair where
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
* is true, will be lost.
* </p>
*
* @param pMap the map whose mappings are to be placed in this map.
*/
public IgnoreCaseMap(Map<String, ? extends V> pMap) {
super(pMap);
}
/**
* Constructs a new {@code Map} with the same key-value mappings as the
* given {@code Map}.
* <p>
* NOTE: The backing map is structuraly cahnged, and it should NOT be
* accessed directly, after the wrapped map is created.
* </p>
* <p>
* NOTE: As the keys in the given map parameter will be converted to
* uppercase (if they are strings), any duplicate key/value pair where
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
* is true, will be lost.
* </p>
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}
*
* @throws IllegalArgumentException if {@code pBacking} is {@code null}
* @throws IllegalArgumentException if {@code pBacking} differs from
* {@code pContent} and is not empty.
*/
public IgnoreCaseMap(Map pBacking, Map<String, ? extends V> pContents) {
super(pBacking, pContents);
}
/**
* Maps the specified key to the specified value in this map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey the map key.
* @param pValue the value.
* @return the previous value of the specified key in this map,
* or null if it did not have one.
*/
public V put(String pKey, V pValue) {
String key = (String) toUpper(pKey);
return unwrap(entries.put(key, new BasicEntry<String, V>(key, pValue)));
}
private V unwrap(Entry<String, V> pEntry) {
return pEntry != null ? pEntry.getValue() : null;
}
/**
* Returns the value to which the specified key is mapped in this
* map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey a key in the map
* @return the value to which the key is mapped in this map; null if
* the key is not mapped to any value in this map.
*/
public V get(Object pKey) {
return unwrap(entries.get(toUpper(pKey)));
}
/**
* Removes the key (and its corresponding value) from this map. This
* method does nothing if the key is not in the map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey the key that needs to be removed.
* @return the value to which the key had been mapped in this map,
* or null if the key did not have a mapping.
*/
public V remove(Object pKey) {
return unwrap(entries.remove(toUpper(pKey)));
}
/**
* Tests if the specified object is a key in this map.
* Note: If the key used is a string, the key will not be case-sensitive.
*
* @param pKey possible key.
* @return true if and only if the specified object is a key in this
* map, as determined by the equals method; false otherwise.
*/
public boolean containsKey(Object pKey) {
return entries.containsKey(toUpper(pKey));
}
/**
* Converts the parameter to uppercase, if it's a String.
*/
protected static Object toUpper(final Object pObject) {
if (pObject instanceof String) {
return ((String) pObject).toUpperCase();
}
return pObject;
}
protected Iterator<Entry<String, V>> newEntryIterator() {
return (Iterator) entries.entrySet().iterator();
}
protected Iterator<String> newKeyIterator() {
return entries.keySet().iterator();
}
protected Iterator<V> newValueIterator() {
return (Iterator<V>) entries.values().iterator();
}
}

View File

@@ -1,468 +1,466 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* Generic map and linked list implementation of the {@code Map} interface,
* with predictable iteration order.
* <p>
* Resembles {@code LinkedHashMap} from JDK 1.4+, but is backed by a generic
* {@code Map}, rather than implementing a particular algoritm.
* <p>
* This linked list defines the iteration ordering, which is normally the order
* in which keys were inserted into the map (<em>insertion-order</em>).
* Note that insertion order is not affected if a key is <em>re-inserted</em>
* into the map (a key {@code k} is reinserted into a map {@code m} if
* {@code m.put(k, v)} is invoked when {@code m.containsKey(k)} would return
* {@code true} immediately prior to the invocation).
* <p>
* A special {@link #LinkedMap(boolean) constructor} is provided to create a
* linked hash map whose order of iteration is the order in which its entries
* were last accessed, from least-recently accessed to most-recently
* (<em>access-order</em>).
* This kind of map is well-suited to building LRU caches.
* Invoking the {@code put} or {@code get} method results in an access to the
* corresponding entry (assuming it exists after the invocation completes).
* The {@code putAll} method generates one entry access for each mapping in
* the specified map, in the order that key-value mappings are provided by the
* specified map's entry set iterator.
* <em>No other methods generate entry accesses.</em>
* In particular, operations on collection-views do not affect the order of
* iteration of the backing map.
* <p>
* The {@link #removeEldestEntry(Map.Entry)} method may be overridden to impose
* a policy for removing stale mappings automatically when new mappings are
* added to the map.
*
* @author inspired by LinkedHashMap from JDK 1.4+, by Josh Bloch
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/LinkedMap.java#1 $
*
* @see LinkedHashMap
* @see LRUMap
*/
public class LinkedMap<K, V> extends AbstractDecoratedMap<K, V> implements Serializable {
transient LinkedEntry<K, V> head;
protected final boolean accessOrder;
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with default
* (insertion) order.
*/
public LinkedMap() {
this(null, false);
}
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with the
* given order.
*
* @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item".
*/
public LinkedMap(boolean pAccessOrder) {
this(null, pAccessOrder);
}
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
* pairs copied from {@code pContents} and default (insertion) order.
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*/
public LinkedMap(Map<? extends K, ? extends V> pContents) {
this(pContents, false);
}
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
* pairs copied from {@code pContents} and the given order.
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item".
*/
public LinkedMap(Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
super(pContents);
accessOrder = pAccessOrder;
}
/**
* Creates a {@code LinkedMap} backed by the given map, with key/value
* pairs copied from {@code pContents} and default (insertion) order.
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*/
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
this(pBacking, pContents, false);
}
/**
* Creates a {@code LinkedMap} backed by the given map, with key/value
* pairs copied from {@code pContents} and the given order.
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item".
*/
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
super(pBacking, pContents);
accessOrder = pAccessOrder;
}
protected void init() {
head = new LinkedEntry<K, V>(null, null, null) {
void addBefore(LinkedEntry pExisting) {
throw new Error();
}
void remove() {
throw new Error();
}
public void recordAccess(Map pMap) {
throw new Error();
}
public void recordRemoval(Map pMap) {
throw new Error();
}
public void recordRemoval() {
throw new Error();
}
public V getValue() {
throw new Error();
}
public V setValue(V pValue) {
throw new Error();
}
public K getKey() {
throw new Error();
}
public String toString() {
return "head";
}
};
head.previous = head.next = head;
}
public boolean containsValue(Object pValue) {
// Overridden to take advantage of faster iterator
if (pValue == null) {
for (LinkedEntry e = head.next; e != head; e = e.next) {
if (e.mValue == null) {
return true;
}
}
} else {
for (LinkedEntry e = head.next; e != head; e = e.next) {
if (pValue.equals(e.mValue)) {
return true;
}
}
}
return false;
}
protected Iterator<K> newKeyIterator() {
return new KeyIterator();
}
protected Iterator<V> newValueIterator() {
return new ValueIterator();
}
protected Iterator<Entry<K, V>> newEntryIterator() {
return new EntryIterator();
}
private abstract class LinkedMapIterator<E> implements Iterator<E> {
LinkedEntry<K, V> mNextEntry = head.next;
LinkedEntry<K, V> mLastReturned = null;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int mExpectedModCount = modCount;
public boolean hasNext() {
return mNextEntry != head;
}
public void remove() {
if (mLastReturned == null) {
throw new IllegalStateException();
}
if (modCount != mExpectedModCount) {
throw new ConcurrentModificationException();
}
LinkedMap.this.remove(mLastReturned.mKey);
mLastReturned = null;
mExpectedModCount = modCount;
}
LinkedEntry<K, V> nextEntry() {
if (modCount != mExpectedModCount) {
throw new ConcurrentModificationException();
}
if (mNextEntry == head) {
throw new NoSuchElementException();
}
LinkedEntry<K, V> e = mLastReturned = mNextEntry;
mNextEntry = e.next;
return e;
}
}
private class KeyIterator extends LinkedMap<K, V>.LinkedMapIterator<K> {
public K next() {
return nextEntry().mKey;
}
}
private class ValueIterator extends LinkedMap<K, V>.LinkedMapIterator<V> {
public V next() {
return nextEntry().mValue;
}
}
private class EntryIterator extends LinkedMap<K, V>.LinkedMapIterator<Entry<K, V>> {
public Entry<K, V> next() {
return nextEntry();
}
}
public V get(Object pKey) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
if (entry != null) {
entry.recordAccess(this);
return entry.mValue;
}
return null;
}
public V remove(Object pKey) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.remove(pKey);
if (entry != null) {
entry.remove();
modCount++;
return entry.mValue;
}
return null;
}
public V put(K pKey, V pValue) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
V oldValue;
if (entry == null) {
oldValue = null;
// Remove eldest entry if instructed, else grow capacity if appropriate
LinkedEntry<K, V> eldest = head.next;
if (removeEldestEntry(eldest)) {
removeEntry(eldest);
}
entry = createEntry(pKey, pValue);
entry.addBefore(head);
entries.put(pKey, entry);
}
else {
oldValue = entry.mValue;
entry.mValue = pValue;
entry.recordAccess(this);
}
modCount++;
return oldValue;
}
/**
* Creates a new {@code LinkedEntry}.
*
* @param pKey the key
* @param pValue the value
* @return a new LinkedEntry
*/
/*protected*/ LinkedEntry<K, V> createEntry(K pKey, V pValue) {
return new LinkedEntry<K, V>(pKey, pValue, null);
}
/**
* @todo
*
* @return a copy of this map, with the same order and same key/value pairs.
*/
public Object clone() throws CloneNotSupportedException {
LinkedMap map;
map = (LinkedMap) super.clone();
// TODO: The rest of the work is PROBABLY handled by
// AbstractDecoratedMap, but need to verify that.
return map;
}
/**
* Returns {@code true} if this map should remove its eldest entry.
* This method is invoked by {@code put} and {@code putAll} after
* inserting a new entry into the map. It provides the implementer
* with the opportunity to remove the eldest entry each time a new one
* is added. This is useful if the map represents a cache: it allows
* the map to reduce memory consumption by deleting stale entries.
*
* <p>Sample use: this override will allow the map to grow up to 100
* entries and then delete the eldest entry each time a new entry is
* added, maintaining a steady state of 100 entries.
* <pre>
* private static final int MAX_ENTRIES = 100;
*
* protected boolean removeEldestEntry(Map.Entry eldest) {
* return size() > MAX_ENTRIES;
* }
* </pre>
*
* <p>This method typically does not modify the map in any way,
* instead allowing the map to modify itself as directed by its
* return value. It <i>is</i> permitted for this method to modify
* the map directly, but if it does so, it <i>must</i> return
* {@code false} (indicating that the map should not attempt any
* further modification). The effects of returning {@code true}
* after modifying the map from within this method are unspecified.
*
* <p>This implementation merely returns {@code false} (so that this
* map acts like a normal map - the eldest element is never removed).
*
* @param pEldest The least recently inserted entry in the map, or if
* this is an access-ordered map, the least recently accessed
* entry. This is the entry that will be removed it this
* method returns {@code true}. If the map was empty prior
* to the {@code put} or {@code putAll} invocation resulting
* in this invocation, this will be the entry that was just
* inserted; in other words, if the map contains a single
* entry, the eldest entry is also the newest.
* @return {@code true} if the eldest entry should be removed
* from the map; {@code false} if it should be retained.
*/
protected boolean removeEldestEntry(Entry<K, V> pEldest) {
return false;
}
/**
* Linked list implementation of {@code Map.Entry}.
*/
protected static class LinkedEntry<K, V> extends BasicEntry<K, V> implements Serializable {
LinkedEntry<K, V> previous;
LinkedEntry<K, V> next;
LinkedEntry(K pKey, V pValue, LinkedEntry<K, V> pNext) {
super(pKey, pValue);
next = pNext;
}
/**
* Adds this entry before the given entry (which must be an existing
* entry) in the list.
*
* @param pExisting the entry to add before
*/
void addBefore(LinkedEntry<K, V> pExisting) {
next = pExisting;
previous = pExisting.previous;
previous.next = this;
next.previous = this;
}
/**
* Removes this entry from the linked list.
*/
void remove() {
previous.next = next;
next.previous = previous;
}
/**
* If the entry is part of an access ordered list, moves the entry to
* the end of the list.
*
* @param pMap the map to record access for
*/
protected void recordAccess(Map<K, V> pMap) {
LinkedMap<K, V> linkedMap = (LinkedMap<K, V>) pMap;
if (linkedMap.accessOrder) {
linkedMap.modCount++;
remove();
addBefore(linkedMap.head);
}
}
/**
* Removes this entry from the linked list.
*
* @param pMap the map to record removal from
*/
protected void recordRemoval(Map<K, V> pMap) {
// TODO: Is this REALLY correct?
remove();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* Generic map and linked list implementation of the {@code Map} interface,
* with predictable iteration order.
* <p>
* Resembles {@code LinkedHashMap} from JDK 1.4+, but is backed by a generic
* {@code Map}, rather than implementing a particular algoritm.
* <p>
* This linked list defines the iteration ordering, which is normally the order
* in which keys were inserted into the map (<em>insertion-order</em>).
* Note that insertion order is not affected if a key is <em>re-inserted</em>
* into the map (a key {@code k} is reinserted into a map {@code m} if
* {@code m.put(k, v)} is invoked when {@code m.containsKey(k)} would return
* {@code true} immediately prior to the invocation).
* <p>
* A special {@link #LinkedMap(boolean) constructor} is provided to create a
* linked hash map whose order of iteration is the order in which its entries
* were last accessed, from least-recently accessed to most-recently
* (<em>access-order</em>).
* This kind of map is well-suited to building LRU caches.
* Invoking the {@code put} or {@code get} method results in an access to the
* corresponding entry (assuming it exists after the invocation completes).
* The {@code putAll} method generates one entry access for each mapping in
* the specified map, in the order that key-value mappings are provided by the
* specified map's entry set iterator.
* <em>No other methods generate entry accesses.</em>
* In particular, operations on collection-views do not affect the order of
* iteration of the backing map.
* <p>
* The {@link #removeEldestEntry(Map.Entry)} method may be overridden to impose
* a policy for removing stale mappings automatically when new mappings are
* added to the map.
*
* @author inspired by LinkedHashMap from JDK 1.4+, by Josh Bloch
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/LinkedMap.java#1 $
*
* @see LinkedHashMap
* @see LRUMap
*/
public class LinkedMap<K, V> extends AbstractDecoratedMap<K, V> implements Serializable {
transient LinkedEntry<K, V> head;
protected final boolean accessOrder;
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with default
* (insertion) order.
*/
public LinkedMap() {
this(null, false);
}
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with the
* given order.
*
* @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item".
*/
public LinkedMap(boolean pAccessOrder) {
this(null, pAccessOrder);
}
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
* pairs copied from {@code pContents} and default (insertion) order.
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*/
public LinkedMap(Map<? extends K, ? extends V> pContents) {
this(pContents, false);
}
/**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
* pairs copied from {@code pContents} and the given order.
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item".
*/
public LinkedMap(Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
super(pContents);
accessOrder = pAccessOrder;
}
/**
* Creates a {@code LinkedMap} backed by the given map, with key/value
* pairs copied from {@code pContents} and default (insertion) order.
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
*/
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
this(pBacking, pContents, false);
}
/**
* Creates a {@code LinkedMap} backed by the given map, with key/value
* pairs copied from {@code pContents} and the given order.
*
* @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item".
*/
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
super(pBacking, pContents);
accessOrder = pAccessOrder;
}
protected void init() {
head = new LinkedEntry<K, V>(null, null, null) {
void addBefore(LinkedEntry pExisting) {
throw new Error();
}
void remove() {
throw new Error();
}
public void recordAccess(Map pMap) {
throw new Error();
}
public void recordRemoval(Map pMap) {
throw new Error();
}
public void recordRemoval() {
throw new Error();
}
public V getValue() {
throw new Error();
}
public V setValue(V pValue) {
throw new Error();
}
public K getKey() {
throw new Error();
}
public String toString() {
return "head";
}
};
head.previous = head.next = head;
}
public boolean containsValue(Object pValue) {
// Overridden to take advantage of faster iterator
if (pValue == null) {
for (LinkedEntry e = head.next; e != head; e = e.next) {
if (e.mValue == null) {
return true;
}
}
} else {
for (LinkedEntry e = head.next; e != head; e = e.next) {
if (pValue.equals(e.mValue)) {
return true;
}
}
}
return false;
}
protected Iterator<K> newKeyIterator() {
return new KeyIterator();
}
protected Iterator<V> newValueIterator() {
return new ValueIterator();
}
protected Iterator<Entry<K, V>> newEntryIterator() {
return new EntryIterator();
}
private abstract class LinkedMapIterator<E> implements Iterator<E> {
LinkedEntry<K, V> mNextEntry = head.next;
LinkedEntry<K, V> mLastReturned = null;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int mExpectedModCount = modCount;
public boolean hasNext() {
return mNextEntry != head;
}
public void remove() {
if (mLastReturned == null) {
throw new IllegalStateException();
}
if (modCount != mExpectedModCount) {
throw new ConcurrentModificationException();
}
LinkedMap.this.remove(mLastReturned.mKey);
mLastReturned = null;
mExpectedModCount = modCount;
}
LinkedEntry<K, V> nextEntry() {
if (modCount != mExpectedModCount) {
throw new ConcurrentModificationException();
}
if (mNextEntry == head) {
throw new NoSuchElementException();
}
LinkedEntry<K, V> e = mLastReturned = mNextEntry;
mNextEntry = e.next;
return e;
}
}
private class KeyIterator extends LinkedMap<K, V>.LinkedMapIterator<K> {
public K next() {
return nextEntry().mKey;
}
}
private class ValueIterator extends LinkedMap<K, V>.LinkedMapIterator<V> {
public V next() {
return nextEntry().mValue;
}
}
private class EntryIterator extends LinkedMap<K, V>.LinkedMapIterator<Entry<K, V>> {
public Entry<K, V> next() {
return nextEntry();
}
}
public V get(Object pKey) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
if (entry != null) {
entry.recordAccess(this);
return entry.mValue;
}
return null;
}
public V remove(Object pKey) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.remove(pKey);
if (entry != null) {
entry.remove();
modCount++;
return entry.mValue;
}
return null;
}
public V put(K pKey, V pValue) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
V oldValue;
if (entry == null) {
oldValue = null;
// Remove eldest entry if instructed, else grow capacity if appropriate
LinkedEntry<K, V> eldest = head.next;
if (removeEldestEntry(eldest)) {
removeEntry(eldest);
}
entry = createEntry(pKey, pValue);
entry.addBefore(head);
entries.put(pKey, entry);
}
else {
oldValue = entry.mValue;
entry.mValue = pValue;
entry.recordAccess(this);
}
modCount++;
return oldValue;
}
/**
* Creates a new {@code LinkedEntry}.
*
* @param pKey the key
* @param pValue the value
* @return a new LinkedEntry
*/
/*protected*/ LinkedEntry<K, V> createEntry(K pKey, V pValue) {
return new LinkedEntry<K, V>(pKey, pValue, null);
}
/**
* @return a copy of this map, with the same order and same key/value pairs.
*/
public Object clone() throws CloneNotSupportedException {
LinkedMap map;
map = (LinkedMap) super.clone();
// TODO: The rest of the work is PROBABLY handled by
// AbstractDecoratedMap, but need to verify that.
return map;
}
/**
* Returns {@code true} if this map should remove its eldest entry.
* This method is invoked by {@code put} and {@code putAll} after
* inserting a new entry into the map. It provides the implementer
* with the opportunity to remove the eldest entry each time a new one
* is added. This is useful if the map represents a cache: it allows
* the map to reduce memory consumption by deleting stale entries.
*
* <p>Sample use: this override will allow the map to grow up to 100
* entries and then delete the eldest entry each time a new entry is
* added, maintaining a steady state of 100 entries.
* <pre>
* private static final int MAX_ENTRIES = 100;
*
* protected boolean removeEldestEntry(Map.Entry eldest) {
* return size() &gt; MAX_ENTRIES;
* }
* </pre>
*
* <p>This method typically does not modify the map in any way,
* instead allowing the map to modify itself as directed by its
* return value. It <i>is</i> permitted for this method to modify
* the map directly, but if it does so, it <i>must</i> return
* {@code false} (indicating that the map should not attempt any
* further modification). The effects of returning {@code true}
* after modifying the map from within this method are unspecified.
*
* <p>This implementation merely returns {@code false} (so that this
* map acts like a normal map - the eldest element is never removed).
*
* @param pEldest The least recently inserted entry in the map, or if
* this is an access-ordered map, the least recently accessed
* entry. This is the entry that will be removed it this
* method returns {@code true}. If the map was empty prior
* to the {@code put} or {@code putAll} invocation resulting
* in this invocation, this will be the entry that was just
* inserted; in other words, if the map contains a single
* entry, the eldest entry is also the newest.
* @return {@code true} if the eldest entry should be removed
* from the map; {@code false} if it should be retained.
*/
protected boolean removeEldestEntry(Entry<K, V> pEldest) {
return false;
}
/**
* Linked list implementation of {@code Map.Entry}.
*/
protected static class LinkedEntry<K, V> extends BasicEntry<K, V> implements Serializable {
LinkedEntry<K, V> previous;
LinkedEntry<K, V> next;
LinkedEntry(K pKey, V pValue, LinkedEntry<K, V> pNext) {
super(pKey, pValue);
next = pNext;
}
/**
* Adds this entry before the given entry (which must be an existing
* entry) in the list.
*
* @param pExisting the entry to add before
*/
void addBefore(LinkedEntry<K, V> pExisting) {
next = pExisting;
previous = pExisting.previous;
previous.next = this;
next.previous = this;
}
/**
* Removes this entry from the linked list.
*/
void remove() {
previous.next = next;
next.previous = previous;
}
/**
* If the entry is part of an access ordered list, moves the entry to
* the end of the list.
*
* @param pMap the map to record access for
*/
protected void recordAccess(Map<K, V> pMap) {
LinkedMap<K, V> linkedMap = (LinkedMap<K, V>) pMap;
if (linkedMap.accessOrder) {
linkedMap.modCount++;
remove();
addBefore(linkedMap.head);
}
}
/**
* Removes this entry from the linked list.
*
* @param pMap the map to record removal from
*/
protected void recordRemoval(Map<K, V> pMap) {
// TODO: Is this REALLY correct?
remove();
}
}
}

View File

@@ -1,84 +1,85 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* Generic map and linked list implementation of the {@code Set} interface,
* with predictable iteration order.
* <p>
* Resembles {@code LinkedHashSet} from JDK 1.4+, but is backed by a generic
* {@code LinkedMap}, rather than implementing a particular algoritm.
* <p/>
* @see LinkedMap
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/LinkedSet.java#1 $
*/
public class LinkedSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
private final static Object DUMMY = new Object();
private final Map<E, Object> map;
public LinkedSet() {
map = new LinkedMap<E, Object>();
}
public LinkedSet(Collection<E> pCollection) {
this();
addAll(pCollection);
}
public boolean addAll(Collection<? extends E> pCollection) {
boolean changed = false;
for (E value : pCollection) {
if (add(value) && !changed) {
changed = true;
}
}
return changed;
}
public boolean add(E pValue) {
return map.put(pValue, DUMMY) == null;
}
public int size() {
return map.size();
}
public Iterator<E> iterator() {
return map.keySet().iterator();
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* Generic map and linked list implementation of the {@code Set} interface,
* with predictable iteration order.
* <p>
* Resembles {@code LinkedHashSet} from JDK 1.4+, but is backed by a generic
* {@code LinkedMap}, rather than implementing a particular algoritm.
* </p>
*
* @see LinkedMap
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/LinkedSet.java#1 $
*/
public class LinkedSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
private final static Object DUMMY = new Object();
private final Map<E, Object> map;
public LinkedSet() {
map = new LinkedMap<E, Object>();
}
public LinkedSet(Collection<E> pCollection) {
this();
addAll(pCollection);
}
public boolean addAll(Collection<? extends E> pCollection) {
boolean changed = false;
for (E value : pCollection) {
if (add(value) && !changed) {
changed = true;
}
}
return changed;
}
public boolean add(E pValue) {
return map.put(pValue, DUMMY) == null;
}
public int size() {
return map.size();
}
public Iterator<E> iterator() {
return map.keySet().iterator();
}
}

View File

@@ -1,122 +1,123 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
* An (immutable) empty {@link Map}, that supports all {@code Map} operations
* without throwing exceptions (in contrast to {@link Collections#EMPTY_MAP}
* that will throw exceptions on {@code put}/{@code remove}).
* <p/>
* NOTE: This is not a general purpose {@code Map} implementation,
* as the {@code put} and {@code putAll} methods will not modify the map.
* Instances of this class will always be an empty map.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: com/twelvemonkeys/util/NullMap.java#2 $
*/
public final class NullMap<K, V> implements Map<K, V>, Serializable {
public final int size() {
return 0;
}
public final void clear() {
}
public final boolean isEmpty() {
return true;
}
public final boolean containsKey(Object pKey) {
return false;
}
public final boolean containsValue(Object pValue) {
return false;
}
public final Collection<V> values() {
return Collections.emptyList();
}
public final void putAll(Map pMap) {
}
public final Set<Entry<K, V>> entrySet() {
return Collections.emptySet();
}
public final Set<K> keySet() {
return Collections.emptySet();
}
public final V get(Object pKey) {
return null;
}
public final V remove(Object pKey) {
return null;
}
public final V put(Object pKey, Object pValue) {
return null;
}
/**
* Tests the given object for equality (wether it is also an empty
* {@code Map}).
* This is consistent with the standard {@code Map} implementations of the
* Java Collections Framework.
*
* @param pOther the object to compare with
* @return {@code true} if {@code pOther} is an empty {@code Map},
* otherwise {@code false}
*/
public boolean equals(Object pOther) {
return (pOther instanceof Map) && ((Map) pOther).isEmpty();
}
/**
* Returns the {@code hashCode} of the empty map, {@code 0}.
* This is consistent with the standard {@code Map} implementations of the
* Java Collections Framework.
*
* @return {@code 0}, always
*/
public int hashCode() {
return 0;
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
* An (immutable) empty {@link Map}, that supports all {@code Map} operations
* without throwing exceptions (in contrast to {@link Collections#EMPTY_MAP}
* that will throw exceptions on {@code put}/{@code remove}).
* <p>
* NOTE: This is not a general purpose {@code Map} implementation,
* as the {@code put} and {@code putAll} methods will not modify the map.
* Instances of this class will always be an empty map.
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: com/twelvemonkeys/util/NullMap.java#2 $
*/
public final class NullMap<K, V> implements Map<K, V>, Serializable {
public final int size() {
return 0;
}
public final void clear() {
}
public final boolean isEmpty() {
return true;
}
public final boolean containsKey(Object pKey) {
return false;
}
public final boolean containsValue(Object pValue) {
return false;
}
public final Collection<V> values() {
return Collections.emptyList();
}
public final void putAll(Map pMap) {
}
public final Set<Entry<K, V>> entrySet() {
return Collections.emptySet();
}
public final Set<K> keySet() {
return Collections.emptySet();
}
public final V get(Object pKey) {
return null;
}
public final V remove(Object pKey) {
return null;
}
public final V put(Object pKey, Object pValue) {
return null;
}
/**
* Tests the given object for equality (wether it is also an empty
* {@code Map}).
* This is consistent with the standard {@code Map} implementations of the
* Java Collections Framework.
*
* @param pOther the object to compare with
* @return {@code true} if {@code pOther} is an empty {@code Map},
* otherwise {@code false}
*/
public boolean equals(Object pOther) {
return (pOther instanceof Map) && ((Map) pOther).isEmpty();
}
/**
* Returns the {@code hashCode} of the empty map, {@code 0}.
* This is consistent with the standard {@code Map} implementations of the
* Java Collections Framework.
*
* @return {@code 0}, always
*/
public int hashCode() {
return 0;
}
}

View File

@@ -1,183 +1,183 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
/**
* Utility class for storing times in a simple way. The internal time is stored
* as an int, counting seconds.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @todo Milliseconds!
*/
public class Time {
private int time = -1;
public final static int SECONDS_IN_MINUTE = 60;
/**
* Creates a new time with 0 seconds, 0 minutes.
*/
public Time() {
this(0);
}
/**
* Creates a new time with the given time (in seconds).
*/
public Time(int pTime) {
setTime(pTime);
}
/**
* Sets the full time in seconds
*/
public void setTime(int pTime) {
if (pTime < 0) {
throw new IllegalArgumentException("Time argument must be 0 or positive!");
}
time = pTime;
}
/**
* Gets the full time in seconds.
*/
public int getTime() {
return time;
}
/**
* Gets the full time in milliseconds, for use in creating dates or
* similar.
*
* @see java.util.Date#setTime(long)
*/
public long getTimeInMillis() {
return (long) time * 1000L;
}
/**
* Sets the seconds part of the time. Note, if the seconds argument is 60
* or greater, the value will "wrap", and increase the minutes also.
*
* @param pSeconds an integer that should be between 0 and 59.
*/
public void setSeconds(int pSeconds) {
time = getMinutes() * SECONDS_IN_MINUTE + pSeconds;
}
/**
* Gets the seconds part of the time.
*
* @return an integer between 0 and 59
*/
public int getSeconds() {
return time % SECONDS_IN_MINUTE;
}
/**
* Sets the minutes part of the time.
*
* @param pMinutes an integer
*/
public void setMinutes(int pMinutes) {
time = pMinutes * SECONDS_IN_MINUTE + getSeconds();
}
/**
* Gets the minutes part of the time.
*
* @return an integer
*/
public int getMinutes() {
return time / SECONDS_IN_MINUTE;
}
/**
* Creates a string representation of the time object.
* The string is returned on the form m:ss,
* where m is variable digits minutes and ss is two digits seconds.
*
* @return a string representation of the time object
* @see #toString(String)
*/
public String toString() {
return "" + getMinutes() + ":"
+ (getSeconds() < 10 ? "0" : "") + getSeconds();
}
/**
* Creates a string representation of the time object.
* The string returned is on the format of the formatstring.
* <DL>
* <DD>m (or any multiple of m's)
* <DT>the minutes part (padded with 0's, if number has less digits than
* the number of m's)
* m -> 0,1,...,59,60,61,...
* mm -> 00,01,...,59,60,61,...
* <DD>s or ss
* <DT>the seconds part (padded with 0's, if number has less digits than
* the number of s's)
* s -> 0,1,...,59
* ss -> 00,01,...,59
* <DD>S
* <DT>all seconds (including the ones above 59)
* </DL>
*
* @param pFormatStr the format where
* @return a string representation of the time object
* @throws NumberFormatException
* @see TimeFormat#format(Time)
* @see #parseTime(String)
* @deprecated
*/
public String toString(String pFormatStr) {
TimeFormat tf = new TimeFormat(pFormatStr);
return tf.format(this);
}
/**
* Creates a string representation of the time object.
* The string is returned on the form m:ss,
* where m is variable digits minutes and ss is two digits seconds.
*
* @return a string representation of the time object
* @throws NumberFormatException
* @see TimeFormat#parse(String)
* @see #toString(String)
* @deprecated
*/
public static Time parseTime(String pStr) {
TimeFormat tf = TimeFormat.getInstance();
return tf.parse(pStr);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
/**
* Utility class for storing times in a simple way. The internal time is stored
* as an int, counting seconds.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/
// TODO: Milliseconds!
public class Time {
private int time = -1;
public final static int SECONDS_IN_MINUTE = 60;
/**
* Creates a new time with 0 seconds, 0 minutes.
*/
public Time() {
this(0);
}
/**
* Creates a new time with the given time (in seconds).
*/
public Time(int pTime) {
setTime(pTime);
}
/**
* Sets the full time in seconds
*/
public void setTime(int pTime) {
if (pTime < 0) {
throw new IllegalArgumentException("Time argument must be 0 or positive!");
}
time = pTime;
}
/**
* Gets the full time in seconds.
*/
public int getTime() {
return time;
}
/**
* Gets the full time in milliseconds, for use in creating dates or
* similar.
*
* @see java.util.Date#setTime(long)
*/
public long getTimeInMillis() {
return (long) time * 1000L;
}
/**
* Sets the seconds part of the time. Note, if the seconds argument is 60
* or greater, the value will "wrap", and increase the minutes also.
*
* @param pSeconds an integer that should be between 0 and 59.
*/
public void setSeconds(int pSeconds) {
time = getMinutes() * SECONDS_IN_MINUTE + pSeconds;
}
/**
* Gets the seconds part of the time.
*
* @return an integer between 0 and 59
*/
public int getSeconds() {
return time % SECONDS_IN_MINUTE;
}
/**
* Sets the minutes part of the time.
*
* @param pMinutes an integer
*/
public void setMinutes(int pMinutes) {
time = pMinutes * SECONDS_IN_MINUTE + getSeconds();
}
/**
* Gets the minutes part of the time.
*
* @return an integer
*/
public int getMinutes() {
return time / SECONDS_IN_MINUTE;
}
/**
* Creates a string representation of the time object.
* The string is returned on the form m:ss,
* where m is variable digits minutes and ss is two digits seconds.
*
* @return a string representation of the time object
* @see #toString(String)
*/
public String toString() {
return "" + getMinutes() + ":"
+ (getSeconds() < 10 ? "0" : "") + getSeconds();
}
/**
* Creates a string representation of the time object.
* The string returned is on the format of the formatstring.
* <DL>
* <DD>m (or any multiple of m's)
* <DT>the minutes part (padded with 0's, if number has less digits than
* the number of m's)
* m -&gt; 0,1,...,59,60,61,...
* mm -&gt; 00,01,...,59,60,61,...
* <DD>s or ss
* <DT>the seconds part (padded with 0's, if number has less digits than
* the number of s's)
* s -&gt; 0,1,...,59
* ss -&gt; 00,01,...,59
* <DD>S
* <DT>all seconds (including the ones above 59)
* </DL>
*
* @param pFormatStr the format where
* @return a string representation of the time object
* @throws NumberFormatException
* @see TimeFormat#format(Time)
* @see #parseTime(String)
* @deprecated
*/
public String toString(String pFormatStr) {
TimeFormat tf = new TimeFormat(pFormatStr);
return tf.format(this);
}
/**
* Creates a string representation of the time object.
* The string is returned on the form m:ss,
* where m is variable digits minutes and ss is two digits seconds.
*
* @return a string representation of the time object
* @throws NumberFormatException
* @see TimeFormat#parse(String)
* @see #toString(String)
* @deprecated
*/
public static Time parseTime(String pStr) {
TimeFormat tf = TimeFormat.getInstance();
return tf.parse(pStr);
}
}

View File

@@ -1,452 +1,452 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import com.twelvemonkeys.lang.StringUtil;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* Format for converting and parsing time.
* <P>
* The format is expressed in a string as follows:
* <DL>
* <DD>m (or any multiple of m's)
* <DT>the minutes part (padded with 0's, if number has less digits than
* the number of m's)
* m -> 0,1,...,59,60,61,...
* mm -> 00,01,...,59,60,61,...
* <DD>s or ss
* <DT>the seconds part (padded with 0's, if number has less digits than
* the number of s's)
* s -> 0,1,...,59
* ss -> 00,01,...,59
* <DD>S
* <DT>all seconds (including the ones above 59)
* </DL>
* <P>
* May not handle all cases, and formats... ;-)
* Safest is: Always delimiters between the minutes (m) and seconds (s) part.
* <P>
* TODO:
* Move to com.twelvemonkeys.text?
* Milliseconds!
* Fix bugs.
* Known bugs:
* <P>
* The last character in the formatString is not escaped, while it should be.
* The first character after an escaped character is escaped while is shouldn't
* be.
* <P>
* This is not a 100% compatible implementation of a java.text.Format.
*
* @see com.twelvemonkeys.util.Time
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/
public class TimeFormat extends Format {
final static String MINUTE = "m";
final static String SECOND = "s";
final static String TIME = "S";
final static String ESCAPE = "\\";
/**
* The default time format
*/
private final static TimeFormat DEFAULT_FORMAT = new TimeFormat("m:ss");
protected String formatString = null;
/**
* Main method for testing ONLY
*/
static void main(String[] argv) {
Time time = null;
TimeFormat in = null;
TimeFormat out = null;
if (argv.length >= 3) {
System.out.println("Creating out TimeFormat: \"" + argv[2] + "\"");
out = new TimeFormat(argv[2]);
}
if (argv.length >= 2) {
System.out.println("Creating in TimeFormat: \"" + argv[1] + "\"");
in = new TimeFormat(argv[1]);
}
else {
System.out.println("Using default format for in");
in = DEFAULT_FORMAT;
}
if (out == null)
out = in;
if (argv.length >= 1) {
System.out.println("Parsing: \"" + argv[0] + "\" with format \""
+ in.formatString + "\"");
time = in.parse(argv[0]);
}
else
time = new Time();
System.out.println("Time is \"" + out.format(time) +
"\" according to format \"" + out.formatString + "\"");
}
/**
* The formatter array.
*/
protected TimeFormatter[] formatter;
/**
* Creates a new TimeFormat with the given formatString,
*/
public TimeFormat(String pStr) {
formatString = pStr;
Vector formatter = new Vector();
StringTokenizer tok = new StringTokenizer(pStr, "\\msS", true);
String previous = null;
String current = null;
int previousCount = 0;
while (tok.hasMoreElements()) {
current = tok.nextToken();
if (previous != null && previous.equals(ESCAPE)) {
// Handle escaping of s, S or m
current = ((current != null) ? current : "")
+ (tok.hasMoreElements() ? tok.nextToken() : "");
previous = null;
previousCount = 0;
}
// Skip over first,
// or if current is the same, increase count, and try again
if (previous == null || previous.equals(current)) {
previousCount++;
previous = current;
}
else {
// Create new formatter for each part
if (previous.equals(MINUTE))
formatter.add(new MinutesFormatter(previousCount));
else if (previous.equals(SECOND))
formatter.add(new SecondsFormatter(previousCount));
else if (previous.equals(TIME))
formatter.add(new SecondsFormatter(-1));
else
formatter.add(new TextFormatter(previous));
previousCount = 1;
previous = current;
}
}
// Add new formatter for last part
if (previous != null) {
if (previous.equals(MINUTE))
formatter.add(new MinutesFormatter(previousCount));
else if (previous.equals(SECOND))
formatter.add(new SecondsFormatter(previousCount));
else if (previous.equals(TIME))
formatter.add(new SecondsFormatter(-1));
else
formatter.add(new TextFormatter(previous));
}
// Debug
/*
for (int i = 0; i < formatter.size(); i++) {
System.out.println("Formatter " + formatter.get(i).getClass()
+ ": length=" + ((TimeFormatter) formatter.get(i)).digits);
}
*/
this.formatter = (TimeFormatter[])
formatter.toArray(new TimeFormatter[formatter.size()]);
}
/**
* DUMMY IMPLEMENTATION!!
* Not locale specific.
*/
public static TimeFormat getInstance() {
return DEFAULT_FORMAT;
}
/** DUMMY IMPLEMENTATION!! */
/* Not locale specific
public static TimeFormat getInstance(Locale pLocale) {
return DEFAULT_FORMAT;
}
*/
/** DUMMY IMPLEMENTATION!! */
/* Not locale specific
public static Locale[] getAvailableLocales() {
return new Locale[] {Locale.getDefault()};
}
*/
/** Gets the format string. */
public String getFormatString() {
return formatString;
}
/** DUMMY IMPLEMENTATION!! */
public StringBuffer format(Object pObj, StringBuffer pToAppendTo,
FieldPosition pPos) {
if (!(pObj instanceof Time)) {
throw new IllegalArgumentException("Must be instance of " + Time.class);
}
return pToAppendTo.append(format(pObj));
}
/**
* Formats the the given time, using this format.
*/
public String format(Time pTime) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < formatter.length; i++) {
buf.append(formatter[i].format(pTime));
}
return buf.toString();
}
/** DUMMY IMPLEMENTATION!! */
public Object parseObject(String pStr, ParsePosition pStatus) {
Time t = parse(pStr);
pStatus.setIndex(pStr.length()); // Not 100%
return t;
}
/**
* Parses a Time, according to this format.
* <p>
* Will bug on some formats. It's safest to always use delimiters between
* the minutes (m) and seconds (s) part.
*
*/
public Time parse(String pStr) {
Time time = new Time();
int sec = 0;
int min = 0;
int pos = 0;
int skip = 0;
boolean onlyUseSeconds = false;
for (int i = 0; (i < formatter.length)
&& (pos + skip < pStr.length()) ; i++) {
// Go to next offset
pos += skip;
if (formatter[i] instanceof MinutesFormatter) {
// Parse MINUTES
if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
// Error in format, try parsing to end
if (skip < 0)
skip = pStr.length();
}
else if ((i + 1) >= formatter.length) {
// Skip until end of string
skip = pStr.length();
}
else {
// Hope this is correct...
skip = formatter[i].digits;
}
// May be first char
if (skip > pos)
min = Integer.parseInt(pStr.substring(pos, skip));
}
else if (formatter[i] instanceof SecondsFormatter) {
// Parse SECONDS
if (formatter[i].digits == -1) {
// Only seconds (or full TIME)
if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
}
else if ((i + 1) >= formatter.length) {
// Skip until end of string
skip = pStr.length();
}
else {
// Cannot possibly know how long?
skip = 0;
continue;
}
// Get seconds
sec = Integer.parseInt(pStr.substring(pos, skip));
// System.out.println("Only seconds: " + sec);
onlyUseSeconds = true;
break;
}
else {
// Normal SECONDS
if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
}
else if ((i + 1) >= formatter.length) {
// Skip until end of string
skip = pStr.length();
}
else {
skip = formatter[i].digits;
}
// Get seconds
sec = Integer.parseInt(pStr.substring(pos, skip));
}
}
else if (formatter[i] instanceof TextFormatter) {
skip = formatter[i].digits;
}
}
// Set the minutes part if we should
if (!onlyUseSeconds)
time.setMinutes(min);
// Set the seconds part
time.setSeconds(sec);
return time;
}
}
/**
* The base class of TimeFormatters
*/
abstract class TimeFormatter {
int digits = 0;
abstract String format(Time t);
}
/**
* Formats the seconds part of the Time
*/
class SecondsFormatter extends TimeFormatter {
SecondsFormatter(int pDigits) {
digits = pDigits;
}
String format(Time t) {
// Negative number of digits, means all seconds, no padding
if (digits < 0) {
return Integer.toString(t.getTime());
}
// If seconds is more than digits long, simply return it
if (t.getSeconds() >= Math.pow(10, digits)) {
return Integer.toString(t.getSeconds());
}
// Else return it with leading 0's
//return StringUtil.formatNumber(t.getSeconds(), digits);
return StringUtil.pad("" + t.getSeconds(), digits, "0", true);
}
}
/**
* Formats the minutes part of the Time
*/
class MinutesFormatter extends TimeFormatter {
MinutesFormatter(int pDigits) {
digits = pDigits;
}
String format(Time t) {
// If minutes is more than digits long, simply return it
if (t.getMinutes() >= Math.pow(10, digits)) {
return Integer.toString(t.getMinutes());
}
// Else return it with leading 0's
//return StringUtil.formatNumber(t.getMinutes(), digits);
return StringUtil.pad("" + t.getMinutes(), digits, "0", true);
}
}
/**
* Formats text constant part of the Time
*/
class TextFormatter extends TimeFormatter {
String text = null;
TextFormatter(String pText) {
text = pText;
// Just to be able to skip over
if (pText != null) {
digits = pText.length();
}
}
String format(Time t) {
// Simply return the text
return text;
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import com.twelvemonkeys.lang.StringUtil;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* Format for converting and parsing time.
* <P>
* The format is expressed in a string as follows:
* <DL>
* <DD>m (or any multiple of m's)
* <DT>the minutes part (padded with 0's, if number has less digits than
* the number of m's)
* m -&gt; 0,1,...,59,60,61,...
* mm -&gt; 00,01,...,59,60,61,...
* <DD>s or ss
* <DT>the seconds part (padded with 0's, if number has less digits than
* the number of s's)
* s -&gt; 0,1,...,59
* ss -&gt; 00,01,...,59
* <DD>S
* <DT>all seconds (including the ones above 59)
* </DL>
* <P>
* May not handle all cases, and formats... ;-)
* Safest is: Always delimiters between the minutes (m) and seconds (s) part.
* <P>
* Known bugs:
* <P>
* The last character in the formatString is not escaped, while it should be.
* The first character after an escaped character is escaped while is shouldn't
* be.
* <P>
* This is not a 100% compatible implementation of a java.text.Format.
*
* @see com.twelvemonkeys.util.Time
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/
// TODO:
// Move to com.twelvemonkeys.text?
// Milliseconds!
// Fix bugs.
public class TimeFormat extends Format {
final static String MINUTE = "m";
final static String SECOND = "s";
final static String TIME = "S";
final static String ESCAPE = "\\";
/**
* The default time format
*/
private final static TimeFormat DEFAULT_FORMAT = new TimeFormat("m:ss");
protected String formatString = null;
/**
* Main method for testing ONLY
*/
static void main(String[] argv) {
Time time = null;
TimeFormat in = null;
TimeFormat out = null;
if (argv.length >= 3) {
System.out.println("Creating out TimeFormat: \"" + argv[2] + "\"");
out = new TimeFormat(argv[2]);
}
if (argv.length >= 2) {
System.out.println("Creating in TimeFormat: \"" + argv[1] + "\"");
in = new TimeFormat(argv[1]);
}
else {
System.out.println("Using default format for in");
in = DEFAULT_FORMAT;
}
if (out == null)
out = in;
if (argv.length >= 1) {
System.out.println("Parsing: \"" + argv[0] + "\" with format \""
+ in.formatString + "\"");
time = in.parse(argv[0]);
}
else
time = new Time();
System.out.println("Time is \"" + out.format(time) +
"\" according to format \"" + out.formatString + "\"");
}
/**
* The formatter array.
*/
protected TimeFormatter[] formatter;
/**
* Creates a new TimeFormat with the given formatString,
*/
public TimeFormat(String pStr) {
formatString = pStr;
Vector formatter = new Vector();
StringTokenizer tok = new StringTokenizer(pStr, "\\msS", true);
String previous = null;
String current = null;
int previousCount = 0;
while (tok.hasMoreElements()) {
current = tok.nextToken();
if (previous != null && previous.equals(ESCAPE)) {
// Handle escaping of s, S or m
current = ((current != null) ? current : "")
+ (tok.hasMoreElements() ? tok.nextToken() : "");
previous = null;
previousCount = 0;
}
// Skip over first,
// or if current is the same, increase count, and try again
if (previous == null || previous.equals(current)) {
previousCount++;
previous = current;
}
else {
// Create new formatter for each part
if (previous.equals(MINUTE))
formatter.add(new MinutesFormatter(previousCount));
else if (previous.equals(SECOND))
formatter.add(new SecondsFormatter(previousCount));
else if (previous.equals(TIME))
formatter.add(new SecondsFormatter(-1));
else
formatter.add(new TextFormatter(previous));
previousCount = 1;
previous = current;
}
}
// Add new formatter for last part
if (previous != null) {
if (previous.equals(MINUTE))
formatter.add(new MinutesFormatter(previousCount));
else if (previous.equals(SECOND))
formatter.add(new SecondsFormatter(previousCount));
else if (previous.equals(TIME))
formatter.add(new SecondsFormatter(-1));
else
formatter.add(new TextFormatter(previous));
}
// Debug
/*
for (int i = 0; i < formatter.size(); i++) {
System.out.println("Formatter " + formatter.get(i).getClass()
+ ": length=" + ((TimeFormatter) formatter.get(i)).digits);
}
*/
this.formatter = (TimeFormatter[])
formatter.toArray(new TimeFormatter[formatter.size()]);
}
/**
* DUMMY IMPLEMENTATION!!
* Not locale specific.
*/
public static TimeFormat getInstance() {
return DEFAULT_FORMAT;
}
/** DUMMY IMPLEMENTATION!! */
/* Not locale specific
public static TimeFormat getInstance(Locale pLocale) {
return DEFAULT_FORMAT;
}
*/
/** DUMMY IMPLEMENTATION!! */
/* Not locale specific
public static Locale[] getAvailableLocales() {
return new Locale[] {Locale.getDefault()};
}
*/
/** Gets the format string. */
public String getFormatString() {
return formatString;
}
/** DUMMY IMPLEMENTATION!! */
public StringBuffer format(Object pObj, StringBuffer pToAppendTo,
FieldPosition pPos) {
if (!(pObj instanceof Time)) {
throw new IllegalArgumentException("Must be instance of " + Time.class);
}
return pToAppendTo.append(format(pObj));
}
/**
* Formats the the given time, using this format.
*/
public String format(Time pTime) {
StringBuilder buf = new StringBuilder();
for (int i = 0; i < formatter.length; i++) {
buf.append(formatter[i].format(pTime));
}
return buf.toString();
}
/** DUMMY IMPLEMENTATION!! */
public Object parseObject(String pStr, ParsePosition pStatus) {
Time t = parse(pStr);
pStatus.setIndex(pStr.length()); // Not 100%
return t;
}
/**
* Parses a Time, according to this format.
* <p>
* Will bug on some formats. It's safest to always use delimiters between
* the minutes (m) and seconds (s) part.
*
*/
public Time parse(String pStr) {
Time time = new Time();
int sec = 0;
int min = 0;
int pos = 0;
int skip = 0;
boolean onlyUseSeconds = false;
for (int i = 0; (i < formatter.length)
&& (pos + skip < pStr.length()) ; i++) {
// Go to next offset
pos += skip;
if (formatter[i] instanceof MinutesFormatter) {
// Parse MINUTES
if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
// Error in format, try parsing to end
if (skip < 0)
skip = pStr.length();
}
else if ((i + 1) >= formatter.length) {
// Skip until end of string
skip = pStr.length();
}
else {
// Hope this is correct...
skip = formatter[i].digits;
}
// May be first char
if (skip > pos)
min = Integer.parseInt(pStr.substring(pos, skip));
}
else if (formatter[i] instanceof SecondsFormatter) {
// Parse SECONDS
if (formatter[i].digits == -1) {
// Only seconds (or full TIME)
if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
}
else if ((i + 1) >= formatter.length) {
// Skip until end of string
skip = pStr.length();
}
else {
// Cannot possibly know how long?
skip = 0;
continue;
}
// Get seconds
sec = Integer.parseInt(pStr.substring(pos, skip));
// System.out.println("Only seconds: " + sec);
onlyUseSeconds = true;
break;
}
else {
// Normal SECONDS
if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
}
else if ((i + 1) >= formatter.length) {
// Skip until end of string
skip = pStr.length();
}
else {
skip = formatter[i].digits;
}
// Get seconds
sec = Integer.parseInt(pStr.substring(pos, skip));
}
}
else if (formatter[i] instanceof TextFormatter) {
skip = formatter[i].digits;
}
}
// Set the minutes part if we should
if (!onlyUseSeconds)
time.setMinutes(min);
// Set the seconds part
time.setSeconds(sec);
return time;
}
}
/**
* The base class of TimeFormatters
*/
abstract class TimeFormatter {
int digits = 0;
abstract String format(Time t);
}
/**
* Formats the seconds part of the Time
*/
class SecondsFormatter extends TimeFormatter {
SecondsFormatter(int pDigits) {
digits = pDigits;
}
String format(Time t) {
// Negative number of digits, means all seconds, no padding
if (digits < 0) {
return Integer.toString(t.getTime());
}
// If seconds is more than digits long, simply return it
if (t.getSeconds() >= Math.pow(10, digits)) {
return Integer.toString(t.getSeconds());
}
// Else return it with leading 0's
//return StringUtil.formatNumber(t.getSeconds(), digits);
return StringUtil.pad("" + t.getSeconds(), digits, "0", true);
}
}
/**
* Formats the minutes part of the Time
*/
class MinutesFormatter extends TimeFormatter {
MinutesFormatter(int pDigits) {
digits = pDigits;
}
String format(Time t) {
// If minutes is more than digits long, simply return it
if (t.getMinutes() >= Math.pow(10, digits)) {
return Integer.toString(t.getMinutes());
}
// Else return it with leading 0's
//return StringUtil.formatNumber(t.getMinutes(), digits);
return StringUtil.pad("" + t.getMinutes(), digits, "0", true);
}
}
/**
* Formats text constant part of the Time
*/
class TextFormatter extends TimeFormatter {
String text = null;
TextFormatter(String pText) {
text = pText;
// Just to be able to skip over
if (pText != null) {
digits = pText.length();
}
}
String format(Time t) {
// Simply return the text
return text;
}
}

View File

@@ -1,451 +1,453 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* A {@code Map} implementation that removes (exipres) its elements after
* a given period. The map is by default backed by a {@link java.util.HashMap},
* or can be instantiated with any given {@code Map} as backing.
* <P/>
* Notes to consider when using this map:
* <ul>
* <li>Elements may not expire on the exact millisecond as expected.</li>
* <li>The value returned by the {@code size()} method of the map, or any of
* its collection views, may not represent
* the exact number of entries in the map at any given time.</li>
* <li>Elements in this map may expire at any time
* (but never between invocations of {@code Iterator.hasNext()}
* and {@code Iterator.next()} or {@code Iterator.remove()},
* when iterating the collection views).</li>
* </ul>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/TimeoutMap.java#2 $
*
* @todo Consider have this Map extend LinkedMap.. That way the removeExpired
* method only have to run from the first element, until it finds an element
* that should not expire, as elements are in insertion order.
* and next expiry time would be the time of the first element.
* @todo Consider running the removeExpiredEntries method in a separate (deamon) thread
* @todo - or document why it is not such a good idea.
*/
public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements ExpiringMap<K, V>, Serializable, Cloneable {
/**
* Expiry time
*/
protected long expiryTime = 60000L; // 1 minute
//////////////////////
private volatile long nextExpiryTime;
//////////////////////
/**
* Creates a {@code TimeoutMap} with the default expiry time of 1 minute.
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
* <p/>
* <small>This is constructor is here to comply with the reccomendations for
* "standard" constructors in the {@code Map} interface.</small>
*
* @see #TimeoutMap(long)
*/
public TimeoutMap() {
super();
}
/**
* Creates a {@code TimeoutMap} containing the same elements as the given map
* with the default expiry time of 1 minute.
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance,
* and <em>not</em> the map passed in as a paramter.
* <p/>
* <small>This is constructor is here to comply with the reccomendations for
* "standard" constructors in the {@code Map} interface.</small>
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @see #TimeoutMap(java.util.Map, Map, long)
* @see java.util.Map
*/
public TimeoutMap(Map<? extends K, ? extends V> pContents) {
super(pContents);
}
/**
* Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
*
* @param pExpiryTime the expiry time (time to live) for elements in this map
*/
public TimeoutMap(long pExpiryTime) {
this();
expiryTime = pExpiryTime;
}
/**
* Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* This {@code TimeoutMap} will be backed by the given {@code Map}.
* <P/>
* <EM>Note that structurally modifying the backing map directly (not
* through this map or its collection views), is not allowed, and will
* produce undeterministic exceptions.</EM>
*
* @param pBacking the map that will be used as backing.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @param pExpiryTime the expiry time (time to live) for elements in this map
*/
public TimeoutMap(Map<K, Map.Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, long pExpiryTime) {
super(pBacking, pContents);
expiryTime = pExpiryTime;
}
/**
* Gets the maximum time any value will be kept in the map, before it expires.
*
* @return the expiry time
*/
public long getExpiryTime() {
return expiryTime;
}
/**
* Sets the maximum time any value will be kept in the map, before it expires.
* Removes any items that are older than the specified time.
*
* @param pExpiryTime the expiry time (time to live) for elements in this map
*/
public void setExpiryTime(long pExpiryTime) {
long oldEexpiryTime = expiryTime;
expiryTime = pExpiryTime;
if (expiryTime < oldEexpiryTime) {
// Expire now
nextExpiryTime = 0;
removeExpiredEntries();
}
}
/**
* Returns the number of key-value mappings in this map. If the
* map contains more than {@code Integer.MAX_VALUE} elements, returns
* {@code Integer.MAX_VALUE}.
*
* @return the number of key-value mappings in this map.
*/
public int size() {
removeExpiredEntries();
return entries.size();
}
/**
* Returns {@code true} if this map contains no key-value mappings.
*
* @return {@code true} if this map contains no key-value mappings.
*/
public boolean isEmpty() {
return (size() <= 0);
}
/**
* Returns {@code true} if this map contains a mapping for the specified
* pKey.
*
* @param pKey pKey whose presence in this map is to be tested.
* @return {@code true} if this map contains a mapping for the specified
* pKey.
*/
public boolean containsKey(Object pKey) {
removeExpiredEntries();
return entries.containsKey(pKey);
}
/**
* Returns the value to which this map maps the specified pKey. Returns
* {@code null} if the map contains no mapping for this pKey. A return
* value of {@code null} does not <i>necessarily</i> indicate that the
* map contains no mapping for the pKey; it's also possible that the map
* explicitly maps the pKey to {@code null}. The {@code containsKey}
* operation may be used to distinguish these two cases.
*
* @param pKey pKey whose associated value is to be returned.
* @return the value to which this map maps the specified pKey, or
* {@code null} if the map contains no mapping for this pKey.
* @see #containsKey(java.lang.Object)
*/
public V get(Object pKey) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
if (entry == null) {
return null;
}
else if (entry.isExpired()) {
//noinspection SuspiciousMethodCalls
entries.remove(pKey);
processRemoved(entry);
return null;
}
return entry.getValue();
}
/**
* Associates the specified pValue with the specified pKey in this map
* (optional operation). If the map previously contained a mapping for
* this pKey, the old pValue is replaced.
*
* @param pKey pKey with which the specified pValue is to be associated.
* @param pValue pValue to be associated with the specified pKey.
* @return previous pValue associated with specified pKey, or {@code null}
* if there was no mapping for pKey. A {@code null} return can
* also indicate that the map previously associated {@code null}
* with the specified pKey, if the implementation supports
* {@code null} values.
*/
public V put(K pKey, V pValue) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
V oldValue;
if (entry == null) {
oldValue = null;
entry = createEntry(pKey, pValue);
entries.put(pKey, entry);
}
else {
oldValue = entry.mValue;
entry.setValue(pValue);
entry.recordAccess(this);
}
// Need to remove expired objects every now and then
// We do it in the put method, to avoid resource leaks over time.
removeExpiredEntries();
modCount++;
return oldValue;
}
/**
* Removes the mapping for this pKey from this map if present (optional
* operation).
*
* @param pKey pKey whose mapping is to be removed from the map.
* @return previous value associated with specified pKey, or {@code null}
* if there was no mapping for pKey. A {@code null} return can
* also indicate that the map previously associated {@code null}
* with the specified pKey, if the implementation supports
* {@code null} values.
*/
public V remove(Object pKey) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
return (entry != null) ? entry.getValue() : null;
}
/**
* Removes all mappings from this map.
*/
public void clear() {
entries.clear(); // Finally something straightforward.. :-)
init();
}
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
return new TimedEntry<K, V>(pKey, pValue);
}
/**
* Removes any expired mappings.
*
*/
protected void removeExpiredEntries() {
// Remove any expired elements
long now = System.currentTimeMillis();
if (now > nextExpiryTime) {
removeExpiredEntriesSynced(now);
}
}
/**
* Okay, I guess this do resemble DCL...
*
* @todo Write some exhausting multi-threaded unit-tests.
*
* @param pTime now
*/
private synchronized void removeExpiredEntriesSynced(long pTime) {
if (pTime > nextExpiryTime) {
////
long next = Long.MAX_VALUE;
nextExpiryTime = next; // Avoid multiple runs...
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
////
long expires = entry.expires();
if (expires < next) {
next = expires;
}
////
}
////
nextExpiryTime = next;
}
}
public Collection<V> values() {
removeExpiredEntries();
return super.values();
}
public Set<Entry<K, V>> entrySet() {
removeExpiredEntries();
return super.entrySet();
}
public Set<K> keySet() {
removeExpiredEntries();
return super.keySet();
}
// Subclass overrides these to alter behavior of views' iterator() method
protected Iterator<K> newKeyIterator() {
return new KeyIterator();
}
protected Iterator<V> newValueIterator() {
return new ValueIterator();
}
protected Iterator<Entry<K, V>> newEntryIterator() {
return new EntryIterator();
}
public void processRemoved(Entry pRemoved) {
}
/**
* Note: Iterating through this iterator will remove any expired values.
*/
private abstract class TimeoutMapIterator<E> implements Iterator<E> {
Iterator<Entry<K, Entry<K, V>>> mIterator = entries.entrySet().iterator();
BasicEntry<K, V> mNext;
long mNow = System.currentTimeMillis();
public void remove() {
mNext = null; // advance
mIterator.remove();
}
public boolean hasNext() {
if (mNext != null) {
return true; // Never expires between hasNext and next/remove!
}
while (mNext == null && mIterator.hasNext()) {
Entry<K, Entry<K, V>> entry = mIterator.next();
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
if (timed.isExpiredBy(mNow)) {
// Remove from map, and continue
mIterator.remove();
processRemoved(timed);
}
else {
// Go with this entry
mNext = timed;
return true;
}
}
return false;
}
BasicEntry<K, V> nextEntry() {
if (!hasNext()) {
throw new NoSuchElementException();
}
BasicEntry<K, V> entry = mNext;
mNext = null; // advance
return entry;
}
}
private class KeyIterator extends TimeoutMapIterator<K> {
public K next() {
return nextEntry().mKey;
}
}
private class ValueIterator extends TimeoutMapIterator<V> {
public V next() {
return nextEntry().mValue;
}
}
private class EntryIterator extends TimeoutMapIterator<Entry<K, V>> {
public Entry<K, V> next() {
return nextEntry();
}
}
/**
* Keeps track of timed objects
*/
private class TimedEntry<K, V> extends BasicEntry<K, V> {
private long mTimestamp;
TimedEntry(K pKey, V pValue) {
super(pKey, pValue);
mTimestamp = System.currentTimeMillis();
}
public V setValue(V pValue) {
mTimestamp = System.currentTimeMillis();
return super.setValue(pValue);
}
final boolean isExpired() {
return isExpiredBy(System.currentTimeMillis());
}
final boolean isExpiredBy(final long pTime) {
return pTime > expires();
}
final long expires() {
return mTimestamp + expiryTime;
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.io.Serializable;
import java.util.*;
/**
* A {@code Map} implementation that removes (exipres) its elements after
* a given period. The map is by default backed by a {@link java.util.HashMap},
* or can be instantiated with any given {@code Map} as backing.
* <p>
* Notes to consider when using this map:
* </p>
* <ul>
* <li>Elements may not expire on the exact millisecond as expected.</li>
* <li>The value returned by the {@code size()} method of the map, or any of
* its collection views, may not represent
* the exact number of entries in the map at any given time.</li>
* <li>Elements in this map may expire at any time
* (but never between invocations of {@code Iterator.hasNext()}
* and {@code Iterator.next()} or {@code Iterator.remove()},
* when iterating the collection views).</li>
* </ul>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/TimeoutMap.java#2 $
*/
// TODO: Consider have this Map extend LinkedMap.. That way the removeExpired
// method only have to run from the first element, until it finds an element
// that should not expire, as elements are in insertion order.
// and next expiry time would be the time of the first element.
// TODO: Consider running the removeExpiredEntries method in a separate (deamon) thread
// TODO: - or document why it is not such a good idea.
public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements ExpiringMap<K, V>, Serializable, Cloneable {
/**
* Expiry time
*/
protected long expiryTime = 60000L; // 1 minute
//////////////////////
private volatile long nextExpiryTime;
//////////////////////
/**
* Creates a {@code TimeoutMap} with the default expiry time of 1 minute.
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
* <p>
* <small>This is constructor is here to comply with the recommendations for
* "standard" constructors in the {@code Map} interface.</small>
* </p>
*
* @see #TimeoutMap(long)
*/
public TimeoutMap() {
super();
}
/**
* Creates a {@code TimeoutMap} containing the same elements as the given map
* with the default expiry time of 1 minute.
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance,
* and <em>not</em> the map passed in as a paramter.
* <p>
* <small>This is constructor is here to comply with the recommendations for
* "standard" constructors in the {@code Map} interface.</small>
* </p>
*
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @see #TimeoutMap(java.util.Map, Map, long)
* @see java.util.Map
*/
public TimeoutMap(Map<? extends K, ? extends V> pContents) {
super(pContents);
}
/**
* Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
*
* @param pExpiryTime the expiry time (time to live) for elements in this map
*/
public TimeoutMap(long pExpiryTime) {
this();
expiryTime = pExpiryTime;
}
/**
* Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* This {@code TimeoutMap} will be backed by the given {@code Map}.
* <p>
* <em>Note that structurally modifying the backing map directly (not
* through this map or its collection views), is not allowed, and will
* produce undeterministic exceptions.</em>
* </p>
*
* @param pBacking the map that will be used as backing.
* @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}.
* @param pExpiryTime the expiry time (time to live) for elements in this map
*/
public TimeoutMap(Map<K, Map.Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, long pExpiryTime) {
super(pBacking, pContents);
expiryTime = pExpiryTime;
}
/**
* Gets the maximum time any value will be kept in the map, before it expires.
*
* @return the expiry time
*/
public long getExpiryTime() {
return expiryTime;
}
/**
* Sets the maximum time any value will be kept in the map, before it expires.
* Removes any items that are older than the specified time.
*
* @param pExpiryTime the expiry time (time to live) for elements in this map
*/
public void setExpiryTime(long pExpiryTime) {
long oldEexpiryTime = expiryTime;
expiryTime = pExpiryTime;
if (expiryTime < oldEexpiryTime) {
// Expire now
nextExpiryTime = 0;
removeExpiredEntries();
}
}
/**
* Returns the number of key-value mappings in this map. If the
* map contains more than {@code Integer.MAX_VALUE} elements, returns
* {@code Integer.MAX_VALUE}.
*
* @return the number of key-value mappings in this map.
*/
public int size() {
removeExpiredEntries();
return entries.size();
}
/**
* Returns {@code true} if this map contains no key-value mappings.
*
* @return {@code true} if this map contains no key-value mappings.
*/
public boolean isEmpty() {
return (size() <= 0);
}
/**
* Returns {@code true} if this map contains a mapping for the specified
* pKey.
*
* @param pKey pKey whose presence in this map is to be tested.
* @return {@code true} if this map contains a mapping for the specified
* pKey.
*/
public boolean containsKey(Object pKey) {
removeExpiredEntries();
return entries.containsKey(pKey);
}
/**
* Returns the value to which this map maps the specified pKey. Returns
* {@code null} if the map contains no mapping for this pKey. A return
* value of {@code null} does not <i>necessarily</i> indicate that the
* map contains no mapping for the pKey; it's also possible that the map
* explicitly maps the pKey to {@code null}. The {@code containsKey}
* operation may be used to distinguish these two cases.
*
* @param pKey pKey whose associated value is to be returned.
* @return the value to which this map maps the specified pKey, or
* {@code null} if the map contains no mapping for this pKey.
* @see #containsKey(java.lang.Object)
*/
public V get(Object pKey) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
if (entry == null) {
return null;
}
else if (entry.isExpired()) {
//noinspection SuspiciousMethodCalls
entries.remove(pKey);
processRemoved(entry);
return null;
}
return entry.getValue();
}
/**
* Associates the specified pValue with the specified pKey in this map
* (optional operation). If the map previously contained a mapping for
* this pKey, the old pValue is replaced.
*
* @param pKey pKey with which the specified pValue is to be associated.
* @param pValue pValue to be associated with the specified pKey.
* @return previous pValue associated with specified pKey, or {@code null}
* if there was no mapping for pKey. A {@code null} return can
* also indicate that the map previously associated {@code null}
* with the specified pKey, if the implementation supports
* {@code null} values.
*/
public V put(K pKey, V pValue) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
V oldValue;
if (entry == null) {
oldValue = null;
entry = createEntry(pKey, pValue);
entries.put(pKey, entry);
}
else {
oldValue = entry.mValue;
entry.setValue(pValue);
entry.recordAccess(this);
}
// Need to remove expired objects every now and then
// We do it in the put method, to avoid resource leaks over time.
removeExpiredEntries();
modCount++;
return oldValue;
}
/**
* Removes the mapping for this pKey from this map if present (optional
* operation).
*
* @param pKey pKey whose mapping is to be removed from the map.
* @return previous value associated with specified pKey, or {@code null}
* if there was no mapping for pKey. A {@code null} return can
* also indicate that the map previously associated {@code null}
* with the specified pKey, if the implementation supports
* {@code null} values.
*/
public V remove(Object pKey) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
return (entry != null) ? entry.getValue() : null;
}
/**
* Removes all mappings from this map.
*/
public void clear() {
entries.clear(); // Finally something straightforward.. :-)
init();
}
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
return new TimedEntry<K, V>(pKey, pValue);
}
/**
* Removes any expired mappings.
*
*/
protected void removeExpiredEntries() {
// Remove any expired elements
long now = System.currentTimeMillis();
if (now > nextExpiryTime) {
removeExpiredEntriesSynced(now);
}
}
/**
* Okay, I guess this do resemble DCL...
*
* @param pTime now
*/
// TODO: Write some exhausting multi-threaded unit-tests.
private synchronized void removeExpiredEntriesSynced(long pTime) {
if (pTime > nextExpiryTime) {
////
long next = Long.MAX_VALUE;
nextExpiryTime = next; // Avoid multiple runs...
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
////
long expires = entry.expires();
if (expires < next) {
next = expires;
}
////
}
////
nextExpiryTime = next;
}
}
public Collection<V> values() {
removeExpiredEntries();
return super.values();
}
public Set<Entry<K, V>> entrySet() {
removeExpiredEntries();
return super.entrySet();
}
public Set<K> keySet() {
removeExpiredEntries();
return super.keySet();
}
// Subclass overrides these to alter behavior of views' iterator() method
protected Iterator<K> newKeyIterator() {
return new KeyIterator();
}
protected Iterator<V> newValueIterator() {
return new ValueIterator();
}
protected Iterator<Entry<K, V>> newEntryIterator() {
return new EntryIterator();
}
public void processRemoved(Entry pRemoved) {
}
/**
* Note: Iterating through this iterator will remove any expired values.
*/
private abstract class TimeoutMapIterator<E> implements Iterator<E> {
Iterator<Entry<K, Entry<K, V>>> mIterator = entries.entrySet().iterator();
BasicEntry<K, V> mNext;
long mNow = System.currentTimeMillis();
public void remove() {
mNext = null; // advance
mIterator.remove();
}
public boolean hasNext() {
if (mNext != null) {
return true; // Never expires between hasNext and next/remove!
}
while (mNext == null && mIterator.hasNext()) {
Entry<K, Entry<K, V>> entry = mIterator.next();
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
if (timed.isExpiredBy(mNow)) {
// Remove from map, and continue
mIterator.remove();
processRemoved(timed);
}
else {
// Go with this entry
mNext = timed;
return true;
}
}
return false;
}
BasicEntry<K, V> nextEntry() {
if (!hasNext()) {
throw new NoSuchElementException();
}
BasicEntry<K, V> entry = mNext;
mNext = null; // advance
return entry;
}
}
private class KeyIterator extends TimeoutMapIterator<K> {
public K next() {
return nextEntry().mKey;
}
}
private class ValueIterator extends TimeoutMapIterator<V> {
public V next() {
return nextEntry().mValue;
}
}
private class EntryIterator extends TimeoutMapIterator<Entry<K, V>> {
public Entry<K, V> next() {
return nextEntry();
}
}
/**
* Keeps track of timed objects
*/
private class TimedEntry<K, V> extends BasicEntry<K, V> {
private long mTimestamp;
TimedEntry(K pKey, V pValue) {
super(pKey, pValue);
mTimestamp = System.currentTimeMillis();
}
public V setValue(V pValue) {
mTimestamp = System.currentTimeMillis();
return super.setValue(pValue);
}
final boolean isExpired() {
return isExpiredBy(System.currentTimeMillis());
}
final boolean isExpiredBy(final long pTime) {
return pTime > expires();
}
final long expires() {
return mTimestamp + expiryTime;
}
}
}

View File

@@ -1,59 +1,58 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.util.Enumeration;
import java.util.Iterator;
/**
* TokenIterator, Iterator-based replacement for StringTokenizer.
* <p/>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/TokenIterator.java#1 $
*/
public interface TokenIterator extends Iterator<String>, Enumeration<String> {
boolean hasMoreTokens();
/**
* Returns the next element in the iteration as a {@code String}.
*
* @return the next element in the iteration.
* @exception java.util.NoSuchElementException iteration has no more elements.
*/
String nextToken();
/**
* Resets this iterator.
*
*/
void reset();
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util;
import java.util.Enumeration;
import java.util.Iterator;
/**
* TokenIterator, Iterator-based replacement for StringTokenizer.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/TokenIterator.java#1 $
*/
public interface TokenIterator extends Iterator<String>, Enumeration<String> {
boolean hasMoreTokens();
/**
* Returns the next element in the iteration as a {@code String}.
*
* @return the next element in the iteration.
* @exception java.util.NoSuchElementException iteration has no more elements.
*/
String nextToken();
/**
* Resets this iterator.
*
*/
void reset();
}

View File

@@ -1,196 +1,198 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.util.Time;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
/**
* The converter (singleton). Converts strings to objects and back.
* This is the entry point to the converter framework.
* <p/>
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
* and {@link Object}
* (the {@link DefaultConverter}) are registered by this class' static
* initializer. You might remove them using the
* {@code unregisterConverter} method.
*
* @see #registerConverter(Class, PropertyConverter)
* @see #unregisterConverter(Class)
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/Converter.java#1 $
*/
// TODO: Get rid of singleton stuff
// Can probably be a pure static class, but is that a good idea?
// Maybe have BeanUtil act as a "proxy", and hide this class all together?
// TODO: ServiceRegistry for registering 3rd party converters
// TODO: URI scheme, for implicit typing? Is that a good idea?
// TODO: Array converters?
public abstract class Converter implements PropertyConverter {
/** Our singleton instance */
protected static final Converter sInstance = new ConverterImpl(); // Thread safe & EASY
/** The converters Map */
protected final Map<Class, PropertyConverter> converters = new Hashtable<Class, PropertyConverter>();
// Register our predefined converters
static {
PropertyConverter defaultConverter = new DefaultConverter();
registerConverter(Object.class, defaultConverter);
registerConverter(Boolean.TYPE, defaultConverter);
PropertyConverter numberConverter = new NumberConverter();
registerConverter(Number.class, numberConverter);
registerConverter(Byte.TYPE, numberConverter);
registerConverter(Double.TYPE, numberConverter);
registerConverter(Float.TYPE, numberConverter);
registerConverter(Integer.TYPE, numberConverter);
registerConverter(Long.TYPE, numberConverter);
registerConverter(Short.TYPE, numberConverter);
registerConverter(Date.class, new DateConverter());
registerConverter(Time.class, new TimeConverter());
}
/**
* Creates a Converter.
*/
protected Converter() {
}
/**
* Gets the Converter instance.
*
* @return the converter instance
*/
public static Converter getInstance() {
return sInstance;
}
/**
* Registers a converter for a given type.
* This converter will also be used for all subclasses, unless a more
* specific version is registered.
* </p>
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
* and {@link Object}
* (the {@link DefaultConverter}) are registered by this class' static
* initializer. You might remove them using the
* {@code unregisterConverter} method.
*
* @param pType the (super) type to register a converter for
* @param pConverter the converter
*
* @see #unregisterConverter(Class)
*/
public static void registerConverter(final Class<?> pType, final PropertyConverter pConverter) {
getInstance().converters.put(pType, pConverter);
}
/**
* Un-registers a converter for a given type. That is, making it unavailable
* for the converter framework, and making it (potentially) available for
* garbage collection.
*
* @param pType the (super) type to remove converter for
*
* @see #registerConverter(Class,PropertyConverter)
*/
@SuppressWarnings("UnusedDeclaration")
public static void unregisterConverter(final Class<?> pType) {
getInstance().converters.remove(pType);
}
/**
* Converts the string to an object of the given type.
*
* @param pString the string to convert
* @param pType the type to convert to
*
* @return the object created from the given string.
*
* @throws ConversionException if the string cannot be converted for any
* reason.
*/
public Object toObject(final String pString, final Class pType) throws ConversionException {
return toObject(pString, pType, null);
}
/**
* Converts the string to an object of the given type, parsing after the
* given format.
*
* @param pString the string to convert
* @param pType the type to convert to
* @param pFormat the (optional) conversion format
*
* @return the object created from the given string.
*
* @throws ConversionException if the string cannot be converted for any
* reason.
*/
public abstract Object toObject(String pString, Class pType, String pFormat)
throws ConversionException;
/**
* Converts the object to a string, using {@code object.toString()}
*
* @param pObject the object to convert.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object cannot be converted to a
* string for any reason.
*/
public String toString(final Object pObject) throws ConversionException {
return toString(pObject, null);
}
/**
* Converts the object to a string, using {@code object.toString()}
*
* @param pObject the object to convert.
* @param pFormat the (optional) conversion format
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object cannot be converted to a
* string for any reason.
*/
public abstract String toString(Object pObject, String pFormat)
throws ConversionException;
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.util.Time;
import java.util.Date;
import java.util.Hashtable;
import java.util.Map;
/**
* The converter (singleton). Converts strings to objects and back.
* This is the entry point to the converter framework.
* <p>
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
* and {@link Object}
* (the {@link DefaultConverter}) are registered by this class' static
* initializer. You might remove them using the
* {@code unregisterConverter} method.
* </p>
*
* @see #registerConverter(Class, PropertyConverter)
* @see #unregisterConverter(Class)
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/Converter.java#1 $
*/
// TODO: Get rid of singleton stuff
// Can probably be a pure static class, but is that a good idea?
// Maybe have BeanUtil act as a "proxy", and hide this class all together?
// TODO: ServiceRegistry for registering 3rd party converters
// TODO: URI scheme, for implicit typing? Is that a good idea?
// TODO: Array converters?
public abstract class Converter implements PropertyConverter {
/** Our singleton instance */
protected static final Converter sInstance = new ConverterImpl(); // Thread safe & EASY
/** The converters Map */
protected final Map<Class, PropertyConverter> converters = new Hashtable<Class, PropertyConverter>();
// Register our predefined converters
static {
PropertyConverter defaultConverter = new DefaultConverter();
registerConverter(Object.class, defaultConverter);
registerConverter(Boolean.TYPE, defaultConverter);
PropertyConverter numberConverter = new NumberConverter();
registerConverter(Number.class, numberConverter);
registerConverter(Byte.TYPE, numberConverter);
registerConverter(Double.TYPE, numberConverter);
registerConverter(Float.TYPE, numberConverter);
registerConverter(Integer.TYPE, numberConverter);
registerConverter(Long.TYPE, numberConverter);
registerConverter(Short.TYPE, numberConverter);
registerConverter(Date.class, new DateConverter());
registerConverter(Time.class, new TimeConverter());
}
/**
* Creates a Converter.
*/
protected Converter() {
}
/**
* Gets the Converter instance.
*
* @return the converter instance
*/
public static Converter getInstance() {
return sInstance;
}
/**
* Registers a converter for a given type.
* This converter will also be used for all subclasses, unless a more
* specific version is registered.
* <p>
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
* and {@link Object}
* (the {@link DefaultConverter}) are registered by this class' static
* initializer. You might remove them using the
* {@code unregisterConverter} method.
* </p>
*
* @param pType the (super) type to register a converter for
* @param pConverter the converter
*
* @see #unregisterConverter(Class)
*/
public static void registerConverter(final Class<?> pType, final PropertyConverter pConverter) {
getInstance().converters.put(pType, pConverter);
}
/**
* Un-registers a converter for a given type. That is, making it unavailable
* for the converter framework, and making it (potentially) available for
* garbage collection.
*
* @param pType the (super) type to remove converter for
*
* @see #registerConverter(Class,PropertyConverter)
*/
@SuppressWarnings("UnusedDeclaration")
public static void unregisterConverter(final Class<?> pType) {
getInstance().converters.remove(pType);
}
/**
* Converts the string to an object of the given type.
*
* @param pString the string to convert
* @param pType the type to convert to
*
* @return the object created from the given string.
*
* @throws ConversionException if the string cannot be converted for any
* reason.
*/
public Object toObject(final String pString, final Class pType) throws ConversionException {
return toObject(pString, pType, null);
}
/**
* Converts the string to an object of the given type, parsing after the
* given format.
*
* @param pString the string to convert
* @param pType the type to convert to
* @param pFormat the (optional) conversion format
*
* @return the object created from the given string.
*
* @throws ConversionException if the string cannot be converted for any
* reason.
*/
public abstract Object toObject(String pString, Class pType, String pFormat)
throws ConversionException;
/**
* Converts the object to a string, using {@code object.toString()}
*
* @param pObject the object to convert.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object cannot be converted to a
* string for any reason.
*/
public String toString(final Object pObject) throws ConversionException {
return toString(pObject, null);
}
/**
* Converts the object to a string, using {@code object.toString()}
*
* @param pObject the object to convert.
* @param pFormat the (optional) conversion format
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object cannot be converted to a
* string for any reason.
*/
public abstract String toString(Object pObject, String pFormat)
throws ConversionException;
}

View File

@@ -1,157 +1,158 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Converts strings to dates and back.
* <p/>
* <small>This class has a static cache of {@code DateFormats}, to avoid
* creation and parsing of date formats every time one is used.</small>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/DateConverter.java#2 $
*/
public class DateConverter extends NumberConverter {
/** Creates a {@code DateConverter} */
public DateConverter() {
}
/**
* Converts the string to a date, using the given format for parsing.
*
* @param pString the string to convert.
* @param pType the type to convert to. {@code java.util.Date} and
* subclasses allowed.
* @param pFormat the format used for parsing. Must be a legal
* {@code SimpleDateFormat} format, or {@code null} which will use the
* default format.
*
* @return the object created from the given string. May safely be typecast
* to {@code java.util.Date}
*
* @see Date
* @see java.text.DateFormat
*
* @throws ConversionException
*/
public Object toObject(String pString, Class pType, String pFormat) throws ConversionException {
if (StringUtil.isEmpty(pString))
return null;
try {
DateFormat format;
if (pFormat == null) {
// Use system default format, using default locale
format = DateFormat.getDateTimeInstance();
}
else {
// Get format from cache
format = getDateFormat(pFormat);
}
Date date = StringUtil.toDate(pString, format);
// Allow for conversion to Date subclasses (ie. java.sql.*)
if (pType != Date.class) {
try {
date = (Date) BeanUtil.createInstance(pType, new Long(date.getTime()));
}
catch (ClassCastException e) {
throw new TypeMismathException(pType);
}
catch (InvocationTargetException e) {
throw new ConversionException(e);
}
}
return date;
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
/**
* Converts the object to a string, using the given format
*
* @param pObject the object to convert.
* @param pFormat the format used for conversion. Must be a legal
* {@code SimpleDateFormat} format, or {@code null} which will use the
* default format.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object is not a subclass of
* {@code java.util.Date}
*
* @see Date
* @see java.text.DateFormat
*/
public String toString(Object pObject, String pFormat) throws ConversionException {
if (pObject == null)
return null;
if (!(pObject instanceof Date)) {
throw new TypeMismathException(pObject.getClass());
}
try {
// Convert to string, default way
if (StringUtil.isEmpty(pFormat)) {
return DateFormat.getDateTimeInstance().format(pObject);
}
// Convert to string, using format
DateFormat format = getDateFormat(pFormat);
return format.format(pObject);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private DateFormat getDateFormat(String pFormat) {
return (DateFormat) getFormat(SimpleDateFormat.class, pFormat, Locale.US);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil;
import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
/**
* Converts strings to dates and back.
* <p>
* <small>This class has a static cache of {@code DateFormats}, to avoid
* creation and parsing of date formats every time one is used.</small>
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/DateConverter.java#2 $
*/
public class DateConverter extends NumberConverter {
/** Creates a {@code DateConverter} */
public DateConverter() {
}
/**
* Converts the string to a date, using the given format for parsing.
*
* @param pString the string to convert.
* @param pType the type to convert to. {@code java.util.Date} and
* subclasses allowed.
* @param pFormat the format used for parsing. Must be a legal
* {@code SimpleDateFormat} format, or {@code null} which will use the
* default format.
*
* @return the object created from the given string. May safely be typecast
* to {@code java.util.Date}
*
* @see Date
* @see java.text.DateFormat
*
* @throws ConversionException
*/
public Object toObject(String pString, Class pType, String pFormat) throws ConversionException {
if (StringUtil.isEmpty(pString))
return null;
try {
DateFormat format;
if (pFormat == null) {
// Use system default format, using default locale
format = DateFormat.getDateTimeInstance();
}
else {
// Get format from cache
format = getDateFormat(pFormat);
}
Date date = StringUtil.toDate(pString, format);
// Allow for conversion to Date subclasses (ie. java.sql.*)
if (pType != Date.class) {
try {
date = (Date) BeanUtil.createInstance(pType, new Long(date.getTime()));
}
catch (ClassCastException e) {
throw new TypeMismathException(pType);
}
catch (InvocationTargetException e) {
throw new ConversionException(e);
}
}
return date;
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
/**
* Converts the object to a string, using the given format
*
* @param pObject the object to convert.
* @param pFormat the format used for conversion. Must be a legal
* {@code SimpleDateFormat} format, or {@code null} which will use the
* default format.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object is not a subclass of
* {@code java.util.Date}
*
* @see Date
* @see java.text.DateFormat
*/
public String toString(Object pObject, String pFormat) throws ConversionException {
if (pObject == null)
return null;
if (!(pObject instanceof Date)) {
throw new TypeMismathException(pObject.getClass());
}
try {
// Convert to string, default way
if (StringUtil.isEmpty(pFormat)) {
return DateFormat.getDateTimeInstance().format(pObject);
}
// Convert to string, using format
DateFormat format = getDateFormat(pFormat);
return format.format(pObject);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private DateFormat getDateFormat(String pFormat) {
return (DateFormat) getFormat(SimpleDateFormat.class, pFormat, Locale.US);
}
}

View File

@@ -1,268 +1,269 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
/**
* Converts strings to objects and back.
* <p/>
* This converter first tries to create an object, using the class' single
* string argument constructor ({@code &lt;type&gt;(String)}) if found,
* otherwise, an attempt to call
* the class' static {@code valueOf(String)} method. If both fails, a
* {@link ConversionException} is thrown.
*
* @author <A href="haraldk@iconmedialab.no">Harald Kuhr</A>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/DefaultConverter.java#2 $
*
*/
public final class DefaultConverter implements PropertyConverter {
/**
* Creates a {@code DefaultConverter}.
*/
public DefaultConverter() {
}
/**
* Converts the string to an object of the given type.
*
* @param pString the string to convert
* @param pType the type to convert to
* @param pFormat ignored.
*
* @return the object created from the given string.
*
* @throws ConversionException if the type is null, or if the string cannot
* be converted into the given type, using a string constructor or static
* {@code valueOf} method.
*/
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
if (pString == null) {
return null;
}
if (pType == null) {
throw new MissingTypeException();
}
if (pType.isArray()) {
return toArray(pString, pType, pFormat);
}
// TODO: Separate CollectionConverter?
// should however, be simple to wrap array using Arrays.asList
// But what about generic type?! It's erased...
// Primitive -> wrapper
Class type = unBoxType(pType);
try {
// Try to create instance from <Constructor>(String)
Object value = BeanUtil.createInstance(type, pString);
if (value == null) {
// createInstance failed for some reason
// Try to invoke the static method valueOf(String)
value = BeanUtil.invokeStaticMethod(type, "valueOf", pString);
if (value == null) {
// If the value is still null, well, then I cannot help...
throw new ConversionException(String.format(
"Could not convert String to %1$s: No constructor %1$s(String) or static %1$s.valueOf(String) method found!",
type.getName()
));
}
}
return value;
}
catch (InvocationTargetException ite) {
throw new ConversionException(ite.getTargetException());
}
catch (ConversionException ce) {
throw ce;
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private Object toArray(final String pString, final Class pType, final String pFormat) {
String[] strings = StringUtil.toStringArray(pString, pFormat != null ? pFormat : StringUtil.DELIMITER_STRING);
Class type = pType.getComponentType();
if (type == String.class) {
return strings;
}
Object array = Array.newInstance(type, strings.length);
try {
for (int i = 0; i < strings.length; i++) {
Array.set(array, i, Converter.getInstance().toObject(strings[i], type));
}
}
catch (ConversionException e) {
if (pFormat != null) {
throw new ConversionException(String.format("%s for string \"%s\" with format \"%s\"", e.getMessage(), pString, pFormat), e);
}
else {
throw new ConversionException(String.format("%s for string \"%s\"", e.getMessage(), pString), e);
}
}
return array;
}
/**
* Converts the object to a string, using {@code pObject.toString()}.
*
* @param pObject the object to convert.
* @param pFormat ignored.
*
* @return the string representation of the object, or {@code null} if {@code pObject == null}
*/
public String toString(final Object pObject, final String pFormat)
throws ConversionException {
try {
return pObject == null ? null : pObject.getClass().isArray() ? arrayToString(toObjectArray(pObject), pFormat) : pObject.toString();
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private String arrayToString(final Object[] pArray, final String pFormat) {
return pFormat == null ? StringUtil.toCSVString(pArray) : StringUtil.toCSVString(pArray, pFormat);
}
private Object[] toObjectArray(final Object pObject) {
// TODO: Extract util method for wrapping/unwrapping native arrays?
Object[] array;
Class<?> componentType = pObject.getClass().getComponentType();
if (componentType.isPrimitive()) {
if (int.class == componentType) {
array = new Integer[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (short.class == componentType) {
array = new Short[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (long.class == componentType) {
array = new Long[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (float.class == componentType) {
array = new Float[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (double.class == componentType) {
array = new Double[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (boolean.class == componentType) {
array = new Boolean[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (byte.class == componentType) {
array = new Byte[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (char.class == componentType) {
array = new Character[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else {
throw new IllegalArgumentException("Unknown type " + componentType);
}
}
else {
array = (Object[]) pObject;
}
return array;
}
private Class<?> unBoxType(final Class<?> pType) {
if (pType.isPrimitive()) {
if (pType == boolean.class) {
return Boolean.class;
}
if (pType == byte.class) {
return Byte.class;
}
if (pType == char.class) {
return Character.class;
}
if (pType == short.class) {
return Short.class;
}
if (pType == int.class) {
return Integer.class;
}
if (pType == float.class) {
return Float.class;
}
if (pType == long.class) {
return Long.class;
}
if (pType == double.class) {
return Double.class;
}
throw new IllegalArgumentException("Unknown type: " + pType);
}
return pType;
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
/**
* Converts strings to objects and back.
* <p>
* This converter first tries to create an object, using the class' single
* string argument constructor ({@code &lt;type&gt;(String)}) if found,
* otherwise, an attempt to call
* the class' static {@code valueOf(String)} method. If both fails, a
* {@link ConversionException} is thrown.
* </p>
*
* @author <A href="haraldk@iconmedialab.no">Harald Kuhr</A>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/DefaultConverter.java#2 $
*
*/
public final class DefaultConverter implements PropertyConverter {
/**
* Creates a {@code DefaultConverter}.
*/
public DefaultConverter() {
}
/**
* Converts the string to an object of the given type.
*
* @param pString the string to convert
* @param pType the type to convert to
* @param pFormat ignored.
*
* @return the object created from the given string.
*
* @throws ConversionException if the type is null, or if the string cannot
* be converted into the given type, using a string constructor or static
* {@code valueOf} method.
*/
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
if (pString == null) {
return null;
}
if (pType == null) {
throw new MissingTypeException();
}
if (pType.isArray()) {
return toArray(pString, pType, pFormat);
}
// TODO: Separate CollectionConverter?
// should however, be simple to wrap array using Arrays.asList
// But what about generic type?! It's erased...
// Primitive -> wrapper
Class type = unBoxType(pType);
try {
// Try to create instance from <Constructor>(String)
Object value = BeanUtil.createInstance(type, pString);
if (value == null) {
// createInstance failed for some reason
// Try to invoke the static method valueOf(String)
value = BeanUtil.invokeStaticMethod(type, "valueOf", pString);
if (value == null) {
// If the value is still null, well, then I cannot help...
throw new ConversionException(String.format(
"Could not convert String to %1$s: No constructor %1$s(String) or static %1$s.valueOf(String) method found!",
type.getName()
));
}
}
return value;
}
catch (InvocationTargetException ite) {
throw new ConversionException(ite.getTargetException());
}
catch (ConversionException ce) {
throw ce;
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private Object toArray(final String pString, final Class pType, final String pFormat) {
String[] strings = StringUtil.toStringArray(pString, pFormat != null ? pFormat : StringUtil.DELIMITER_STRING);
Class type = pType.getComponentType();
if (type == String.class) {
return strings;
}
Object array = Array.newInstance(type, strings.length);
try {
for (int i = 0; i < strings.length; i++) {
Array.set(array, i, Converter.getInstance().toObject(strings[i], type));
}
}
catch (ConversionException e) {
if (pFormat != null) {
throw new ConversionException(String.format("%s for string \"%s\" with format \"%s\"", e.getMessage(), pString, pFormat), e);
}
else {
throw new ConversionException(String.format("%s for string \"%s\"", e.getMessage(), pString), e);
}
}
return array;
}
/**
* Converts the object to a string, using {@code pObject.toString()}.
*
* @param pObject the object to convert.
* @param pFormat ignored.
*
* @return the string representation of the object, or {@code null} if {@code pObject == null}
*/
public String toString(final Object pObject, final String pFormat)
throws ConversionException {
try {
return pObject == null ? null : pObject.getClass().isArray() ? arrayToString(toObjectArray(pObject), pFormat) : pObject.toString();
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private String arrayToString(final Object[] pArray, final String pFormat) {
return pFormat == null ? StringUtil.toCSVString(pArray) : StringUtil.toCSVString(pArray, pFormat);
}
private Object[] toObjectArray(final Object pObject) {
// TODO: Extract util method for wrapping/unwrapping native arrays?
Object[] array;
Class<?> componentType = pObject.getClass().getComponentType();
if (componentType.isPrimitive()) {
if (int.class == componentType) {
array = new Integer[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (short.class == componentType) {
array = new Short[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (long.class == componentType) {
array = new Long[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (float.class == componentType) {
array = new Float[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (double.class == componentType) {
array = new Double[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (boolean.class == componentType) {
array = new Boolean[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (byte.class == componentType) {
array = new Byte[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else if (char.class == componentType) {
array = new Character[Array.getLength(pObject)];
for (int i = 0; i < array.length; i++) {
Array.set(array, i, Array.get(pObject, i));
}
}
else {
throw new IllegalArgumentException("Unknown type " + componentType);
}
}
else {
array = (Object[]) pObject;
}
return array;
}
private Class<?> unBoxType(final Class<?> pType) {
if (pType.isPrimitive()) {
if (pType == boolean.class) {
return Boolean.class;
}
if (pType == byte.class) {
return Byte.class;
}
if (pType == char.class) {
return Character.class;
}
if (pType == short.class) {
return Short.class;
}
if (pType == int.class) {
return Integer.class;
}
if (pType == float.class) {
return Float.class;
}
if (pType == long.class) {
return Long.class;
}
if (pType == double.class) {
return Double.class;
}
throw new IllegalArgumentException("Unknown type: " + pType);
}
return pType;
}
}

View File

@@ -1,210 +1,211 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.LRUHashMap;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.*;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
/**
* Converts strings to numbers and back.
* <p/>
* <small>This class has a static cache of {@code NumberFormats}, to avoid
* creation and parsing of number formats every time one is used.</small>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/NumberConverter.java#2 $
*/
public class NumberConverter implements PropertyConverter {
// TODO: Need to either make this non-locale aware, or document that it is...
private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols(Locale.US);
private static final NumberFormat sDefaultFormat = new DecimalFormat("#0.#", SYMBOLS);
private static final Map<String, Format> sFormats = new LRUHashMap<String, Format>(50);
public NumberConverter() {
}
/**
* Converts the string to a number, using the given format for parsing.
*
* @param pString the string to convert.
* @param pType the type to convert to. PropertyConverter
* implementations may choose to ignore this parameter.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the object created from the given string. May safely be typecast
* to {@code java.lang.Number} or the class of the {@code type} parameter.
*
* @see Number
* @see java.text.NumberFormat
*
* @throws ConversionException
*/
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
if (StringUtil.isEmpty(pString)) {
return null;
}
try {
if (pType.equals(BigInteger.class)) {
return new BigInteger(pString); // No format?
}
if (pType.equals(BigDecimal.class)) {
return new BigDecimal(pString); // No format?
}
NumberFormat format;
if (pFormat == null) {
// Use system default format, using default locale
format = sDefaultFormat;
}
else {
// Get format from cache
format = getNumberFormat(pFormat);
}
Number num;
synchronized (format) {
num = format.parse(pString);
}
if (pType == Integer.TYPE || pType == Integer.class) {
return num.intValue();
}
else if (pType == Long.TYPE || pType == Long.class) {
return num.longValue();
}
else if (pType == Double.TYPE || pType == Double.class) {
return num.doubleValue();
}
else if (pType == Float.TYPE || pType == Float.class) {
return num.floatValue();
}
else if (pType == Byte.TYPE || pType == Byte.class) {
return num.byteValue();
}
else if (pType == Short.TYPE || pType == Short.class) {
return num.shortValue();
}
return num;
}
catch (ParseException pe) {
throw new ConversionException(pe);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
/**
* Converts the object to a string, using the given format
*
* @param pObject the object to convert.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object is not a subclass of {@link java.lang.Number}
*/
public String toString(final Object pObject, final String pFormat)
throws ConversionException {
if (pObject == null) {
return null;
}
if (!(pObject instanceof Number)) {
throw new TypeMismathException(pObject.getClass());
}
try {
// Convert to string, default way
if (StringUtil.isEmpty(pFormat)) {
return sDefaultFormat.format(pObject);
}
// Convert to string, using format
NumberFormat format = getNumberFormat(pFormat);
synchronized (format) {
return format.format(pObject);
}
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private NumberFormat getNumberFormat(String pFormat) {
return (NumberFormat) getFormat(DecimalFormat.class, pFormat, SYMBOLS);
}
protected final Format getFormat(Class pFormatterClass, Object... pFormat) {
// Try to get format from cache
synchronized (sFormats) {
String key = pFormatterClass.getName() + ":" + Arrays.toString(pFormat);
Format format = sFormats.get(key);
if (format == null) {
// If not found, create...
try {
format = (Format) BeanUtil.createInstance(pFormatterClass, pFormat);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
// ...and store in cache
sFormats.put(key, format);
}
return format;
}
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.LRUHashMap;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.*;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
/**
* Converts strings to numbers and back.
* <p>
* <small>This class has a static cache of {@code NumberFormats}, to avoid
* creation and parsing of number formats every time one is used.</small>
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/NumberConverter.java#2 $
*/
public class NumberConverter implements PropertyConverter {
// TODO: Need to either make this non-locale aware, or document that it is...
private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols(Locale.US);
private static final NumberFormat sDefaultFormat = new DecimalFormat("#0.#", SYMBOLS);
private static final Map<String, Format> sFormats = new LRUHashMap<String, Format>(50);
public NumberConverter() {
}
/**
* Converts the string to a number, using the given format for parsing.
*
* @param pString the string to convert.
* @param pType the type to convert to. PropertyConverter
* implementations may choose to ignore this parameter.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the object created from the given string. May safely be typecast
* to {@code java.lang.Number} or the class of the {@code type} parameter.
*
* @see Number
* @see java.text.NumberFormat
*
* @throws ConversionException
*/
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
if (StringUtil.isEmpty(pString)) {
return null;
}
try {
if (pType.equals(BigInteger.class)) {
return new BigInteger(pString); // No format?
}
if (pType.equals(BigDecimal.class)) {
return new BigDecimal(pString); // No format?
}
NumberFormat format;
if (pFormat == null) {
// Use system default format, using default locale
format = sDefaultFormat;
}
else {
// Get format from cache
format = getNumberFormat(pFormat);
}
Number num;
synchronized (format) {
num = format.parse(pString);
}
if (pType == Integer.TYPE || pType == Integer.class) {
return num.intValue();
}
else if (pType == Long.TYPE || pType == Long.class) {
return num.longValue();
}
else if (pType == Double.TYPE || pType == Double.class) {
return num.doubleValue();
}
else if (pType == Float.TYPE || pType == Float.class) {
return num.floatValue();
}
else if (pType == Byte.TYPE || pType == Byte.class) {
return num.byteValue();
}
else if (pType == Short.TYPE || pType == Short.class) {
return num.shortValue();
}
return num;
}
catch (ParseException pe) {
throw new ConversionException(pe);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
/**
* Converts the object to a string, using the given format
*
* @param pObject the object to convert.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object is not a subclass of {@link java.lang.Number}
*/
public String toString(final Object pObject, final String pFormat)
throws ConversionException {
if (pObject == null) {
return null;
}
if (!(pObject instanceof Number)) {
throw new TypeMismathException(pObject.getClass());
}
try {
// Convert to string, default way
if (StringUtil.isEmpty(pFormat)) {
return sDefaultFormat.format(pObject);
}
// Convert to string, using format
NumberFormat format = getNumberFormat(pFormat);
synchronized (format) {
return format.format(pObject);
}
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private NumberFormat getNumberFormat(String pFormat) {
return (NumberFormat) getFormat(DecimalFormat.class, pFormat, SYMBOLS);
}
protected final Format getFormat(Class pFormatterClass, Object... pFormat) {
// Try to get format from cache
synchronized (sFormats) {
String key = pFormatterClass.getName() + ":" + Arrays.toString(pFormat);
Format format = sFormats.get(key);
if (format == null) {
// If not found, create...
try {
format = (Format) BeanUtil.createInstance(pFormatterClass, pFormat);
}
catch (Exception e) {
e.printStackTrace();
return null;
}
// ...and store in cache
sFormats.put(key, format);
}
return format;
}
}
}

View File

@@ -1,138 +1,139 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.Time;
import com.twelvemonkeys.util.TimeFormat;
/**
* Converts strings to times and back.
* <p/>
* <small>This class has a static cache of {@code TimeFormats}, to avoid creation and
* parsing of timeformats every time one is used.</small>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/TimeConverter.java#1 $
*/
public class TimeConverter extends NumberConverter {
public TimeConverter() {
}
/**
* Converts the string to a time, using the given format for parsing.
*
* @param pString the string to convert.
* @param pType the type to convert to. PropertyConverter
* implementations may choose to ignore this parameter.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the object created from the given string. May safely be typecast
* to {@code com.twelvemonkeys.util.Time}
*
* @see com.twelvemonkeys.util.Time
* @see com.twelvemonkeys.util.TimeFormat
*
* @throws ConversionException
*/
public Object toObject(String pString, Class pType, String pFormat)
throws ConversionException {
if (StringUtil.isEmpty(pString))
return null;
TimeFormat format;
try {
if (pFormat == null) {
// Use system default format
format = TimeFormat.getInstance();
}
else {
// Get format from cache
format = getTimeFormat(pFormat);
}
return format.parse(pString);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
/**
* Converts the object to a string, using the given format
*
* @param pObject the object to convert.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object is not a subclass of
* {@code com.twelvemonkeys.util.Time}
*
* @see com.twelvemonkeys.util.Time
* @see com.twelvemonkeys.util.TimeFormat
*/
public String toString(Object pObject, String pFormat)
throws ConversionException {
if (pObject == null)
return null;
if (!(pObject instanceof com.twelvemonkeys.util.Time))
throw new TypeMismathException(pObject.getClass());
try {
// Convert to string, default way
if (StringUtil.isEmpty(pFormat))
return pObject.toString();
// Convert to string, using format
TimeFormat format = getTimeFormat(pFormat);
return format.format((Time) pObject);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private TimeFormat getTimeFormat(String pFormat) {
return (TimeFormat) getFormat(TimeFormat.class, pFormat);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.Time;
import com.twelvemonkeys.util.TimeFormat;
/**
* Converts strings to times and back.
* <p>
* <small>This class has a static cache of {@code TimeFormats}, to avoid creation and
* parsing of timeformats every time one is used.</small>
* </p>
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/TimeConverter.java#1 $
*/
public class TimeConverter extends NumberConverter {
public TimeConverter() {
}
/**
* Converts the string to a time, using the given format for parsing.
*
* @param pString the string to convert.
* @param pType the type to convert to. PropertyConverter
* implementations may choose to ignore this parameter.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the object created from the given string. May safely be typecast
* to {@code com.twelvemonkeys.util.Time}
*
* @see com.twelvemonkeys.util.Time
* @see com.twelvemonkeys.util.TimeFormat
*
* @throws ConversionException
*/
public Object toObject(String pString, Class pType, String pFormat)
throws ConversionException {
if (StringUtil.isEmpty(pString))
return null;
TimeFormat format;
try {
if (pFormat == null) {
// Use system default format
format = TimeFormat.getInstance();
}
else {
// Get format from cache
format = getTimeFormat(pFormat);
}
return format.parse(pString);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
/**
* Converts the object to a string, using the given format
*
* @param pObject the object to convert.
* @param pFormat the format used for parsing. PropertyConverter
* implementations may choose to ignore this parameter. Also,
* implementations that require a parser format, should provide a default
* format, and allow {@code null} as the format argument.
*
* @return the string representation of the object, on the correct format.
*
* @throws ConversionException if the object is not a subclass of
* {@code com.twelvemonkeys.util.Time}
*
* @see com.twelvemonkeys.util.Time
* @see com.twelvemonkeys.util.TimeFormat
*/
public String toString(Object pObject, String pFormat)
throws ConversionException {
if (pObject == null)
return null;
if (!(pObject instanceof com.twelvemonkeys.util.Time))
throw new TypeMismathException(pObject.getClass());
try {
// Convert to string, default way
if (StringUtil.isEmpty(pFormat))
return pObject.toString();
// Convert to string, using format
TimeFormat format = getTimeFormat(pFormat);
return format.format((Time) pObject);
}
catch (RuntimeException rte) {
throw new ConversionException(rte);
}
}
private TimeFormat getTimeFormat(String pFormat) {
return (TimeFormat) getFormat(TimeFormat.class, pFormat);
}
}

View File

@@ -1,107 +1,107 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.regex;
import com.twelvemonkeys.util.AbstractTokenIterator;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* {@code StringTokenizer} replacement, that uses regular expressions to split
* strings into tokens.
* <p/>
* @see java.util.regex.Pattern for pattern syntax.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/regex/RegExTokenIterator.java#1 $
*/
public class RegExTokenIterator extends AbstractTokenIterator {
private final Matcher matcher;
private boolean next = false;
/**
* Creates a {@code RegExTokenIterator}.
* Default pettern is {@code "\S+"}.
*
* @param pString the string to be parsed.
*
* @throws IllegalArgumentException if {@code pString} is {@code null}
*/
public RegExTokenIterator(String pString) {
this(pString, "\\S+");
}
/**
* Creates a {@code RegExTokenIterator}.
*
* @see Pattern for pattern syntax.
*
* @param pString the string to be parsed.
* @param pPattern the pattern
*
* @throws PatternSyntaxException if {@code pPattern} is not a valid pattern
* @throws IllegalArgumentException if any of the arguments are {@code null}
*/
public RegExTokenIterator(String pString, String pPattern) {
if (pString == null) {
throw new IllegalArgumentException("string == null");
}
if (pPattern == null) {
throw new IllegalArgumentException("pattern == null");
}
matcher = Pattern.compile(pPattern).matcher(pString);
}
/**
* Resets this iterator.
*
*/
public void reset() {
matcher.reset();
}
public boolean hasNext() {
return next || (next = matcher.find());
}
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
next = false;
return matcher.group();
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.regex;
import com.twelvemonkeys.util.AbstractTokenIterator;
import java.util.NoSuchElementException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* {@code StringTokenizer} replacement, that uses regular expressions to split
* strings into tokens.
*
*@see java.util.regex.Pattern for pattern syntax.
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/regex/RegExTokenIterator.java#1 $
*/
public class RegExTokenIterator extends AbstractTokenIterator {
private final Matcher matcher;
private boolean next = false;
/**
* Creates a {@code RegExTokenIterator}.
* Default pettern is {@code "\S+"}.
*
* @param pString the string to be parsed.
*
* @throws IllegalArgumentException if {@code pString} is {@code null}
*/
public RegExTokenIterator(String pString) {
this(pString, "\\S+");
}
/**
* Creates a {@code RegExTokenIterator}.
*
* @see Pattern for pattern syntax.
*
* @param pString the string to be parsed.
* @param pPattern the pattern
*
* @throws PatternSyntaxException if {@code pPattern} is not a valid pattern
* @throws IllegalArgumentException if any of the arguments are {@code null}
*/
public RegExTokenIterator(String pString, String pPattern) {
if (pString == null) {
throw new IllegalArgumentException("string == null");
}
if (pPattern == null) {
throw new IllegalArgumentException("pattern == null");
}
matcher = Pattern.compile(pPattern).matcher(pString);
}
/**
* Resets this iterator.
*
*/
public void reset() {
matcher.reset();
}
public boolean hasNext() {
return next || (next = matcher.find());
}
public String next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
next = false;
return matcher.group();
}
}

View File

@@ -1,63 +1,64 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.service;
/**
* An optional interface that may be implemented by service provider objects.
* <p/>
* If this interface is implemented, the service provider objects will receive
* notification of registration and deregistration from the
* {@code ServiceRegistry}.
*
* @see ServiceRegistry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/service/RegisterableService.java#1 $
*/
public interface RegisterableService {
/**
* Called right after this service provider object is added to
* the given category of the given {@code ServiceRegistry}.
*
* @param pRegistry the {@code ServiceRegistry} {@code this} was added to
* @param pCategory the category {@code this} was added to
*/
void onRegistration(ServiceRegistry pRegistry, Class pCategory);
/**
* Called right after this service provider object is removed
* from the given category of the given {@code ServiceRegistry}.
*
* @param pRegistry the {@code ServiceRegistry} {@code this} was added to
* @param pCategory the category {@code this} was added to
*/
void onDeregistration(ServiceRegistry pRegistry, Class pCategory);
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.service;
/**
* An optional interface that may be implemented by service provider objects.
* <p>
* If this interface is implemented, the service provider objects will receive
* notification of registration and deregistration from the
* {@code ServiceRegistry}.
* </p>
*
* @see ServiceRegistry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/service/RegisterableService.java#1 $
*/
public interface RegisterableService {
/**
* Called right after this service provider object is added to
* the given category of the given {@code ServiceRegistry}.
*
* @param pRegistry the {@code ServiceRegistry} {@code this} was added to
* @param pCategory the category {@code this} was added to
*/
void onRegistration(ServiceRegistry pRegistry, Class pCategory);
/**
* Called right after this service provider object is removed
* from the given category of the given {@code ServiceRegistry}.
*
* @param pRegistry the {@code ServiceRegistry} {@code this} was added to
* @param pCategory the category {@code this} was added to
*/
void onDeregistration(ServiceRegistry pRegistry, Class pCategory);
}

View File

@@ -1,54 +1,54 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.service;
/**
* Error thrown by the {@code ServiceRegistry} in case of a configuration
* error.
* <p/>
* @see ServiceRegistry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/service/ServiceConfigurationError.java#1 $
*/
public class ServiceConfigurationError extends Error {
ServiceConfigurationError(Throwable pCause) {
super(pCause);
}
ServiceConfigurationError(String pMessage) {
super(pMessage);
}
ServiceConfigurationError(String pMessage, Throwable pCause) {
super(pMessage, pCause);
}
}
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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 com.twelvemonkeys.util.service;
/**
* Error thrown by the {@code ServiceRegistry} in case of a configuration
* error.
*
* @see ServiceRegistry
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/service/ServiceConfigurationError.java#1 $
*/
public class ServiceConfigurationError extends Error {
ServiceConfigurationError(Throwable pCause) {
super(pCause);
}
ServiceConfigurationError(String pMessage) {
super(pMessage);
}
ServiceConfigurationError(String pMessage, Throwable pCause) {
super(pMessage, pCause);
}
}

View File

@@ -1,39 +1,40 @@
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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.
*/
/**
* Provides a service provider registry.
* <p/>
* This package contains a service provider registry, as specified in the
* <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a>.
*
* @see <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a>
*/
package com.twelvemonkeys.util.service;
/*
* Copyright (c) 2008, Harald Kuhr
* 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 copyright holder 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 HOLDER 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.
*/
/**
* Provides a service provider registry.
* <p>
* This package contains a service provider registry, as specified in the
* <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a>.
* </p>
*
* @see <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a>
*/
package com.twelvemonkeys.util.service;

View File

@@ -36,13 +36,12 @@ import java.util.Map;
/**
* BeanMapTestCase
* <p/>
* @todo Extend with BeanMap specific tests
*
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author last modified by $Author: haku $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/util/BeanMapTestCase.java#2 $
*/
// TODO: Extend with BeanMap specific tests
public class BeanMapTest extends MapAbstractTest {
public boolean isPutAddSupported() {