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.opentrafficsim.editor.EvalWrapper.EvalListener;
7   import org.opentrafficsim.editor.OtsEditor;
8   import org.opentrafficsim.editor.XsdTreeNode;
9   import org.opentrafficsim.xml.bindings.ExpressionAdapter;
10  
11  /**
12   * Part of the map data structure.
13   * <p>
14   * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
15   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
16   * </p>
17   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
18   */
19  public abstract class MapData implements EvalListener
20  {
21  
22      /** Map showing all the elements. */
23      private final EditorMap map;
24  
25      /** Tree node. */
26      private final XsdTreeNode node;
27  
28      /** Editor. */
29      private final OtsEditor editor;
30  
31      /**
32       * Constructor.
33       * @param map Map; map.
34       * @param node XsdTreeNode; tree node.
35       * @param editor OtsEditor; editor.
36       */
37      public MapData(final EditorMap map, final XsdTreeNode node, final OtsEditor editor)
38      {
39          this.map = map;
40          this.node = node;
41          this.editor = editor;
42          this.editor.addEvalListener(this);
43      }
44  
45      /**
46       * Returns the tree node.
47       * @return XsdTreeNode; tree node.
48       */
49      public XsdTreeNode getNode()
50      {
51          return this.node;
52      }
53  
54      /**
55       * Returns the evaluator for expressions.
56       * @return Eval; evaluator for expressions.
57       */
58      public Eval getEval()
59      {
60          return this.editor.getEval();
61      }
62  
63      /**
64       * Destroy this data object, e.g. remove self as listener. Override and call super if subclasses remove their own listeners.
65       * Using weak references for listeners is another option to deal with obsolete listening.
66       */
67      public void destroy()
68      {
69          this.editor.removeEvalListener(this);
70      }
71  
72      /**
73       * Notify the map that the element can be drawn.
74       */
75      void setValid()
76      {
77          this.map.setValid(this);
78      }
79  
80      /**
81       * Notify the map that the element cannot be drawn.
82       */
83      void setInvalid()
84      {
85          this.map.setInvalid(this);
86      }
87  
88      /**
89       * Returns the map.
90       * @return Map; map.
91       */
92      // If public, this will remove the map from the tabbed pane when the inspector wants a renderer for this element, which is 
93      // the panel itself. It gets 'reparented' towards the cell in the inspection window.
94      protected EditorMap getMap()
95      {
96          return this.map;
97      }
98  
99      /**
100      * Generic method to set a value based on a changed attribute. If the new value is {@code null}, the setter is invoked to
101      * set a {@code null} value. If the value is an invalid expression, or the value cannot be unmarshalled by the adapter, no
102      * change is made.
103      * @param <T> type of the value to set.
104      * @param setter Consumer&lt;T&gt;; setter that receives a successfully derived value.
105      * @param adapter ExpressionAdapter&lt;T, ?&gt;; adapter.
106      * @param node XsdTreeNode; node that has the attribute, will often be {@code getNode()}.
107      * @param attribute String; name of the attribute.
108      */
109     protected <T> void setValue(final Consumer<T> setter, final ExpressionAdapter<T, ?> adapter, final XsdTreeNode node,
110             final String attribute)
111     {
112         try
113         {
114             String stringValue = node.getAttributeValue(attribute);
115             if (stringValue == null)
116             {
117                 setter.accept(null);
118                 return;
119             }
120             T value = adapter.unmarshal(stringValue).get(getEval());
121             setter.accept(value);
122         }
123         catch (RuntimeException ex)
124         {
125             setInvalid();
126             // RuntimeException: expression not valid => we should add expression validators baked in to XsdTreeNode
127             // IllegalArgumentException: invalid coordinate value, keep old coordinate
128         }
129         catch (Exception ex)
130         {
131             throw new RuntimeException("Unexpected exception", ex);
132         }
133     }
134 
135 }