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

Last change on this file was 19101, checked in by taylor.smock, 19 months ago

Cleanup some new PMD warnings from PMD 7.x

I haven't updated to PMD 7.x in ivy/maven yet since I still have 83 PMD violations
to go through, and I don't know (yet) if any of them were supposed to be ignored
by the xpath expressions in tools/pmd/josm-ruleset.xml.

I may re-enable some of the PMD checks I've temporarily disabled prior to committing
the update for PMD.

Additionally cleanup some SonarLint issues in the modified files.

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