View Javadoc
1   package nl.tudelft.simulation.introspection;
2   
3   import java.lang.reflect.Array;
4   import java.util.Collection;
5   import java.util.Map;
6   
7   import org.djutils.immutablecollections.ImmutableCollection;
8   import org.djutils.immutablecollections.ImmutableMap;
9   
10  /**
11   * A default Property implementation that provides a standard way to handle composite values.
12   * <p>
13   * Copyright (c) 2002-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
14   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
15   * project is distributed under a three-clause BSD-style license, which can be found at
16   * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
17   * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
18   * </p>
19   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
20   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
21   * @author Niels Lang.
22   * @since 1.5
23   */
24  public abstract class AbstractProperty implements Property
25  {
26      /**
27       * Basic 'setValue' implementation. It is checked whether this property contains a composite value. If so, the composite
28       * value of this property is updated. Composite values are expected to be supplied as a {see java.util.Collection}. If
29       * needed, array conversion takes place. If the property is not composite, the value-setting is delegated to the
30       * 'setRegularValue' method. Maps cannot be updated at this moment.
31       * @see nl.tudelft.simulation.introspection.Property#setValue(java.lang.Object)
32       */
33      @SuppressWarnings("unchecked")
34      @Override
35      public void setValue(final Object value)
36      {
37          if (!this.getComposedType().isComposed())
38          {
39              this.setRegularValue(value);
40              return;
41          }
42          if (!(value instanceof Collection))
43          {
44              throw new IllegalArgumentException(this + " - tried to assign a singular value to composite properties");
45          }
46          if (this.getComposedType().isArray())
47          {
48              Object[] array = (Object[]) Array.newInstance(getType().getComponentType(), 0);
49              this.setRegularValue(((Collection<?>) value).toArray(array));
50          }
51          else if (this.getComposedType().isCollection())
52          {
53              synchronized (this.getInstance())
54              {
55                  Collection<Object> oldValues = (Collection<Object>) getValue();
56                  try
57                  {
58                      oldValues.clear();
59                      oldValues.addAll((Collection<Object>) value);
60                  }
61                  catch (UnsupportedOperationException e)
62                  {
63                      throw new IllegalArgumentException(
64                              this + " - setValue: could not empty " + oldValues + "setValue method canceled");
65                  }
66              }
67          }
68          else
69          {
70              throw new IllegalArgumentException(this + " - tried to assign a value to a map or an immutable collection");
71          }
72  
73      }
74  
75      /**
76       * Method used to set a regular (i.e. not-composite) property value.
77       * @param value Object; the new value
78       */
79      protected abstract void setRegularValue(final Object value);
80  
81      /** {@inheritDoc} */
82      @Override
83      public ComposedTypeEnum getComposedType()
84      {
85          if (getType().isArray())
86          {
87              return ComposedTypeEnum.ARRAY;
88          }
89          else if (Collection.class.isAssignableFrom(getType()))
90          {
91              return ComposedTypeEnum.COLLECTION;
92          }
93          else if (ImmutableCollection.class.isAssignableFrom(getType()))
94          {
95              return ComposedTypeEnum.IMMUTABLECOLLECTION;
96          }
97          else if (Map.class.isAssignableFrom(getType()))
98          {
99              return ComposedTypeEnum.MAP;
100         }
101         else if (ImmutableMap.class.isAssignableFrom(getType()))
102         {
103             return ComposedTypeEnum.IMMUTABLEMAP;
104         }
105         return ComposedTypeEnum.NONE;
106     }
107 
108     /** {@inheritDoc} */
109     @Override
110     public Class<?> getComponentType()
111     {
112         if (!this.getComposedType().isComposed())
113         {
114             return null;
115         }
116         if (getComposedType().isArray())
117         {
118             return getType().getComponentType();
119         }
120         if (getComposedType().isCollection())
121         {
122             Collection<?> value = (Collection<?>) getValue();
123             if (value == null || value.size() == 0)
124             {
125                 return null;
126             }
127             return value.toArray()[0].getClass();
128         }
129         if (getComposedType().isImmutableCollection())
130         {
131             ImmutableCollection<?> value = (ImmutableCollection<?>) getValue();
132             if (value == null || value.size() == 0)
133             {
134                 return null;
135             }
136             return value.toArray()[0].getClass();
137         }
138         // TODO: is this ok? Map or ImmutableMap do not have a single type...
139         return null;
140     }
141 
142     /** {@inheritDoc} */
143     @Override
144     public String toString()
145     {
146         return "Property [getName()=" + this.getName() + "]";
147     }
148 }