Return-Path: X-Original-To: apmail-commons-commits-archive@minotaur.apache.org Delivered-To: apmail-commons-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 91768106C0 for ; Sun, 27 Oct 2013 09:59:08 +0000 (UTC) Received: (qmail 27927 invoked by uid 500); 27 Oct 2013 09:59:04 -0000 Delivered-To: apmail-commons-commits-archive@commons.apache.org Received: (qmail 27901 invoked by uid 500); 27 Oct 2013 09:59:04 -0000 Mailing-List: contact commits-help@commons.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@commons.apache.org Delivered-To: mailing list commits@commons.apache.org Received: (qmail 27893 invoked by uid 99); 27 Oct 2013 09:59:03 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Oct 2013 09:59:03 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Oct 2013 09:58:59 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 21E2623889E0; Sun, 27 Oct 2013 09:58:38 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1536075 - /commons/proper/beanutils/branches/java5/src/main/java/org/apache/commons/beanutils/BaseDynaBeanMapDecorator.java Date: Sun, 27 Oct 2013 09:58:38 -0000 To: commits@commons.apache.org From: oheger@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20131027095838.21E2623889E0@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: oheger Date: Sun Oct 27 09:58:37 2013 New Revision: 1536075 URL: http://svn.apache.org/r1536075 Log: Added new abstract base class for DynaBeanMapDecorators. With DynaBeanMapDecorator there is the problem that the correct generic type parameters cannot be used because this would break backwards compatibility. Therefore, a new abstract base class was added which accepts a type parameter for the decorated map's key type. This makes it very easy to create multiple concrete decorator classes with different key types. So DynaBeanMapDecorator can be kept as it is, and a new decorator class using String as key type can be added easily. (We can think about deprecating DynaBeanMapDecorator in favor of the new decorator with correct generic types.) Added: commons/proper/beanutils/branches/java5/src/main/java/org/apache/commons/beanutils/BaseDynaBeanMapDecorator.java Added: commons/proper/beanutils/branches/java5/src/main/java/org/apache/commons/beanutils/BaseDynaBeanMapDecorator.java URL: http://svn.apache.org/viewvc/commons/proper/beanutils/branches/java5/src/main/java/org/apache/commons/beanutils/BaseDynaBeanMapDecorator.java?rev=1536075&view=auto ============================================================================== --- commons/proper/beanutils/branches/java5/src/main/java/org/apache/commons/beanutils/BaseDynaBeanMapDecorator.java (added) +++ commons/proper/beanutils/branches/java5/src/main/java/org/apache/commons/beanutils/BaseDynaBeanMapDecorator.java Sun Oct 27 09:58:37 2013 @@ -0,0 +1,381 @@ +/* + * 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.commons.beanutils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

A base class for decorators providing Map behavior on + * {@link DynaBean}s.

+ * + *

The motivation for this implementation is to provide access to {@link DynaBean} + * properties in technologies that are unaware of BeanUtils and {@link DynaBean}s - + * such as the expression languages of JSTL and JSF.

+ * + *

This rather technical base class implements the methods of the + * {@code Map} interface on top of a {@code DynaBean}. It was introduced + * to handle generic parameters in a meaningful way without breaking + * backwards compatibility of the {@link DynaBeanMapDecorator} class: A + * map wrapping a {@code DynaBean} should be of type {@code Map}. + * However, when using these generic parameters in {@code DynaBeanMapDecorator} + * this would be an incompatible change (as method signatures would have to + * be adapted). To solve this problem, this generic base class is added + * which allows specifying the key type as parameter. This makes it easy to + * have a new subclass using the correct generic parameters while + * {@code DynaBeanMapDecorator} could still remain with compatible + * parameters.

+ * + * @param the type of the keys in the decorated map + * @since BeanUtils 1.9.0 + * @version $Id$ + */ +public abstract class BaseDynaBeanMapDecorator implements Map { + + private final DynaBean dynaBean; + private final boolean readOnly; + private transient Set keySet; + + // ------------------- Constructors ---------------------------------- + + /** + * Constructs a read only Map for the specified + * {@link DynaBean}. + * + * @param dynaBean The dyna bean being decorated + * @throws IllegalArgumentException if the {@link DynaBean} is null. + */ + public BaseDynaBeanMapDecorator(DynaBean dynaBean) { + this(dynaBean, true); + } + + /** + * Construct a Map for the specified {@link DynaBean}. + * + * @param dynaBean The dyna bean being decorated + * @param readOnly true if the Map is read only + * otherwise false + * @throws IllegalArgumentException if the {@link DynaBean} is null. + */ + public BaseDynaBeanMapDecorator(DynaBean dynaBean, boolean readOnly) { + if (dynaBean == null) { + throw new IllegalArgumentException("DynaBean is null"); + } + this.dynaBean = dynaBean; + this.readOnly = readOnly; + } + + + // ------------------- public Methods -------------------------------- + + + /** + * Indicate whether the Map is read only. + * + * @return true if the Map is read only, + * otherwise false. + */ + public boolean isReadOnly() { + return readOnly; + } + + // ------------------- java.util.Map Methods ------------------------- + + /** + * clear() operation is not supported. + * + * @throws UnsupportedOperationException + */ + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Indicate whether the {@link DynaBean} contains a specified + * value for one (or more) of its properties. + * + * @param key The {@link DynaBean}'s property name + * @return true if one of the {@link DynaBean}'s + * properties contains a specified value. + */ + public boolean containsKey(Object key) { + DynaClass dynaClass = getDynaBean().getDynaClass(); + DynaProperty dynaProperty = dynaClass.getDynaProperty(toString(key)); + return (dynaProperty == null ? false : true); + } + + /** + * Indicates whether the decorated {@link DynaBean} contains + * a specified value. + * + * @param value The value to check for. + * @return true if one of the the {@link DynaBean}'s + * properties contains the specified value, otherwise + * false. + */ + public boolean containsValue(Object value) { + DynaProperty[] properties = getDynaProperties(); + for (int i = 0; i < properties.length; i++) { + String key = properties[i].getName(); + Object prop = getDynaBean().get(key); + if (value == null) { + if (prop == null) { + return true; + } + } else { + if (value.equals(prop)) { + return true; + } + } + } + return false; + } + + /** + *

Returns the Set of the property/value mappings + * in the decorated {@link DynaBean}.

+ * + *

Each element in the Set is a Map.Entry + * type.

+ * + * @return An unmodifiable set of the DynaBean + * property name/value pairs + */ + public Set> entrySet() { + DynaProperty[] properties = getDynaProperties(); + Set> set = new HashSet>(properties.length); + for (int i = 0; i < properties.length; i++) { + K key = convertKey(properties[i].getName()); + Object value = getDynaBean().get(properties[i].getName()); + set.add(new MapEntry(key, value)); + } + return Collections.unmodifiableSet(set); + } + + /** + * Return the value for the specified key from + * the decorated {@link DynaBean}. + * + * @param key The {@link DynaBean}'s property name + * @return The value for the specified property. + */ + public Object get(Object key) { + return getDynaBean().get(toString(key)); + } + + /** + * Indicate whether the decorated {@link DynaBean} has + * any properties. + * + * @return true if the {@link DynaBean} has + * no properties, otherwise false. + */ + public boolean isEmpty() { + return (getDynaProperties().length == 0); + } + + /** + *

Returns the Set of the property + * names in the decorated {@link DynaBean}.

+ * + *

N.B.For {@link DynaBean}s whose associated {@link DynaClass} + * is a {@link MutableDynaClass} a new Set is created every + * time, otherwise the Set is created only once and cached.

+ * + * @return An unmodifiable set of the {@link DynaBean}s + * property names. + */ + public Set keySet() { + if (keySet != null) { + return keySet; + } + + // Create a Set of the keys + DynaProperty[] properties = getDynaProperties(); + Set set = new HashSet(properties.length); + for (int i = 0; i < properties.length; i++) { + set.add(convertKey(properties[i].getName())); + } + set = Collections.unmodifiableSet(set); + + // Cache the keySet if Not a MutableDynaClass + DynaClass dynaClass = getDynaBean().getDynaClass(); + if (!(dynaClass instanceof MutableDynaClass)) { + keySet = set; + } + + return set; + + } + + /** + * Set the value for the specified property in + * the decorated {@link DynaBean}. + * + * @param key The {@link DynaBean}'s property name + * @param value The value for the specified property. + * @return The previous property's value. + * @throws UnsupportedOperationException if + * isReadOnly() is true. + */ + public Object put(K key, Object value) { + if (isReadOnly()) { + throw new UnsupportedOperationException("Map is read only"); + } + String property = toString(key); + Object previous = getDynaBean().get(property); + getDynaBean().set(property, value); + return previous; + } + + /** + * Copy the contents of a Map to the decorated {@link DynaBean}. + * + * @param map The Map of values to copy. + * @throws UnsupportedOperationException if + * isReadOnly() is true. + */ + public void putAll(Map map) { + if (isReadOnly()) { + throw new UnsupportedOperationException("Map is read only"); + } + for (Map.Entry e : map.entrySet()) { + put(e.getKey(), e.getValue()); + } + } + + /** + * remove() operation is not supported. + * + * @param key The {@link DynaBean}'s property name + * @return the value removed + * @throws UnsupportedOperationException + */ + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the number properties in the decorated + * {@link DynaBean}. + * @return The number of properties. + */ + public int size() { + return getDynaProperties().length; + } + + /** + * Returns the set of property values in the + * decorated {@link DynaBean}. + * + * @return Unmodifiable collection of values. + */ + public Collection values() { + DynaProperty[] properties = getDynaProperties(); + List values = new ArrayList(properties.length); + for (int i = 0; i < properties.length; i++) { + String key = properties[i].getName(); + Object value = getDynaBean().get(key); + values.add(value); + } + return Collections.unmodifiableList(values); + } + + // ------------------- protected Methods ----------------------------- + + /** + * Provide access to the underlying {@link DynaBean} + * this Map decorates. + * + * @return the decorated {@link DynaBean}. + */ + public DynaBean getDynaBean() { + return dynaBean; + } + + /** + * Converts the name of a property to the key type of this decorator. + * + * @param propertyName the name of a property + * @return the converted key to be used in the decorated map + */ + protected abstract K convertKey(String propertyName); + + // ------------------- private Methods ------------------------------- + + /** + * Convenience method to retrieve the {@link DynaProperty}s + * for this {@link DynaClass}. + * + * @return The an array of the {@link DynaProperty}s. + */ + private DynaProperty[] getDynaProperties() { + return getDynaBean().getDynaClass().getDynaProperties(); + } + + /** + * Convenience method to convert an Object + * to a String. + * + * @param obj The Object to convert + * @return String representation of the object + */ + private String toString(Object obj) { + return (obj == null ? null : obj.toString()); + } + + /** + * Map.Entry implementation. + */ + private static class MapEntry implements Map.Entry { + private final K key; + private final Object value; + MapEntry(K key, Object value) { + this.key = key; + this.value = value; + } + @Override + public boolean equals(Object o) { + if (!(o instanceof Map.Entry)) { + return false; + } + Map.Entry e = (Map.Entry)o; + return ((key.equals(e.getKey())) && + (value == null ? e.getValue() == null + : value.equals(e.getValue()))); + } + @Override + public int hashCode() { + return key.hashCode() + (value == null ? 0 : value.hashCode()); + } + public K getKey() { + return key; + } + public Object getValue() { + return value; + } + public Object setValue(Object value) { + throw new UnsupportedOperationException(); + } + } + +}