source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/LabelCompositionStrategy.java

Last change on this file was 19050, checked in by taylor.smock, 36 hours ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

  • Property svn:eol-style set to native
File size: 10.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.styleelement;
3
4import java.util.ArrayList;
5import java.util.Arrays;
6import java.util.Collections;
7import java.util.List;
8import java.util.Objects;
9import java.util.stream.Collectors;
10
11import org.openstreetmap.josm.data.osm.IPrimitive;
12import org.openstreetmap.josm.spi.preferences.Config;
13import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
14import org.openstreetmap.josm.spi.preferences.PreferenceChangedListener;
15import org.openstreetmap.josm.tools.LanguageInfo;
16import org.openstreetmap.josm.tools.Utils;
17
18/**
19 * <p>Provides an abstract parent class and three concrete sub classes for various
20 * strategies on how to compose the text label which can be rendered close to a node
21 * or within an area in an OSM map.</p>
22 *
23 * <p>The three strategies below support three rules for composing a label:
24 * <ul>
25 * <li>{@link StaticLabelCompositionStrategy} - the label is given by a static text
26 * specified in the MapCSS style file</li>
27 *
28 * <li>{@link TagLookupCompositionStrategy} - the label is given by the content of a
29 * tag whose name specified in the MapCSS style file</li>
30 *
31 * <li>{@link DeriveLabelFromNameTagsCompositionStrategy} - the label is given by the value
32 * of one of the configured "name tags". The list of relevant name tags can be configured
33 * in the JOSM preferences
34 * see the preference options <code>mappaint.nameOrder</code> and <code>mappaint.nameComplementOrder</code>.</li>
35 * </ul>
36 * @since 3987 (creation)
37 * @since 10599 (functional interface)
38 */
39@FunctionalInterface
40public interface LabelCompositionStrategy {
41
42 /**
43 * Replies the text value to be rendered as label for the primitive {@code primitive}.
44 *
45 * @param primitive the primitive
46 *
47 * @return the text value to be rendered or null, if primitive is null or
48 * if no suitable value could be composed
49 */
50 String compose(IPrimitive primitive);
51
52 /**
53 * Strategy where the label is given by a static text specified in the MapCSS style file.
54 */
55 class StaticLabelCompositionStrategy implements LabelCompositionStrategy {
56 private final String defaultLabel;
57
58 public StaticLabelCompositionStrategy(String defaultLabel) {
59 this.defaultLabel = defaultLabel;
60 }
61
62 @Override
63 public String compose(IPrimitive primitive) {
64 return defaultLabel;
65 }
66
67 public String getDefaultLabel() {
68 return defaultLabel;
69 }
70
71 @Override
72 public String toString() {
73 return '{' + getClass().getSimpleName() + " defaultLabel=" + defaultLabel + '}';
74 }
75
76 @Override
77 public int hashCode() {
78 return Objects.hash(defaultLabel);
79 }
80
81 @Override
82 public boolean equals(Object obj) {
83 if (this == obj) return true;
84 if (obj == null || getClass() != obj.getClass()) return false;
85 StaticLabelCompositionStrategy that = (StaticLabelCompositionStrategy) obj;
86 return Objects.equals(defaultLabel, that.defaultLabel);
87 }
88 }
89
90 /**
91 * Strategy where the label is given by the content of a tag whose name specified in the MapCSS style file.
92 */
93 class TagLookupCompositionStrategy implements LabelCompositionStrategy {
94
95 private final String defaultLabelTag;
96
97 public TagLookupCompositionStrategy(String defaultLabelTag) {
98 if (defaultLabelTag != null) {
99 defaultLabelTag = defaultLabelTag.trim();
100 if (defaultLabelTag.isEmpty()) {
101 defaultLabelTag = null;
102 }
103 }
104 this.defaultLabelTag = defaultLabelTag;
105 }
106
107 @Override
108 public String compose(IPrimitive primitive) {
109 if (defaultLabelTag == null) return null;
110 if (primitive == null) return null;
111 return primitive.get(defaultLabelTag);
112 }
113
114 public String getDefaultLabelTag() {
115 return defaultLabelTag;
116 }
117
118 @Override
119 public String toString() {
120 return '{' + getClass().getSimpleName() + " defaultLabelTag=" + defaultLabelTag + '}';
121 }
122
123 @Override
124 public int hashCode() {
125 return Objects.hash(defaultLabelTag);
126 }
127
128 @Override
129 public boolean equals(Object obj) {
130 if (this == obj) return true;
131 if (obj == null || getClass() != obj.getClass()) return false;
132 TagLookupCompositionStrategy that = (TagLookupCompositionStrategy) obj;
133 return Objects.equals(defaultLabelTag, that.defaultLabelTag);
134 }
135 }
136
137 /**
138 * Strategy where the label is given by the value of one of the configured "name tags".
139 * The list of relevant name tags can be configured in the JOSM preferences
140 * see the preference options <code>mappaint.nameOrder</code> and <code>mappaint.nameComplementOrder</code>
141 */
142 class DeriveLabelFromNameTagsCompositionStrategy implements LabelCompositionStrategy, PreferenceChangedListener {
143
144 /**
145 * The list of default name tags from which a label candidate is derived.
146 */
147 private static final String[] DEFAULT_NAME_TAGS = getDefaultNameTags();
148
149 /**
150 * The list of default name complement tags from which a label candidate is derived.
151 */
152 private static final String[] DEFAULT_NAME_COMPLEMENT_TAGS = {
153 "capacity"
154 };
155
156 private List<String> nameTags = new ArrayList<>();
157 private List<String> nameComplementTags = new ArrayList<>();
158
159 /**
160 * <p>Creates the strategy and initializes its name tags from the preferences.</p>
161 */
162 public DeriveLabelFromNameTagsCompositionStrategy() {
163 Config.getPref().addPreferenceChangeListener(this);
164 initNameTagsFromPreferences();
165 }
166
167 /* Is joining an array really that complicated in Java? */
168 private static String[] getDefaultNameTags() {
169 final var tags = new ArrayList<>(Arrays.asList(LanguageInfo.getOSMLocaleCodes("name:")));
170 tags.addAll(Arrays.asList("name",
171 "int_name",
172 "distance",
173 "railway:position",
174 "ref",
175 "operator",
176 "brand",
177 "addr:unit",
178 "addr:flats",
179 "addr:housenumber"));
180 return tags.toArray(String[]::new);
181 }
182
183 private static List<String> buildNameTags(List<String> nameTags) {
184 if (nameTags == null) {
185 return new ArrayList<>();
186 }
187 return nameTags.stream()
188 .filter(tag -> !Utils.isStripEmpty(tag))
189 .collect(Collectors.toList());
190 }
191
192 /**
193 * Sets the name tags to be looked up in order to build up the label.
194 *
195 * @param nameTags the name tags. null values are ignored.
196 */
197 public void setNameTags(List<String> nameTags) {
198 this.nameTags = buildNameTags(nameTags);
199 }
200
201 /**
202 * Sets the name complement tags to be looked up in order to build up the label.
203 *
204 * @param nameComplementTags the name complement tags. null values are ignored.
205 * @since 6541
206 */
207 public void setNameComplementTags(List<String> nameComplementTags) {
208 this.nameComplementTags = buildNameTags(nameComplementTags);
209 }
210
211 /**
212 * Replies an unmodifiable list of the name tags used to compose the label.
213 *
214 * @return the list of name tags
215 */
216 public List<String> getNameTags() {
217 return Collections.unmodifiableList(nameTags);
218 }
219
220 /**
221 * Replies an unmodifiable list of the name complement tags used to compose the label.
222 *
223 * @return the list of name complement tags
224 * @since 6541
225 */
226 public List<String> getNameComplementTags() {
227 return Collections.unmodifiableList(nameComplementTags);
228 }
229
230 /**
231 * Initializes the name tags to use from a list of default name tags (see
232 * {@link #DEFAULT_NAME_TAGS} and {@link #DEFAULT_NAME_COMPLEMENT_TAGS})
233 * and from name tags configured in the preferences using the keys
234 * <code>mappaint.nameOrder</code> and <code>mappaint.nameComplementOrder</code>.
235 */
236 public final void initNameTagsFromPreferences() {
237 if (Config.getPref() == null) {
238 this.nameTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_TAGS));
239 this.nameComplementTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS));
240 } else {
241 this.nameTags = new ArrayList<>(
242 Config.getPref().getList("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS))
243 );
244 this.nameComplementTags = new ArrayList<>(
245 Config.getPref().getList("mappaint.nameComplementOrder", Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS))
246 );
247 }
248 }
249
250 private String getPrimitiveName(IPrimitive n) {
251 StringBuilder name = new StringBuilder();
252 if (!n.hasKeys()) return null;
253 nameTags.stream().map(n::get).filter(Objects::nonNull).findFirst()
254 .ifPresent(name::append);
255 for (String rn : nameComplementTags) {
256 String comp = n.get(rn);
257 if (comp != null) {
258 if (name.length() == 0) {
259 name.append(comp);
260 } else {
261 name.append(" (").append(comp).append(')');
262 }
263 break;
264 }
265 }
266 return name.toString();
267 }
268
269 @Override
270 public String compose(IPrimitive primitive) {
271 if (primitive == null) return null;
272 return getPrimitiveName(primitive);
273 }
274
275 @Override
276 public String toString() {
277 return '{' + getClass().getSimpleName() + '}';
278 }
279
280 @Override
281 public void preferenceChanged(PreferenceChangeEvent e) {
282 if (e.getKey() != null && e.getKey().startsWith("mappaint.name")) {
283 initNameTagsFromPreferences();
284 }
285 }
286 }
287}
Note: See TracBrowser for help on using the repository browser.