001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.mappaint; 003 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Collections; 007import java.util.List; 008 009import org.openstreetmap.josm.Main; 010import org.openstreetmap.josm.data.osm.OsmPrimitive; 011import org.openstreetmap.josm.tools.LanguageInfo; 012 013/** 014 * <p>Provides an abstract parent class and three concrete sub classes for various 015 * strategies on how to compose the text label which can be rendered close to a node 016 * or within an area in an OSM map.</p> 017 * 018 * <p>The three strategies below support three rules for composing a label: 019 * <ul> 020 * <li>{@link StaticLabelCompositionStrategy} - the label is given by a static text 021 * specified in the MapCSS style file</li> 022 * 023 * <li>{@link TagLookupCompositionStrategy} - the label is given by the content of a 024 * tag whose name specified in the MapCSS style file</li> 025 * 026 * <li>{@link DeriveLabelFromNameTagsCompositionStrategy} - the label is given by the value 027 * of one 028 * of the configured "name tags". The list of relevant name tags can be configured 029 * in the JOSM preferences 030 * content of a tag whose name specified in the MapCSS style file, see the preference 031 * options <tt>mappaint.nameOrder</tt> and <tt>mappaint.nameComplementOrder</tt>.</li> 032 * </ul> 033 * 034 */ 035public abstract class LabelCompositionStrategy { 036 037 /** 038 * Replies the text value to be rendered as label for the primitive {@code primitive}. 039 * 040 * @param primitive the primitive 041 * 042 * @return the text value to be rendered or null, if primitive is null or 043 * if no suitable value could be composed 044 */ 045 public abstract String compose(OsmPrimitive primitive); 046 047 public static class StaticLabelCompositionStrategy extends LabelCompositionStrategy { 048 private String defaultLabel; 049 050 public StaticLabelCompositionStrategy(String defaultLabel){ 051 this.defaultLabel = defaultLabel; 052 } 053 054 @Override 055 public String compose(OsmPrimitive primitive) { 056 return defaultLabel; 057 } 058 059 public String getDefaultLabel() { 060 return defaultLabel; 061 } 062 063 @Override 064 public String toString() { 065 return "{" + getClass().getSimpleName() + " defaultLabel=" + defaultLabel + "}"; 066 } 067 068 @Override 069 public int hashCode() { 070 final int prime = 31; 071 int result = 1; 072 result = prime * result + ((defaultLabel == null) ? 0 : defaultLabel.hashCode()); 073 return result; 074 } 075 076 @Override 077 public boolean equals(Object obj) { 078 if (this == obj) 079 return true; 080 if (obj == null) 081 return false; 082 if (getClass() != obj.getClass()) 083 return false; 084 StaticLabelCompositionStrategy other = (StaticLabelCompositionStrategy) obj; 085 if (defaultLabel == null) { 086 if (other.defaultLabel != null) 087 return false; 088 } else if (!defaultLabel.equals(other.defaultLabel)) 089 return false; 090 return true; 091 } 092 } 093 094 public static class TagLookupCompositionStrategy extends LabelCompositionStrategy { 095 096 private String defaultLabelTag; 097 public TagLookupCompositionStrategy(String defaultLabelTag){ 098 if (defaultLabelTag != null) { 099 defaultLabelTag = defaultLabelTag.trim(); 100 if (defaultLabelTag.isEmpty()) { 101 defaultLabelTag = null; 102 } 103 } 104 this.defaultLabelTag = defaultLabelTag; 105 } 106 107 @Override 108 public String compose(OsmPrimitive 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 final int prime = 31; 126 int result = 1; 127 result = prime * result + ((defaultLabelTag == null) ? 0 : defaultLabelTag.hashCode()); 128 return result; 129 } 130 131 @Override 132 public boolean equals(Object obj) { 133 if (this == obj) 134 return true; 135 if (obj == null) 136 return false; 137 if (getClass() != obj.getClass()) 138 return false; 139 TagLookupCompositionStrategy other = (TagLookupCompositionStrategy) obj; 140 if (defaultLabelTag == null) { 141 if (other.defaultLabelTag != null) 142 return false; 143 } else if (!defaultLabelTag.equals(other.defaultLabelTag)) 144 return false; 145 return true; 146 } 147 } 148 149 public static class DeriveLabelFromNameTagsCompositionStrategy extends LabelCompositionStrategy { 150 151 /** 152 * The list of default name tags from which a label candidate is derived. 153 */ 154 private static final String[] DEFAULT_NAME_TAGS = { 155 "name:" + LanguageInfo.getJOSMLocaleCode(), 156 "name", 157 "int_name", 158 "ref", 159 "operator", 160 "brand", 161 "addr:housenumber" 162 }; 163 164 /** 165 * The list of default name complement tags from which a label candidate is derived. 166 */ 167 private static final String[] DEFAULT_NAME_COMPLEMENT_TAGS = { 168 "capacity" 169 }; 170 171 private List<String> nameTags = new ArrayList<>(); 172 private List<String> nameComplementTags = new ArrayList<>(); 173 174 /** 175 * <p>Creates the strategy and initializes its name tags from the preferences.</p> 176 * 177 * <p><strong>Note:</strong> If the list of name tags in the preferences changes, strategy instances 178 * are not notified. It's up to the client to listen to preference changes and 179 * invoke {@link #initNameTagsFromPreferences()} accordingly.</p> 180 * 181 */ 182 public DeriveLabelFromNameTagsCompositionStrategy() { 183 initNameTagsFromPreferences(); 184 } 185 186 private static List<String> buildNameTags(List<String> nameTags) { 187 if (nameTags == null) { 188 nameTags = Collections.emptyList(); 189 } 190 ArrayList<String> result = new ArrayList<>(); 191 for(String tag: nameTags) { 192 if (tag == null) { 193 continue; 194 } 195 tag = tag.trim(); 196 if (tag.isEmpty()) { 197 continue; 198 } 199 result.add(tag); 200 } 201 return result; 202 } 203 204 /** 205 * Sets the name tags to be looked up in order to build up the label. 206 * 207 * @param nameTags the name tags. null values are ignored. 208 */ 209 public void setNameTags(List<String> nameTags){ 210 this.nameTags = buildNameTags(nameTags); 211 } 212 213 /** 214 * Sets the name complement tags to be looked up in order to build up the label. 215 * 216 * @param nameComplementTags the name complement tags. null values are ignored. 217 * @since 6541 218 */ 219 public void setNameComplementTags(List<String> nameComplementTags){ 220 this.nameComplementTags = buildNameTags(nameComplementTags); 221 } 222 223 /** 224 * Replies an unmodifiable list of the name tags used to compose the label. 225 * 226 * @return the list of name tags 227 */ 228 public List<String> getNameTags() { 229 return Collections.unmodifiableList(nameTags); 230 } 231 232 /** 233 * Replies an unmodifiable list of the name complement tags used to compose the label. 234 * 235 * @return the list of name complement tags 236 * @since 6541 237 */ 238 public List<String> getNameComplementTags() { 239 return Collections.unmodifiableList(nameComplementTags); 240 } 241 242 /** 243 * Initializes the name tags to use from a list of default name tags (see 244 * {@link #DEFAULT_NAME_TAGS} and {@link #DEFAULT_NAME_COMPLEMENT_TAGS}) 245 * and from name tags configured in the preferences using the keys 246 * <tt>mappaint.nameOrder</tt> and <tt>mappaint.nameComplementOrder</tt>. 247 */ 248 public final void initNameTagsFromPreferences() { 249 if (Main.pref == null){ 250 this.nameTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_TAGS)); 251 this.nameComplementTags = new ArrayList<>(Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS)); 252 } else { 253 this.nameTags = new ArrayList<>( 254 Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS)) 255 ); 256 this.nameComplementTags = new ArrayList<>( 257 Main.pref.getCollection("mappaint.nameComplementOrder", Arrays.asList(DEFAULT_NAME_COMPLEMENT_TAGS)) 258 ); 259 } 260 } 261 262 private String getPrimitiveName(OsmPrimitive n) { 263 String name = null; 264 if (!n.hasKeys()) return null; 265 for (String rn : nameTags) { 266 name = n.get(rn); 267 if (name != null) { 268 break; 269 } 270 } 271 for (String rn : nameComplementTags) { 272 String comp = n.get(rn); 273 if (comp != null) { 274 if (name == null) { 275 name = comp; 276 } else { 277 name += " (" + comp + ")"; 278 } 279 break; 280 } 281 } 282 return name; 283 } 284 285 @Override 286 public String compose(OsmPrimitive primitive) { 287 if (primitive == null) return null; 288 return getPrimitiveName(primitive); 289 } 290 291 @Override 292 public String toString() { 293 return "{" + getClass().getSimpleName() +"}"; 294 } 295 } 296}