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

Last change on this file since 17318 was 16438, checked in by simon04, 4 years ago

see #19251 - Java 8: use Stream

  • Property svn:eol-style set to native
File size: 10.0 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 = {
148 "name:" + LanguageInfo.getJOSMLocaleCode(),
149 "name",
150 "int_name",
151 "distance",
152 "ref",
153 "operator",
154 "brand",
155 "addr:unit",
156 "addr:flats",
157 "addr:housenumber"
158 };
159
160 /**
161 * The list of default name complement tags from which a label candidate is derived.
162 */
163 private static final String[] DEFAULT_NAME_COMPLEMENT_TAGS = {
164 "capacity"
165 };
166
167 private List<String> nameTags = new ArrayList<>();
168 private List<String> nameComplementTags = new ArrayList<>();
169
170 /**
171 * <p>Creates the strategy and initializes its name tags from the preferences.</p>
172 */
173 public DeriveLabelFromNameTagsCompositionStrategy() {
174 Config.getPref().addPreferenceChangeListener(this);
175 initNameTagsFromPreferences();
176 }
177
178 private static List<String> buildNameTags(List<String> nameTags) {
179 if (nameTags == null) {
180 return new ArrayList<>();
181 }
182 return nameTags.stream()
183 .filter(tag -> !Utils.isStripEmpty(tag))
184 .collect(Collectors.toList());
185 }
186
187 /**
188 * Sets the name tags to be looked up in order to build up the label.
189 *
190 * @param nameTags the name tags. null values are ignored.
191 */
192 public void setNameTags(List<String> nameTags) {
193 this.nameTags = buildNameTags(nameTags);
194 }
195
196 /**
197 * Sets the name complement tags to be looked up in order to build up the label.
198 *
199 * @param nameComplementTags the name complement tags. null values are ignored.
200 * @since 6541
201 */
202 public void setNameComplementTags(List<String> nameComplementTags) {
203 this.nameComplementTags = buildNameTags(nameComplementTags);
204 }
205
206 /**
207 * Replies an unmodifiable list of the name tags used to compose the label.
208 *
209 * @return the list of name tags
210 */
211 public List<String> getNameTags() {
212 return Collections.unmodifiableList(nameTags);
213 }
214
215 /**
216 * Replies an unmodifiable list of the name complement tags used to compose the label.
217 *
218 * @return the list of name complement tags
219 * @since 6541
220 */
221 public List<String> getNameComplementTags() {
222 return Collections.unmodifiableList(nameComplementTags);
223 }
224
225 /**
226 * Initializes the name tags to use from a list of default name tags (see
227 * {@link #DEFAULT_NAME_TAGS} and {@link #DEFAULT_NAME_COMPLEMENT_TAGS})
228 * and from name tags configured in the preferences using the keys
229 * <code>mappaint.nameOrder</code> and <code>mappaint.nameComplementOrder</code>.
230 */
231 public final void initNameTagsFromPreferences() {
232 if (Config.getPref() == null) {
233 this.nameTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_TAGS));
234 this.nameComplementTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS));
235 } else {
236 this.nameTags = new ArrayList<>(
237 Config.getPref().getList("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS))
238 );
239 this.nameComplementTags = new ArrayList<>(
240 Config.getPref().getList("mappaint.nameComplementOrder", Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS))
241 );
242 }
243 }
244
245 private String getPrimitiveName(IPrimitive n) {
246 StringBuilder name = new StringBuilder();
247 if (!n.hasKeys()) return null;
248 nameTags.stream().map(n::get).filter(Objects::nonNull).findFirst()
249 .ifPresent(name::append);
250 for (String rn : nameComplementTags) {
251 String comp = n.get(rn);
252 if (comp != null) {
253 if (name.length() == 0) {
254 name.append(comp);
255 } else {
256 name.append(" (").append(comp).append(')');
257 }
258 break;
259 }
260 }
261 return name.toString();
262 }
263
264 @Override
265 public String compose(IPrimitive primitive) {
266 if (primitive == null) return null;
267 return getPrimitiveName(primitive);
268 }
269
270 @Override
271 public String toString() {
272 return '{' + getClass().getSimpleName() + '}';
273 }
274
275 @Override
276 public void preferenceChanged(PreferenceChangeEvent e) {
277 if (e.getKey() != null && e.getKey().startsWith("mappaint.name")) {
278 initNameTagsFromPreferences();
279 }
280 }
281 }
282}
Note: See TracBrowser for help on using the repository browser.