You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by pe...@apache.org on 2012/02/01 00:22:06 UTC

git commit: WICKET-4356 StringValueConversionException should not be thrown when requesting a conversion on StringValue with a default value

Updated Branches:
  refs/heads/master abe41a90f -> 818620be0


WICKET-4356 StringValueConversionException should not be thrown when requesting a conversion on StringValue with a default value

- removed (even more) StringValueConversionException from method signature when it's actually not thrown


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/818620be
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/818620be
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/818620be

Branch: refs/heads/master
Commit: 818620be0697562f4afdcec5e06cc914a818919b
Parents: abe41a9
Author: Peter Ertl <pe...@apache.org>
Authored: Wed Feb 1 00:15:53 2012 +0100
Committer: Peter Ertl <pe...@apache.org>
Committed: Wed Feb 1 00:15:53 2012 +0100

----------------------------------------------------------------------
 .../wicket/util/value/CopyOnWriteValueMap.java     |  905 ++++----
 .../org/apache/wicket/util/value/ValueMap.java     | 1708 +++++++--------
 2 files changed, 1293 insertions(+), 1320 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/818620be/wicket-util/src/main/java/org/apache/wicket/util/value/CopyOnWriteValueMap.java
----------------------------------------------------------------------
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/value/CopyOnWriteValueMap.java b/wicket-util/src/main/java/org/apache/wicket/util/value/CopyOnWriteValueMap.java
index 9993906..d91b429 100755
--- a/wicket-util/src/main/java/org/apache/wicket/util/value/CopyOnWriteValueMap.java
+++ b/wicket-util/src/main/java/org/apache/wicket/util/value/CopyOnWriteValueMap.java
@@ -1,455 +1,452 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.util.value;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.wicket.util.string.StringValue;
-import org.apache.wicket.util.string.StringValueConversionException;
-import org.apache.wicket.util.time.Duration;
-import org.apache.wicket.util.time.Time;
-
-
-/**
- * An implementation of <code>IValueMap</code> that makes a copy when a caller tries to change an
- * immutable <code>Map</code>. That is, the <code>Map</code> may or may not be immutable, but if it
- * is, a copy is made.
- * 
- * @author Johan Compagner
- * @author Doug Donohoe
- * @since 1.2.6
- */
-public class CopyOnWriteValueMap implements IValueMap, Serializable
-{
-	private static final long serialVersionUID = 1L;
-
-	/** the wrapped <code>IValueMap</code> */
-	private IValueMap wrapped;
-
-	/**
-	 * Constructor.
-	 * 
-	 * @param wrapped
-	 *            the wrapped <code>IValueMap</code>
-	 */
-	public CopyOnWriteValueMap(final IValueMap wrapped)
-	{
-		this.wrapped = wrapped;
-	}
-
-	/**
-	 * @see java.util.Map#clear()
-	 */
-	public void clear()
-	{
-		checkAndCopy();
-		wrapped.clear();
-	}
-
-	/**
-	 * Checks if this <code>IValueMap</code> is immutable. If it is, this method makes a new
-	 * <code>IValueMap</code> using the <code>ValueMap</code> copy constructor, and sets it to be
-	 * this <code>CopyOnWriteValueMap</code>.
-	 */
-	private void checkAndCopy()
-	{
-		if (wrapped.isImmutable())
-		{
-			wrapped = new ValueMap(wrapped);
-		}
-	}
-
-	/**
-	 * @see java.util.Map#containsKey(Object)
-	 */
-	public boolean containsKey(final Object key)
-	{
-		return wrapped.containsKey(key);
-	}
-
-	/**
-	 * @see java.util.Map#containsValue(Object)
-	 */
-	public boolean containsValue(final Object value)
-	{
-		return wrapped.containsValue(value);
-	}
-
-	/**
-	 * @see java.util.Map#entrySet()
-	 */
-	public Set<Entry<String, Object>> entrySet()
-	{
-		checkAndCopy();
-		return wrapped.entrySet();
-	}
-
-	/**
-	 * @see java.util.Map#equals(Object)
-	 */
-	@Override
-	public boolean equals(final Object o)
-	{
-		return wrapped.equals(o);
-	}
-
-	/**
-	 * @see java.util.Map#get(Object)
-	 */
-	public Object get(final Object key)
-	{
-		return wrapped.get(key);
-	}
-
-	/**
-	 * @see IValueMap#getBoolean(String)
-	 */
-	public boolean getBoolean(final String key) throws StringValueConversionException
-	{
-		return wrapped.getBoolean(key);
-	}
-
-	/**
-	 * @see IValueMap#getCharSequence(String)
-	 */
-	public CharSequence getCharSequence(final String key)
-	{
-		return wrapped.getCharSequence(key);
-	}
-
-	/**
-	 * @see IValueMap#getDouble(String)
-	 */
-	public double getDouble(final String key) throws StringValueConversionException
-	{
-		return wrapped.getDouble(key);
-	}
-
-	/**
-	 * @see IValueMap#getDouble(String, double)
-	 */
-	public double getDouble(final String key, final double defaultValue)
-		throws StringValueConversionException
-	{
-		return wrapped.getDouble(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getDuration(String)
-	 */
-	public Duration getDuration(final String key) throws StringValueConversionException
-	{
-		return wrapped.getDuration(key);
-	}
-
-	/**
-	 * @see IValueMap#getInt(String, int)
-	 */
-	public int getInt(final String key, final int defaultValue)
-		throws StringValueConversionException
-	{
-		return wrapped.getInt(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getInt(String)
-	 */
-	public int getInt(final String key) throws StringValueConversionException
-	{
-		return wrapped.getInt(key);
-	}
-
-	/**
-	 * @see IValueMap#getKey(String)
-	 */
-	public String getKey(final String key)
-	{
-		return wrapped.getKey(key);
-	}
-
-	/**
-	 * @see IValueMap#getLong(String, long)
-	 */
-	public long getLong(final String key, final long defaultValue)
-		throws StringValueConversionException
-	{
-		return wrapped.getLong(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getLong(String)
-	 */
-	public long getLong(final String key) throws StringValueConversionException
-	{
-		return wrapped.getLong(key);
-	}
-
-	/**
-	 * @see IValueMap#getString(String, String)
-	 */
-	public String getString(final String key, final String defaultValue)
-	{
-		return wrapped.getString(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getString(String)
-	 */
-	public String getString(final String key)
-	{
-		return wrapped.getString(key);
-	}
-
-	/**
-	 * @see IValueMap#getStringArray(String)
-	 */
-	public String[] getStringArray(final String key)
-	{
-		return wrapped.getStringArray(key);
-	}
-
-	/**
-	 * @see IValueMap#getStringValue(String)
-	 */
-	public StringValue getStringValue(final String key)
-	{
-		return wrapped.getStringValue(key);
-	}
-
-	/**
-	 * @see IValueMap#getTime(String)
-	 */
-	public Time getTime(final String key) throws StringValueConversionException
-	{
-		return wrapped.getTime(key);
-	}
-
-	/**
-	 * @see java.util.Map#isEmpty()
-	 */
-	public boolean isEmpty()
-	{
-		return wrapped.isEmpty();
-	}
-
-	/**
-	 * @see IValueMap#isImmutable()
-	 */
-	public boolean isImmutable()
-	{
-		return false;
-	}
-
-	/**
-	 * @see java.util.Map#keySet()
-	 */
-	public Set<String> keySet()
-	{
-		checkAndCopy();
-		return wrapped.keySet();
-	}
-
-	/**
-	 * @see IValueMap#makeImmutable()
-	 */
-	public IValueMap makeImmutable()
-	{
-		return wrapped.makeImmutable();
-	}
-
-	/**
-	 * @see java.util.Map#put(Object, Object)
-	 */
-	public Object put(final String key, final Object value)
-	{
-		checkAndCopy();
-		return wrapped.put(key, value);
-	}
-
-	/**
-	 * @see java.util.Map#putAll(Map)
-	 */
-	public void putAll(final Map<? extends String, ?> map)
-	{
-		checkAndCopy();
-		wrapped.putAll(map);
-	}
-
-	/**
-	 * @see java.util.Map#remove(Object)
-	 */
-	public Object remove(final Object key)
-	{
-		checkAndCopy();
-		return wrapped.remove(key);
-	}
-
-	/**
-	 * @see java.util.Map#size()
-	 */
-	public int size()
-	{
-		return wrapped.size();
-	}
-
-	/**
-	 * @see java.util.Map#values()
-	 */
-	public Collection<Object> values()
-	{
-		return wrapped.values();
-	}
-
-	/**
-	 * @see IValueMap#toString()
-	 */
-	@Override
-	public String toString()
-	{
-		return super.toString();
-	}
-
-	// //
-	// // getAs convenience methods
-	// //
-
-	/**
-	 * @see IValueMap#getAsBoolean(String)
-	 * 
-	 */
-	public Boolean getAsBoolean(final String key)
-	{
-		return wrapped.getAsBoolean(key);
-	}
-
-	/**
-	 * @see IValueMap#getAsBoolean(String, boolean)
-	 * 
-	 */
-	public boolean getAsBoolean(final String key, final boolean defaultValue)
-	{
-		return wrapped.getAsBoolean(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getAsInteger(String)
-	 */
-	public Integer getAsInteger(final String key)
-	{
-		return wrapped.getAsInteger(key);
-	}
-
-	/**
-	 * @see IValueMap#getAsInteger(String, int)
-	 */
-	public int getAsInteger(final String key, final int defaultValue)
-	{
-		return wrapped.getAsInteger(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getAsLong(String)
-	 */
-	public Long getAsLong(final String key)
-	{
-		return wrapped.getAsLong(key);
-	}
-
-	/**
-	 * @see IValueMap#getAsLong(String, long)
-	 */
-	public long getAsLong(final String key, final long defaultValue)
-	{
-		return wrapped.getAsLong(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getAsDouble(String)
-	 */
-	public Double getAsDouble(final String key)
-	{
-		return wrapped.getAsDouble(key);
-	}
-
-	/**
-	 * @see IValueMap#getAsDouble(String, double)
-	 */
-	public double getAsDouble(final String key, final double defaultValue)
-	{
-		return wrapped.getAsDouble(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getAsDuration(String)
-	 */
-	public Duration getAsDuration(final String key)
-	{
-		return wrapped.getAsDuration(key);
-	}
-
-	/**
-	 * @see IValueMap#getAsDuration(String, Duration)
-	 */
-	public Duration getAsDuration(final String key, final Duration defaultValue)
-	{
-		return wrapped.getAsDuration(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getAsTime(String)
-	 */
-	public Time getAsTime(final String key)
-	{
-		return wrapped.getAsTime(key);
-	}
-
-	/**
-	 * @see IValueMap#getAsTime(String, Time)
-	 */
-	public Time getAsTime(final String key, final Time defaultValue)
-	{
-		return wrapped.getAsTime(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getAsEnum(String, Class)
-	 */
-	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass)
-	{
-		return wrapped.getAsEnum(key, eClass);
-	}
-
-	/**
-	 * @see IValueMap#getAsEnum
-	 */
-	public <T extends Enum<T>> T getAsEnum(final String key, final T defaultValue)
-	{
-		return wrapped.getAsEnum(key, defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getAsEnum(String, Class, Enum)
-	 */
-	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass,
-		final T defaultValue)
-	{
-		return wrapped.getAsEnum(key, eClass, defaultValue);
-	}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.util.value;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.wicket.util.string.StringValue;
+import org.apache.wicket.util.string.StringValueConversionException;
+import org.apache.wicket.util.time.Duration;
+import org.apache.wicket.util.time.Time;
+
+
+/**
+ * An implementation of <code>IValueMap</code> that makes a copy when a caller tries to change an
+ * immutable <code>Map</code>. That is, the <code>Map</code> may or may not be immutable, but if it
+ * is, a copy is made.
+ * 
+ * @author Johan Compagner
+ * @author Doug Donohoe
+ * @since 1.2.6
+ */
+public class CopyOnWriteValueMap implements IValueMap, Serializable
+{
+	private static final long serialVersionUID = 1L;
+
+	/** the wrapped <code>IValueMap</code> */
+	private IValueMap wrapped;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param wrapped
+	 *            the wrapped <code>IValueMap</code>
+	 */
+	public CopyOnWriteValueMap(final IValueMap wrapped)
+	{
+		this.wrapped = wrapped;
+	}
+
+	/**
+	 * @see java.util.Map#clear()
+	 */
+	public void clear()
+	{
+		checkAndCopy();
+		wrapped.clear();
+	}
+
+	/**
+	 * Checks if this <code>IValueMap</code> is immutable. If it is, this method makes a new
+	 * <code>IValueMap</code> using the <code>ValueMap</code> copy constructor, and sets it to be
+	 * this <code>CopyOnWriteValueMap</code>.
+	 */
+	private void checkAndCopy()
+	{
+		if (wrapped.isImmutable())
+		{
+			wrapped = new ValueMap(wrapped);
+		}
+	}
+
+	/**
+	 * @see java.util.Map#containsKey(Object)
+	 */
+	public boolean containsKey(final Object key)
+	{
+		return wrapped.containsKey(key);
+	}
+
+	/**
+	 * @see java.util.Map#containsValue(Object)
+	 */
+	public boolean containsValue(final Object value)
+	{
+		return wrapped.containsValue(value);
+	}
+
+	/**
+	 * @see java.util.Map#entrySet()
+	 */
+	public Set<Entry<String, Object>> entrySet()
+	{
+		checkAndCopy();
+		return wrapped.entrySet();
+	}
+
+	/**
+	 * @see java.util.Map#equals(Object)
+	 */
+	@Override
+	public boolean equals(final Object o)
+	{
+		return wrapped.equals(o);
+	}
+
+	/**
+	 * @see java.util.Map#get(Object)
+	 */
+	public Object get(final Object key)
+	{
+		return wrapped.get(key);
+	}
+
+	/**
+	 * @see IValueMap#getBoolean(String)
+	 */
+	public boolean getBoolean(final String key) throws StringValueConversionException
+	{
+		return wrapped.getBoolean(key);
+	}
+
+	/**
+	 * @see IValueMap#getCharSequence(String)
+	 */
+	public CharSequence getCharSequence(final String key)
+	{
+		return wrapped.getCharSequence(key);
+	}
+
+	/**
+	 * @see IValueMap#getDouble(String)
+	 */
+	public double getDouble(final String key) throws StringValueConversionException
+	{
+		return wrapped.getDouble(key);
+	}
+
+	/**
+	 * @see IValueMap#getDouble(String, double)
+	 */
+	public double getDouble(final String key, final double defaultValue)
+	{
+		return wrapped.getDouble(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getDuration(String)
+	 */
+	public Duration getDuration(final String key) throws StringValueConversionException
+	{
+		return wrapped.getDuration(key);
+	}
+
+	/**
+	 * @see IValueMap#getInt(String, int)
+	 */
+	public int getInt(final String key, final int defaultValue)
+	{
+		return wrapped.getInt(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getInt(String)
+	 */
+	public int getInt(final String key) throws StringValueConversionException
+	{
+		return wrapped.getInt(key);
+	}
+
+	/**
+	 * @see IValueMap#getKey(String)
+	 */
+	public String getKey(final String key)
+	{
+		return wrapped.getKey(key);
+	}
+
+	/**
+	 * @see IValueMap#getLong(String, long)
+	 */
+	public long getLong(final String key, final long defaultValue)
+	{
+		return wrapped.getLong(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getLong(String)
+	 */
+	public long getLong(final String key) throws StringValueConversionException
+	{
+		return wrapped.getLong(key);
+	}
+
+	/**
+	 * @see IValueMap#getString(String, String)
+	 */
+	public String getString(final String key, final String defaultValue)
+	{
+		return wrapped.getString(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getString(String)
+	 */
+	public String getString(final String key)
+	{
+		return wrapped.getString(key);
+	}
+
+	/**
+	 * @see IValueMap#getStringArray(String)
+	 */
+	public String[] getStringArray(final String key)
+	{
+		return wrapped.getStringArray(key);
+	}
+
+	/**
+	 * @see IValueMap#getStringValue(String)
+	 */
+	public StringValue getStringValue(final String key)
+	{
+		return wrapped.getStringValue(key);
+	}
+
+	/**
+	 * @see IValueMap#getTime(String)
+	 */
+	public Time getTime(final String key) throws StringValueConversionException
+	{
+		return wrapped.getTime(key);
+	}
+
+	/**
+	 * @see java.util.Map#isEmpty()
+	 */
+	public boolean isEmpty()
+	{
+		return wrapped.isEmpty();
+	}
+
+	/**
+	 * @see IValueMap#isImmutable()
+	 */
+	public boolean isImmutable()
+	{
+		return false;
+	}
+
+	/**
+	 * @see java.util.Map#keySet()
+	 */
+	public Set<String> keySet()
+	{
+		checkAndCopy();
+		return wrapped.keySet();
+	}
+
+	/**
+	 * @see IValueMap#makeImmutable()
+	 */
+	public IValueMap makeImmutable()
+	{
+		return wrapped.makeImmutable();
+	}
+
+	/**
+	 * @see java.util.Map#put(Object, Object)
+	 */
+	public Object put(final String key, final Object value)
+	{
+		checkAndCopy();
+		return wrapped.put(key, value);
+	}
+
+	/**
+	 * @see java.util.Map#putAll(Map)
+	 */
+	public void putAll(final Map<? extends String, ?> map)
+	{
+		checkAndCopy();
+		wrapped.putAll(map);
+	}
+
+	/**
+	 * @see java.util.Map#remove(Object)
+	 */
+	public Object remove(final Object key)
+	{
+		checkAndCopy();
+		return wrapped.remove(key);
+	}
+
+	/**
+	 * @see java.util.Map#size()
+	 */
+	public int size()
+	{
+		return wrapped.size();
+	}
+
+	/**
+	 * @see java.util.Map#values()
+	 */
+	public Collection<Object> values()
+	{
+		return wrapped.values();
+	}
+
+	/**
+	 * @see IValueMap#toString()
+	 */
+	@Override
+	public String toString()
+	{
+		return super.toString();
+	}
+
+	// //
+	// // getAs convenience methods
+	// //
+
+	/**
+	 * @see IValueMap#getAsBoolean(String)
+	 * 
+	 */
+	public Boolean getAsBoolean(final String key)
+	{
+		return wrapped.getAsBoolean(key);
+	}
+
+	/**
+	 * @see IValueMap#getAsBoolean(String, boolean)
+	 * 
+	 */
+	public boolean getAsBoolean(final String key, final boolean defaultValue)
+	{
+		return wrapped.getAsBoolean(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsInteger(String)
+	 */
+	public Integer getAsInteger(final String key)
+	{
+		return wrapped.getAsInteger(key);
+	}
+
+	/**
+	 * @see IValueMap#getAsInteger(String, int)
+	 */
+	public int getAsInteger(final String key, final int defaultValue)
+	{
+		return wrapped.getAsInteger(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsLong(String)
+	 */
+	public Long getAsLong(final String key)
+	{
+		return wrapped.getAsLong(key);
+	}
+
+	/**
+	 * @see IValueMap#getAsLong(String, long)
+	 */
+	public long getAsLong(final String key, final long defaultValue)
+	{
+		return wrapped.getAsLong(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsDouble(String)
+	 */
+	public Double getAsDouble(final String key)
+	{
+		return wrapped.getAsDouble(key);
+	}
+
+	/**
+	 * @see IValueMap#getAsDouble(String, double)
+	 */
+	public double getAsDouble(final String key, final double defaultValue)
+	{
+		return wrapped.getAsDouble(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsDuration(String)
+	 */
+	public Duration getAsDuration(final String key)
+	{
+		return wrapped.getAsDuration(key);
+	}
+
+	/**
+	 * @see IValueMap#getAsDuration(String, Duration)
+	 */
+	public Duration getAsDuration(final String key, final Duration defaultValue)
+	{
+		return wrapped.getAsDuration(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsTime(String)
+	 */
+	public Time getAsTime(final String key)
+	{
+		return wrapped.getAsTime(key);
+	}
+
+	/**
+	 * @see IValueMap#getAsTime(String, Time)
+	 */
+	public Time getAsTime(final String key, final Time defaultValue)
+	{
+		return wrapped.getAsTime(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsEnum(String, Class)
+	 */
+	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass)
+	{
+		return wrapped.getAsEnum(key, eClass);
+	}
+
+	/**
+	 * @see IValueMap#getAsEnum
+	 */
+	public <T extends Enum<T>> T getAsEnum(final String key, final T defaultValue)
+	{
+		return wrapped.getAsEnum(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsEnum(String, Class, Enum)
+	 */
+	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass,
+		final T defaultValue)
+	{
+		return wrapped.getAsEnum(key, eClass, defaultValue);
+	}
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/818620be/wicket-util/src/main/java/org/apache/wicket/util/value/ValueMap.java
----------------------------------------------------------------------
diff --git a/wicket-util/src/main/java/org/apache/wicket/util/value/ValueMap.java b/wicket-util/src/main/java/org/apache/wicket/util/value/ValueMap.java
index 7f508fa..8693ab0 100755
--- a/wicket-util/src/main/java/org/apache/wicket/util/value/ValueMap.java
+++ b/wicket-util/src/main/java/org/apache/wicket/util/value/ValueMap.java
@@ -1,866 +1,842 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.wicket.util.value;
-
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-import org.apache.wicket.util.parse.metapattern.MetaPattern;
-import org.apache.wicket.util.parse.metapattern.parsers.VariableAssignmentParser;
-import org.apache.wicket.util.string.IStringIterator;
-import org.apache.wicket.util.string.StringList;
-import org.apache.wicket.util.string.StringValue;
-import org.apache.wicket.util.string.StringValueConversionException;
-import org.apache.wicket.util.time.Duration;
-import org.apache.wicket.util.time.Time;
-
-
-/**
- * A <code>IValueMap</code> implementation that holds values, parses <code>String</code>s, and
- * exposes a variety of convenience methods.
- * <p>
- * In addition to a no-arg constructor and a copy constructor that takes a <code>Map</code>
- * argument, <code>ValueMap</code>s can be constructed using a parsing constructor.
- * <code>ValueMap(String)</code> will parse values from the string in comma separated key/value
- * assignment pairs. For example, <code>new ValueMap("a=9,b=foo")</code>.
- * <p>
- * Values can be retrieved from the <code>ValueMap</code> in the usual way or with methods that do
- * handy conversions to various types, including <code>String</code>, <code>StringValue</code>,
- * <code>int</code>, <code>long</code>, <code>double</code>, <code>Time</code> and
- * <code>Duration</code>.
- * <p>
- * The <code>makeImmutable</code> method will make the underlying <code>Map</code> immutable.
- * Further attempts to change the <code>Map</code> will result in a <code>RuntimeException</code>.
- * <p>
- * The <code>toString</code> method converts a <code>ValueMap</code> object to a readable key/value
- * string for diagnostics.
- * 
- * @author Jonathan Locke
- * @author Doug Donohoe
- * @since 1.2.6
- */
-public class ValueMap extends LinkedHashMap<String, Object> implements IValueMap
-{
-	/** an empty <code>ValueMap</code>. */
-	public static final ValueMap EMPTY_MAP;
-
-	/** create EMPTY_MAP, make immutable * */
-	static
-	{
-		EMPTY_MAP = new ValueMap();
-		EMPTY_MAP.makeImmutable();
-	}
-
-	private static final long serialVersionUID = 1L;
-
-	/**
-	 * <code>true</code> if this <code>ValueMap</code> has been made immutable.
-	 */
-	private boolean immutable = false;
-
-	/**
-	 * Constructs empty <code>ValueMap</code>.
-	 */
-	public ValueMap()
-	{
-		super();
-	}
-
-	/**
-	 * Copy constructor.
-	 * 
-	 * @param map
-	 *            the <code>ValueMap</code> to copy
-	 */
-	public ValueMap(final Map<? extends String, ?> map)
-	{
-		super();
-
-		super.putAll(map);
-	}
-
-	/**
-	 * Constructor.
-	 * <p>
-	 * NOTE: Please use <code>RequestUtils.decodeParameters()</code> if you wish to properly decode
-	 * a request URL.
-	 * 
-	 * @param keyValuePairs
-	 *            list of key/value pairs separated by commas. For example, "
-	 *            <code>param1=foo,param2=bar</code>"
-	 */
-	public ValueMap(final String keyValuePairs)
-	{
-		this(keyValuePairs, ",");
-	}
-
-	/**
-	 * Constructor.
-	 * <p>
-	 * NOTE: Please use <code>RequestUtils.decodeParameters()</code> if you wish to properly decode
-	 * a request URL.
-	 * 
-	 * @param keyValuePairs
-	 *            list of key/value pairs separated by a given delimiter. For example, "
-	 *            <code>param1=foo,param2=bar</code>" where delimiter is "<code>,</code>".
-	 * @param delimiter
-	 *            delimiter <code>String</code> used to separate key/value pairs
-	 */
-	public ValueMap(final String keyValuePairs, final String delimiter)
-	{
-		super();
-
-		int start = 0;
-		int equalsIndex = keyValuePairs.indexOf('=');
-		int delimiterIndex = keyValuePairs.indexOf(delimiter, equalsIndex);
-		if (delimiterIndex == -1)
-		{
-			delimiterIndex = keyValuePairs.length();
-		}
-		while (equalsIndex != -1)
-		{
-			if (delimiterIndex < keyValuePairs.length())
-			{
-				int equalsIndex2 = keyValuePairs.indexOf('=', delimiterIndex + 1);
-				if (equalsIndex2 != -1)
-				{
-					delimiterIndex = keyValuePairs.lastIndexOf(delimiter, equalsIndex2);
-				}
-				else
-				{
-					delimiterIndex = keyValuePairs.length();
-				}
-			}
-			String key = keyValuePairs.substring(start, equalsIndex);
-			String value = keyValuePairs.substring(equalsIndex + 1, delimiterIndex);
-			add(key, value);
-			if (delimiterIndex < keyValuePairs.length())
-			{
-				start = delimiterIndex + 1;
-				equalsIndex = keyValuePairs.indexOf('=', start);
-				if (equalsIndex != -1)
-				{
-					delimiterIndex = keyValuePairs.indexOf(delimiter, equalsIndex);
-					if (delimiterIndex == -1)
-					{
-						delimiterIndex = keyValuePairs.length();
-					}
-				}
-			}
-			else
-			{
-				equalsIndex = -1;
-			}
-		}
-	}
-
-	/**
-	 * Constructor.
-	 * 
-	 * @param keyValuePairs
-	 *            list of key/value pairs separated by a given delimiter. For example, "
-	 *            <code>param1=foo,param2=bar</code>" where delimiter is "<code>,</code>".
-	 * @param delimiter
-	 *            delimiter string used to separate key/value pairs
-	 * @param valuePattern
-	 *            pattern for value. To pass a simple regular expression, pass "
-	 *            <code>new MetaPattern(regexp)</code>".
-	 */
-	public ValueMap(final String keyValuePairs, final String delimiter,
-		final MetaPattern valuePattern)
-	{
-		super();
-
-		// Get list of strings separated by the delimiter
-		final StringList pairs = StringList.tokenize(keyValuePairs, delimiter);
-
-		// Go through each string in the list
-		for (IStringIterator iterator = pairs.iterator(); iterator.hasNext();)
-		{
-			// Get the next key value pair
-			final String pair = iterator.next();
-
-			// Parse using metapattern parser for variable assignments
-			final VariableAssignmentParser parser = new VariableAssignmentParser(pair, valuePattern);
-
-			// Does it parse?
-			if (parser.matches())
-			{
-				// Succeeded. Put key and value into map
-				put(parser.getKey(), parser.getValue());
-			}
-			else
-			{
-				throw new IllegalArgumentException("Invalid key value list: '" + keyValuePairs +
-					'\'');
-			}
-		}
-	}
-
-	/**
-	 * @see java.util.Map#clear()
-	 */
-	@Override
-	public final void clear()
-	{
-		checkMutability();
-		super.clear();
-	}
-
-	/**
-	 * @see IValueMap#getBoolean(String)
-	 */
-	public final boolean getBoolean(final String key) throws StringValueConversionException
-	{
-		return getStringValue(key).toBoolean();
-	}
-
-	/**
-	 * @see IValueMap#getDouble(String)
-	 */
-	public final double getDouble(final String key) throws StringValueConversionException
-	{
-		return getStringValue(key).toDouble();
-	}
-
-	/**
-	 * @see IValueMap#getDouble(String, double)
-	 */
-	public final double getDouble(final String key, final double defaultValue)
-		throws StringValueConversionException
-	{
-		return getStringValue(key).toDouble(defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getDuration(String)
-	 */
-	public final Duration getDuration(final String key) throws StringValueConversionException
-	{
-		return getStringValue(key).toDuration();
-	}
-
-	/**
-	 * @see IValueMap#getInt(String)
-	 */
-	public final int getInt(final String key) throws StringValueConversionException
-	{
-		return getStringValue(key).toInt();
-	}
-
-	/**
-	 * @see IValueMap#getInt(String, int)
-	 */
-	public final int getInt(final String key, final int defaultValue)
-		throws StringValueConversionException
-	{
-		return getStringValue(key).toInt(defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getLong(String)
-	 */
-	public final long getLong(final String key) throws StringValueConversionException
-	{
-		return getStringValue(key).toLong();
-	}
-
-	/**
-	 * @see IValueMap#getLong(String, long)
-	 */
-	public final long getLong(final String key, final long defaultValue)
-		throws StringValueConversionException
-	{
-		return getStringValue(key).toLong(defaultValue);
-	}
-
-	/**
-	 * @see IValueMap#getString(String, String)
-	 */
-	public final String getString(final String key, final String defaultValue)
-	{
-		final String value = getString(key);
-		return value != null ? value : defaultValue;
-	}
-
-	/**
-	 * @see IValueMap#getString(String)
-	 */
-	public final String getString(final String key)
-	{
-		final Object o = get(key);
-		if (o == null)
-		{
-			return null;
-		}
-		else if (o.getClass().isArray() && (Array.getLength(o) > 0))
-		{
-			// if it is an array just get the first value
-			final Object arrayValue = Array.get(o, 0);
-			if (arrayValue == null)
-			{
-				return null;
-			}
-			else
-			{
-				return arrayValue.toString();
-			}
-
-		}
-		else
-		{
-			return o.toString();
-		}
-	}
-
-	/**
-	 * @see IValueMap#getCharSequence(String)
-	 */
-	public final CharSequence getCharSequence(final String key)
-	{
-		final Object o = get(key);
-		if (o == null)
-		{
-			return null;
-		}
-		else if (o.getClass().isArray() && (Array.getLength(o) > 0))
-		{
-			// if it is an array just get the first value
-			final Object arrayValue = Array.get(o, 0);
-			if (arrayValue == null)
-			{
-				return null;
-			}
-			else
-			{
-				if (arrayValue instanceof CharSequence)
-				{
-					return (CharSequence)arrayValue;
-				}
-				return arrayValue.toString();
-			}
-
-		}
-		else
-		{
-			if (o instanceof CharSequence)
-			{
-				return (CharSequence)o;
-			}
-			return o.toString();
-		}
-	}
-
-	/**
-	 * @see IValueMap#getStringArray(String)
-	 */
-	public String[] getStringArray(final String key)
-	{
-		final Object o = get(key);
-		if (o == null)
-		{
-			return null;
-		}
-		else if (o instanceof String[])
-		{
-			return (String[])o;
-		}
-		else if (o.getClass().isArray())
-		{
-			int length = Array.getLength(o);
-			String[] array = new String[length];
-			for (int i = 0; i < length; i++)
-			{
-				final Object arrayValue = Array.get(o, i);
-				if (arrayValue != null)
-				{
-					array[i] = arrayValue.toString();
-				}
-			}
-			return array;
-		}
-		return new String[] { o.toString() };
-	}
-
-	/**
-	 * @see IValueMap#getStringValue(String)
-	 */
-	public StringValue getStringValue(final String key)
-	{
-		return StringValue.valueOf(getString(key));
-	}
-
-	/**
-	 * @see IValueMap#getTime(String)
-	 */
-	public final Time getTime(final String key) throws StringValueConversionException
-	{
-		return getStringValue(key).toTime();
-	}
-
-	/**
-	 * @see IValueMap#isImmutable()
-	 */
-	public final boolean isImmutable()
-	{
-		return immutable;
-	}
-
-	/**
-	 * @see IValueMap#makeImmutable()
-	 */
-	public final IValueMap makeImmutable()
-	{
-		immutable = true;
-		return this;
-	}
-
-	/**
-	 * @see java.util.Map#put(Object, Object)
-	 */
-	@Override
-	public Object put(final String key, final Object value)
-	{
-		checkMutability();
-		return super.put(key, value);
-	}
-
-	/**
-	 * Adds the value to this <code>ValueMap</code> with the given key. If the key already is in the
-	 * <code>ValueMap</code> it will combine the values into a <code>String</code> array, else it
-	 * will just store the value itself.
-	 * 
-	 * @param key
-	 *            the key to store the value under
-	 * @param value
-	 *            the value that must be added/merged to the <code>ValueMap</code>
-	 * @return the value itself if there was no previous value, or a <code>String</code> array with
-	 *         the combined values
-	 */
-	public final Object add(final String key, final String value)
-	{
-		checkMutability();
-		final Object o = get(key);
-		if (o == null)
-		{
-			return put(key, value);
-		}
-		else if (o.getClass().isArray())
-		{
-			int length = Array.getLength(o);
-			String destArray[] = new String[length + 1];
-			for (int i = 0; i < length; i++)
-			{
-				final Object arrayValue = Array.get(o, i);
-				if (arrayValue != null)
-				{
-					destArray[i] = arrayValue.toString();
-				}
-			}
-			destArray[length] = value;
-
-			return put(key, destArray);
-		}
-		else
-		{
-			return put(key, new String[] { o.toString(), value });
-		}
-	}
-
-	/**
-	 * @see java.util.Map#putAll(java.util.Map)
-	 */
-	@Override
-	public void putAll(final Map<? extends String, ?> map)
-	{
-		checkMutability();
-		super.putAll(map);
-	}
-
-	/**
-	 * @see java.util.Map#remove(java.lang.Object)
-	 */
-	@Override
-	public Object remove(final Object key)
-	{
-		checkMutability();
-		return super.remove(key);
-	}
-
-	/**
-	 * @see IValueMap#getKey(String)
-	 */
-	public String getKey(final String key)
-	{
-		for (String other : keySet())
-		{
-			if (other.equalsIgnoreCase(key))
-			{
-				return other;
-			}
-		}
-		return null;
-	}
-
-	/**
-	 * Generates a <code>String</code> representation of this object.
-	 * 
-	 * @return <code>String</code> representation of this <code>ValueMap</code> consistent with the
-	 *         tag-attribute style of markup elements. For example: <code>a="x" b="y" c="z"</code>.
-	 */
-	@Override
-	public String toString()
-	{
-		final StringBuilder buffer = new StringBuilder();
-		boolean first = true;
-		for (Map.Entry<String, Object> entry : entrySet())
-		{
-			if (first == false)
-			{
-				buffer.append(' ');
-			}
-			first = false;
-
-			buffer.append(entry.getKey());
-			buffer.append(" = \"");
-			final Object value = entry.getValue();
-			if (value == null)
-			{
-				buffer.append("null");
-			}
-			else if (value.getClass().isArray())
-			{
-				buffer.append(Arrays.asList((Object[])value));
-			}
-			else
-			{
-				buffer.append(value);
-			}
-
-			buffer.append('\"');
-		}
-		return buffer.toString();
-	}
-
-	/**
-	 * Throws an exception if <code>ValueMap</code> is immutable.
-	 */
-	private void checkMutability()
-	{
-		if (immutable)
-		{
-			throw new UnsupportedOperationException("Map is immutable");
-		}
-	}
-
-	// //
-	// // getAs convenience methods
-	// //
-
-	/**
-	 * @see IValueMap#getAsBoolean(String)
-	 * 
-	 */
-	public Boolean getAsBoolean(final String key)
-	{
-		if (!containsKey(key))
-		{
-			return null;
-		}
-
-		try
-		{
-			return getBoolean(key);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return null;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsBoolean(String, boolean)
-	 * 
-	 */
-	public boolean getAsBoolean(final String key, final boolean defaultValue)
-	{
-		if (!containsKey(key))
-		{
-			return defaultValue;
-		}
-
-		try
-		{
-			return getBoolean(key);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return defaultValue;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsInteger(String)
-	 */
-	public Integer getAsInteger(final String key)
-	{
-		if (!containsKey(key))
-		{
-			return null;
-		}
-
-		try
-		{
-			return getInt(key);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return null;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsInteger(String, int)
-	 */
-	public int getAsInteger(final String key, final int defaultValue)
-	{
-		try
-		{
-			return getInt(key, defaultValue);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return defaultValue;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsLong(String)
-	 */
-	public Long getAsLong(final String key)
-	{
-		if (!containsKey(key))
-		{
-			return null;
-		}
-
-		try
-		{
-			return getLong(key);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return null;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsLong(String, long)
-	 */
-	public long getAsLong(final String key, final long defaultValue)
-	{
-		try
-		{
-			return getLong(key, defaultValue);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return defaultValue;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsDouble(String)
-	 */
-	public Double getAsDouble(final String key)
-	{
-		if (!containsKey(key))
-		{
-			return null;
-		}
-
-		try
-		{
-			return getDouble(key);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return null;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsDouble(String, double)
-	 */
-	public double getAsDouble(final String key, final double defaultValue)
-	{
-		try
-		{
-			return getDouble(key, defaultValue);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return defaultValue;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsDuration(String)
-	 */
-	public Duration getAsDuration(final String key)
-	{
-		return getAsDuration(key, null);
-	}
-
-	/**
-	 * @see IValueMap#getAsDuration(String, Duration)
-	 */
-	public Duration getAsDuration(final String key, final Duration defaultValue)
-	{
-		if (!containsKey(key))
-		{
-			return defaultValue;
-		}
-
-		try
-		{
-			return getDuration(key);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return defaultValue;
-		}
-	}
-
-	/**
-	 * @see IValueMap#getAsTime(String)
-	 */
-	public Time getAsTime(final String key)
-	{
-		return getAsTime(key, null);
-	}
-
-	/**
-	 * @see IValueMap#getAsTime(String, Time)
-	 */
-	public Time getAsTime(final String key, final Time defaultValue)
-	{
-		if (!containsKey(key))
-		{
-			return defaultValue;
-		}
-
-		try
-		{
-			return getTime(key);
-		}
-		catch (StringValueConversionException ignored)
-		{
-			return defaultValue;
-		}
-	}
-
-	/**
-	 * @see org.apache.wicket.util.value.IValueMap#getAsEnum(java.lang.String, java.lang.Class)
-	 */
-	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass)
-	{
-		return getEnumImpl(key, eClass, null);
-	}
-
-	/**
-	 * @see org.apache.wicket.util.value.IValueMap#getAsEnum(java.lang.String, java.lang.Enum)
-	 */
-	public <T extends Enum<T>> T getAsEnum(final String key, final T defaultValue)
-	{
-		if (defaultValue == null)
-		{
-			throw new IllegalArgumentException("Default value cannot be null");
-		}
-
-		return getEnumImpl(key, defaultValue.getClass(), defaultValue);
-	}
-
-	/**
-	 * @see org.apache.wicket.util.value.IValueMap#getAsEnum(java.lang.String, java.lang.Class,
-	 *      java.lang.Enum)
-	 */
-	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass,
-		final T defaultValue)
-	{
-		return getEnumImpl(key, eClass, defaultValue);
-	}
-
-	/**
-	 * get enum implementation
-	 * 
-	 * @param key
-	 * @param eClass
-	 * @param defaultValue
-	 * @param <T>
-	 * @return Enum
-	 */
-	@SuppressWarnings({ "unchecked" })
-	private <T extends Enum<T>> T getEnumImpl(final String key, final Class<?> eClass,
-		final T defaultValue)
-	{
-		if (eClass == null)
-		{
-			throw new IllegalArgumentException("eClass value cannot be null");
-		}
-
-		String value = getString(key);
-		if (value == null)
-		{
-			return defaultValue;
-		}
-
-		Method valueOf = null;
-		try
-		{
-			valueOf = eClass.getMethod("valueOf", String.class);
-		}
-		catch (NoSuchMethodException e)
-		{
-			throw new RuntimeException("Could not find method valueOf(String s) for " +
-				eClass.getName(), e);
-		}
-
-		try
-		{
-			return (T)valueOf.invoke(eClass, value);
-		}
-		catch (IllegalAccessException e)
-		{
-			throw new RuntimeException("Could not invoke method valueOf(String s) on " +
-				eClass.getName(), e);
-		}
-		catch (InvocationTargetException e)
-		{
-			// IllegalArgumentException thrown if enum isn't defined - just return default
-			if (e.getCause() instanceof IllegalArgumentException)
-			{
-				return defaultValue;
-			}
-			throw new RuntimeException(e); // shouldn't happen
-		}
-	}
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.wicket.util.value;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.wicket.util.parse.metapattern.MetaPattern;
+import org.apache.wicket.util.parse.metapattern.parsers.VariableAssignmentParser;
+import org.apache.wicket.util.string.IStringIterator;
+import org.apache.wicket.util.string.StringList;
+import org.apache.wicket.util.string.StringValue;
+import org.apache.wicket.util.string.StringValueConversionException;
+import org.apache.wicket.util.time.Duration;
+import org.apache.wicket.util.time.Time;
+
+
+/**
+ * A <code>IValueMap</code> implementation that holds values, parses <code>String</code>s, and
+ * exposes a variety of convenience methods.
+ * <p>
+ * In addition to a no-arg constructor and a copy constructor that takes a <code>Map</code>
+ * argument, <code>ValueMap</code>s can be constructed using a parsing constructor.
+ * <code>ValueMap(String)</code> will parse values from the string in comma separated key/value
+ * assignment pairs. For example, <code>new ValueMap("a=9,b=foo")</code>.
+ * <p>
+ * Values can be retrieved from the <code>ValueMap</code> in the usual way or with methods that do
+ * handy conversions to various types, including <code>String</code>, <code>StringValue</code>,
+ * <code>int</code>, <code>long</code>, <code>double</code>, <code>Time</code> and
+ * <code>Duration</code>.
+ * <p>
+ * The <code>makeImmutable</code> method will make the underlying <code>Map</code> immutable.
+ * Further attempts to change the <code>Map</code> will result in a <code>RuntimeException</code>.
+ * <p>
+ * The <code>toString</code> method converts a <code>ValueMap</code> object to a readable key/value
+ * string for diagnostics.
+ * 
+ * @author Jonathan Locke
+ * @author Doug Donohoe
+ * @since 1.2.6
+ */
+public class ValueMap extends LinkedHashMap<String, Object> implements IValueMap
+{
+	/** an empty <code>ValueMap</code>. */
+	public static final ValueMap EMPTY_MAP;
+
+	/** create EMPTY_MAP, make immutable * */
+	static
+	{
+		EMPTY_MAP = new ValueMap();
+		EMPTY_MAP.makeImmutable();
+	}
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * <code>true</code> if this <code>ValueMap</code> has been made immutable.
+	 */
+	private boolean immutable = false;
+
+	/**
+	 * Constructs empty <code>ValueMap</code>.
+	 */
+	public ValueMap()
+	{
+		super();
+	}
+
+	/**
+	 * Copy constructor.
+	 * 
+	 * @param map
+	 *            the <code>ValueMap</code> to copy
+	 */
+	public ValueMap(final Map<? extends String, ?> map)
+	{
+		super();
+
+		super.putAll(map);
+	}
+
+	/**
+	 * Constructor.
+	 * <p>
+	 * NOTE: Please use <code>RequestUtils.decodeParameters()</code> if you wish to properly decode
+	 * a request URL.
+	 * 
+	 * @param keyValuePairs
+	 *            list of key/value pairs separated by commas. For example, "
+	 *            <code>param1=foo,param2=bar</code>"
+	 */
+	public ValueMap(final String keyValuePairs)
+	{
+		this(keyValuePairs, ",");
+	}
+
+	/**
+	 * Constructor.
+	 * <p>
+	 * NOTE: Please use <code>RequestUtils.decodeParameters()</code> if you wish to properly decode
+	 * a request URL.
+	 * 
+	 * @param keyValuePairs
+	 *            list of key/value pairs separated by a given delimiter. For example, "
+	 *            <code>param1=foo,param2=bar</code>" where delimiter is "<code>,</code>".
+	 * @param delimiter
+	 *            delimiter <code>String</code> used to separate key/value pairs
+	 */
+	public ValueMap(final String keyValuePairs, final String delimiter)
+	{
+		super();
+
+		int start = 0;
+		int equalsIndex = keyValuePairs.indexOf('=');
+		int delimiterIndex = keyValuePairs.indexOf(delimiter, equalsIndex);
+		if (delimiterIndex == -1)
+		{
+			delimiterIndex = keyValuePairs.length();
+		}
+		while (equalsIndex != -1)
+		{
+			if (delimiterIndex < keyValuePairs.length())
+			{
+				int equalsIndex2 = keyValuePairs.indexOf('=', delimiterIndex + 1);
+				if (equalsIndex2 != -1)
+				{
+					delimiterIndex = keyValuePairs.lastIndexOf(delimiter, equalsIndex2);
+				}
+				else
+				{
+					delimiterIndex = keyValuePairs.length();
+				}
+			}
+			String key = keyValuePairs.substring(start, equalsIndex);
+			String value = keyValuePairs.substring(equalsIndex + 1, delimiterIndex);
+			add(key, value);
+			if (delimiterIndex < keyValuePairs.length())
+			{
+				start = delimiterIndex + 1;
+				equalsIndex = keyValuePairs.indexOf('=', start);
+				if (equalsIndex != -1)
+				{
+					delimiterIndex = keyValuePairs.indexOf(delimiter, equalsIndex);
+					if (delimiterIndex == -1)
+					{
+						delimiterIndex = keyValuePairs.length();
+					}
+				}
+			}
+			else
+			{
+				equalsIndex = -1;
+			}
+		}
+	}
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param keyValuePairs
+	 *            list of key/value pairs separated by a given delimiter. For example, "
+	 *            <code>param1=foo,param2=bar</code>" where delimiter is "<code>,</code>".
+	 * @param delimiter
+	 *            delimiter string used to separate key/value pairs
+	 * @param valuePattern
+	 *            pattern for value. To pass a simple regular expression, pass "
+	 *            <code>new MetaPattern(regexp)</code>".
+	 */
+	public ValueMap(final String keyValuePairs, final String delimiter,
+		final MetaPattern valuePattern)
+	{
+		super();
+
+		// Get list of strings separated by the delimiter
+		final StringList pairs = StringList.tokenize(keyValuePairs, delimiter);
+
+		// Go through each string in the list
+		for (IStringIterator iterator = pairs.iterator(); iterator.hasNext();)
+		{
+			// Get the next key value pair
+			final String pair = iterator.next();
+
+			// Parse using metapattern parser for variable assignments
+			final VariableAssignmentParser parser = new VariableAssignmentParser(pair, valuePattern);
+
+			// Does it parse?
+			if (parser.matches())
+			{
+				// Succeeded. Put key and value into map
+				put(parser.getKey(), parser.getValue());
+			}
+			else
+			{
+				throw new IllegalArgumentException("Invalid key value list: '" + keyValuePairs +
+					'\'');
+			}
+		}
+	}
+
+	/**
+	 * @see java.util.Map#clear()
+	 */
+	@Override
+	public final void clear()
+	{
+		checkMutability();
+		super.clear();
+	}
+
+	/**
+	 * @see IValueMap#getBoolean(String)
+	 */
+	public final boolean getBoolean(final String key) throws StringValueConversionException
+	{
+		return getStringValue(key).toBoolean();
+	}
+
+	/**
+	 * @see IValueMap#getDouble(String)
+	 */
+	public final double getDouble(final String key) throws StringValueConversionException
+	{
+		return getStringValue(key).toDouble();
+	}
+
+	/**
+	 * @see IValueMap#getDouble(String, double)
+	 */
+	public final double getDouble(final String key, final double defaultValue)
+	{
+		return getStringValue(key).toDouble(defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getDuration(String)
+	 */
+	public final Duration getDuration(final String key) throws StringValueConversionException
+	{
+		return getStringValue(key).toDuration();
+	}
+
+	/**
+	 * @see IValueMap#getInt(String)
+	 */
+	public final int getInt(final String key) throws StringValueConversionException
+	{
+		return getStringValue(key).toInt();
+	}
+
+	/**
+	 * @see IValueMap#getInt(String, int)
+	 */
+	public final int getInt(final String key, final int defaultValue)
+	{
+		return getStringValue(key).toInt(defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getLong(String)
+	 */
+	public final long getLong(final String key) throws StringValueConversionException
+	{
+		return getStringValue(key).toLong();
+	}
+
+	/**
+	 * @see IValueMap#getLong(String, long)
+	 */
+	public final long getLong(final String key, final long defaultValue)
+	{
+		return getStringValue(key).toLong(defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getString(String, String)
+	 */
+	public final String getString(final String key, final String defaultValue)
+	{
+		final String value = getString(key);
+		return value != null ? value : defaultValue;
+	}
+
+	/**
+	 * @see IValueMap#getString(String)
+	 */
+	public final String getString(final String key)
+	{
+		final Object o = get(key);
+		if (o == null)
+		{
+			return null;
+		}
+		else if (o.getClass().isArray() && (Array.getLength(o) > 0))
+		{
+			// if it is an array just get the first value
+			final Object arrayValue = Array.get(o, 0);
+			if (arrayValue == null)
+			{
+				return null;
+			}
+			else
+			{
+				return arrayValue.toString();
+			}
+
+		}
+		else
+		{
+			return o.toString();
+		}
+	}
+
+	/**
+	 * @see IValueMap#getCharSequence(String)
+	 */
+	public final CharSequence getCharSequence(final String key)
+	{
+		final Object o = get(key);
+		if (o == null)
+		{
+			return null;
+		}
+		else if (o.getClass().isArray() && (Array.getLength(o) > 0))
+		{
+			// if it is an array just get the first value
+			final Object arrayValue = Array.get(o, 0);
+			if (arrayValue == null)
+			{
+				return null;
+			}
+			else
+			{
+				if (arrayValue instanceof CharSequence)
+				{
+					return (CharSequence)arrayValue;
+				}
+				return arrayValue.toString();
+			}
+
+		}
+		else
+		{
+			if (o instanceof CharSequence)
+			{
+				return (CharSequence)o;
+			}
+			return o.toString();
+		}
+	}
+
+	/**
+	 * @see IValueMap#getStringArray(String)
+	 */
+	public String[] getStringArray(final String key)
+	{
+		final Object o = get(key);
+		if (o == null)
+		{
+			return null;
+		}
+		else if (o instanceof String[])
+		{
+			return (String[])o;
+		}
+		else if (o.getClass().isArray())
+		{
+			int length = Array.getLength(o);
+			String[] array = new String[length];
+			for (int i = 0; i < length; i++)
+			{
+				final Object arrayValue = Array.get(o, i);
+				if (arrayValue != null)
+				{
+					array[i] = arrayValue.toString();
+				}
+			}
+			return array;
+		}
+		return new String[] { o.toString() };
+	}
+
+	/**
+	 * @see IValueMap#getStringValue(String)
+	 */
+	public StringValue getStringValue(final String key)
+	{
+		return StringValue.valueOf(getString(key));
+	}
+
+	/**
+	 * @see IValueMap#getTime(String)
+	 */
+	public final Time getTime(final String key) throws StringValueConversionException
+	{
+		return getStringValue(key).toTime();
+	}
+
+	/**
+	 * @see IValueMap#isImmutable()
+	 */
+	public final boolean isImmutable()
+	{
+		return immutable;
+	}
+
+	/**
+	 * @see IValueMap#makeImmutable()
+	 */
+	public final IValueMap makeImmutable()
+	{
+		immutable = true;
+		return this;
+	}
+
+	/**
+	 * @see java.util.Map#put(Object, Object)
+	 */
+	@Override
+	public Object put(final String key, final Object value)
+	{
+		checkMutability();
+		return super.put(key, value);
+	}
+
+	/**
+	 * Adds the value to this <code>ValueMap</code> with the given key. If the key already is in the
+	 * <code>ValueMap</code> it will combine the values into a <code>String</code> array, else it
+	 * will just store the value itself.
+	 * 
+	 * @param key
+	 *            the key to store the value under
+	 * @param value
+	 *            the value that must be added/merged to the <code>ValueMap</code>
+	 * @return the value itself if there was no previous value, or a <code>String</code> array with
+	 *         the combined values
+	 */
+	public final Object add(final String key, final String value)
+	{
+		checkMutability();
+		final Object o = get(key);
+		if (o == null)
+		{
+			return put(key, value);
+		}
+		else if (o.getClass().isArray())
+		{
+			int length = Array.getLength(o);
+			String destArray[] = new String[length + 1];
+			for (int i = 0; i < length; i++)
+			{
+				final Object arrayValue = Array.get(o, i);
+				if (arrayValue != null)
+				{
+					destArray[i] = arrayValue.toString();
+				}
+			}
+			destArray[length] = value;
+
+			return put(key, destArray);
+		}
+		else
+		{
+			return put(key, new String[] { o.toString(), value });
+		}
+	}
+
+	/**
+	 * @see java.util.Map#putAll(java.util.Map)
+	 */
+	@Override
+	public void putAll(final Map<? extends String, ?> map)
+	{
+		checkMutability();
+		super.putAll(map);
+	}
+
+	/**
+	 * @see java.util.Map#remove(java.lang.Object)
+	 */
+	@Override
+	public Object remove(final Object key)
+	{
+		checkMutability();
+		return super.remove(key);
+	}
+
+	/**
+	 * @see IValueMap#getKey(String)
+	 */
+	public String getKey(final String key)
+	{
+		for (String other : keySet())
+		{
+			if (other.equalsIgnoreCase(key))
+			{
+				return other;
+			}
+		}
+		return null;
+	}
+
+	/**
+	 * Generates a <code>String</code> representation of this object.
+	 * 
+	 * @return <code>String</code> representation of this <code>ValueMap</code> consistent with the
+	 *         tag-attribute style of markup elements. For example: <code>a="x" b="y" c="z"</code>.
+	 */
+	@Override
+	public String toString()
+	{
+		final StringBuilder buffer = new StringBuilder();
+		boolean first = true;
+		for (Map.Entry<String, Object> entry : entrySet())
+		{
+			if (first == false)
+			{
+				buffer.append(' ');
+			}
+			first = false;
+
+			buffer.append(entry.getKey());
+			buffer.append(" = \"");
+			final Object value = entry.getValue();
+			if (value == null)
+			{
+				buffer.append("null");
+			}
+			else if (value.getClass().isArray())
+			{
+				buffer.append(Arrays.asList((Object[])value));
+			}
+			else
+			{
+				buffer.append(value);
+			}
+
+			buffer.append('\"');
+		}
+		return buffer.toString();
+	}
+
+	/**
+	 * Throws an exception if <code>ValueMap</code> is immutable.
+	 */
+	private void checkMutability()
+	{
+		if (immutable)
+		{
+			throw new UnsupportedOperationException("Map is immutable");
+		}
+	}
+
+	// //
+	// // getAs convenience methods
+	// //
+
+	/**
+	 * @see IValueMap#getAsBoolean(String)
+	 * 
+	 */
+	public Boolean getAsBoolean(final String key)
+	{
+		if (!containsKey(key))
+		{
+			return null;
+		}
+
+		try
+		{
+			return getBoolean(key);
+		}
+		catch (StringValueConversionException ignored)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * @see IValueMap#getAsBoolean(String, boolean)
+	 * 
+	 */
+	public boolean getAsBoolean(final String key, final boolean defaultValue)
+	{
+		if (!containsKey(key))
+		{
+			return defaultValue;
+		}
+
+		try
+		{
+			return getBoolean(key);
+		}
+		catch (StringValueConversionException ignored)
+		{
+			return defaultValue;
+		}
+	}
+
+	/**
+	 * @see IValueMap#getAsInteger(String)
+	 */
+	public Integer getAsInteger(final String key)
+	{
+		if (!containsKey(key))
+		{
+			return null;
+		}
+
+		try
+		{
+			return getInt(key);
+		}
+		catch (StringValueConversionException ignored)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * @see IValueMap#getAsInteger(String, int)
+	 */
+	public int getAsInteger(final String key, final int defaultValue)
+	{
+		return getInt(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsLong(String)
+	 */
+	public Long getAsLong(final String key)
+	{
+		if (!containsKey(key))
+		{
+			return null;
+		}
+
+		try
+		{
+			return getLong(key);
+		}
+		catch (StringValueConversionException ignored)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * @see IValueMap#getAsLong(String, long)
+	 */
+	public long getAsLong(final String key, final long defaultValue)
+	{
+		return getLong(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsDouble(String)
+	 */
+	public Double getAsDouble(final String key)
+	{
+		if (!containsKey(key))
+		{
+			return null;
+		}
+
+		try
+		{
+			return getDouble(key);
+		}
+		catch (StringValueConversionException ignored)
+		{
+			return null;
+		}
+	}
+
+	/**
+	 * @see IValueMap#getAsDouble(String, double)
+	 */
+	public double getAsDouble(final String key, final double defaultValue)
+	{
+		return getDouble(key, defaultValue);
+	}
+
+	/**
+	 * @see IValueMap#getAsDuration(String)
+	 */
+	public Duration getAsDuration(final String key)
+	{
+		return getAsDuration(key, null);
+	}
+
+	/**
+	 * @see IValueMap#getAsDuration(String, Duration)
+	 */
+	public Duration getAsDuration(final String key, final Duration defaultValue)
+	{
+		if (!containsKey(key))
+		{
+			return defaultValue;
+		}
+
+		try
+		{
+			return getDuration(key);
+		}
+		catch (StringValueConversionException ignored)
+		{
+			return defaultValue;
+		}
+	}
+
+	/**
+	 * @see IValueMap#getAsTime(String)
+	 */
+	public Time getAsTime(final String key)
+	{
+		return getAsTime(key, null);
+	}
+
+	/**
+	 * @see IValueMap#getAsTime(String, Time)
+	 */
+	public Time getAsTime(final String key, final Time defaultValue)
+	{
+		if (!containsKey(key))
+		{
+			return defaultValue;
+		}
+
+		try
+		{
+			return getTime(key);
+		}
+		catch (StringValueConversionException ignored)
+		{
+			return defaultValue;
+		}
+	}
+
+	/**
+	 * @see org.apache.wicket.util.value.IValueMap#getAsEnum(java.lang.String, java.lang.Class)
+	 */
+	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass)
+	{
+		return getEnumImpl(key, eClass, null);
+	}
+
+	/**
+	 * @see org.apache.wicket.util.value.IValueMap#getAsEnum(java.lang.String, java.lang.Enum)
+	 */
+	public <T extends Enum<T>> T getAsEnum(final String key, final T defaultValue)
+	{
+		if (defaultValue == null)
+		{
+			throw new IllegalArgumentException("Default value cannot be null");
+		}
+
+		return getEnumImpl(key, defaultValue.getClass(), defaultValue);
+	}
+
+	/**
+	 * @see org.apache.wicket.util.value.IValueMap#getAsEnum(java.lang.String, java.lang.Class,
+	 *      java.lang.Enum)
+	 */
+	public <T extends Enum<T>> T getAsEnum(final String key, final Class<T> eClass,
+		final T defaultValue)
+	{
+		return getEnumImpl(key, eClass, defaultValue);
+	}
+
+	/**
+	 * get enum implementation
+	 * 
+	 * @param key
+	 * @param eClass
+	 * @param defaultValue
+	 * @param <T>
+	 * @return Enum
+	 */
+	@SuppressWarnings({ "unchecked" })
+	private <T extends Enum<T>> T getEnumImpl(final String key, final Class<?> eClass,
+		final T defaultValue)
+	{
+		if (eClass == null)
+		{
+			throw new IllegalArgumentException("eClass value cannot be null");
+		}
+
+		String value = getString(key);
+		if (value == null)
+		{
+			return defaultValue;
+		}
+
+		Method valueOf = null;
+		try
+		{
+			valueOf = eClass.getMethod("valueOf", String.class);
+		}
+		catch (NoSuchMethodException e)
+		{
+			throw new RuntimeException("Could not find method valueOf(String s) for " +
+				eClass.getName(), e);
+		}
+
+		try
+		{
+			return (T)valueOf.invoke(eClass, value);
+		}
+		catch (IllegalAccessException e)
+		{
+			throw new RuntimeException("Could not invoke method valueOf(String s) on " +
+				eClass.getName(), e);
+		}
+		catch (InvocationTargetException e)
+		{
+			// IllegalArgumentException thrown if enum isn't defined - just return default
+			if (e.getCause() instanceof IllegalArgumentException)
+			{
+				return defaultValue;
+			}
+			throw new RuntimeException(e); // shouldn't happen
+		}
+	}
+}