All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.wicket.util.value.ValueMap Maven / Gradle / Ivy

There is a newer version: 10.1.0
Show newest version
/*
 * 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 IValueMap implementation that holds values, parses Strings, and
 * exposes a variety of convenience methods.
 * 

* In addition to a no-arg constructor and a copy constructor that takes a Map * argument, ValueMaps can be constructed using a parsing constructor. * ValueMap(String) will parse values from the string in comma separated key/value * assignment pairs. For example, new ValueMap("a=9,b=foo"). *

* Values can be retrieved from the ValueMap in the usual way or with methods that do * handy conversions to various types, including String, StringValue, * int, long, double, Time and * Duration. *

* The makeImmutable method will make the underlying Map immutable. * Further attempts to change the Map will result in a RuntimeException. *

* The toString method converts a ValueMap object to a readable key/value * string for diagnostics. * * @author Jonathan Locke * @author Doug Donohoe * @since 1.2.6 */ public class ValueMap extends LinkedHashMap implements IValueMap { /** an empty ValueMap. */ 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; /** * true if this ValueMap has been made immutable. */ private boolean immutable = false; /** * Constructs empty ValueMap. */ public ValueMap() { super(); } /** * Copy constructor. * * @param map * the ValueMap to copy */ public ValueMap(final Map map) { super(); super.putAll(map); } /** * Constructor. *

* NOTE: Please use RequestUtils.decodeParameters() if you wish to properly decode * a request URL. * * @param keyValuePairs * list of key/value pairs separated by commas. For example, " * param1=foo,param2=bar" */ public ValueMap(final String keyValuePairs) { this(keyValuePairs, ","); } /** * Constructor. *

* NOTE: Please use RequestUtils.decodeParameters() if you wish to properly decode * a request URL. * * @param keyValuePairs * list of key/value pairs separated by a given delimiter. For example, " * param1=foo,param2=bar" where delimiter is ",". * @param delimiter * delimiter String 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, " * param1=foo,param2=bar" where delimiter is ",". * @param delimiter * delimiter string used to separate key/value pairs * @param valuePattern * pattern for value. To pass a simple regular expression, pass " * new MetaPattern(regexp)". */ 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) */ @Override public final boolean getBoolean(final String key) throws StringValueConversionException { return getStringValue(key).toBoolean(); } /** * @see IValueMap#getDouble(String) */ @Override public final double getDouble(final String key) throws StringValueConversionException { return getStringValue(key).toDouble(); } /** * @see IValueMap#getDouble(String, double) */ @Override public final double getDouble(final String key, final double defaultValue) { return getStringValue(key).toDouble(defaultValue); } /** * @see IValueMap#getDuration(String) */ @Override public final Duration getDuration(final String key) throws StringValueConversionException { return getStringValue(key).toDuration(); } /** * @see IValueMap#getInt(String) */ @Override public final int getInt(final String key) throws StringValueConversionException { return getStringValue(key).toInt(); } /** * @see IValueMap#getInt(String, int) */ @Override public final int getInt(final String key, final int defaultValue) { return getStringValue(key).toInt(defaultValue); } /** * @see IValueMap#getLong(String) */ @Override public final long getLong(final String key) throws StringValueConversionException { return getStringValue(key).toLong(); } /** * @see IValueMap#getLong(String, long) */ @Override public final long getLong(final String key, final long defaultValue) { return getStringValue(key).toLong(defaultValue); } /** * @see IValueMap#getString(String, String) */ @Override public final String getString(final String key, final String defaultValue) { final String value = getString(key); return value != null ? value : defaultValue; } /** * @see IValueMap#getString(String) */ @Override 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) */ @Override 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) */ @Override 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) */ @Override public StringValue getStringValue(final String key) { return StringValue.valueOf(getString(key)); } /** * @see IValueMap#getTime(String) */ @Override public final Time getTime(final String key) throws StringValueConversionException { return getStringValue(key).toTime(); } /** * @see IValueMap#isImmutable() */ @Override public final boolean isImmutable() { return immutable; } /** * @see IValueMap#makeImmutable() */ @Override 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 ValueMap with the given key. If the key already is in the * ValueMap it will combine the values into a String 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 ValueMap * @return the value itself if there was no previous value, or a String 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 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) */ @Override public String getKey(final String key) { for (String other : keySet()) { if (other.equalsIgnoreCase(key)) { return other; } } return null; } /** * Generates a String representation of this object. * * @return String representation of this ValueMap consistent with the * tag-attribute style of markup elements. For example: a="x" b="y" c="z". */ @Override public String toString() { final StringBuilder buffer = new StringBuilder(); boolean first = true; for (Map.Entry 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 ValueMap is immutable. */ private void checkMutability() { if (immutable) { throw new UnsupportedOperationException("Map is immutable"); } } // // // // getAs convenience methods // // /** * @see IValueMap#getAsBoolean(String) * */ @Override 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) * */ @Override 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) */ @Override 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) */ @Override public int getAsInteger(final String key, final int defaultValue) { return getInt(key, defaultValue); } /** * @see IValueMap#getAsLong(String) */ @Override 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) */ @Override public long getAsLong(final String key, final long defaultValue) { return getLong(key, defaultValue); } /** * @see IValueMap#getAsDouble(String) */ @Override 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) */ @Override public double getAsDouble(final String key, final double defaultValue) { return getDouble(key, defaultValue); } /** * @see IValueMap#getAsDuration(String) */ @Override public Duration getAsDuration(final String key) { return getAsDuration(key, null); } /** * @see IValueMap#getAsDuration(String, Duration) */ @Override 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) */ @Override public Time getAsTime(final String key) { return getAsTime(key, null); } /** * @see IValueMap#getAsTime(String, Time) */ @Override 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) */ @Override public > T getAsEnum(final String key, final Class eClass) { // explicitly pass T as type to be able to build with JDK 1.8. WICKET-5427 return this.getEnumImpl(key, eClass, (T)null); } /** * @see org.apache.wicket.util.value.IValueMap#getAsEnum(java.lang.String, java.lang.Enum) */ @Override public > 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) */ @Override public > T getAsEnum(final String key, final Class eClass, final T defaultValue) { return getEnumImpl(key, eClass, defaultValue); } /** * get enum implementation * * @param key * @param eClass * @param defaultValue * @param * @return Enum */ @SuppressWarnings({ "unchecked" }) private > 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 } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy