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

Last change on this file was 19050, checked in by taylor.smock, 3 days 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.