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