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    }