source: josm/trunk/src/org/openstreetmap/josm/gui/DefaultNameFormatter.java@ 4163

Last change on this file since 4163 was 4144, checked in by stoecker, 13 years ago

see #6064 - better handling of translation of relations

  • Property svn:eol-style set to native
File size: 20.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trc;
6import static org.openstreetmap.josm.tools.I18n.trc_lazy;
7import static org.openstreetmap.josm.tools.I18n.trn;
8
9import java.util.ArrayList;
10import java.util.Arrays;
11import java.util.Collections;
12import java.util.Comparator;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.data.coor.CoordinateFormat;
19import org.openstreetmap.josm.data.osm.Changeset;
20import org.openstreetmap.josm.data.osm.INode;
21import org.openstreetmap.josm.data.osm.IPrimitive;
22import org.openstreetmap.josm.data.osm.IRelation;
23import org.openstreetmap.josm.data.osm.IWay;
24import org.openstreetmap.josm.data.osm.NameFormatter;
25import org.openstreetmap.josm.data.osm.Node;
26import org.openstreetmap.josm.data.osm.OsmPrimitive;
27import org.openstreetmap.josm.data.osm.OsmUtils;
28import org.openstreetmap.josm.data.osm.Relation;
29import org.openstreetmap.josm.data.osm.RelationMember;
30import org.openstreetmap.josm.data.osm.Way;
31import org.openstreetmap.josm.data.osm.history.HistoryNameFormatter;
32import org.openstreetmap.josm.data.osm.history.HistoryNode;
33import org.openstreetmap.josm.data.osm.history.HistoryOsmPrimitive;
34import org.openstreetmap.josm.data.osm.history.HistoryRelation;
35import org.openstreetmap.josm.data.osm.history.HistoryWay;
36
37/**
38 * This is the default implementation of a {@see NameFormatter} for names of {@see OsmPrimitive}s.
39 *
40 */
41public class DefaultNameFormatter implements NameFormatter, HistoryNameFormatter {
42
43 static private DefaultNameFormatter instance;
44
45 /**
46 * Replies the unique instance of this formatter
47 *
48 * @return the unique instance of this formatter
49 */
50 static public DefaultNameFormatter getInstance() {
51 if (instance == null) {
52 instance = new DefaultNameFormatter();
53 }
54 return instance;
55 }
56
57 /** the default list of tags which are used as naming tags in relations */
58 static public final String[] DEFAULT_NAMING_TAGS_FOR_RELATIONS = {"name", "ref", "restriction", "landuse", "natural",
59 "public_transport", ":LocationCode", "note"};
60
61 /** the current list of tags used as naming tags in relations */
62 static private List<String> namingTagsForRelations = null;
63
64 /**
65 * Replies the list of naming tags used in relations. The list is given (in this order) by:
66 * <ul>
67 * <li>by the tag names in the preference <tt>relation.nameOrder</tt></li>
68 * <li>by the default tags in {@see #DEFAULT_NAMING_TAGS_FOR_RELATIONS}
69 * </ul>
70 *
71 * @return the list of naming tags used in relations
72 */
73 static public List<String> getNamingtagsForRelations() {
74 if (namingTagsForRelations == null) {
75 namingTagsForRelations = new ArrayList<String>(
76 Main.pref.getCollection("relation.nameOrder", Arrays.asList(DEFAULT_NAMING_TAGS_FOR_RELATIONS))
77 );
78 }
79 return namingTagsForRelations;
80 }
81
82 /**
83 * Decorates the name of primitive with its id, if the preference
84 * <tt>osm-primitives.showid</tt> is set. Shows unique id if osm-primitives.showid.new-primitives is set
85 *
86 * @param name the name without the id
87 * @param primitive the primitive
88 * @return the decorated name
89 */
90 protected String decorateNameWithId(String name, IPrimitive primitive) {
91 if (Main.pref.getBoolean("osm-primitives.showid"))
92 if (Main.pref.getBoolean("osm-primitives.showid.new-primitives"))
93 return name + tr(" [id: {0}]", primitive.getUniqueId());
94 else
95 return name + tr(" [id: {0}]", primitive.getId());
96 else
97 return name;
98 }
99
100 /**
101 * Formats a name for a node
102 *
103 * @param node the node
104 * @return the name
105 */
106 public String format(INode node) {
107 String name = "";
108 if (node.isIncomplete()) {
109 name = tr("incomplete");
110 } else {
111 if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
112 name = node.getLocalName();
113 } else {
114 name = node.getName();
115 }
116 if(name == null)
117 {
118 String s;
119 if((s = node.get("addr:housename")) != null) {
120 /* I18n: name of house as parameter */
121 name = tr("House {0}", s);
122 }
123 if(name == null && (s = node.get("addr:housenumber")) != null) {
124 String t = node.get("addr:street");
125 if(t != null) {
126 /* I18n: house number, street as parameter, number should remain
127 before street for better visibility */
128 name = tr("House number {0} at {1}", s, t);
129 }
130 else {
131 /* I18n: house number as parameter */
132 name = tr("House number {0}", s);
133 }
134 }
135 }
136
137 if (name == null) {
138 name = node.isNew() ? tr("node") : ""+ node.getId();
139 }
140 name += " (" + node.getCoor().latToString(CoordinateFormat.getDefaultFormat()) + ", " + node.getCoor().lonToString(CoordinateFormat.getDefaultFormat()) + ")";
141 }
142 name = decorateNameWithId(name, node);
143 return name;
144 }
145
146 private final Comparator<Node> nodeComparator = new Comparator<Node>() {
147 @Override
148 public int compare(Node n1, Node n2) {
149 return format(n1).compareTo(format(n2));
150 }
151 };
152
153 public Comparator<Node> getNodeComparator() {
154 return nodeComparator;
155 }
156
157
158 /**
159 * Formats a name for a way
160 *
161 * @param way the way
162 * @return the name
163 */
164 public String format(IWay way) {
165 String name = "";
166 if (way.isIncomplete()) {
167 name = tr("incomplete");
168 } else {
169 if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
170 name = way.getLocalName();
171 } else {
172 name = way.getName();
173 }
174 if (name == null) {
175 name = way.get("ref");
176 }
177 if (name == null) {
178 name =
179 (way.get("highway") != null) ? tr("highway") :
180 (way.get("railway") != null) ? tr("railway") :
181 (way.get("waterway") != null) ? tr("waterway") :
182 (way.get("landuse") != null) ? tr("landuse") : null;
183 }
184 if(name == null)
185 {
186 String s;
187 if((s = way.get("addr:housename")) != null) {
188 /* I18n: name of house as parameter */
189 name = tr("House {0}", s);
190 }
191 if(name == null && (s = way.get("addr:housenumber")) != null) {
192 String t = way.get("addr:street");
193 if(t != null) {
194 /* I18n: house number, street as parameter, number should remain
195 before street for better visibility */
196 name = tr("House number {0} at {1}", s, t);
197 }
198 else {
199 /* I18n: house number as parameter */
200 name = tr("House number {0}", s);
201 }
202 }
203 }
204
205 int nodesNo = way.getNodesCount();
206 if (nodesNo > 1 && way.isClosed()) {
207 nodesNo--;
208 }
209 if(name == null || name.length() == 0) {
210 name = String.valueOf(way.getId());
211 }
212 /* note: length == 0 should no longer happen, but leave the bracket code
213 nevertheless, who knows what future brings */
214 /* I18n: count of nodes as parameter */
215 String nodes = trn("{0} node", "{0} nodes", nodesNo, nodesNo);
216 name += (name.length() > 0) ? " ("+nodes+")" : nodes;
217 }
218 name = decorateNameWithId(name, way);
219 return name;
220 }
221
222 private final Comparator<Way> wayComparator = new Comparator<Way>() {
223 @Override
224 public int compare(Way w1, Way w2) {
225 return format(w1).compareTo(format(w2));
226 }
227 };
228
229 public Comparator<Way> getWayComparator() {
230 return wayComparator;
231 }
232
233
234 /**
235 * Formats a name for a relation
236 *
237 * @param relation the relation
238 * @return the name
239 */
240 public String format(IRelation relation) {
241 String name;
242 if (relation.isIncomplete()) {
243 name = tr("incomplete");
244 } else {
245 name = getRelationTypeName(relation);
246 String relationName = getRelationName(relation);
247 if (relationName == null) {
248 relationName = Long.toString(relation.getId());
249 } else {
250 relationName = "\"" + relationName + "\"";
251 }
252 name += " (" + relationName + ", ";
253
254 int mbno = relation.getMembersCount();
255 name += trn("{0} member", "{0} members", mbno, mbno);
256
257 if (relation instanceof Relation) {
258 if (relationHasIncompleteMember((Relation) relation)) {
259 name += ", "+tr("incomplete");
260 }
261 }
262
263 name += ")";
264 }
265 name = decorateNameWithId(name, relation);
266 return name;
267 }
268
269 private final Comparator<Relation> relationComparator = new Comparator<Relation>() {
270 @Override
271 public int compare(Relation r1, Relation r2) {
272 String type1 = getRelationTypeName(r1);
273 String type2 = getRelationTypeName(r2);
274
275 int comp = type1.compareTo(type2);
276 if (comp != 0)
277 return comp;
278
279 String name1 = getRelationName(r1);
280 String name2 = getRelationName(r2);
281
282 if (name1 == null && name2 == null)
283 return (r1.getUniqueId() > r2.getUniqueId())?1:-1;
284 else if (name1 == null)
285 return -1;
286 else if (name2 == null)
287 return 1;
288 else if (!name1.isEmpty() && !name2.isEmpty() && Character.isDigit(name1.charAt(0)) && Character.isDigit(name2.charAt(0))) {
289 //Compare numerically
290 String ln1 = getLeadingNumber(name1);
291 String ln2 = getLeadingNumber(name2);
292
293 comp = Long.valueOf(ln1).compareTo(Long.valueOf(ln2));
294 if (comp != 0)
295 return comp;
296
297 // put 1 before 0001
298 comp = ln1.compareTo(ln2);
299 if (comp != 0)
300 return comp;
301
302 comp = name1.substring(ln1.length()).compareTo(name2.substring(ln2.length()));
303 if (comp != 0)
304 return comp;
305 } else {
306 comp = name1.compareToIgnoreCase(name2);
307 if (comp != 0)
308 return comp;
309 }
310
311 if (r1.getMembersCount() != r2.getMembersCount())
312 return (r1.getMembersCount() > r2.getMembersCount())?1:-1;
313
314 comp = Boolean.valueOf(relationHasIncompleteMember(r1)).compareTo(Boolean.valueOf(relationHasIncompleteMember(r2)));
315 if (comp != 0)
316 return comp;
317
318 return r1.getUniqueId() > r2.getUniqueId()?1:-1;
319 }
320 };
321
322 public Comparator<Relation> getRelationComparator() {
323 return relationComparator;
324 }
325
326 private String getLeadingNumber(String s) {
327 int i = 0;
328 while (i < s.length() && Character.isDigit(s.charAt(i))) {
329 i++;
330 }
331 return s.substring(0, i);
332 }
333
334 private String getRelationTypeName(IRelation relation) {
335 String name = trc("Relation type", relation.get("type"));
336 if (name == null) {
337 name = (relation.get("public_transport") != null) ? tr("public transport") : null;
338 }
339 if (name == null) {
340 String building = relation.get("building");
341 if(OsmUtils.isTrue(building)) {
342 name = tr("building");
343 } else if(building != null)
344 {
345 name = tr(building); // translate tag!
346 }
347 }
348 if (name == null) {
349 name = trc("Place type", relation.get("place"));
350 }
351 if (name == null) {
352 name = tr("relation");
353 }
354 String admin_level = relation.get("admin_level");
355 if (admin_level != null) {
356 name += "["+admin_level+"]";
357 }
358
359 return name;
360 }
361
362 private String getNameTagValue(IRelation relation, String nameTag) {
363 if (nameTag.equals("name")) {
364 if (Main.pref.getBoolean("osm-primitives.localize-name", true))
365 return relation.getLocalName();
366 else
367 return relation.getName();
368 } else if (nameTag.equals(":LocationCode")) {
369 for (String m : relation.keySet()) {
370 if (m.endsWith(nameTag))
371 return relation.get(m);
372 }
373 return null;
374 } else
375 return trc_lazy(nameTag, relation.get(nameTag));
376 }
377
378 private String getRelationName(IRelation relation) {
379 String nameTag = null;
380 for (String n : getNamingtagsForRelations()) {
381 nameTag = getNameTagValue(relation, n);
382 if (nameTag != null)
383 return nameTag;
384 }
385 return null;
386 }
387
388 private boolean relationHasIncompleteMember(Relation relation) {
389 for (RelationMember m : relation.getMembers()) {
390 if (m.getMember().isIncomplete())
391 return true;
392 }
393 return false;
394 }
395
396 /**
397 * Formats a name for a changeset
398 *
399 * @param changeset the changeset
400 * @return the name
401 */
402 public String format(Changeset changeset) {
403 return tr("Changeset {0}",changeset.getId());
404 }
405
406 /**
407 * Builds a default tooltip text for the primitive <code>primitive</code>.
408 *
409 * @param primitive the primitmive
410 * @return the tooltip text
411 */
412 public String buildDefaultToolTip(IPrimitive primitive) {
413 StringBuilder sb = new StringBuilder();
414 sb.append("<html>");
415 sb.append("<strong>id</strong>=")
416 .append(primitive.getId())
417 .append("<br>");
418 ArrayList<String> keyList = new ArrayList<String>(primitive.keySet());
419 Collections.sort(keyList);
420 for (int i = 0; i < keyList.size(); i++) {
421 if (i > 0) {
422 sb.append("<br>");
423 }
424 String key = keyList.get(i);
425 sb.append("<strong>")
426 .append(key)
427 .append("</strong>")
428 .append("=");
429 String value = primitive.get(key);
430 while(value.length() != 0) {
431 sb.append(value.substring(0,Math.min(50, value.length())));
432 if (value.length() > 50) {
433 sb.append("<br>");
434 value = value.substring(50);
435 } else {
436 value = "";
437 }
438 }
439 }
440 sb.append("</html>");
441 return sb.toString();
442 }
443
444 /**
445 * Decorates the name of primitive with its id, if the preference
446 * <tt>osm-primitives.showid</tt> is set.
447 *
448 * The id is append to the {@see StringBuilder} passed in in <code>name</code>.
449 *
450 * @param name the name without the id
451 * @param primitive the primitive
452 */
453 protected void decorateNameWithId(StringBuilder name, HistoryOsmPrimitive primitive) {
454 if (Main.pref.getBoolean("osm-primitives.showid")) {
455 name.append(tr(" [id: {0}]", primitive.getId()));
456 }
457 }
458
459 /**
460 * Formats a name for a history node
461 *
462 * @param node the node
463 * @return the name
464 */
465 public String format(HistoryNode node) {
466 StringBuilder sb = new StringBuilder();
467 String name;
468 if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
469 name = node.getLocalName();
470 } else {
471 name = node.getName();
472 }
473 if (name == null) {
474 sb.append(node.getId());
475 } else {
476 sb.append(name);
477 }
478 sb.append(" (")
479 .append(node.getCoords().latToString(CoordinateFormat.getDefaultFormat()))
480 .append(", ")
481 .append(node.getCoords().lonToString(CoordinateFormat.getDefaultFormat()))
482 .append(")");
483 decorateNameWithId(sb, node);
484 return sb.toString();
485 }
486
487 /**
488 * Formats a name for a way
489 *
490 * @param way the way
491 * @return the name
492 */
493 public String format(HistoryWay way) {
494 StringBuilder sb = new StringBuilder();
495 String name;
496 if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
497 name = way.getLocalName();
498 } else {
499 name = way.getName();
500 }
501 if (name != null) {
502 sb.append(name);
503 }
504 if (sb.length() == 0 && way.get("ref") != null) {
505 sb.append(way.get("ref"));
506 }
507 if (sb.length() == 0) {
508 sb.append(
509 (way.get("highway") != null) ? tr("highway") :
510 (way.get("railway") != null) ? tr("railway") :
511 (way.get("waterway") != null) ? tr("waterway") :
512 (way.get("landuse") != null) ? tr("landuse") : ""
513 );
514 }
515
516 int nodesNo = way.isClosed() ? way.getNumNodes() -1 : way.getNumNodes();
517 String nodes = trn("{0} node", "{0} nodes", nodesNo, nodesNo);
518 if(sb.length() == 0 ) {
519 sb.append(way.getId());
520 }
521 /* note: length == 0 should no longer happen, but leave the bracket code
522 nevertheless, who knows what future brings */
523 sb.append((sb.length() > 0) ? " ("+nodes+")" : nodes);
524 decorateNameWithId(sb, way);
525 return sb.toString();
526 }
527
528 /**
529 * Formats a name for a {@see HistoryRelation})
530 *
531 * @param relation the relation
532 * @return the name
533 */
534 public String format(HistoryRelation relation) {
535 StringBuilder sb = new StringBuilder();
536 if (relation.get("type") != null) {
537 sb.append(relation.get("type"));
538 } else {
539 sb.append(tr("relation"));
540 }
541 sb.append(" (");
542 String nameTag = null;
543 Set<String> namingTags = new HashSet<String>(getNamingtagsForRelations());
544 for (String n : relation.getTags().keySet()) {
545 // #3328: "note " and " note" are name tags too
546 if (namingTags.contains(n.trim())) {
547 if (Main.pref.getBoolean("osm-primitives.localize-name", true)) {
548 nameTag = relation.getLocalName();
549 } else {
550 nameTag = relation.getName();
551 }
552 if (nameTag == null) {
553 nameTag = relation.get(n);
554 }
555 }
556 if (nameTag != null) {
557 break;
558 }
559 }
560 if (nameTag == null) {
561 sb.append(Long.toString(relation.getId())).append(", ");
562 } else {
563 sb.append("\"").append(nameTag).append("\", ");
564 }
565
566 int mbno = relation.getNumMembers();
567 sb.append(trn("{0} member", "{0} members", mbno, mbno)).append(")");
568
569 decorateNameWithId(sb, relation);
570 return sb.toString();
571 }
572
573 /**
574 * Builds a default tooltip text for an HistoryOsmPrimitive <code>primitive</code>.
575 *
576 * @param primitive the primitmive
577 * @return the tooltip text
578 */
579 public String buildDefaultToolTip(HistoryOsmPrimitive primitive) {
580 StringBuilder sb = new StringBuilder();
581 sb.append("<html>");
582 sb.append("<strong>id</strong>=")
583 .append(primitive.getId())
584 .append("<br>");
585 ArrayList<String> keyList = new ArrayList<String>(primitive.getTags().keySet());
586 Collections.sort(keyList);
587 for (int i = 0; i < keyList.size(); i++) {
588 if (i > 0) {
589 sb.append("<br>");
590 }
591 String key = keyList.get(i);
592 sb.append("<strong>")
593 .append(key)
594 .append("</strong>")
595 .append("=");
596 String value = primitive.get(key);
597 while(value.length() != 0) {
598 sb.append(value.substring(0,Math.min(50, value.length())));
599 if (value.length() > 50) {
600 sb.append("<br>");
601 value = value.substring(50);
602 } else {
603 value = "";
604 }
605 }
606 }
607 sb.append("</html>");
608 return sb.toString();
609 }
610}
Note: See TracBrowser for help on using the repository browser.