001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.gui.mappaint;
003    
004    import java.util.ArrayList;
005    import java.util.Arrays;
006    import java.util.Collections;
007    import java.util.List;
008    
009    import org.openstreetmap.josm.Main;
010    import org.openstreetmap.josm.data.osm.OsmPrimitive;
011    import 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     *   option <tt>mappaint.nameOrder</tt>.</li>
032     * </ul>
033     * </p>
034     *
035     */
036    public abstract class LabelCompositionStrategy {
037    
038        /**
039         * Replies the text value to be rendered as label for the primitive {@code primitive}.
040         *
041         * @param primitive the primitive
042         *
043         * @return the text value to be rendered or null, if primitive is null or
044         * if no suitable value could be composed
045         */
046        abstract public String compose(OsmPrimitive primitive);
047    
048        static public class StaticLabelCompositionStrategy extends LabelCompositionStrategy {
049            private String defaultLabel;
050    
051            public StaticLabelCompositionStrategy(String defaultLabel){
052                this.defaultLabel = defaultLabel;
053            }
054    
055            @Override
056            public String compose(OsmPrimitive primitive) {
057                return defaultLabel;
058            }
059    
060            public String getDefaultLabel() {
061                return defaultLabel;
062            }
063    
064            @Override
065            public String toString() {
066                return "{"  + getClass().getSimpleName() + " defaultLabel=" + defaultLabel + "}";
067            }
068    
069            @Override
070            public int hashCode() {
071                final int prime = 31;
072                int result = 1;
073                result = prime * result + ((defaultLabel == null) ? 0 : defaultLabel.hashCode());
074                return result;
075            }
076    
077            @Override
078            public boolean equals(Object obj) {
079                if (this == obj)
080                    return true;
081                if (obj == null)
082                    return false;
083                if (getClass() != obj.getClass())
084                    return false;
085                StaticLabelCompositionStrategy other = (StaticLabelCompositionStrategy) obj;
086                if (defaultLabel == null) {
087                    if (other.defaultLabel != null)
088                        return false;
089                } else if (!defaultLabel.equals(other.defaultLabel))
090                    return false;
091                return true;
092            }
093        }
094    
095        static public class TagLookupCompositionStrategy extends LabelCompositionStrategy {
096    
097            private String defaultLabelTag;
098            public TagLookupCompositionStrategy(String defaultLabelTag){
099                if (defaultLabelTag != null) {
100                    defaultLabelTag = defaultLabelTag.trim();
101                    if (defaultLabelTag.isEmpty()) {
102                        defaultLabelTag = null;
103                    }
104                }
105                this.defaultLabelTag = defaultLabelTag;
106            }
107    
108            @Override
109            public String compose(OsmPrimitive primitive) {
110                if (defaultLabelTag == null) return null;
111                if (primitive == null) return null;
112                return primitive.get(defaultLabelTag);
113            }
114    
115            public String getDefaultLabelTag() {
116                return defaultLabelTag;
117            }
118    
119            @Override
120            public String toString() {
121                return "{" + getClass().getSimpleName() + " defaultLabelTag=" + defaultLabelTag + "}";
122            }
123    
124            @Override
125            public int hashCode() {
126                final int prime = 31;
127                int result = 1;
128                result = prime * result + ((defaultLabelTag == null) ? 0 : defaultLabelTag.hashCode());
129                return result;
130            }
131    
132            @Override
133            public boolean equals(Object obj) {
134                if (this == obj)
135                    return true;
136                if (obj == null)
137                    return false;
138                if (getClass() != obj.getClass())
139                    return false;
140                TagLookupCompositionStrategy other = (TagLookupCompositionStrategy) obj;
141                if (defaultLabelTag == null) {
142                    if (other.defaultLabelTag != null)
143                        return false;
144                } else if (!defaultLabelTag.equals(other.defaultLabelTag))
145                    return false;
146                return true;
147            }
148        }
149    
150        static public class DeriveLabelFromNameTagsCompositionStrategy extends LabelCompositionStrategy {
151    
152            /**
153             * The list of default name tags from which a label candidate is derived.
154             */
155            static public final String[] DEFAULT_NAME_TAGS = {
156                "name:" + LanguageInfo.getJOSMLocaleCode(),
157                "name",
158                "int_name",
159                "ref",
160                "operator",
161                "brand",
162                "addr:housenumber"
163            };
164    
165            private  List<String> nameTags = new ArrayList<String>();
166    
167            /**
168             * <p>Creates the strategy and initializes its name tags from the preferences.</p>
169             * 
170             * <p><strong>Note:</strong> If the list of name tags in the preferences changes, strategy instances
171             * are not notified. It's up to the client to listen to preference changes and
172             * invoke {@link #initNameTagsFromPreferences()} accordingly.</p>
173             * 
174             */
175            public DeriveLabelFromNameTagsCompositionStrategy() {
176                initNameTagsFromPreferences();
177            }
178    
179            /**
180             * Sets the name tags to be looked up in order to build up the label
181             * 
182             * @param nameTags the name tags. null values are ignore.
183             */
184            public void setNameTags(List<String> nameTags){
185                if (nameTags == null) {
186                    nameTags = Collections.emptyList();
187                }
188                this.nameTags = new ArrayList<String>();
189                for(String tag: nameTags) {
190                    if (tag == null) {
191                        continue;
192                    }
193                    tag = tag.trim();
194                    if (tag.isEmpty()) {
195                        continue;
196                    }
197                    this.nameTags.add(tag);
198                }
199            }
200    
201            /**
202             * Replies an unmodifiable list of the name tags used to compose the label.
203             * 
204             * @return the list of name tags
205             */
206            public List<String> getNameTags() {
207                return Collections.unmodifiableList(nameTags);
208            }
209    
210            /**
211             * Initializes the name tags to use from a list of default name tags (see
212             * {@link #DEFAULT_NAME_TAGS}) and from name tags configured in the preferences
213             * using the preference key <tt>mappaint.nameOrder</tt>.
214             */
215            public void initNameTagsFromPreferences() {
216                if (Main.pref == null){
217                    this.nameTags = new ArrayList<String>(Arrays.asList(DEFAULT_NAME_TAGS));
218                } else {
219                    this.nameTags = new ArrayList<String>(
220                            Main.pref.getCollection("mappaint.nameOrder", Arrays.asList(DEFAULT_NAME_TAGS))
221                    );
222                }
223            }
224    
225            private String getPrimitiveName(OsmPrimitive n) {
226                String name = null;
227                if (!n.hasKeys()) return null;
228                for (String rn : nameTags) {
229                    name = n.get(rn);
230                    if (name != null) return name;
231                }
232                return null;
233            }
234    
235            @Override
236            public String compose(OsmPrimitive primitive) {
237                if (primitive == null) return null;
238                return getPrimitiveName(primitive);
239            }
240    
241            @Override
242            public String toString() {
243                return "{" + getClass().getSimpleName() +"}";
244            }
245        }
246    }