001 // License: GPL. See LICENSE file for details.
002 package org.openstreetmap.josm.data.validation.tests;
003
004 import static org.openstreetmap.josm.tools.I18n.tr;
005
006 import java.util.Collection;
007 import java.util.HashSet;
008 import java.util.Map.Entry;
009
010 import org.openstreetmap.josm.data.osm.OsmPrimitive;
011 import org.openstreetmap.josm.data.validation.Severity;
012 import org.openstreetmap.josm.data.validation.Test;
013 import org.openstreetmap.josm.data.validation.TestError;
014
015 /**
016 * Check for missing name:* translations.
017 * <p>
018 * This test finds multilingual objects whose 'name' attribute is not
019 * equal to any 'name:*' attribute and not a composition of some
020 * 'name:*' attributes separated by ' - '.
021 * <p>
022 * For example, a node with name=Europe, name:de=Europa should have
023 * name:en=Europe to avoid triggering this test. An object with
024 * name='Suomi - Finland' should have at least name:fi=Suomi and
025 * name:sv=Finland to avoid a warning (name:et=Soome would not
026 * matter). Also, complain if an object has some name:* attribute but
027 * no name.
028 *
029 * @author Skela
030 */
031 public class NameMismatch extends Test {
032 protected static final int NAME_MISSING = 1501;
033 protected static final int NAME_TRANSLATION_MISSING = 1502;
034
035 public NameMismatch() {
036 super(tr("Missing name:* translation"),
037 tr("This test finds multilingual objects whose ''name'' attribute is not equal to some ''name:*'' attribute and not a composition of ''name:*'' attributes, e.g., Italia - Italien - Italy."));
038 }
039
040 /**
041 * Report a missing translation.
042 *
043 * @param p The primitive whose translation is missing
044 */
045 private void missingTranslation(OsmPrimitive p) {
046 errors.add(new TestError(this, Severity.OTHER,
047 tr("A name:* translation is missing."),
048 NAME_TRANSLATION_MISSING, p));
049 }
050
051 /**
052 * Check a primitive for a name mismatch.
053 *
054 * @param p The primitive to be tested
055 */
056 public void check(OsmPrimitive p) {
057 HashSet<String> names = new HashSet<String>();
058
059 for (Entry<String, String> entry : p.getKeys().entrySet()) {
060 if (entry.getKey().startsWith("name:")) {
061 String name_s = entry.getValue();
062 if (name_s != null) {
063 names.add(name_s);
064 }
065 }
066 }
067
068 if (names.isEmpty()) return;
069
070 String name = p.get("name");
071
072 if (name == null) {
073 errors.add(new TestError(this, Severity.OTHER,
074 tr("A name is missing, even though name:* exists."),
075 NAME_MISSING, p));
076 return;
077 }
078
079 if (names.contains(name)) return;
080 /* If name is not equal to one of the name:*, it should be a
081 composition of some (not necessarily all) name:* labels.
082 Check if this is the case. */
083
084 String split_names[] = name.split(" - ");
085 if (split_names.length == 1) {
086 /* The name is not composed of multiple parts. Complain. */
087 missingTranslation(p);
088 return;
089 }
090
091 /* Check that each part corresponds to a translated name:*. */
092 for (String n : split_names) {
093 if (!names.contains(n)) {
094 missingTranslation(p);
095 return;
096 }
097 }
098 }
099
100 /**
101 * Checks a name mismatch in all primitives.
102 *
103 * @param selection The primitives to be tested
104 */
105 @Override public void visit(Collection<OsmPrimitive> selection) {
106 for (OsmPrimitive p : selection)
107 if (!p.isDeleted() && !p.isIncomplete()) {
108 check(p);
109 }
110 }
111 }