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 }