mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-10-04 11:26:44 -04:00
Fixed JavaDoc errors to enable Java 8 build.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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 $
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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 $
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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() > 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();
|
||||
}
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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 -> 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);
|
||||
}
|
||||
}
|
||||
|
@@ -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 -> 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>
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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 <type>(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 <type>(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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
@@ -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() {
|
||||
|
Reference in New Issue
Block a user