Ticket #9844: RelationChecker.java.patch

File RelationChecker.java.patch, 12.2 KB (added by wiktorn, 10 years ago)

Patch for defect

  • src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java

     
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
    77import java.text.MessageFormat;
    8 import java.util.ArrayList;
    98import java.util.Collection;
    109import java.util.HashMap;
    1110import java.util.HashSet;
    1211import java.util.LinkedList;
    13 import java.util.List;
    1412import java.util.Set;
    1513
    1614import org.openstreetmap.josm.command.Command;
    1715import org.openstreetmap.josm.command.DeleteCommand;
    18 import org.openstreetmap.josm.data.osm.Node;
    1916import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2017import org.openstreetmap.josm.data.osm.Relation;
    2118import org.openstreetmap.josm.data.osm.RelationMember;
    22 import org.openstreetmap.josm.data.osm.Way;
    2319import org.openstreetmap.josm.data.validation.Severity;
    2420import org.openstreetmap.josm.data.validation.Test;
    2521import org.openstreetmap.josm.data.validation.TestError;
     
    8278        }
    8379    }
    8480
     81    private static class RolePreset {
     82        public RolePreset(LinkedList<Role> roles, String name) {
     83            this.roles = roles;
     84            this.name = name;
     85        }
     86        private LinkedList<Role> roles;
     87        private String name;
     88    }
     89
    8590    private static class RoleInfo {
    8691        private int total = 0;
    87         private Collection<Node> nodes = new LinkedList<>();
    88         private Collection<Way> ways = new LinkedList<>();
    89         private Collection<Way> openways = new LinkedList<>();
    90         private Collection<Relation> relations = new LinkedList<>();
    9192    }
    9293
    9394    @Override
    9495    public void visit(Relation n) {
    95         LinkedList<Role> allroles = buildAllRoles(n);
     96        HashMap<String, RolePreset> allroles = buildAllRoles(n);
    9697        if (allroles.isEmpty() && n.hasTag("type", "route")
    9798                && n.hasTag("route", "train", "subway", "monorail", "tram", "bus", "trolleybus", "aerialway", "ferry")) {
    9899            errors.add(new TestError(this, Severity.WARNING,
     
    117118            RoleInfo ri = map.get(role);
    118119            if (ri == null) {
    119120                ri = new RoleInfo();
     121                map.put(role, ri);
    120122            }
    121123            ri.total++;
    122             if (m.isRelation()) {
    123                 ri.relations.add(m.getRelation());
    124             } else if(m.isWay()) {
    125                 ri.ways.add(m.getWay());
    126                 if (!m.getWay().isClosed()) {
    127                     ri.openways.add(m.getWay());
    128                 }
    129             }
    130             else if (m.isNode()) {
    131                 ri.nodes.add(m.getNode());
    132             }
    133             map.put(role, ri);
    134124        }
    135125        return map;
    136126    }
    137127
    138     private LinkedList<Role> buildAllRoles(Relation n) {
    139         LinkedList<Role> allroles = new LinkedList<>();
     128    // return Roles grouped by key
     129    private HashMap<String, RolePreset> buildAllRoles(Relation n) {
     130        HashMap<String, RolePreset> allroles = new HashMap<>();
     131
    140132        for (TaggingPreset p : relationpresets) {
    141133            boolean matches = true;
    142134            Roles r = null;
     
    152144                }
    153145            }
    154146            if (matches && r != null) {
    155                 allroles.addAll(r.roles);
     147                for(Role role: r.roles) {
     148                    String key = role.key;
     149                    LinkedList<Role> roleGroup = null;
     150                    if (allroles.containsKey(key)) {
     151                        roleGroup = allroles.get(key).roles;
     152                    } else {
     153                        roleGroup = new LinkedList<>();
     154                        allroles.put(key, new RolePreset(roleGroup, p.name));
     155                    }
     156                    roleGroup.add(role);
     157                }
    156158            }
    157159        }
    158160        return allroles;
    159161    }
    160162
    161     private void checkRoles(Relation n, LinkedList<Role> allroles, HashMap<String, RoleInfo> map) {
    162         List<String> done = new LinkedList<>();
    163         // Remove empty roles if several exist (like in route=hiking, see #9844)
    164         List<Role> emptyRoles = new LinkedList<>();
    165         for (Role r : allroles) {
    166             if ("".equals(r.key)) {
    167                 emptyRoles.add(r);
     163    private boolean checkMemberType(Role r, RelationMember member) {
     164        if (r.types != null) {
     165            for (TaggingPresetType type: r.types) {
     166                if (type.equals(TaggingPresetType.CLOSEDWAY) && member.isWay() && member.getWay().isClosed()) {
     167                    return true;
     168                }
     169                if(type.equals(TaggingPresetType.NODE) && member.isNode()) {
     170                    return true;
     171                }
     172                if(type.equals(TaggingPresetType.RELATION) && member.isRelation()) {
     173                    return true;
     174                }
     175                if(type.equals(TaggingPresetType.WAY) && member.isWay()) {
     176                    return true;
     177                }
    168178            }
     179        } else {
     180            // if no types specified, then test is passed
     181            return true;
    169182        }
    170         if (emptyRoles.size() > 1) {
    171             allroles.removeAll(emptyRoles);
     183        // if no match is found, then test failed
     184        return false;
     185    }
     186
     187    // get all role definition for specified key and check, if some definition matches
     188    private boolean checkMemberExpressionAndType(RolePreset rolePreset, RelationMember member, Relation n) {
     189        TestError possibleMatchError = null;
     190        if (rolePreset == null || rolePreset.roles == null) {
     191            // no restrictions on role types
     192            return true;
    172193        }
    173         for (Role r : allroles) {
    174             done.add(r.key);
    175             String keyname = r.key;
    176             if ("".equals(keyname)) {
    177                 keyname = tr("<empty>");
    178             }
    179             RoleInfo ri = map.get(r.key);
    180             checkRoleCounts(n, r, keyname, ri);
    181             if (ri != null) {
    182                 if (r.types != null) {
    183                     checkRoleTypes(n, r, keyname, ri);
    184                 }
     194        for (Role r: rolePreset.roles) {
     195            if (checkMemberType(r, member)) {
    185196                if (r.memberExpression != null) {
    186                     checkRoleMemberExpressions(n, r, keyname, ri);
     197                    OsmPrimitive primitive = member.getMember();
     198                    if(primitive.isUsable()) {
     199                        if(r.memberExpression.match(primitive)) {
     200                            return true;
     201                        } else {
     202                            // possible match error
     203                            String s = marktr("Role member didn''t match expression {0} in template {1}");
     204                            possibleMatchError = new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
     205                                    tr(s, r.memberExpression, rolePreset.name), s, WRONG_TYPE,
     206                                        member.getMember().isUsable() ? member.getMember() : n);
     207
     208                        }
     209                    } else {
     210                        // if it is not usable, we can't verify memberExpression
     211                        return true;
     212                    }
     213                } else {
     214                    return true;
     215                }
     216            } else {
     217
     218            }
     219        }
     220        if( possibleMatchError != null) {
     221            errors.add(possibleMatchError);
     222        } else {
     223            String s = marktr("Role member type {0} didn''t match accepted list of {1} in template {2}");
     224            Set<Enum<TaggingPresetType>> types = new HashSet<>();
     225            for (Role r: rolePreset.roles) {
     226                types.addAll(r.types);
     227            }
     228            errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
     229                    tr(s, member.getType(), types, rolePreset.name), s, WRONG_TYPE,
     230                        member.getMember().isUsable() ? member.getMember() : n));
     231        }
     232        return false;
     233    }
     234
     235
     236    private void checkRoles(Relation n, HashMap<String, RolePreset> allroles, HashMap<String, RoleInfo> map) {
     237        Set<String> done = new HashSet<>();
     238
     239        // go through all members of relation
     240        for (RelationMember member: n.getMembers()) {
     241            String role = member.getRole();
     242            done.add(role);
     243
     244            // error reporting done inside
     245            checkMemberExpressionAndType(allroles.get(role), member, n);
     246        }
     247
     248        // verify role counts based on whole role sets
     249        for(RolePreset rp: allroles.values()) {
     250            for (Role r: rp.roles) {
     251                String keyname = r.key;
     252                if ("".equals(keyname)) {
     253                    keyname = tr("<empty>");
    187254                }
     255                checkRoleCounts(n, r, keyname, map.get(r.key));
    188256            }
    189257        }
     258        // verify unwanted members
    190259        for (String key : map.keySet()) {
    191260            if (!done.contains(key)) {
     261                StringBuilder templates = new StringBuilder();
     262                for (RolePreset rp: allroles.values()) {
     263                    templates.append(rp.name);
     264                }
     265
    192266                if (key.length() > 0) {
    193                     String s = marktr("Role {0} unknown");
     267                    String s = marktr("Role {0} unknown in templates {1}");
     268
    194269                    errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    195                             tr(s, key), MessageFormat.format(s, key), ROLE_UNKNOWN, n));
     270                            tr(s, key, templates.toString()), MessageFormat.format(s, key), ROLE_UNKNOWN, n));
    196271                } else {
    197                     String s = marktr("Empty role found");
     272                    String s = marktr("Empty role found in templates {0}");
    198273                    errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    199                             tr(s), s, ROLE_EMPTY, n));
     274                            tr(s, templates), s, ROLE_EMPTY, n));
    200275                }
    201276            }
    202277        }
    203278    }
    204279
    205     private void checkRoleMemberExpressions(Relation n, Role r, String keyname, RoleInfo ri) {
    206         Set<OsmPrimitive> notMatching = new HashSet<>();
    207         Collection<OsmPrimitive> allPrimitives = new ArrayList<>();
    208         allPrimitives.addAll(ri.nodes);
    209         allPrimitives.addAll(ri.ways);
    210         allPrimitives.addAll(ri.relations);
    211         for (OsmPrimitive p : allPrimitives) {
    212             if (p.isUsable() && !r.memberExpression.match(p)) {
    213                 notMatching.add(p);
    214             }
    215         }
    216         if (!notMatching.isEmpty()) {
    217             String s = marktr("Member for role ''{0}'' does not match ''{1}''");
    218             LinkedList<OsmPrimitive> highlight = new LinkedList<>(notMatching);
    219             highlight.addFirst(n);
    220             errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    221                     tr(s, keyname, r.memberExpression), MessageFormat.format(s, keyname, r.memberExpression), WRONG_TYPE,
    222                     highlight, notMatching));
    223         }
    224     }
    225 
    226     private void checkRoleTypes(Relation n, Role r, String keyname, RoleInfo ri) {
    227         Set<OsmPrimitive> wrongTypes = new HashSet<>();
    228         if (!r.types.contains(TaggingPresetType.WAY)) {
    229             wrongTypes.addAll(r.types.contains(TaggingPresetType.CLOSEDWAY) ? ri.openways : ri.ways);
    230         }
    231         if (!r.types.contains(TaggingPresetType.NODE)) {
    232             wrongTypes.addAll(ri.nodes);
    233         }
    234         if (!r.types.contains(TaggingPresetType.RELATION)) {
    235             wrongTypes.addAll(ri.relations);
    236         }
    237         if (!wrongTypes.isEmpty()) {
    238             String s = marktr("Member for role {0} of wrong type");
    239             LinkedList<OsmPrimitive> highlight = new LinkedList<>(wrongTypes);
    240             highlight.addFirst(n);
    241             errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    242                     tr(s, keyname), MessageFormat.format(s, keyname), WRONG_TYPE,
    243                     highlight, wrongTypes));
    244         }
    245     }
    246280
    247281    private void checkRoleCounts(Relation n, Role r, String keyname, RoleInfo ri) {
    248282        long count = (ri == null) ? 0 : ri.total;