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