Ticket #18970: 18970.2.patch

File 18970.2.patch, 13.3 KB (added by GerdP, 6 years ago)

use flags, remember status also for relations, add unit tests

  • src/org/openstreetmap/josm/data/osm/AbstractPrimitive.java

     
    127127    protected static final short FLAG_PRESERVED = 1 << 13;
    128128
    129129    /**
     130     * Determines if the primitive refers to incomplete members
     131     * Members can be nodes of a way or members of a relation.
     132     */
     133    protected static final short FLAG_INCOMPLETE_MEMBERS = 1 << 14;
     134
     135    /**
    130136     * Put several boolean flags to one short int field to save memory.
    131137     * Other bits of this field are used in subclasses.
    132138     */
  • src/org/openstreetmap/josm/data/osm/Node.java

     
    434434    protected void updateDirectionFlags() {
    435435        // Nodes do not need/have a direction, greatly improves performance, see #18886
    436436    }
     437
     438    @Override
     439    void childIncompleteStatusWillChange(OsmPrimitive osmPrimitive) {
     440        // should not happen, nodes don't have children
     441    }
    437442}
  • src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

     
    485485        checkDatasetNotReadOnly();
    486486        boolean locked = writeLock();
    487487        try {
    488             if (dataSet != null && incomplete != this.isIncomplete()) {
    489                 if (incomplete) {
    490                     dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
    491                 } else {
    492                     dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
     488            if (incomplete != this.isIncomplete()) {
     489                referrers(true, OsmPrimitive.class).forEach(ref -> ref.childIncompleteStatusWillChange(this));
     490                if (dataSet != null) {
     491                    if (incomplete) {
     492                        dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
     493                    } else {
     494                        dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
     495                    }
    493496                }
    494497            }
    495498            super.setIncomplete(incomplete);
     
    498501        }
    499502    }
    500503
     504    /**
     505     * Called when the incomplete status of the primitive was changed.
     506     * @param osmPrimitive before the change
     507     */
     508    abstract void childIncompleteStatusWillChange(OsmPrimitive osmPrimitive);
     509
    501510    @Override
    502511    public boolean isSelected() {
    503512        return dataSet != null && dataSet.isSelected(this);
     
    837846            OsmPrimitive[] refs = (OsmPrimitive[]) this.referrers;
    838847            for (OsmPrimitive ref: refs) {
    839848                if (ref.dataSet == dataSet) {
    840                     visitor.accept(ref);
    841849                }
     850                visitor.accept(ref);
    842851            }
    843852        }
    844853    }
  • src/org/openstreetmap/josm/data/osm/Relation.java

     
    5454            } else {
    5555                this.members = new RelationMember[0];
    5656            }
     57            boolean hasIncompleteMember = false;
    5758            for (RelationMember rm : this.members) {
    5859                rm.getMember().addReferrer(this);
    5960                rm.getMember().clearCachedStyle();
     61                hasIncompleteMember |= (rm.getMember().isIncomplete());
    6062            }
    61 
     63            setIncompleteMembers(hasIncompleteMember);
    6264            fireMembersChanged();
    6365        } finally {
    6466            writeUnlock(locked);
     
    8486        boolean locked = writeLock();
    8587        try {
    8688            members = Utils.addInArrayCopy(members, member);
     89            if (member.getMember().isIncomplete())
     90                setIncompleteMembers(true);
    8791            member.getMember().addReferrer(this);
    8892            member.getMember().clearCachedStyle();
    8993            fireMembersChanged();
     
    106110            System.arraycopy(members, index, newMembers, index + 1, members.length - index);
    107111            newMembers[index] = member;
    108112            members = newMembers;
     113            if (member.getMember().isIncomplete())
     114                setIncompleteMembers(true);
    109115            member.getMember().addReferrer(this);
    110116            member.getMember().clearCachedStyle();
    111117            fireMembersChanged();
     
    129135            if (originalMember.getMember() != member.getMember()) {
    130136                member.getMember().addReferrer(this);
    131137                member.getMember().clearCachedStyle();
     138                if (!originalMember.getMember().isIncomplete() && member.getMember().isIncomplete())
     139                    setIncompleteMembers(true);
     140                else if (originalMember.getMember().isIncomplete() && !member.getMember().isIncomplete()) {
     141                    setIncompleteMembers(Stream.of(members).anyMatch(m -> m.getMember().isIncomplete()));
     142                }
    132143                originalMember.getMember().removeReferrer(this);
    133144                originalMember.getMember().clearCachedStyle();
    134145                fireMembersChanged();
     
    493504        }
    494505    }
    495506
     507    /**
     508     * If set to true, this object refers to one or more incomplete members.
     509     * Members can be nodes of a way or members of a relation.
     510     * @param incomplete incomplete flag value
     511     */
     512    private void setIncompleteMembers(boolean incomplete) {
     513        updateFlags(FLAG_INCOMPLETE_MEMBERS, incomplete);
     514    }
     515
    496516    @Override
    497517    public boolean hasIncompleteMembers() {
    498         for (RelationMember rm: members) {
    499             if (rm.getMember().isIncomplete()) return true;
    500         }
    501         return false;
     518        return (flags & FLAG_INCOMPLETE_MEMBERS) != 0;
    502519    }
    503520
    504521    /**
     
    508525     */
    509526    @Override
    510527    public Collection<OsmPrimitive> getIncompleteMembers() {
    511         Set<OsmPrimitive> ret = new HashSet<>();
    512         for (RelationMember rm: members) {
    513             if (!rm.getMember().isIncomplete()) {
    514                 continue;
     528        return Stream.of(members).map(RelationMember::getMember).filter(OsmPrimitive::isIncomplete)
     529                .collect(Collectors.toSet());
     530    }
     531
     532    @Override
     533    void childIncompleteStatusWillChange(OsmPrimitive osmPrimitive) {
     534        for (RelationMember originalMember : getMembersFor(Collections.singleton(osmPrimitive))) {
     535            if (!originalMember.getMember().isIncomplete())
     536                setIncompleteMembers(true);
     537            else if (originalMember.getMember().isIncomplete()) {
     538                setIncompleteMembers(Stream.of(members).filter(m -> m != originalMember)
     539                        .anyMatch(m -> m.getMember().isIncomplete()));
    515540            }
    516             ret.add(rm.getMember());
    517541        }
    518         return ret;
    519542    }
    520543
    521544    @Override
  • src/org/openstreetmap/josm/data/osm/Way.java

     
    5656            } else {
    5757                this.nodes = nodes.toArray(new Node[0]);
    5858            }
     59            boolean hasIncompleteNodes = false;
    5960            for (Node node: this.nodes) {
    6061                node.addReferrer(this);
    6162                node.clearCachedStyle();
     63                hasIncompleteNodes |= node.isIncomplete();
    6264            }
     65            setIncompleteNodes(hasIncompleteNodes);
    6366
    6467            clearCachedStyle();
    6568            fireNodesChanged();
     
    6972    }
    7073
    7174    /**
     75     * If set to true, this object refers to one or more incomplete members.
     76     * Members can be nodes of a way or members of a relation.
     77     * @param incomplete incomplete flag value
     78     */
     79    private void setIncompleteNodes(boolean incomplete) {
     80        updateFlags(FLAG_INCOMPLETE_MEMBERS, incomplete);
     81    }
     82
     83    /**
    7284     * Prevent directly following identical nodes in ways.
    7385     * @param nodes list of nodes
    7486     * @return {@code nodes} with consecutive identical nodes removed
     
    401413            clearCachedStyle();
    402414            n.addReferrer(this);
    403415            nodes = Utils.addInArrayCopy(nodes, n);
     416            if (n.isIncomplete())
     417                setIncompleteNodes(true);
    404418            n.clearCachedStyle();
    405419            fireNodesChanged();
    406420        } finally {
     
    434448            System.arraycopy(nodes, offs, newNodes, offs + 1, nodes.length - offs);
    435449            newNodes[offs] = n;
    436450            nodes = newNodes;
     451            if (n.isIncomplete())
     452                setIncompleteNodes(true);
    437453            n.clearCachedStyle();
    438454            fireNodesChanged();
    439455        } finally {
     
    590606     * @since 2587
    591607     */
    592608    public boolean hasIncompleteNodes() {
    593         return Arrays.stream(nodes).anyMatch(Node::isIncomplete);
     609        return (flags & FLAG_INCOMPLETE_MEMBERS) != 0;
    594610    }
    595611
    596612    /**
     
    755771    public UniqueIdGenerator getIdGenerator() {
    756772        return idGenerator;
    757773    }
     774
     775    @Override
     776    void childIncompleteStatusWillChange(OsmPrimitive osmPrimitive) {
     777        if (hasIncompleteNodes() && osmPrimitive.isIncomplete()) {
     778            for (Node n : nodes) {
     779                if (n != osmPrimitive && n.isIncomplete()) {
     780                    setIncomplete(true);
     781                    break;
     782                }
     783            }
     784        }
     785    }
    758786}
  • test/unit/org/openstreetmap/josm/data/osm/RelationTest.java

     
    154154    public void testLoadIAE() {
    155155        new Relation().load(new NodeData());
    156156    }
     157
     158    /**
     159     * Test {@link Relation#hasIncompleteNodes()}
     160     */
     161    @Test
     162    public void testIncompleteMembers() {
     163        DataSet ds = new DataSet();
     164
     165        Node n1 = new Node(new LatLon(10, 10));
     166        Node n2 = new Node(new LatLon(20, 20));
     167        Way w1 = new Way();
     168        w1.addNode(n1);
     169        w1.addNode(n2);
     170        Relation r1 = new Relation();
     171        ds.addPrimitive(r1);
     172        ds.addPrimitive(n1);
     173        ds.addPrimitive(n2);
     174        ds.addPrimitive(w1);
     175        r1.addMember(new RelationMember("", n1));
     176        r1.addMember(new RelationMember("", w1));
     177        r1.addMember(new RelationMember("", r1));
     178        assertFalse(r1.hasIncompleteMembers());
     179        Node n3 = new Node(1); // incomplete node
     180        ds.addPrimitive(n3);
     181        r1.addMember(new RelationMember("", n3));
     182        assertTrue(r1.hasIncompleteMembers());
     183        r1.removeMembersFor(n3);
     184        assertFalse(r1.hasIncompleteMembers());
     185        r1.addMember(1, new RelationMember("", n3));
     186        assertTrue(r1.hasIncompleteMembers());
     187        r1.removeMember(1);
     188        assertFalse(r1.hasIncompleteMembers());
     189        r1.addMember(1, new RelationMember("", n3));
     190        assertTrue(r1.hasIncompleteMembers());
     191        r1.setMember(1, new RelationMember("", n2));
     192        assertFalse(r1.hasIncompleteMembers());
     193        r1.setMember(1, new RelationMember("", n3));
     194        assertTrue(r1.hasIncompleteMembers());
     195        n3.setCoor(new LatLon(30, 30));
     196        n3.setIncomplete(false);
     197        assertFalse(r1.hasIncompleteMembers());
     198    }
     199
    157200}
  • test/unit/org/openstreetmap/josm/data/osm/WayTest.java

     
    6464    }
    6565
    6666    /**
    67      * Test that {@link Way#load} throws IAE for invalid arguments
     67     * Test {@link Way#hasIncompleteNodes()}
    6868     */
    69     @Test(expected = IllegalArgumentException.class)
    70     public void testLoadIAE() {
    71         new Way().load(new NodeData());
     69    @Test
     70    public void testIncompleteNodes() {
     71        DataSet ds = new DataSet();
     72        Node n1 = new Node(1);
     73        Node n2 = new Node(2);
     74        n1.setIncomplete(true);
     75        n2.setCoor(new LatLon(20, 20));
     76        n2.setIncomplete(false);
     77        Way way = new Way(1);
     78        way.setIncomplete(false);
     79        ds.addPrimitive(n1);
     80        ds.addPrimitive(n2);
     81        ds.addPrimitive(way);
     82        assertFalse(way.hasIncompleteNodes());
     83        way.setNodes(Arrays.asList(n1));
     84        assertTrue(way.hasIncompleteNodes());
     85        way.setNodes(Arrays.asList(n2));
     86        assertFalse(way.hasIncompleteNodes());
     87        way.setNodes(Arrays.asList(n1, n2));
     88        assertTrue(way.hasIncompleteNodes());
     89        way.removeNode(n1);
     90        assertFalse(way.hasIncompleteNodes());
     91        way.addNode(n1);
     92        assertTrue(way.hasIncompleteNodes());
     93        n1.setCoor(new LatLon(10, 10));
     94        n1.setIncomplete(false);
     95        assertFalse(way.hasIncompleteNodes());
    7296    }
    7397}