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<T>; setter that receives a successfully derived value.
105 * @param adapter ExpressionAdapter<T, ?>; 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 }