View Javadoc
1   package nl.tudelft.simulation.introspection.fields;
2   
3   import java.lang.reflect.Field;
4   import java.util.ArrayList;
5   import java.util.Arrays;
6   import java.util.LinkedHashSet;
7   import java.util.List;
8   import java.util.Set;
9   
10  import nl.tudelft.simulation.introspection.DelegateIntrospection;
11  import nl.tudelft.simulation.introspection.Introspector;
12  import nl.tudelft.simulation.introspection.Property;
13  
14  /**
15   * The IntrospectionField introspector provides a field manipulation implementation of the introspection interfaces. Its
16   * behavior adheres to the following:
17   * <ul>
18   * <li>Properties are discovered by searching for an object's fields (visibility neutral)</li>
19   * <li>Property value are manipulated by setting field values (visibility neutral)</li>
20   * </ul>
21   * <p>
22   * Copyright (c) 2002-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
23   * for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
24   * project is distributed under a three-clause BSD-style license, which can be found at
25   * <a href="https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
26   * </p>
27   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
28   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
29   * @author Niels Lang.
30   * @since 1.5
31   */
32  
33  public class FieldIntrospector implements Introspector
34  {
35      /** useDeepIntrospection. */
36      private boolean useDeepIntrospection = true;
37  
38      /**
39       * constructs a new FieldIntrospector.
40       */
41      public FieldIntrospector()
42      {
43          this(false);
44      }
45  
46      /**
47       * constructs a new FieldIntrospector.
48       * @param useDeepIntrospection whether to use deep introspection
49       */
50      public FieldIntrospector(final boolean useDeepIntrospection)
51      {
52          this.useDeepIntrospection = useDeepIntrospection;
53      }
54  
55      @Override
56      public Property[] getProperties(final Object introspectedObject)
57      {
58          Object introspected = introspectedObject;
59          while (introspected instanceof DelegateIntrospection)
60          {
61              introspected = ((DelegateIntrospection) introspected).getParentIntrospectionObject();
62          }
63          Set<Property> props = new LinkedHashSet<Property>();
64          try
65          {
66              Field[] fields = collectFields(introspected.getClass());
67              for (int i = 0; i < fields.length; i++)
68              {
69                  props.add(new FieldProperty(introspected, fields[i]));
70              }
71          }
72          catch (Exception e)
73          {
74              throw new IllegalArgumentException(this + " - getProperties", e);
75          }
76          return props.toArray(new Property[props.size()]);
77      }
78  
79      /**
80       * Collect the fields for the given class, taking the preference for deep introspection into account.
81       * @param clasz the class to use
82       * @return Field[] the fields
83       */
84      private Field[] collectFields(final Class<?> clasz)
85      {
86          List<Field> fields = new ArrayList<Field>(10);
87          this.addFields(fields, clasz, this.useDeepIntrospection);
88          return fields.toArray(new Field[fields.size()]);
89      }
90  
91      /**
92       * Add fields of 'clasz' to the fieldList. Optionally iterate over the class hierarchy.
93       * @param fieldList the fieldList
94       * @param clasz the class
95       * @param iterate whether to iterate
96       */
97      private void addFields(final List<Field> fieldList, final Class<?> clasz, final boolean iterate)
98      {
99          fieldList.addAll(Arrays.asList(clasz.getDeclaredFields()));
100         if (iterate && clasz.getSuperclass() != null)
101         { addFields(fieldList, clasz.getSuperclass(), iterate); }
102     }
103 
104     @Override
105     public Property getProperty(final Object introspectedObject, final String property)
106     {
107         Object introspected = introspectedObject;
108         while (introspected instanceof DelegateIntrospection)
109         {
110             introspected = ((DelegateIntrospection) introspected).getParentIntrospectionObject();
111         }
112         try
113         {
114             Field[] fields = collectFields(introspected.getClass());
115             for (int i = 0; i < fields.length; i++)
116             {
117                 if (fields[i].getName().equals(property))
118                 { return new FieldProperty(introspected, fields[i]); }
119             }
120         }
121         catch (Exception e)
122         {
123             throw new IllegalArgumentException(this + " - getProperty", e);
124         }
125         throw new IllegalArgumentException("Property '" + property + "' not found for " + introspected);
126     }
127 
128     @Override
129     public String[] getPropertyNames(final Object introspectedObject)
130     {
131         Object introspected = introspectedObject;
132         while (introspected instanceof DelegateIntrospection)
133         {
134             introspected = ((DelegateIntrospection) introspected).getParentIntrospectionObject();
135         }
136         Set<String> props = new LinkedHashSet<String>();
137         try
138         {
139             Field[] fields = collectFields(introspected.getClass());
140             for (int i = 0; i < fields.length; i++)
141             {
142                 props.add(fields[i].getName());
143             }
144         }
145         catch (Exception e)
146         {
147             throw new IllegalArgumentException(this + " - getPropertyNames", e);
148         }
149         return props.toArray(new String[props.size()]);
150     }
151 }