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 }