View Javadoc
1   package org.opentrafficsim.editor.extensions.map;
2   
3   import java.util.function.Consumer;
4   
5   import org.djutils.eval.Eval;
6   import org.djutils.event.Event;
7   import org.djutils.event.EventType;
8   import org.djutils.event.LocalEventProducer;
9   import org.djutils.metadata.MetaData;
10  import org.opentrafficsim.base.OtsRuntimeException;
11  import org.opentrafficsim.base.geometry.OtsShape;
12  import org.opentrafficsim.editor.EvalWrapper.EvalListener;
13  import org.opentrafficsim.editor.OtsEditor;
14  import org.opentrafficsim.editor.XsdTreeNode;
15  import org.opentrafficsim.xml.bindings.ExpressionAdapter;
16  
17  /**
18   * Part of the map data structure.
19   * <p>
20   * Copyright (c) 2023-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
21   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
22   * </p>
23   * @author Wouter Schakel
24   */
25  public abstract class MapData extends LocalEventProducer implements EvalListener, OtsShape
26  {
27  
28      /** Event for a change in data that entails a change in visualization. */
29      public static final EventType MAP_DATA_CHANGED =
30              new EventType("MAP_DATA_CHANGED", new MetaData("MAP_DATA_CHANGED", "Data was changed that affects visualization."));
31  
32      /** Map showing all the elements. */
33      private final EditorMap map;
34  
35      /** Tree node. */
36      private final XsdTreeNode node;
37  
38      /** Editor. */
39      private final OtsEditor editor;
40  
41      /** Whether this data is valid. */
42      private boolean valid;
43  
44      /**
45       * Constructor.
46       * @param map map.
47       * @param node tree node.
48       * @param editor editor.
49       */
50      public MapData(final EditorMap map, final XsdTreeNode node, final OtsEditor editor)
51      {
52          this.map = map;
53          this.node = node;
54          this.editor = editor;
55          this.editor.addEvalListener(this);
56      }
57  
58      /**
59       * Returns the tree node.
60       * @return tree node.
61       */
62      public XsdTreeNode getNode()
63      {
64          return this.node;
65      }
66  
67      /**
68       * Returns the evaluator for expressions.
69       * @return evaluator for expressions.
70       */
71      public Eval getEval()
72      {
73          return this.editor.getEval();
74      }
75  
76      /**
77       * Destroy this data object, e.g. remove self as listener. Override and call super if subclasses remove their own listeners.
78       * Using weak references for listeners is another option to deal with obsolete listening.
79       */
80      public void destroy()
81      {
82          this.editor.removeEvalListener(this);
83      }
84  
85      /**
86       * Notify the map that the element can be drawn.
87       */
88      void setValid()
89      {
90          this.valid = true;
91          this.map.setValid(this);
92          fireEvent(new Event(MAP_DATA_CHANGED, null));
93      }
94  
95      /**
96       * Notify the map that the element cannot be drawn.
97       */
98      void setInvalid()
99      {
100         this.valid = false;
101         this.map.setInvalid(this);
102         fireEvent(new Event(MAP_DATA_CHANGED, null));
103     }
104 
105     /**
106      * Returns whether this data is valid.
107      * @return whether this data is valid
108      */
109     public boolean isValid()
110     {
111         return this.valid;
112     }
113 
114     /**
115      * Returns the map.
116      * @return map.
117      */
118     // If public, this will remove the map from the tabbed pane when the inspector wants a renderer for this element, which is
119     // the panel itself. It gets 'reparented' towards the cell in the inspection window.
120     protected EditorMap getMap()
121     {
122         return this.map;
123     }
124 
125     /**
126      * Generic method to set a value based on a changed attribute. If the new value is {@code null}, the setter is invoked to
127      * set a {@code null} value. If the value is an invalid expression, or the value cannot be unmarshalled by the adapter, no
128      * change is made.
129      * @param <T> type of the value to set.
130      * @param setter setter that receives a successfully derived value.
131      * @param adapter adapter.
132      * @param treeNode node that has the attribute, will often be {@code getNode()}.
133      * @param attribute name of the attribute.
134      */
135     protected <T> void setValue(final Consumer<T> setter, final ExpressionAdapter<T, ?> adapter, final XsdTreeNode treeNode,
136             final String attribute)
137     {
138         try
139         {
140             String stringValue = treeNode.getAttributeValueOrDefault(attribute);
141             if (stringValue == null)
142             {
143                 setter.accept(null);
144                 return;
145             }
146             T value = adapter.unmarshal(stringValue).get(getEval());
147             setter.accept(value);
148         }
149         catch (IllegalArgumentException ex)
150         {
151             setInvalid();
152             // IllegalArgumentException: invalid coordinate value, keep old coordinate
153         }
154         catch (Exception ex)
155         {
156             throw new OtsRuntimeException("Unexpected exception", ex);
157         }
158     }
159 
160 }