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

org.apache.velocity.tools.generic.ValueParser Maven / Gradle / Ivy

Go to download

Generic tools that can be used in any context. PLEASE NOTE: this is a temporary fork to unblock projects migrating to Jakarta, but I won't continue maintaining it in the future as the Velocity team doesn't understand the value of Jakarta. I strongly suggest you plan a switch to a more modern template engine such as Thymeleaf.

The newest version!
package org.apache.velocity.tools.generic;

/*
 * 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.
 */

import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.velocity.tools.ConversionUtils;
import org.apache.velocity.tools.Scope;
import org.apache.velocity.tools.config.DefaultKey;
import org.apache.velocity.tools.config.InvalidScope;
import org.apache.velocity.tools.config.SkipSetters;

/**
 * 

Utility class for easy parsing of String values held in a Map.

* *

This comes in very handy when parsing parameters.

* *

When subkeys are allowed, getValue("foo") will also search for all keys * of the form "foo.bar" and return a ValueParser of the type "bar" -> value for all found values.

* * TODO: someone doing java configuration ought to be able to put a source Map * in the tool properties, allowing this to be used like other tools * * @author Nathan Bubna * @version $Revision$ $Date$ * @since VelocityTools 1.2 */ @DefaultKey("parser") @InvalidScope(Scope.SESSION) /* session scope forbidden: Object may not be Serializable */ @SkipSetters public class ValueParser extends FormatConfig implements Map { public static final String STRINGS_DELIMITER_FORMAT_KEY = "stringsDelimiter"; public static final String DEFAULT_STRINGS_DELIMITER = ","; private String stringsDelimiter = DEFAULT_STRINGS_DELIMITER; private Map source = null; private boolean allowSubkeys = true; /* when using subkeys, cache at least the presence of any subkey, so that the rendering of templates not using subkeys will only look once for subkeys */ private Boolean hasSubkeys = null; /* whether the wrapped map should be read-only or not */ private boolean readOnly = true; /** * The key used for specifying whether to support subkeys */ public static final String ALLOWSUBKEYS_KEY = "allowSubkeys"; /** * The key used for specifying whether to be read-only */ public static final String READONLY_KEY = "readOnly"; public ValueParser() { } public ValueParser(Map source) { setSource(source); } protected void setSource(Map source) { this.source = source; } protected Map getSource(boolean create) { // If this method has not been overrided, make sure source is not null if (source == null && create) { source = new HashMap(); } return this.source; } protected Map getSource() { return getSource(true); } /** * Are subkeys allowed ? * @return yes/no */ protected boolean getAllowSubkeys() { return allowSubkeys; } /** * allow or disallow subkeys * @param allow flag value */ protected void setAllowSubkeys(boolean allow) { allowSubkeys = allow; } /** * Is the Map read-only? * @return yes/no */ protected boolean getReadOnly() { return readOnly; } /** * Set or unset read-only behaviour * @param ro flag value */ protected void setReadOnly(boolean ro) { readOnly = ro; } /** * Sets the delimiter used for separating values in a single String value. * The default string delimiter is a comma. * * @param stringsDelimiter strings delimiter * @see #getValues(String) */ protected final void setStringsDelimiter(String stringsDelimiter) { this.stringsDelimiter = stringsDelimiter; } /** * Does the actual configuration. This is protected, so * subclasses may share the same ValueParser and call configure * at any time, while preventing templates from doing so when * configure(Map) is locked. * @param values configuration values */ @Override protected void configure(ValueParser values) { super.configure(values); String delimiter = values.getString(STRINGS_DELIMITER_FORMAT_KEY); if (delimiter != null) { setStringsDelimiter(delimiter); } Boolean allow = values.getBoolean(ALLOWSUBKEYS_KEY); if(allow != null) { setAllowSubkeys(allow); } Boolean ro = values.getBoolean(READONLY_KEY); if(ro != null) { setReadOnly(ro); } } // ----------------- public parsing methods -------------------------- /** * Convenience method for checking whether a certain parameter exists. * * @param key the parameter's key * @return true if a parameter exists for the specified * key; otherwise, returns false. */ public boolean exists(String key) { return (getValue(key) != null); } /** * Convenience method for use in Velocity templates. * This allows for easy "dot" access to parameters. * * e.g. $params.foo instead of $params.getString('foo') * * @param key the parameter's key * @return parameter matching the specified key or * null if there is no matching * parameter */ public Object get(String key) { Object value = getValue(key); if (value == null && getSource() != null && getAllowSubkeys()) { value = getSubkey(key); } return value; } /** * Returns the value mapped to the specified key * in the {@link Map} returned by {@link #getSource()}. If there is * no source, then this will always return {@code null}. * @param key property key * @return property value, or null */ public Object getValue(String key) { if (getSource() == null) { return null; } return getSource().get(key); } /** * @param key the desired parameter's key * @param alternate The alternate value * @return parameter matching the specified key or the * specified alternate Object if there is no matching * parameter */ public Object getValue(String key, Object alternate) { Object value = getValue(key); if (value == null) { return alternate; } return value; } protected String[] parseStringList(String value) { String[] values; if (stringsDelimiter.length() == 0 || value.indexOf(stringsDelimiter) < 0) { values = new String[] { value }; } else { values = value.split(stringsDelimiter); } return values; } /** *

Returns an array of values. If the internal value is a string, it is split using the configured delimitor * (',' by default).

*

If the internal value is not an array or is a string without any delimiter, a singletin array is returned.

* @param key the desired parameter's key * @return array of values, or null of the key has not been found. * specified alternate Object if there is no matching * parameter */ public Object[] getValues(String key) { Object value = getValue(key); if (value == null) { return null; } if (value instanceof String) { return parseStringList((String)value); } if (value instanceof Object[]) { return (Object[])value; } return new Object[] { value }; } /** * @param key the parameter's key * @return parameter matching the specified key or * null if there is no matching * parameter */ public String getString(String key) { return ConversionUtils.toString(getValue(key)); } /** * @param key the desired parameter's key * @param alternate The alternate value * @return parameter matching the specified key or the * specified alternate String if there is no matching * parameter */ public String getString(String key, String alternate) { String s = getString(key); return (s != null) ? s : alternate; } /** * @param key the desired parameter's key * @return a {@link Boolean} object for the specified key or * null if no matching parameter is found */ public Boolean getBoolean(String key) { return ConversionUtils.toBoolean(getValue(key)); } /** * @param key the desired parameter's key * @param alternate The alternate boolean value * @return boolean value for the specified key or the * alternate boolean is no value is found */ public boolean getBoolean(String key, boolean alternate) { Boolean bool = getBoolean(key); return (bool != null) ? bool.booleanValue() : alternate; } /** * @param key the desired parameter's key * @param alternate the alternate {@link Boolean} * @return a {@link Boolean} for the specified key or the specified * alternate if no matching parameter is found */ public Boolean getBoolean(String key, Boolean alternate) { Boolean bool = getBoolean(key); return (bool != null) ? bool : alternate; } /** * @param key the desired parameter's key * @return a {@link Integer} for the specified key or * null if no matching parameter is found */ public Integer getInteger(String key) { Object value = getValue(key); if (value == null) { return null; } Number number = ConversionUtils.toNumber(value, getFormat(), getLocale()); return number == null ? null : number.intValue(); } /** * @param key the desired parameter's key * @param alternate The alternate Integer * @return an Integer for the specified key or the specified * alternate if no matching parameter is found */ public Integer getInteger(String key, Integer alternate) { Integer num = getInteger(key); if (num == null) { return alternate; } return num; } /** * @param key the desired parameter's key * @return a {@link Long} for the specified key or * null if no matching parameter is found */ public Long getLong(String key) { Object value = getValue(key); if (value == null) { return null; } Number number = ConversionUtils.toNumber(value, getFormat(), getLocale()); return number == null ? null : number.longValue(); } /** * @param key the desired parameter's key * @param alternate The alternate Long * @return a Long for the specified key or the specified * alternate if no matching parameter is found */ public Long getLong(String key, Long alternate) { Long num = getLong(key); if (num == null) { return alternate; } return num; } /** * @param key the desired parameter's key * @return a {@link Double} for the specified key or * null if no matching parameter is found */ public Double getDouble(String key) { Object value = getValue(key); if (value == null) { return null; } Number number = ConversionUtils.toNumber(value, getFormat(), getLocale()); return number == null ? null : number.doubleValue(); } /** * @param key the desired parameter's key * @param alternate The alternate Double * @return an Double for the specified key or the specified * alternate if no matching parameter is found */ public Double getDouble(String key, Double alternate) { Double num = getDouble(key); if (num == null) { return alternate; } return num; } /** * @param key the desired parameter's key * @return a {@link Number} for the specified key or * null if no matching parameter is found */ public Number getNumber(String key) { return ConversionUtils.toNumber(getValue(key), getFormat(), getLocale()); } /** * @param key the desired parameter's key * @return a {@link Locale} for the specified key or * null if no matching parameter is found */ public Locale getLocale(String key) { return toLocale(getValue(key)); } /** * @param key the desired parameter's key * @param alternate The alternate Number * @return a Number for the specified key or the specified * alternate if no matching parameter is found */ public Number getNumber(String key, Number alternate) { Number n = getNumber(key); return (n != null) ? n : alternate; } /** * @param key the desired parameter's key * @param alternate The alternate int value * @return the int value for the specified key or the specified * alternate value if no matching parameter is found */ public int getInt(String key, int alternate) { Number n = getNumber(key); return (n != null) ? n.intValue() : alternate; } /** * @param key the desired parameter's key * @param alternate The alternate double value * @return the double value for the specified key or the specified * alternate value if no matching parameter is found */ public double getDouble(String key, double alternate) { Number n = getNumber(key); return (n != null) ? n.doubleValue() : alternate; } /** * @param key the desired parameter's key * @param alternate The alternate Locale * @return a Locale for the specified key or the specified * alternate if no matching parameter is found */ public Locale getLocale(String key, Locale alternate) { Locale l = getLocale(key); return (l != null) ? l : alternate; } /** * @param key the key for the desired parameter * @return an array of String objects containing all of the values * associated with the given key, or null * if the no values are associated with the given key */ public String[] getStrings(String key) { Object[] array = getValues(key); if (array == null || String.class.isAssignableFrom(array.getClass().getComponentType())) { return (String[])array; } String[] ret = new String[array.length]; for (int i = 0; i < array.length; ++i) { ret[i] = ConversionUtils.toString(array[i]); } return ret; } /** * @param key the key for the desired parameter * @return an array of Boolean objects associated with the given key. */ public Boolean[] getBooleans(String key) { Object[] array = getValues(key); if (array == null || Boolean.class.isAssignableFrom(array.getClass().getComponentType())) { return (Boolean[])array; } Boolean[] ret = new Boolean[array.length]; for (int i = 0; i < array.length; ++i) { ret[i] = ConversionUtils.toBoolean(array[i]); } return ret; } /** * @param key the key for the desired parameter * @return an array of Number objects associated with the given key, * or null if Numbers are not associated with it. */ public Number[] getNumbers(String key) { Object[] array = getValues(key); if (array == null || Number.class.isAssignableFrom(array.getClass().getComponentType())) { return (Number[])array; } Number[] ret = new Number[array.length]; for (int i = 0; i < array.length; ++i) { ret[i] = ConversionUtils.toNumber(array[i], getFormat(), getLocale()); } return ret; } /** * @param key the key for the desired parameter * @return an array of int values associated with the given key, * or null if numbers are not associated with it. */ public int[] getInts(String key) { Object[] array = getValues(key); if (array == null) { return null; } int[] ret = new int[array.length]; for (int i = 0; i < array.length; ++i) { ret[i] = ConversionUtils.toNumber(array[i], getFormat(), getLocale()).intValue(); } return ret; } /** * @param key the key for the desired parameter * @return an array of double values associated with the given key, * or null if numbers are not associated with it. */ public double[] getDoubles(String key) { Object[] array = getValues(key); if (array == null) { return null; } double[] ret = new double[array.length]; for (int i = 0; i < array.length; ++i) { ret[i] = ConversionUtils.toNumber(array[i], getFormat(), getLocale()).doubleValue(); } return ret; } /** * @param key the key for the desired parameter * @return an array of Locale objects associated with the given key, * or null if Locales are not associated with it. */ public Locale[] getLocales(String key) { Object[] array = getValues(key); if (array == null || Locale.class.isAssignableFrom(array.getClass().getComponentType())) { return (Locale[])array; } Locale[] ret = new Locale[array.length]; for (int i = 0; i < array.length; ++i) { ret[i] = ConversionUtils.toLocale(String.valueOf(array[i])); } return ret; } /** * Determines whether there are subkeys available in the source map. * @return true if there are subkeys (key names containing a dot) */ public boolean hasSubkeys() { if (getSource() == null || !getAllowSubkeys()) { return false; } if (hasSubkeys == null) { for (String key : getSource().keySet()) { int dot = key.indexOf('.'); if (dot > 0 && dot < key.length()) { hasSubkeys = Boolean.TRUE; break; } } if (hasSubkeys == null) { hasSubkeys = Boolean.FALSE; } } return hasSubkeys; } /** * returns the set of all possible first-level subkeys, including complete keys without dots (or returns keySet() if allowSubKeys is false) * @return the set of all possible first-level subkeys */ public Set getSubkeys() { Set keys = keySet(); if (getSource() == null || !getAllowSubkeys()) { return keys; } else { Set result = new TreeSet(); for (String key: keys) { int dot = key.indexOf('.'); if (dot > 0 && dot < key.length()) { result.add(key.substring(0, dot)); } } return result; } } /** * subkey getter that returns a map subkey#2 -> value * for every "subkey.subkey2" found entry * * @param subkey subkey to search for * @return the map of found values */ public ValueParser getSubkey(String subkey) { if (!hasSubkeys() || subkey == null || subkey.length() == 0) { return null; } Map values = null; subkey = subkey.concat("."); for (Map.Entry entry : getSource().entrySet()) { if (entry.getKey().startsWith(subkey) && entry.getKey().length() > subkey.length()) { if (values == null) { values = new HashMap(); } values.put(entry.getKey().substring(subkey.length()),entry.getValue()); } } if (values == null) { return null; } else { ValueParser ret = new ValueParser(values); /* honnor readOnly option on submaps */ ret.setReadOnly(getReadOnly()); return ret; } } public int size() { return getSource() == null ? 0 : getSource().size(); } public boolean isEmpty() { return getSource() == null || getSource().isEmpty(); } public boolean containsKey(Object key) { return getSource() == null ? false : getSource().containsKey(key); } public boolean containsValue(Object value) { return getSource() == null ? false : getSource().containsValue(value); } public Object get(Object key) { return get(String.valueOf(key)); } public Object put(String key, Object value) { if(readOnly) { throw new UnsupportedOperationException("Cannot put("+key+","+value+"); "+getClass().getName()+" is read-only"); } if(hasSubkeys != null && hasSubkeys.equals(Boolean.FALSE) && key.indexOf('.') != -1) { hasSubkeys = Boolean.TRUE; } return getSource().put(key,value); // TODO this tool should be made thread-safe (the request-scoped ParameterTool doesn't need it, but other uses could...) } public Object remove(Object key) { if(readOnly) { throw new UnsupportedOperationException("Cannot remove("+key+"); "+getClass().getName()+" is read-only"); } if(hasSubkeys != null && hasSubkeys.equals(Boolean.TRUE) && ((String)key).indexOf('.') != -1) { hasSubkeys = null; } return getSource().remove(key); } public void putAll(Map m) { if(readOnly) { throw new UnsupportedOperationException("Cannot putAll("+m+"); "+getClass().getName()+" is read-only"); } hasSubkeys = null; getSource().putAll(m); } public void clear() { if(readOnly) { throw new UnsupportedOperationException("Cannot clear(); "+getClass().getName()+" is read-only"); } hasSubkeys = Boolean.FALSE; getSource().clear(); } public Set keySet() { return getSource() == null ? null : getSource().keySet(); } public Collection values() { return getSource() == null ? null : getSource().values(); } public Set> entrySet() { return getSource().entrySet(); } public String toString() { StringBuilder builder = new StringBuilder(); builder.append('{'); boolean empty = true; for(Map.Entry entry:entrySet()) { if(!empty) { builder.append(", "); } empty = false; builder.append(entry.getKey()); builder.append('='); builder.append(String.valueOf(entry.getValue())); } builder.append('}'); return builder.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy