DynaProperty.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.beanutils2;

  18. import java.util.List;
  19. import java.util.Map;
  20. import java.util.Objects;

  21. /**
  22.  * <p>
  23.  * The metadata describing an individual property of a DynaBean.
  24.  * </p>
  25.  *
  26.  * <p>
  27.  * The meta contains an <em>optional</em> content type property ({@link #getContentType}) for use by mapped and iterated properties. A mapped or iterated
  28.  * property may choose to indicate the type it expects. The DynaBean implementation may choose to enforce this type on its entries. Alternatively, an
  29.  * implementation may choose to ignore this property. All keys for maps must be of type String so no meta data is needed for map keys.
  30.  * </p>
  31.  */
  32. public class DynaProperty {

  33.     /*
  34.      * There are issues with serializing primitive class types on certain JVM versions (including Java 1.3). This class uses a custom serialization
  35.      * implementation that writes an integer for these primitive class. This list of constants are the ones used in serialization. If these values are changed,
  36.      * then older versions will no longer be read correctly
  37.      */
  38.     private static final int BOOLEAN_TYPE = 1;
  39.     private static final int BYTE_TYPE = 2;
  40.     private static final int CHAR_TYPE = 3;
  41.     private static final int DOUBLE_TYPE = 4;
  42.     private static final int FLOAT_TYPE = 5;
  43.     private static final int INT_TYPE = 6;
  44.     private static final int LONG_TYPE = 7;
  45.     private static final int SHORT_TYPE = 8;

  46.     /**
  47.      * Empty array.
  48.      */
  49.     public static final DynaProperty[] EMPTY_ARRAY = {};

  50.     /** Property name */
  51.     protected String name;

  52.     /** Property type */
  53.     protected transient Class<?> type;

  54.     /** The <em>(optional)</em> type of content elements for indexed {@code DynaProperty} */
  55.     protected transient Class<?> contentType;

  56.     /**
  57.      * Constructs a property that accepts any data type.
  58.      *
  59.      * @param name Name of the property being described
  60.      */
  61.     public DynaProperty(final String name) {
  62.         this(name, Object.class);
  63.     }

  64.     /**
  65.      * Constructs a property of the specified data type.
  66.      *
  67.      * @param name Name of the property being described
  68.      * @param type Java class representing the property data type
  69.      */
  70.     public DynaProperty(final String name, final Class<?> type) {
  71.         this.name = name;
  72.         this.type = type;
  73.         if (type != null && type.isArray()) {
  74.             this.contentType = type.getComponentType();
  75.         }
  76.     }

  77.     /**
  78.      * Constructs an indexed or mapped {@code DynaProperty} that supports (pseudo)-introspection of the content type.
  79.      *
  80.      * @param name        Name of the property being described
  81.      * @param type        Java class representing the property data type
  82.      * @param contentType Class that all indexed or mapped elements are instances of
  83.      */
  84.     public DynaProperty(final String name, final Class<?> type, final Class<?> contentType) {
  85.         this.name = name;
  86.         this.type = type;
  87.         this.contentType = contentType;
  88.     }

  89.     /**
  90.      * Checks this instance against the specified Object for equality. Overrides the default reference test for equality provided by
  91.      * {@link Object#equals(Object)}
  92.      *
  93.      * @param obj The object to compare to
  94.      * @return {@code true} if object is a dyna property with the same name type and content type, otherwise {@code false}
  95.      * @since 1.8.0
  96.      */
  97.     @Override
  98.     public boolean equals(final Object obj) {
  99.         boolean result;

  100.         result = obj == this;

  101.         if (!result && obj instanceof DynaProperty) {
  102.             final DynaProperty that = (DynaProperty) obj;
  103.             result = Objects.equals(this.name, that.name) && Objects.equals(this.type, that.type) && Objects.equals(this.contentType, that.contentType);
  104.         }

  105.         return result;
  106.     }

  107.     /**
  108.      * Gets the <em>(optional)</em> type of the indexed content for {@code DynaProperty}'s that support this feature.
  109.      *
  110.      * <p>
  111.      * There are issues with serializing primitive class types on certain JVM versions (including Java 1.3). Therefore, this field <strong>must not be
  112.      * serialized using the standard methods</strong>.
  113.      * </p>
  114.      *
  115.      * @return the Class for the content type if this is an indexed {@code DynaProperty} and this feature is supported. Otherwise null.
  116.      */
  117.     public Class<?> getContentType() {
  118.         return contentType;
  119.     }

  120.     /**
  121.      * Gets the name of this property.
  122.      *
  123.      * @return the name of the property
  124.      */
  125.     public String getName() {
  126.         return this.name;
  127.     }

  128.     /**
  129.      * <p>
  130.      * Gets the Java class representing the data type of the underlying property values.
  131.      * </p>
  132.      *
  133.      * <p>
  134.      * There are issues with serializing primitive class types on certain JVM versions (including Java 1.3). Therefore, this field <strong>must not be
  135.      * serialized using the standard methods</strong>.
  136.      * </p>
  137.      *
  138.      * <p>
  139.      * <strong>Please leave this field as {@code transient}</strong>
  140.      * </p>
  141.      *
  142.      * @return the property type
  143.      */
  144.     public Class<?> getType() {
  145.         return this.type;
  146.     }

  147.     /**
  148.      * @return the hash code for this dyna property
  149.      * @see Object#hashCode
  150.      * @since 1.8.0
  151.      */
  152.     @Override
  153.     public int hashCode() {
  154.         int result = 1;

  155.         result = result * 31 + (name == null ? 0 : name.hashCode());
  156.         result = result * 31 + (type == null ? 0 : type.hashCode());
  157.         result = result * 31 + (contentType == null ? 0 : contentType.hashCode());

  158.         return result;
  159.     }

  160.     /**
  161.      * Does this property represent an indexed value (ie an array or List)?
  162.      *
  163.      * @return {@code true} if the property is indexed (i.e. is a List or array), otherwise {@code false}
  164.      */
  165.     public boolean isIndexed() {
  166.         if (type == null) {
  167.             return false;
  168.         }
  169.         if (type.isArray() || List.class.isAssignableFrom(type)) {
  170.             return true;
  171.         }
  172.         return false;
  173.     }

  174.     /**
  175.      * Does this property represent a mapped value (ie a Map)?
  176.      *
  177.      * @return {@code true} if the property is a Map otherwise {@code false}
  178.      */
  179.     public boolean isMapped() {
  180.         if (type == null) {
  181.             return false;
  182.         }
  183.         return Map.class.isAssignableFrom(type);

  184.     }

  185.     /**
  186.      * Gets a String representation of this Object.
  187.      *
  188.      * @return a String representation of the dyna property
  189.      */
  190.     @Override
  191.     public String toString() {
  192.         final StringBuilder sb = new StringBuilder("DynaProperty[name=");
  193.         sb.append(this.name);
  194.         sb.append(",type=");
  195.         sb.append(this.type);
  196.         if (isMapped() || isIndexed()) {
  197.             sb.append(" <").append(this.contentType).append(">");
  198.         }
  199.         sb.append("]");
  200.         return sb.toString();
  201.     }

  202. }