Ignore:
Timestamp:
2014-12-25T16:31:17+01:00 (9 years ago)
Author:
Don-vip
Message:

fix #9844, fix #9992, fix #10133 - fix relation checker tests against member expressions (modified patch by wiktorn)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java

    r7523 r7884  
    66
    77import java.text.MessageFormat;
    8 import java.util.ArrayList;
    98import java.util.Collection;
     9import java.util.EnumSet;
    1010import java.util.HashMap;
    11 import java.util.HashSet;
    1211import java.util.LinkedList;
    13 import java.util.List;
    14 import java.util.Set;
     12import java.util.Map;
    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;
     17import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    2018import org.openstreetmap.josm.data.osm.Relation;
    2119import org.openstreetmap.josm.data.osm.RelationMember;
    22 import org.openstreetmap.josm.data.osm.Way;
    2320import org.openstreetmap.josm.data.validation.Severity;
    2421import org.openstreetmap.josm.data.validation.Test;
     
    3128import org.openstreetmap.josm.gui.tagging.TaggingPresetType;
    3229import org.openstreetmap.josm.gui.tagging.TaggingPresets;
     30import org.openstreetmap.josm.tools.Utils;
    3331
    3432/**
     
    8684    }
    8785
     86    private static class RolePreset {
     87        public RolePreset(LinkedList<Role> roles, String name) {
     88            this.roles = roles;
     89            this.name = name;
     90        }
     91        private final LinkedList<Role> roles;
     92        private final String name;
     93    }
     94
    8895    private static class RoleInfo {
    8996        private int total = 0;
    90         private Collection<Node> nodes = new LinkedList<>();
    91         private Collection<Way> ways = new LinkedList<>();
    92         private Collection<Way> openways = new LinkedList<>();
    93         private Collection<Relation> relations = new LinkedList<>();
    9497    }
    9598
    9699    @Override
    97100    public void visit(Relation n) {
    98         LinkedList<Role> allroles = buildAllRoles(n);
     101        Map<String, RolePreset> allroles = buildAllRoles(n);
    99102        if (allroles.isEmpty() && n.hasTag("type", "route")
    100103                && n.hasTag("route", "train", "subway", "monorail", "tram", "bus", "trolleybus", "aerialway", "ferry")) {
     
    106109        }
    107110
    108         HashMap<String, RoleInfo> map = buildRoleInfoMap(n);
     111        Map<String, RoleInfo> map = buildRoleInfoMap(n);
    109112        if (map.isEmpty()) {
    110113            errors.add(new TestError(this, Severity.ERROR, tr("Relation is empty"), RELATION_EMPTY, n));
     
    114117    }
    115118
    116     private HashMap<String, RoleInfo> buildRoleInfoMap(Relation n) {
    117         HashMap<String,RoleInfo> map = new HashMap<>();
     119    private Map<String, RoleInfo> buildRoleInfoMap(Relation n) {
     120        Map<String,RoleInfo> map = new HashMap<>();
    118121        for (RelationMember m : n.getMembers()) {
    119122            String role = m.getRole();
     
    121124            if (ri == null) {
    122125                ri = new RoleInfo();
     126                map.put(role, ri);
    123127            }
    124128            ri.total++;
    125             if (m.isRelation()) {
    126                 ri.relations.add(m.getRelation());
    127             } else if(m.isWay()) {
    128                 ri.ways.add(m.getWay());
    129                 if (!m.getWay().isClosed()) {
    130                     ri.openways.add(m.getWay());
    131                 }
    132             }
    133             else if (m.isNode()) {
    134                 ri.nodes.add(m.getNode());
    135             }
    136             map.put(role, ri);
    137129        }
    138130        return map;
    139131    }
    140132
    141     private LinkedList<Role> buildAllRoles(Relation n) {
    142         LinkedList<Role> allroles = new LinkedList<>();
     133    // return Roles grouped by key
     134    private Map<String, RolePreset> buildAllRoles(Relation n) {
     135        Map<String, RolePreset> allroles = new HashMap<>();
     136
    143137        for (TaggingPreset p : relationpresets) {
    144138            boolean matches = true;
     
    156150            }
    157151            if (matches && r != null) {
    158                 allroles.addAll(r.roles);
     152                for(Role role: r.roles) {
     153                    String key = role.key;
     154                    LinkedList<Role> roleGroup = null;
     155                    if (allroles.containsKey(key)) {
     156                        roleGroup = allroles.get(key).roles;
     157                    } else {
     158                        roleGroup = new LinkedList<>();
     159                        allroles.put(key, new RolePreset(roleGroup, p.name));
     160                    }
     161                    roleGroup.add(role);
     162                }
    159163            }
    160164        }
     
    162166    }
    163167
    164     private void checkRoles(Relation n, LinkedList<Role> allroles, HashMap<String, RoleInfo> map) {
    165         List<String> done = new LinkedList<>();
    166         // Remove empty roles if several exist (like in route=hiking, see #9844)
    167         List<Role> emptyRoles = new LinkedList<>();
    168         for (Role r : allroles) {
    169             if ("".equals(r.key)) {
    170                 emptyRoles.add(r);
    171             }
    172         }
    173         if (emptyRoles.size() > 1) {
    174             allroles.removeAll(emptyRoles);
    175         }
    176         for (Role r : allroles) {
    177             done.add(r.key);
    178             String keyname = r.key;
    179             if ("".equals(keyname)) {
    180                 keyname = tr("<empty>");
    181             }
    182             RoleInfo ri = map.get(r.key);
    183             checkRoleCounts(n, r, keyname, ri);
    184             if (ri != null) {
    185                 if (r.types != null) {
    186                     checkRoleTypes(n, r, keyname, ri);
    187                 }
    188                 if (r.memberExpression != null) {
    189                     checkRoleMemberExpressions(n, r, keyname, ri);
    190                 }
    191             }
    192         }
     168    private boolean checkMemberType(Role r, RelationMember member) {
     169        if (r.types != null) {
     170            if (member.getDisplayType().equals(OsmPrimitiveType.NODE)) {
     171                return r.types.contains(TaggingPresetType.NODE);
     172            }
     173            if (member.getDisplayType().equals(OsmPrimitiveType.CLOSEDWAY)) {
     174                return r.types.contains(TaggingPresetType.CLOSEDWAY);
     175            }
     176            if (member.getDisplayType().equals(OsmPrimitiveType.WAY)) {
     177                return r.types.contains(TaggingPresetType.WAY);
     178            }
     179            if (member.getDisplayType().equals(OsmPrimitiveType.RELATION)) {
     180                return r.types.contains(TaggingPresetType.RELATION);
     181            }
     182            // not matching type
     183            return false;
     184        } else {
     185            // if no types specified, then test is passed
     186            return true;
     187        }
     188    }
     189
     190    /**
     191     * get all role definition for specified key and check, if some definition matches
     192     *
     193     * @param rolePreset containing preset for role of the member
     194     * @param member to be verified
     195     * @param n relation to be verified
     196     * @return <tt>true</tt> if member passed any of definition within preset
     197     *
     198     */
     199    private boolean checkMemberExpressionAndType(RolePreset rolePreset, RelationMember member, Relation n) {
     200        TestError possibleMatchError = null;
     201        if (rolePreset == null || rolePreset.roles == null) {
     202            // no restrictions on role types
     203            return true;
     204        }
     205        // iterate through all of the role definition within preset
     206        // and look for any matching definition
     207        for (Role r: rolePreset.roles) {
     208            if (checkMemberType(r, member)) {
     209                // member type accepted by role definition
     210                if (r.memberExpression == null) {
     211                    // no member expression - so all requirements met
     212                    return true;
     213                } else {
     214                    // verify if preset accepts such member
     215                    OsmPrimitive primitive = member.getMember();
     216                    if(!primitive.isUsable()) {
     217                        // if member is not usable (i.e. not present in working set)
     218                        // we can't verify expression - so we just skip it
     219                        return true;
     220                    } else {
     221                        // verify expression
     222                        if(r.memberExpression.match(primitive)) {
     223                            return true;
     224                        } else {
     225                            // possible match error
     226                            // we still need to iterate further, as we might have
     227                            // different present, for which memberExpression will match
     228                            // but stash the error in case no better reason will be found later
     229                            String s = marktr("Role member does not match expression {0} in template {1}");
     230                            possibleMatchError = new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
     231                                    tr(s, r.memberExpression, rolePreset.name), s, WRONG_TYPE,
     232                                    member.getMember().isUsable() ? member.getMember() : n);
     233
     234                        }
     235                    }
     236                }
     237            }
     238        }
     239
     240        if( possibleMatchError != null) {
     241            // if any error found, then assume that member type was correct
     242            // and complain about not matching the memberExpression
     243            // (the only failure, that we could gather)
     244            errors.add(possibleMatchError);
     245        } else {
     246            // no errors found till now. So member at least failed at matching the type
     247            // it could also fail at memberExpression, but we can't guess at which
     248            String s = marktr("Role member type {0} does not match accepted list of {1} in template {2}");
     249
     250            // prepare Set of all accepted types in template
     251            EnumSet<TaggingPresetType> types = EnumSet.noneOf(TaggingPresetType.class);
     252            for (Role r: rolePreset.roles) {
     253                types.addAll(r.types);
     254            }
     255
     256            // convert in localization friendly way to string of accepted types
     257            String typesStr = Utils.join("/", Utils.transform(types, new Utils.Function<TaggingPresetType, Object>() {
     258                public Object apply(TaggingPresetType x) {
     259                    return tr(x.getName());
     260                }
     261            }));
     262
     263            errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
     264                    tr(s, member.getType(), typesStr, rolePreset.name), s, WRONG_TYPE,
     265                    member.getMember().isUsable() ? member.getMember() : n));
     266        }
     267        return false;
     268    }
     269
     270    /**
     271     *
     272     * @param n relation to validate
     273     * @param allroles contains presets for specified relation
     274     * @param map contains statistics of occurances of specified role types in relation
     275     */
     276    private void checkRoles(Relation n, Map<String, RolePreset> allroles, Map<String, RoleInfo> map) {
     277        // go through all members of relation
     278        for (RelationMember member: n.getMembers()) {
     279            String role = member.getRole();
     280
     281            // error reporting done inside
     282            checkMemberExpressionAndType(allroles.get(role), member, n);
     283        }
     284
     285        // verify role counts based on whole role sets
     286        for(RolePreset rp: allroles.values()) {
     287            for (Role r: rp.roles) {
     288                String keyname = r.key;
     289                if (keyname.isEmpty()) {
     290                    keyname = tr("<empty>");
     291                }
     292                checkRoleCounts(n, r, keyname, map.get(r.key));
     293            }
     294        }
     295        // verify unwanted members
    193296        for (String key : map.keySet()) {
    194             if (!done.contains(key)) {
     297            if (!allroles.containsKey(key)) {
     298                String templates = Utils.join("/", Utils.transform(allroles.keySet(), new Utils.Function<String, Object>() {
     299                    public Object apply(String x) {
     300                        return tr(x);
     301                    }
     302                }));
     303
    195304                if (key.length() > 0) {
    196                     String s = marktr("Role {0} unknown");
     305                    String s = marktr("Role {0} unknown in templates {1}");
     306
    197307                    errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    198                             tr(s, key), MessageFormat.format(s, key), ROLE_UNKNOWN, n));
     308                            tr(s, key, templates.toString()), MessageFormat.format(s, key), ROLE_UNKNOWN, n));
    199309                } else {
    200                     String s = marktr("Empty role found");
     310                    String s = marktr("Empty role type found when expecting one of {0}");
    201311                    errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    202                             tr(s), s, ROLE_EMPTY, n));
    203                 }
    204             }
    205         }
    206     }
    207 
    208     private void checkRoleMemberExpressions(Relation n, Role r, String keyname, RoleInfo ri) {
    209         Set<OsmPrimitive> notMatching = new HashSet<>();
    210         Collection<OsmPrimitive> allPrimitives = new ArrayList<>();
    211         allPrimitives.addAll(ri.nodes);
    212         allPrimitives.addAll(ri.ways);
    213         allPrimitives.addAll(ri.relations);
    214         for (OsmPrimitive p : allPrimitives) {
    215             if (p.isUsable() && !r.memberExpression.match(p)) {
    216                 notMatching.add(p);
    217             }
    218         }
    219         if (!notMatching.isEmpty()) {
    220             String s = marktr("Member for role ''{0}'' does not match ''{1}''");
    221             LinkedList<OsmPrimitive> highlight = new LinkedList<>(notMatching);
    222             highlight.addFirst(n);
    223             errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    224                     tr(s, keyname, r.memberExpression), MessageFormat.format(s, keyname, r.memberExpression), WRONG_TYPE,
    225                     highlight, notMatching));
    226         }
    227     }
    228 
    229     private void checkRoleTypes(Relation n, Role r, String keyname, RoleInfo ri) {
    230         Set<OsmPrimitive> wrongTypes = new HashSet<>();
    231         if (!r.types.contains(TaggingPresetType.WAY)) {
    232             wrongTypes.addAll(r.types.contains(TaggingPresetType.CLOSEDWAY) ? ri.openways : ri.ways);
    233         }
    234         if (!r.types.contains(TaggingPresetType.NODE)) {
    235             wrongTypes.addAll(ri.nodes);
    236         }
    237         if (!r.types.contains(TaggingPresetType.RELATION)) {
    238             wrongTypes.addAll(ri.relations);
    239         }
    240         if (!wrongTypes.isEmpty()) {
    241             String s = marktr("Member for role {0} of wrong type");
    242             LinkedList<OsmPrimitive> highlight = new LinkedList<>(wrongTypes);
    243             highlight.addFirst(n);
    244             errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    245                     tr(s, keyname), MessageFormat.format(s, keyname), WRONG_TYPE,
    246                     highlight, wrongTypes));
     312                            tr(s, templates), s, ROLE_EMPTY, n));
     313                }
     314            }
    247315        }
    248316    }
Note: See TracChangeset for help on using the changeset viewer.