Changeset 3348 in josm for trunk/src/org/openstreetmap/josm


Ignore:
Timestamp:
2010-06-27T17:07:49+02:00 (14 years ago)
Author:
jttt
Message:

Thread safe Dataset and OsmPrimitive, read/write lock for dataset

Location:
trunk/src/org/openstreetmap/josm/data/osm
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/DataSet.java

    r3316 r3348  
    1515import java.util.List;
    1616import java.util.Map;
     17import java.util.concurrent.locks.Lock;
     18import java.util.concurrent.locks.ReadWriteLock;
     19import java.util.concurrent.locks.ReentrantReadWriteLock;
    1720
    1821import org.openstreetmap.josm.data.SelectionChangedListener;
     
    4245public class DataSet implements Cloneable {
    4346
     47    /**
     48     * Maximum number of events that can be fired between beginUpdate/endUpdate to be send as single events (ie without DatasetChangedEvent)
     49     */
     50    private static final int MAX_SINGLE_EVENTS = 30;
     51
     52    /**
     53     * Maximum number of events to kept between beginUpdate/endUpdate. When more events are created, that simple DatasetChangedEvent is sent)
     54     */
     55    private static final int MAX_EVENTS = 1000;
     56
    4457    private static class IdHash implements Hash<PrimitiveId,OsmPrimitive> {
    4558
     
    5770    private Map<PrimitiveId, OsmPrimitive> primitivesMap = allPrimitives.foreignKey(new IdHash());
    5871    private List<DataSetListener> listeners = new ArrayList<DataSetListener>();
     72
    5973    // Number of open calls to beginUpdate
    6074    private int updateCount;
     75    // Events that occurred while dataset was locked but should be fired after write lock is released
     76    private final List<AbstractDatasetChangedEvent> cachedEvents = new ArrayList<AbstractDatasetChangedEvent>();
    6177
    6278    private int highlightUpdateCount;
     79
     80    private final ReadWriteLock lock = new ReentrantReadWriteLock();
     81
     82    public Lock getReadLock() {
     83        return lock.readLock();
     84    }
    6385
    6486    /**
     
    212234     */
    213235    public void addPrimitive(OsmPrimitive primitive) {
    214         if (getPrimitiveById(primitive) != null)
    215             throw new DataIntegrityProblemException(
    216                     tr("Unable to add primitive {0} to the dataset because it is already included", primitive.toString()));
    217 
    218         primitive.updatePosition(); // Set cached bbox for way and relation (required for reindexWay and reinexRelation to work properly)
    219         if (primitive instanceof Node) {
    220             nodes.add((Node) primitive);
    221         } else if (primitive instanceof Way) {
    222             ways.add((Way) primitive);
    223         } else if (primitive instanceof Relation) {
    224             relations.add((Relation) primitive);
    225         }
    226         allPrimitives.add(primitive);
    227         primitive.setDataset(this);
    228         firePrimitivesAdded(Collections.singletonList(primitive), false);
     236        beginUpdate();
     237        try {
     238            if (getPrimitiveById(primitive) != null)
     239                throw new DataIntegrityProblemException(
     240                        tr("Unable to add primitive {0} to the dataset because it is already included", primitive.toString()));
     241
     242            primitive.updatePosition(); // Set cached bbox for way and relation (required for reindexWay and reinexRelation to work properly)
     243            if (primitive instanceof Node) {
     244                nodes.add((Node) primitive);
     245            } else if (primitive instanceof Way) {
     246                ways.add((Way) primitive);
     247            } else if (primitive instanceof Relation) {
     248                relations.add((Relation) primitive);
     249            }
     250            allPrimitives.add(primitive);
     251            primitive.setDataset(this);
     252            firePrimitivesAdded(Collections.singletonList(primitive), false);
     253        } finally {
     254            endUpdate();
     255        }
    229256    }
    230257
    231258    public OsmPrimitive addPrimitive(PrimitiveData data) {
    232         OsmPrimitive result;
    233         if (data instanceof NodeData) {
    234             result = new Node();
    235         } else if (data instanceof WayData) {
    236             result = new Way();
    237         } else if (data instanceof RelationData) {
    238             result = new Relation();
    239         } else
    240             throw new AssertionError();
    241         result.setDataset(this);
    242         result.load(data);
    243         addPrimitive(result);
    244         return result;
     259        beginUpdate();
     260        try {
     261            OsmPrimitive result;
     262            if (data instanceof NodeData) {
     263                result = new Node();
     264            } else if (data instanceof WayData) {
     265                result = new Way();
     266            } else if (data instanceof RelationData) {
     267                result = new Relation();
     268            } else
     269                throw new AssertionError();
     270            result.setDataset(this);
     271            result.load(data);
     272            addPrimitive(result);
     273            return result;
     274        } finally {
     275            endUpdate();
     276        }
    245277    }
    246278
     
    255287     */
    256288    public void removePrimitive(PrimitiveId primitiveId) {
    257         OsmPrimitive primitive = getPrimitiveByIdChecked(primitiveId);
    258         if (primitive == null)
    259             return;
    260         if (primitive instanceof Node) {
    261             nodes.remove(primitive);
    262         } else if (primitive instanceof Way) {
    263             ways.remove(primitive);
    264         } else if (primitive instanceof Relation) {
    265             relations.remove(primitive);
    266         }
    267         selectedPrimitives.remove(primitive);
    268         allPrimitives.remove(primitive);
    269         primitive.setDataset(null);
    270         firePrimitivesRemoved(Collections.singletonList(primitive), false);
     289        beginUpdate();
     290        try {
     291            OsmPrimitive primitive = getPrimitiveByIdChecked(primitiveId);
     292            if (primitive == null)
     293                return;
     294            if (primitive instanceof Node) {
     295                nodes.remove(primitive);
     296            } else if (primitive instanceof Way) {
     297                ways.remove(primitive);
     298            } else if (primitive instanceof Relation) {
     299                relations.remove(primitive);
     300            }
     301            selectedPrimitives.remove(primitive);
     302            allPrimitives.remove(primitive);
     303            primitive.setDataset(null);
     304            firePrimitivesRemoved(Collections.singletonList(primitive), false);
     305        } finally {
     306            endUpdate();
     307        }
    271308    }
    272309
     
    492529     */
    493530
    494 //    public void setDisabled(OsmPrimitive... osm) {
    495 //        if (osm.length == 1 && osm[0] == null) {
    496 //            setDisabled();
    497 //            return;
    498 //        }
    499 //        clearDisabled(allPrimitives());
    500 //        for (OsmPrimitive o : osm)
    501 //            if (o != null) {
    502 //                o.setDisabled(true);
    503 //            }
    504 //    }
    505 //
    506 //    public void setDisabled(Collection<? extends OsmPrimitive> selection) {
    507 //        clearDisabled(nodes);
    508 //        clearDisabled(ways);
    509 //        clearDisabled(relations);
    510 //        for (OsmPrimitive osm : selection) {
    511 //            osm.setDisabled(true);
    512 //        }
    513 //    }
    514 //
    515 //    /**
    516 //     * Remove the disabled parameter from every value in the collection.
    517 //     * @param list The collection to remove the disabled parameter from.
    518 //     */
    519 //    private void clearDisabled(Collection<? extends OsmPrimitive> list) {
    520 //        for (OsmPrimitive osm : list) {
    521 //            osm.setDisabled(false);
    522 //        }
    523 //    }
    524 //
    525 //
    526 //    public void setFiltered(Collection<? extends OsmPrimitive> selection) {
    527 //        clearFiltered(nodes);
    528 //        clearFiltered(ways);
    529 //        clearFiltered(relations);
    530 //        for (OsmPrimitive osm : selection) {
    531 //            osm.setFiltered(true);
    532 //        }
    533 //    }
    534 //
    535 //    public void setFiltered(OsmPrimitive... osm) {
    536 //        if (osm.length == 1 && osm[0] == null) {
    537 //            setFiltered();
    538 //            return;
    539 //        }
    540 //        clearFiltered(nodes);
    541 //        clearFiltered(ways);
    542 //        clearFiltered(relations);
    543 //        for (OsmPrimitive o : osm)
    544 //            if (o != null) {
    545 //                o.setFiltered(true);
    546 //            }
    547 //    }
    548 //
    549 //    /**
    550 //     * Remove the filtered parameter from every value in the collection.
    551 //     * @param list The collection to remove the filtered parameter from.
    552 //     */
    553 //    private void clearFiltered(Collection<? extends OsmPrimitive> list) {
    554 //        if (list == null)
    555 //            return;
    556 //        for (OsmPrimitive osm : list) {
    557 //            osm.setFiltered(false);
    558 //        }
    559 //    }
     531    //    public void setDisabled(OsmPrimitive... osm) {
     532    //        if (osm.length == 1 && osm[0] == null) {
     533    //            setDisabled();
     534    //            return;
     535    //        }
     536    //        clearDisabled(allPrimitives());
     537    //        for (OsmPrimitive o : osm)
     538    //            if (o != null) {
     539    //                o.setDisabled(true);
     540    //            }
     541    //    }
     542    //
     543    //    public void setDisabled(Collection<? extends OsmPrimitive> selection) {
     544    //        clearDisabled(nodes);
     545    //        clearDisabled(ways);
     546    //        clearDisabled(relations);
     547    //        for (OsmPrimitive osm : selection) {
     548    //            osm.setDisabled(true);
     549    //        }
     550    //    }
     551    //
     552    //    /**
     553    //     * Remove the disabled parameter from every value in the collection.
     554    //     * @param list The collection to remove the disabled parameter from.
     555    //     */
     556    //    private void clearDisabled(Collection<? extends OsmPrimitive> list) {
     557    //        for (OsmPrimitive osm : list) {
     558    //            osm.setDisabled(false);
     559    //        }
     560    //    }
     561    //
     562    //
     563    //    public void setFiltered(Collection<? extends OsmPrimitive> selection) {
     564    //        clearFiltered(nodes);
     565    //        clearFiltered(ways);
     566    //        clearFiltered(relations);
     567    //        for (OsmPrimitive osm : selection) {
     568    //            osm.setFiltered(true);
     569    //        }
     570    //    }
     571    //
     572    //    public void setFiltered(OsmPrimitive... osm) {
     573    //        if (osm.length == 1 && osm[0] == null) {
     574    //            setFiltered();
     575    //            return;
     576    //        }
     577    //        clearFiltered(nodes);
     578    //        clearFiltered(ways);
     579    //        clearFiltered(relations);
     580    //        for (OsmPrimitive o : osm)
     581    //            if (o != null) {
     582    //                o.setFiltered(true);
     583    //            }
     584    //    }
     585    //
     586    //    /**
     587    //     * Remove the filtered parameter from every value in the collection.
     588    //     * @param list The collection to remove the filtered parameter from.
     589    //     */
     590    //    private void clearFiltered(Collection<? extends OsmPrimitive> list) {
     591    //        if (list == null)
     592    //            return;
     593    //        for (OsmPrimitive osm : list) {
     594    //            osm.setFiltered(false);
     595    //        }
     596    //    }
    560597
    561598    @Override public DataSet clone() {
    562         DataSet ds = new DataSet();
    563         HashMap<OsmPrimitive, OsmPrimitive> primitivesMap = new HashMap<OsmPrimitive, OsmPrimitive>();
    564         for (Node n : nodes) {
    565             Node newNode = new Node(n);
    566             primitivesMap.put(n, newNode);
    567             ds.addPrimitive(newNode);
    568         }
    569         for (Way w : ways) {
    570             Way newWay = new Way(w);
    571             primitivesMap.put(w, newWay);
    572             List<Node> newNodes = new ArrayList<Node>();
    573             for (Node n: w.getNodes()) {
    574                 newNodes.add((Node)primitivesMap.get(n));
    575             }
    576             newWay.setNodes(newNodes);
    577             ds.addPrimitive(newWay);
    578         }
    579         // Because relations can have other relations as members we first clone all relations
    580         // and then get the cloned members
    581         for (Relation r : relations) {
    582             Relation newRelation = new Relation(r, r.isNew());
    583             newRelation.setMembers(null);
    584             primitivesMap.put(r, newRelation);
    585             ds.addPrimitive(newRelation);
    586         }
    587         for (Relation r : relations) {
    588             Relation newRelation = (Relation)primitivesMap.get(r);
    589             List<RelationMember> newMembers = new ArrayList<RelationMember>();
    590             for (RelationMember rm: r.getMembers()) {
    591                 newMembers.add(new RelationMember(rm.getRole(), primitivesMap.get(rm.getMember())));
    592             }
    593             newRelation.setMembers(newMembers);
    594         }
    595         for (DataSource source : dataSources) {
    596             ds.dataSources.add(new DataSource(source.bounds, source.origin));
    597         }
    598         ds.version = version;
    599         return ds;
     599        getReadLock().lock();
     600        try {
     601            DataSet ds = new DataSet();
     602            HashMap<OsmPrimitive, OsmPrimitive> primitivesMap = new HashMap<OsmPrimitive, OsmPrimitive>();
     603            for (Node n : nodes) {
     604                Node newNode = new Node(n);
     605                primitivesMap.put(n, newNode);
     606                ds.addPrimitive(newNode);
     607            }
     608            for (Way w : ways) {
     609                Way newWay = new Way(w);
     610                primitivesMap.put(w, newWay);
     611                List<Node> newNodes = new ArrayList<Node>();
     612                for (Node n: w.getNodes()) {
     613                    newNodes.add((Node)primitivesMap.get(n));
     614                }
     615                newWay.setNodes(newNodes);
     616                ds.addPrimitive(newWay);
     617            }
     618            // Because relations can have other relations as members we first clone all relations
     619            // and then get the cloned members
     620            for (Relation r : relations) {
     621                Relation newRelation = new Relation(r, r.isNew());
     622                newRelation.setMembers(null);
     623                primitivesMap.put(r, newRelation);
     624                ds.addPrimitive(newRelation);
     625            }
     626            for (Relation r : relations) {
     627                Relation newRelation = (Relation)primitivesMap.get(r);
     628                List<RelationMember> newMembers = new ArrayList<RelationMember>();
     629                for (RelationMember rm: r.getMembers()) {
     630                    newMembers.add(new RelationMember(rm.getRole(), primitivesMap.get(rm.getMember())));
     631                }
     632                newRelation.setMembers(newMembers);
     633            }
     634            for (DataSource source : dataSources) {
     635                ds.dataSources.add(new DataSource(source.bounds, source.origin));
     636            }
     637            ds.version = version;
     638            return ds;
     639        } finally {
     640            getReadLock().unlock();
     641        }
    600642    }
    601643
     
    663705    }
    664706
    665     protected void deleteWay(Way way) {
     707    private void deleteWay(Way way) {
    666708        way.setNodes(null);
    667709        way.setDeleted(true);
     
    674716     */
    675717    public void unlinkNodeFromWays(Node node) {
    676         for (Way way: ways) {
    677             List<Node> nodes = way.getNodes();
    678             if (nodes.remove(node)) {
    679                 if (nodes.size() < 2) {
    680                     deleteWay(way);
    681                 } else {
    682                     way.setNodes(nodes);
     718        beginUpdate();
     719        try {
     720            for (Way way: ways) {
     721                List<Node> nodes = way.getNodes();
     722                if (nodes.remove(node)) {
     723                    if (nodes.size() < 2) {
     724                        deleteWay(way);
     725                    } else {
     726                        way.setNodes(nodes);
     727                    }
    683728                }
    684729            }
     730        } finally {
     731            endUpdate();
    685732        }
    686733    }
     
    692739     */
    693740    public void unlinkPrimitiveFromRelations(OsmPrimitive primitive) {
    694         for (Relation relation : relations) {
    695             List<RelationMember> members = relation.getMembers();
    696 
    697             Iterator<RelationMember> it = members.iterator();
    698             boolean removed = false;
    699             while(it.hasNext()) {
    700                 RelationMember member = it.next();
    701                 if (member.getMember().equals(primitive)) {
    702                     it.remove();
    703                     removed = true;
     741        beginUpdate();
     742        try {
     743            for (Relation relation : relations) {
     744                List<RelationMember> members = relation.getMembers();
     745
     746                Iterator<RelationMember> it = members.iterator();
     747                boolean removed = false;
     748                while(it.hasNext()) {
     749                    RelationMember member = it.next();
     750                    if (member.getMember().equals(primitive)) {
     751                        it.remove();
     752                        removed = true;
     753                    }
    704754                }
    705             }
    706 
    707             if (removed) {
    708                 relation.setMembers(members);
    709             }
     755
     756                if (removed) {
     757                    relation.setMembers(members);
     758                }
     759            }
     760        } finally {
     761            endUpdate();
    710762        }
    711763    }
     
    718770     */
    719771    public void unlinkReferencesToPrimitive(OsmPrimitive referencedPrimitive) {
    720         if (referencedPrimitive instanceof Node) {
    721             unlinkNodeFromWays((Node)referencedPrimitive);
    722             unlinkPrimitiveFromRelations(referencedPrimitive);
    723         } else {
    724             unlinkPrimitiveFromRelations(referencedPrimitive);
     772        beginUpdate();
     773        try {
     774            if (referencedPrimitive instanceof Node) {
     775                unlinkNodeFromWays((Node)referencedPrimitive);
     776                unlinkPrimitiveFromRelations(referencedPrimitive);
     777            } else {
     778                unlinkPrimitiveFromRelations(referencedPrimitive);
     779            }
     780        } finally {
     781            endUpdate();
    725782        }
    726783    }
     
    804861     */
    805862    public void beginUpdate() {
     863        lock.writeLock().lock();
    806864        updateCount++;
    807865    }
     
    814872            updateCount--;
    815873            if (updateCount == 0) {
    816                 fireDataChanged();
    817             }
     874                List<AbstractDatasetChangedEvent> eventsCopy = new ArrayList<AbstractDatasetChangedEvent>(cachedEvents);
     875                cachedEvents.clear();
     876                lock.writeLock().unlock();
     877
     878                if (!eventsCopy.isEmpty()) {
     879                    lock.readLock().lock();
     880                    try {
     881                        if (eventsCopy.size() < MAX_SINGLE_EVENTS) {
     882                            for (AbstractDatasetChangedEvent event: eventsCopy) {
     883                                fireEventToListeners(event);
     884                            }
     885                        } else if (eventsCopy.size() == MAX_EVENTS) {
     886                            fireEventToListeners(new DataChangedEvent(this));
     887                        } else {
     888                            fireEventToListeners(new DataChangedEvent(this, eventsCopy));
     889                        }
     890                    } finally {
     891                        lock.readLock().unlock();
     892                    }
     893                }
     894            } else {
     895                lock.writeLock().unlock();
     896            }
     897
    818898        } else
    819899            throw new AssertionError("endUpdate called without beginUpdate");
    820900    }
    821901
     902    private void fireEventToListeners(AbstractDatasetChangedEvent event) {
     903        for (DataSetListener listener: listeners) {
     904            event.fire(listener);
     905        }
     906    }
     907
    822908    private void fireEvent(AbstractDatasetChangedEvent event) {
    823         if (updateCount == 0) {
    824             for (DataSetListener dsl : listeners) {
    825                 event.fire(dsl);
    826             }
    827         }
    828     }
    829 
    830     private void fireDataChanged() {
    831         fireEvent(new DataChangedEvent(this));
     909        if (updateCount == 0)
     910            throw new AssertionError("dataset events can be fired only when dataset is locked");
     911        if (cachedEvents.size() < MAX_EVENTS) {
     912            cachedEvents.add(event);
     913        }
    832914    }
    833915
     
    868950
    869951    public void clenupDeletedPrimitives() {
    870         if (cleanupDeleted(nodes.iterator())
    871                 | cleanupDeleted(ways.iterator())
    872                 | cleanupDeleted(relations.iterator())) {
    873             fireSelectionChanged();
     952        beginUpdate();
     953        try {
     954            if (cleanupDeleted(nodes.iterator())
     955                    | cleanupDeleted(ways.iterator())
     956                    | cleanupDeleted(relations.iterator())) {
     957                fireSelectionChanged();
     958            }
     959        } finally {
     960            endUpdate();
    874961        }
    875962    }
     
    896983     */
    897984    public void clear() {
    898         clearSelection();
    899         for (OsmPrimitive primitive:allPrimitives) {
    900             primitive.setDataset(null);
    901         }
    902         nodes.clear();
    903         ways.clear();
    904         relations.clear();
    905         allPrimitives.clear();
     985        beginUpdate();
     986        try {
     987            clearSelection();
     988            for (OsmPrimitive primitive:allPrimitives) {
     989                primitive.setDataset(null);
     990            }
     991            nodes.clear();
     992            ways.clear();
     993            relations.clear();
     994            allPrimitives.clear();
     995        } finally {
     996            endUpdate();
     997        }
    906998    }
    907999}
  • trunk/src/org/openstreetmap/josm/data/osm/Node.java

    r3253 r3348  
    2020        if(coor != null){
    2121            if (getDataSet() != null) {
    22                 getDataSet().fireNodeMoved(this, coor);
     22                boolean locked = writeLock();
     23                try {
     24                    getDataSet().fireNodeMoved(this, coor);
     25                } finally {
     26                    writeUnlock(locked);
     27                }
    2328            } else {
    2429                setCoorInternal(coor);
     
    115120
    116121    @Override public void cloneFrom(OsmPrimitive osm) {
    117         super.cloneFrom(osm);
    118         setCoor(((Node)osm).coor);
     122        boolean locked = writeLock();
     123        try {
     124            super.cloneFrom(osm);
     125            setCoor(((Node)osm).coor);
     126        } finally {
     127            writeUnlock(locked);
     128        }
    119129    }
    120130
     
    132142    @Override
    133143    public void mergeFrom(OsmPrimitive other) {
    134         super.mergeFrom(other);
    135         if (!other.isIncomplete()) {
    136             setCoor(new LatLon(((Node)other).coor));
     144        boolean locked = writeLock();
     145        try {
     146            super.mergeFrom(other);
     147            if (!other.isIncomplete()) {
     148                setCoor(new LatLon(((Node)other).coor));
     149            }
     150        } finally {
     151            writeUnlock(locked);
    137152        }
    138153    }
    139154
    140155    @Override public void load(PrimitiveData data) {
    141         super.load(data);
    142         setCoor(((NodeData)data).getCoor());
     156        boolean locked = writeLock();
     157        try {
     158            super.load(data);
     159            setCoor(((NodeData)data).getCoor());
     160        } finally {
     161            writeUnlock(locked);
     162        }
    143163    }
    144164
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r3342 r3348  
    345345    }
    346346
     347    protected boolean writeLock() {
     348        if (dataSet != null) {
     349            dataSet.beginUpdate();
     350            return true;
     351        } else
     352            return false;
     353    }
     354
     355    protected void writeUnlock(boolean locked) {
     356        if (locked) {
     357            // It shouldn't be possible for dataset to become null because method calling setDataset would need write lock which is owned by this thread
     358            dataSet.endUpdate();
     359        }
     360    }
     361
     362
    347363    /*-------------------
    348364     * OTHER PROPERTIES
    349      *-------------------/
     365     *-------------------*/
    350366
    351367    /**
     
    392408     */
    393409    public long getId() {
     410        long id = this.id;
    394411        return id >= 0?id:0;
    395412    }
     
    434451     */
    435452    public void setOsmId(long id, int version) {
    436         if (id <= 0)
    437             throw new IllegalArgumentException(tr("ID > 0 expected. Got {0}.", id));
    438         if (version <= 0)
    439             throw new IllegalArgumentException(tr("Version > 0 expected. Got {0}.", version));
    440         if (dataSet != null && id != this.id) {
    441             DataSet datasetCopy = dataSet;
    442             // Reindex primitive
    443             datasetCopy.removePrimitive(this);
     453        boolean locked = writeLock();
     454        try {
     455            if (id <= 0)
     456                throw new IllegalArgumentException(tr("ID > 0 expected. Got {0}.", id));
     457            if (version <= 0)
     458                throw new IllegalArgumentException(tr("Version > 0 expected. Got {0}.", version));
     459            if (dataSet != null && id != this.id) {
     460                DataSet datasetCopy = dataSet;
     461                // Reindex primitive
     462                datasetCopy.removePrimitive(this);
     463                this.id = id;
     464                datasetCopy.addPrimitive(this);
     465            }
    444466            this.id = id;
    445             datasetCopy.addPrimitive(this);
    446         }
    447         this.id = id;
    448         this.version = version;
    449         this.setIncomplete(false);
     467            this.version = version;
     468            this.setIncomplete(false);
     469        } finally {
     470            writeUnlock(locked);
     471        }
    450472    }
    451473
     
    462484        if (dataSet != null)
    463485            throw new DataIntegrityProblemException("Method cannot be called after primitive was added to the dataset");
     486
     487        // Not part of dataset - no lock necessary
    464488        this.id = generateUniqueId();
    465489        this.version = 0;
     
    483507     */
    484508    public void setUser(User user) {
    485         this.user = user;
     509        boolean locked = writeLock();
     510        try {
     511            this.user = user;
     512        } finally {
     513            writeUnlock(locked);
     514        }
    486515    }
    487516
     
    506535     */
    507536    public void setChangesetId(int changesetId) throws IllegalStateException, IllegalArgumentException {
    508         if (this.changesetId == changesetId)
    509             return;
    510         if (changesetId < 0)
    511             throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' >= 0 expected, got {1}", "changesetId", changesetId));
    512         if (isNew() && changesetId > 0)
    513             throw new IllegalStateException(tr("Cannot assign a changesetId > 0 to a new primitive. Value of changesetId is {0}", changesetId));
    514         int old = this.changesetId;
    515         this.changesetId = changesetId;
    516         if (dataSet != null) {
    517             dataSet.fireChangesetIdChanged(this, old, changesetId);
     537        boolean locked = writeLock();
     538        try {
     539            if (this.changesetId == changesetId)
     540                return;
     541            if (changesetId < 0)
     542                throw new IllegalArgumentException(MessageFormat.format("Parameter ''{0}'' >= 0 expected, got {1}", "changesetId", changesetId));
     543            if (isNew() && changesetId > 0)
     544                throw new IllegalStateException(tr("Cannot assign a changesetId > 0 to a new primitive. Value of changesetId is {0}", changesetId));
     545
     546            int old = this.changesetId;
     547            this.changesetId = changesetId;
     548            if (dataSet != null) {
     549                dataSet.fireChangesetIdChanged(this, old, changesetId);
     550            }
     551        } finally {
     552            writeUnlock(locked);
    518553        }
    519554    }
     
    529564
    530565    public void setTimestamp(Date timestamp) {
    531         this.timestamp = (int)(timestamp.getTime() / 1000);
     566        boolean locked = writeLock();
     567        try {
     568            this.timestamp = (int)(timestamp.getTime() / 1000);
     569        } finally {
     570            writeUnlock(locked);
     571        }
    532572    }
    533573
     
    554594    private volatile short flags = FLAG_VISIBLE;   // visible per default
    555595
    556     private void updateFlags(int flag, boolean value) {
     596    private void updateFlagsNoLock(int flag, boolean value) {
    557597        if (value) {
    558598            flags |= flag;
     
    562602    }
    563603
     604    private void updateFlags(int flag, boolean value) {
     605        boolean locked = writeLock();
     606        try {
     607            updateFlagsNoLock(flag, value);
     608        } finally {
     609            writeUnlock(locked);
     610        }
     611    }
     612
    564613    /**
    565614     * Make the primitive disabled (e.g. if a filter applies).
     
    569618     */
    570619    public void setDisabledState(boolean hide) {
    571         flags |= FLAG_DISABLED;
    572         if (hide) {
    573             flags |= FLAG_HIDE_IF_DISABLED;
    574         } else {
    575             flags &= ~FLAG_HIDE_IF_DISABLED;
     620        boolean locked = writeLock();
     621        try {
     622            updateFlagsNoLock(FLAG_DISABLED, true);
     623            updateFlagsNoLock(FLAG_HIDE_IF_DISABLED, hide);
     624        } finally {
     625            writeUnlock(locked);
    576626        }
    577627    }
     
    688738     */
    689739    public void setVisible(boolean visible) throws IllegalStateException{
    690         if (isNew() && visible == false)
    691             throw new IllegalStateException(tr("A primitive with ID = 0 cannot be invisible."));
    692         updateFlags(FLAG_VISIBLE, visible);
     740        boolean locked = writeLock();
     741        try {
     742            if (isNew() && visible == false)
     743                throw new IllegalStateException(tr("A primitive with ID = 0 cannot be invisible."));
     744            updateFlagsNoLock(FLAG_VISIBLE, visible);
     745        } finally {
     746            writeUnlock(locked);
     747        }
    693748    }
    694749
     
    701756     */
    702757    public void setDeleted(boolean deleted) {
    703         updateFlags(FLAG_DELETED, deleted);
    704         setModified(deleted ^ !isVisible());
    705         if (dataSet != null) {
    706             if (deleted) {
    707                 dataSet.firePrimitivesRemoved(Collections.singleton(this), false);
    708             } else {
    709                 dataSet.firePrimitivesAdded(Collections.singleton(this), false);
    710             }
     758        boolean locked = writeLock();
     759        try {
     760            updateFlagsNoLock(FLAG_DELETED, deleted);
     761            setModified(deleted ^ !isVisible());
     762            if (dataSet != null) {
     763                if (deleted) {
     764                    dataSet.firePrimitivesRemoved(Collections.singleton(this), false);
     765                } else {
     766                    dataSet.firePrimitivesAdded(Collections.singleton(this), false);
     767                }
     768            }
     769        } finally {
     770            writeUnlock(locked);
    711771        }
    712772    }
     
    718778     */
    719779    private void setIncomplete(boolean incomplete) {
    720         if (dataSet != null && incomplete != this.isIncomplete()) {
    721             if (incomplete) {
    722                 dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
    723             } else {
    724                 dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
    725             }
    726         }
    727         updateFlags(FLAG_INCOMPLETE, incomplete);
     780        boolean locked = writeLock();
     781        try {
     782            if (dataSet != null && incomplete != this.isIncomplete()) {
     783                if (incomplete) {
     784                    dataSet.firePrimitivesRemoved(Collections.singletonList(this), true);
     785                } else {
     786                    dataSet.firePrimitivesAdded(Collections.singletonList(this), true);
     787                }
     788            }
     789            updateFlagsNoLock(FLAG_INCOMPLETE, incomplete);
     790        }  finally {
     791            writeUnlock(locked);
     792        }
    728793    }
    729794
     
    842907            for (String key: keySet()) {
    843908                if (!isUninterestingKey(key)) {
    844                     updateFlags(FLAG_TAGGED, true);
     909                    updateFlagsNoLock(FLAG_TAGGED, true);
    845910                    return;
    846911                }
    847912            }
    848913        }
    849         updateFlags(FLAG_TAGGED, false);
     914        updateFlagsNoLock(FLAG_TAGGED, false);
    850915    }
    851916
     
    871936        }
    872937
    873         updateFlags(FLAG_DIRECTION_REVERSED, directionReversed);
    874         updateFlags(FLAG_HAS_DIRECTIONS, hasDirections);
     938        updateFlagsNoLock(FLAG_DIRECTION_REVERSED, directionReversed);
     939        updateFlagsNoLock(FLAG_HAS_DIRECTIONS, hasDirections);
    875940    }
    876941
     
    890955     ------------*/
    891956
     957    // Note that all methods that read keys first make local copy of keys array reference. This is to ensure thread safety - reading
     958    // doesn't have to be locked so it's possible that keys array will be modified. But all write methods make copy of keys array so
     959    // the array itself will be never modified - only reference will be changed
     960
    892961    /**
    893962     * The key/value list for this primitive.
     
    901970     * @return tags of this primitive. Changes made in returned map are not mapped
    902971     * back to the primitive, use setKeys() to modify the keys
    903      * @since 1924
    904972     */
    905973    public Map<String, String> getKeys() {
    906974        Map<String, String> result = new HashMap<String, String>();
     975        String[] keys = this.keys;
    907976        if (keys != null) {
    908977            for (int i=0; i<keys.length ; i+=2) {
     
    918987     *
    919988     * @param keys the key/value pairs to set. If null, removes all existing key/value pairs.
    920      * @since 1924
    921989     */
    922990    public void setKeys(Map<String, String> keys) {
    923         Map<String, String> originalKeys = getKeys();
    924         if (keys == null || keys.isEmpty()) {
    925             this.keys = null;
     991        boolean locked = writeLock();
     992        try {
     993            Map<String, String> originalKeys = getKeys();
     994            if (keys == null || keys.isEmpty()) {
     995                this.keys = null;
     996                keysChangedImpl(originalKeys);
     997                return;
     998            }
     999            String[] newKeys = new String[keys.size() * 2];
     1000            int index = 0;
     1001            for (Entry<String, String> entry:keys.entrySet()) {
     1002                newKeys[index++] = entry.getKey();
     1003                newKeys[index++] = entry.getValue();
     1004            }
     1005            this.keys = newKeys;
    9261006            keysChangedImpl(originalKeys);
    927             return;
    928         }
    929         String[] newKeys = new String[keys.size() * 2];
    930         int index = 0;
    931         for (Entry<String, String> entry:keys.entrySet()) {
    932             newKeys[index++] = entry.getKey();
    933             newKeys[index++] = entry.getValue();
    934         }
    935         this.keys = newKeys;
    936         keysChangedImpl(originalKeys);
     1007        } finally {
     1008            writeUnlock(locked);
     1009        }
    9371010    }
    9381011
     
    9471020     */
    9481021    public final void put(String key, String value) {
    949         Map<String, String> originalKeys = getKeys();
    950         if (key == null)
    951             return;
    952         else if (value == null) {
    953             remove(key);
    954         } else if (keys == null){
    955             keys = new String[] {key, value};
    956             keysChangedImpl(originalKeys);
    957         } else {
    958             for (int i=0; i<keys.length;i+=2) {
    959                 if (keys[i].equals(key)) {
    960                     keys[i+1] = value;
    961                     keysChangedImpl(originalKeys);
    962                     return;
     1022        boolean locked = writeLock();
     1023        try {
     1024            Map<String, String> originalKeys = getKeys();
     1025            if (key == null)
     1026                return;
     1027            else if (value == null) {
     1028                remove(key);
     1029            } else if (keys == null){
     1030                keys = new String[] {key, value};
     1031                keysChangedImpl(originalKeys);
     1032            } else {
     1033                for (int i=0; i<keys.length;i+=2) {
     1034                    if (keys[i].equals(key)) {
     1035                        keys[i+1] = value;  // This modifies the keys array but it doesn't make it invalidate for any time so its ok (see note no top)
     1036                        keysChangedImpl(originalKeys);
     1037                        return;
     1038                    }
    9631039                }
    964             }
    965             String[] newKeys = new String[keys.length + 2];
    966             for (int i=0; i< keys.length;i+=2) {
    967                 newKeys[i] = keys[i];
    968                 newKeys[i+1] = keys[i+1];
    969             }
    970             newKeys[keys.length] = key;
    971             newKeys[keys.length + 1] = value;
     1040                String[] newKeys = new String[keys.length + 2];
     1041                for (int i=0; i< keys.length;i+=2) {
     1042                    newKeys[i] = keys[i];
     1043                    newKeys[i+1] = keys[i+1];
     1044                }
     1045                newKeys[keys.length] = key;
     1046                newKeys[keys.length + 1] = value;
     1047                keys = newKeys;
     1048                keysChangedImpl(originalKeys);
     1049            }
     1050        } finally {
     1051            writeUnlock(locked);
     1052        }
     1053    }
     1054    /**
     1055     * Remove the given key from the list
     1056     *
     1057     * @param key  the key to be removed. Ignored, if key is null.
     1058     */
     1059    public final void remove(String key) {
     1060        boolean locked = writeLock();
     1061        try {
     1062            if (key == null || keys == null) return;
     1063            if (!hasKey(key))
     1064                return;
     1065            Map<String, String> originalKeys = getKeys();
     1066            if (keys.length == 2) {
     1067                keys = null;
     1068                keysChangedImpl(originalKeys);
     1069                return;
     1070            }
     1071            String[] newKeys = new String[keys.length - 2];
     1072            int j=0;
     1073            for (int i=0; i < keys.length; i+=2) {
     1074                if (!keys[i].equals(key)) {
     1075                    newKeys[j++] = keys[i];
     1076                    newKeys[j++] = keys[i+1];
     1077                }
     1078            }
    9721079            keys = newKeys;
    9731080            keysChangedImpl(originalKeys);
    974         }
    975     }
    976     /**
    977      * Remove the given key from the list
    978      *
    979      * @param key  the key to be removed. Ignored, if key is null.
    980      */
    981     public final void remove(String key) {
    982         if (key == null || keys == null) return;
    983         if (!hasKey(key))
    984             return;
    985         Map<String, String> originalKeys = getKeys();
    986         if (keys.length == 2) {
    987             keys = null;
    988             keysChangedImpl(originalKeys);
    989             return;
    990         }
    991         String[] newKeys = new String[keys.length - 2];
    992         int j=0;
    993         for (int i=0; i < keys.length; i+=2) {
    994             if (!keys[i].equals(key)) {
    995                 newKeys[j++] = keys[i];
    996                 newKeys[j++] = keys[i+1];
    997             }
    998         }
    999         keys = newKeys;
    1000         keysChangedImpl(originalKeys);
     1081        } finally {
     1082            writeUnlock(locked);
     1083        }
    10011084    }
    10021085
     
    10071090     */
    10081091    public final void removeAll() {
    1009         if (keys != null) {
    1010             Map<String, String> originalKeys = getKeys();
    1011             keys = null;
    1012             keysChangedImpl(originalKeys);
     1092        boolean locked = writeLock();
     1093        try {
     1094            if (keys != null) {
     1095                Map<String, String> originalKeys = getKeys();
     1096                keys = null;
     1097                keysChangedImpl(originalKeys);
     1098            }
     1099        } finally {
     1100            writeUnlock(locked);
    10131101        }
    10141102    }
     
    10221110     */
    10231111    public final String get(String key) {
     1112        String[] keys = this.keys;
    10241113        if (key == null)
    10251114            return null;
     
    10331122
    10341123    public final Collection<String> keySet() {
     1124        String[] keys = this.keys;
    10351125        if (keys == null)
    10361126            return Collections.emptySet();
     
    10681158     */
    10691159    public boolean hasKey(String key) {
     1160        String[] keys = this.keys;
    10701161        if (key == null) return false;
    10711162        if (keys == null) return false;
     
    11661257        // when way is cloned
    11671258        checkDataset();
     1259        Object referrers = this.referrers;
    11681260        List<OsmPrimitive> result = new ArrayList<OsmPrimitive>();
    11691261        if (referrers != null) {
     
    12021294     */
    12031295    public void cloneFrom(OsmPrimitive other) {
     1296        // write lock is provided by subclasses
    12041297        if (id != other.id && dataSet != null)
    12051298            throw new DataIntegrityProblemException("Osm id cannot be changed after primitive was added to the dataset");
     
    12401333     */
    12411334    public void mergeFrom(OsmPrimitive other) {
    1242         CheckParameterUtil.ensureParameterNotNull(other, "other");
    1243         if (other.isNew() ^ isNew())
    1244             throw new DataIntegrityProblemException(tr("Cannot merge because either of the participating primitives is new and the other is not"));
    1245         if (! other.isNew() && other.getId() != id)
    1246             throw new DataIntegrityProblemException(tr("Cannot merge primitives with different ids. This id is {0}, the other is {1}", id, other.getId()));
    1247 
    1248         setKeys(other.getKeys());
    1249         timestamp = other.timestamp;
    1250         version = other.version;
    1251         setIncomplete(other.isIncomplete());
    1252         flags = other.flags;
    1253         user= other.user;
    1254         changesetId = other.changesetId;
     1335        boolean locked = writeLock();
     1336        try {
     1337            CheckParameterUtil.ensureParameterNotNull(other, "other");
     1338            if (other.isNew() ^ isNew())
     1339                throw new DataIntegrityProblemException(tr("Cannot merge because either of the participating primitives is new and the other is not"));
     1340            if (! other.isNew() && other.getId() != id)
     1341                throw new DataIntegrityProblemException(tr("Cannot merge primitives with different ids. This id is {0}, the other is {1}", id, other.getId()));
     1342
     1343            setKeys(other.getKeys());
     1344            timestamp = other.timestamp;
     1345            version = other.version;
     1346            setIncomplete(other.isIncomplete());
     1347            flags = other.flags;
     1348            user= other.user;
     1349            changesetId = other.changesetId;
     1350        } finally {
     1351            writeUnlock(locked);
     1352        }
    12551353    }
    12561354
     
    13131411     */
    13141412    public String getName() {
    1315         if (get("name") != null)
    1316             return get("name");
    1317         return null;
     1413        return get("name");
    13181414    }
    13191415
     
    13561452     */
    13571453    public void load(PrimitiveData data) {
     1454        // Write lock is provided by subclasses
    13581455        setKeys(data.getKeys());
    13591456        setTimestamp(data.getTimestamp());
  • trunk/src/org/openstreetmap/josm/data/osm/Relation.java

    r3347 r3348  
    3838     */
    3939    public void setMembers(List<RelationMember> members) {
    40         for (RelationMember rm:this.members) {
    41             rm.getMember().removeReferrer(this);
    42         }
    43 
    44         if (members != null) {
    45             this.members = members.toArray(new RelationMember[members.size()]);
    46         }
    47         for (RelationMember rm:this.members) {
    48             rm.getMember().addReferrer(this);
    49         }
    50 
    51         fireMembersChanged();
     40        boolean locked = writeLock();
     41        try {
     42            for (RelationMember rm:this.members) {
     43                rm.getMember().removeReferrer(this);
     44            }
     45
     46            if (members != null) {
     47                this.members = members.toArray(new RelationMember[members.size()]);
     48            }
     49            for (RelationMember rm:this.members) {
     50                rm.getMember().addReferrer(this);
     51            }
     52
     53            fireMembersChanged();
     54        } finally {
     55            writeUnlock(locked);
     56        }
    5257    }
    5358
     
    6469
    6570    public void addMember(RelationMember member) {
    66         RelationMember[] newMembers = new RelationMember[members.length + 1];
    67         System.arraycopy(members, 0, newMembers, 0, members.length);
    68         newMembers[members.length] = member;
    69         members = newMembers;
    70         member.getMember().addReferrer(this);
    71         fireMembersChanged();
     71        boolean locked = writeLock();
     72        try {
     73            RelationMember[] newMembers = new RelationMember[members.length + 1];
     74            System.arraycopy(members, 0, newMembers, 0, members.length);
     75            newMembers[members.length] = member;
     76            members = newMembers;
     77            member.getMember().addReferrer(this);
     78            fireMembersChanged();
     79        } finally {
     80            writeUnlock(locked);
     81        }
    7282    }
    7383
    7484    public void addMember(int index, RelationMember member) {
    75         RelationMember[] newMembers = new RelationMember[members.length + 1];
    76         System.arraycopy(members, 0, newMembers, 0, index);
    77         System.arraycopy(members, index, newMembers, index + 1, members.length - index);
    78         newMembers[index] = member;
    79         members = newMembers;
    80         member.getMember().addReferrer(this);
    81         fireMembersChanged();
     85        boolean locked = writeLock();
     86        try {
     87            RelationMember[] newMembers = new RelationMember[members.length + 1];
     88            System.arraycopy(members, 0, newMembers, 0, index);
     89            System.arraycopy(members, index, newMembers, index + 1, members.length - index);
     90            newMembers[index] = member;
     91            members = newMembers;
     92            member.getMember().addReferrer(this);
     93            fireMembersChanged();
     94        } finally {
     95            writeUnlock(locked);
     96        }
    8297    }
    8398
     
    89104     */
    90105    public RelationMember setMember(int index, RelationMember member) {
    91         RelationMember originalMember = members[index];
    92         members[index] = member;
    93         if (originalMember.getMember() != member.getMember()) {
    94             member.getMember().addReferrer(this);
    95             originalMember.getMember().removeReferrer(this);
    96             fireMembersChanged();
    97         }
    98         return originalMember;
     106        boolean locked = writeLock();
     107        try {
     108            RelationMember originalMember = members[index];
     109            members[index] = member;
     110            if (originalMember.getMember() != member.getMember()) {
     111                member.getMember().addReferrer(this);
     112                originalMember.getMember().removeReferrer(this);
     113                fireMembersChanged();
     114            }
     115            return originalMember;
     116        } finally {
     117            writeUnlock(locked);
     118        }
    99119    }
    100120
     
    105125     */
    106126    public RelationMember removeMember(int index) {
    107         List<RelationMember> members = getMembers();
    108         RelationMember result = members.remove(index);
    109         setMembers(members);
    110         return result;
     127        boolean locked = writeLock();
     128        try {
     129            List<RelationMember> members = getMembers();
     130            RelationMember result = members.remove(index);
     131            setMembers(members);
     132            return result;
     133        } finally {
     134            writeUnlock(locked);
     135        }
    111136    }
    112137
     
    162187
    163188    @Override public void cloneFrom(OsmPrimitive osm) {
    164         super.cloneFrom(osm);
    165         // It's not necessary to clone members as RelationMember class is immutable
    166         setMembers(((Relation)osm).getMembers());
     189        boolean locked = writeLock();
     190        try {
     191            super.cloneFrom(osm);
     192            // It's not necessary to clone members as RelationMember class is immutable
     193            setMembers(((Relation)osm).getMembers());
     194        } finally {
     195            writeUnlock(locked);
     196        }
    167197    }
    168198
    169199    @Override public void load(PrimitiveData data) {
    170         super.load(data);
    171 
    172         RelationData relationData = (RelationData) data;
    173 
    174         List<RelationMember> newMembers = new ArrayList<RelationMember>();
    175         for (RelationMemberData member : relationData.getMembers()) {
    176             OsmPrimitive primitive = getDataSet().getPrimitiveById(member);
    177             if (primitive == null)
    178                 throw new AssertionError("Data consistency problem - relation with missing member detected");
    179             newMembers.add(new RelationMember(member.getRole(), primitive));
    180         }
    181         setMembers(newMembers);
     200        boolean locked = writeLock();
     201        try {
     202            super.load(data);
     203
     204            RelationData relationData = (RelationData) data;
     205
     206            List<RelationMember> newMembers = new ArrayList<RelationMember>();
     207            for (RelationMemberData member : relationData.getMembers()) {
     208                OsmPrimitive primitive = getDataSet().getPrimitiveById(member);
     209                if (primitive == null)
     210                    throw new AssertionError("Data consistency problem - relation with missing member detected");
     211                newMembers.add(new RelationMember(member.getRole(), primitive));
     212            }
     213            setMembers(newMembers);
     214        } finally {
     215            writeUnlock(locked);
     216        }
    182217    }
    183218
     
    228263    public RelationMember firstMember() {
    229264        if (isIncomplete()) return null;
     265
     266        RelationMember[] members = this.members;
    230267        return (members.length == 0) ? null : members[0];
    231268    }
    232269    public RelationMember lastMember() {
    233270        if (isIncomplete()) return null;
     271
     272        RelationMember[] members = this.members;
    234273        return (members.length == 0) ? null : members[members.length - 1];
    235274    }
     
    244283            return;
    245284
    246         List<RelationMember> todelete = new ArrayList<RelationMember>();
    247         for (RelationMember member: members) {
    248             if (member.getMember() == primitive) {
    249                 todelete.add(member);
    250             }
    251         }
    252         List<RelationMember> members = getMembers();
    253         members.removeAll(todelete);
    254         setMembers(members);
     285        boolean locked = writeLock();
     286        try {
     287            List<RelationMember> todelete = new ArrayList<RelationMember>();
     288            for (RelationMember member: members) {
     289                if (member.getMember() == primitive) {
     290                    todelete.add(member);
     291                }
     292            }
     293            List<RelationMember> members = getMembers();
     294            members.removeAll(todelete);
     295            setMembers(members);
     296        } finally {
     297            writeUnlock(locked);
     298        }
    255299    }
    256300
    257301    @Override
    258302    public void setDeleted(boolean deleted) {
    259         for (RelationMember rm:members) {
    260             if (deleted) {
    261                 rm.getMember().removeReferrer(this);
    262             } else {
    263                 rm.getMember().addReferrer(this);
    264             }
    265         }
    266         super.setDeleted(deleted);
     303        boolean locked = writeLock();
     304        try {
     305            for (RelationMember rm:members) {
     306                if (deleted) {
     307                    rm.getMember().removeReferrer(this);
     308                } else {
     309                    rm.getMember().addReferrer(this);
     310                }
     311            }
     312            super.setDeleted(deleted);
     313        } finally {
     314            writeUnlock(locked);
     315        }
    267316    }
    268317
     
    276325            return;
    277326
    278         ArrayList<RelationMember> todelete = new ArrayList<RelationMember>();
    279         for (RelationMember member: members) {
    280             if (primitives.contains(member.getMember())) {
    281                 todelete.add(member);
    282             }
    283         }
    284         List<RelationMember> members = getMembers();
    285         members.removeAll(todelete);
    286         setMembers(members);
     327        boolean locked = writeLock();
     328        try {
     329            ArrayList<RelationMember> todelete = new ArrayList<RelationMember>();
     330            for (RelationMember member: members) {
     331                if (primitives.contains(member.getMember())) {
     332                    todelete.add(member);
     333                }
     334            }
     335            List<RelationMember> members = getMembers();
     336            members.removeAll(todelete);
     337            setMembers(members);
     338        } finally {
     339            writeUnlock(locked);
     340        }
    287341    }
    288342
     
    301355    public Set<OsmPrimitive> getMemberPrimitives() {
    302356        HashSet<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
     357        RelationMember[] members = this.members;
    303358        for (RelationMember m: members) {
    304359            if (m.getMember() != null) {
     
    315370    @Override
    316371    public BBox getBBox() {
     372        RelationMember[] members = this.members;
     373
    317374        if (members.length == 0)
    318375            return new BBox(0, 0, 0, 0);
     
    334391            return null;
    335392        visitedRelations.add(this);
     393
     394        RelationMember[] members = this.members;
    336395        if (members.length == 0)
    337396            return null;
     
    367426        DataSet dataSet = getDataSet();
    368427        if (dataSet != null) {
     428            RelationMember[] members = this.members;
    369429            for (RelationMember rm: members) {
    370430                if (rm.getMember().getDataSet() != dataSet)
     
    393453     */
    394454    public boolean hasIncompleteMembers() {
     455        RelationMember[] members = this.members;
    395456        for (RelationMember rm: members) {
    396457            if (rm.getMember().isIncomplete()) return true;
     
    407468    public Collection<OsmPrimitive> getIncompleteMembers() {
    408469        Set<OsmPrimitive> ret = new HashSet<OsmPrimitive>();
     470        RelationMember[] members = this.members;
    409471        for (RelationMember rm: members) {
    410472            if (!rm.getMember().isIncomplete()) {
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r3254 r3348  
    4747     */
    4848    public void setNodes(List<Node> nodes) {
    49         for (Node node:this.nodes) {
    50             node.removeReferrer(this);
    51         }
    52 
    53         if (nodes == null) {
    54             this.nodes = new Node[0];
    55         } else {
    56             this.nodes = nodes.toArray(new Node[nodes.size()]);
    57         }
    58         for (Node node:this.nodes) {
    59             node.addReferrer(this);
    60         }
    61 
    62         clearCached();
    63         fireNodesChanged();
     49        boolean locked = writeLock();
     50        try {
     51            for (Node node:this.nodes) {
     52                node.removeReferrer(this);
     53            }
     54
     55            if (nodes == null) {
     56                this.nodes = new Node[0];
     57            } else {
     58                this.nodes = nodes.toArray(new Node[nodes.size()]);
     59            }
     60            for (Node node:this.nodes) {
     61                node.addReferrer(this);
     62            }
     63
     64            clearCached();
     65            fireNodesChanged();
     66        } finally {
     67            writeUnlock(locked);
     68        }
    6469    }
    6570
     
    98103    public boolean containsNode(Node node) {
    99104        if (node == null) return false;
     105
     106        Node[] nodes = this.nodes;
    100107        for (int i=0; i<nodes.length; i++) {
    101108            if (nodes[i].equals(node))
     
    115122    }
    116123
    117     public ArrayList<Pair<Node,Node>> getNodePairs(boolean sort) {
    118         ArrayList<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>();
     124    public List<Pair<Node,Node>> getNodePairs(boolean sort) {
     125        List<Pair<Node,Node>> chunkSet = new ArrayList<Pair<Node,Node>>();
    119126        if (isIncomplete()) return chunkSet;
    120127        Node lastN = null;
    121         for (Node n : this.nodes) {
     128        Node[] nodes = this.nodes;
     129        for (Node n : nodes) {
    122130            if (lastN == null) {
    123131                lastN = n;
     
    198206    @Override
    199207    public void load(PrimitiveData data) {
    200         super.load(data);
    201 
    202         WayData wayData = (WayData) data;
    203 
    204         List<Node> newNodes = new ArrayList<Node>(wayData.getNodes().size());
    205         for (Long nodeId : wayData.getNodes()) {
    206             Node node = (Node)getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE);
    207             if (node != null) {
    208                 newNodes.add(node);
    209             } else
    210                 throw new AssertionError("Data consistency problem - way with missing node detected");
    211         }
    212         setNodes(newNodes);
     208        boolean locked = writeLock();
     209        try {
     210            super.load(data);
     211
     212            WayData wayData = (WayData) data;
     213
     214            List<Node> newNodes = new ArrayList<Node>(wayData.getNodes().size());
     215            for (Long nodeId : wayData.getNodes()) {
     216                Node node = (Node)getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE);
     217                if (node != null) {
     218                    newNodes.add(node);
     219                } else
     220                    throw new AssertionError("Data consistency problem - way with missing node detected");
     221            }
     222            setNodes(newNodes);
     223        } finally {
     224            writeUnlock(locked);
     225        }
    213226    }
    214227
     
    223236
    224237    @Override public void cloneFrom(OsmPrimitive osm) {
    225         super.cloneFrom(osm);
    226         Way otherWay = (Way)osm;
    227         setNodes(otherWay.getNodes());
     238        boolean locked = writeLock();
     239        try {
     240            super.cloneFrom(osm);
     241            Way otherWay = (Way)osm;
     242            setNodes(otherWay.getNodes());
     243        } finally {
     244            writeUnlock(locked);
     245        }
    228246    }
    229247
     
    256274    public void removeNode(Node n) {
    257275        if (isIncomplete()) return;
    258         boolean closed = (lastNode() == n && firstNode() == n);
    259         int i;
    260         List<Node> copy = getNodes();
    261         while ((i = copy.indexOf(n)) >= 0) {
    262             copy.remove(i);
    263         }
    264         i = copy.size();
    265         if (closed && i > 2) {
    266             copy.add(copy.get(0));
    267         } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
    268             copy.remove(i-1);
    269         }
    270         setNodes(copy);
     276        boolean locked = writeLock();
     277        try {
     278            boolean closed = (lastNode() == n && firstNode() == n);
     279            int i;
     280            List<Node> copy = getNodes();
     281            while ((i = copy.indexOf(n)) >= 0) {
     282                copy.remove(i);
     283            }
     284            i = copy.size();
     285            if (closed && i > 2) {
     286                copy.add(copy.get(0));
     287            } else if (i >= 2 && i <= 3 && copy.get(0) == copy.get(i-1)) {
     288                copy.remove(i-1);
     289            }
     290            setNodes(copy);
     291        } finally {
     292            writeUnlock(locked);
     293        }
    271294    }
    272295
    273296    public void removeNodes(Collection<? extends OsmPrimitive> selection) {
    274297        if (isIncomplete()) return;
    275         for(OsmPrimitive p : selection) {
    276             if (p instanceof Node) {
    277                 removeNode((Node)p);
    278             }
     298        boolean locked = writeLock();
     299        try {
     300            for(OsmPrimitive p : selection) {
     301                if (p instanceof Node) {
     302                    removeNode((Node)p);
     303                }
     304            }
     305        } finally {
     306            writeUnlock(locked);
    279307        }
    280308    }
     
    289317    public void addNode(Node n) throws IllegalStateException {
    290318        if (n==null) return;
    291         if (isIncomplete())
    292             throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
    293         clearCached();
    294         n.addReferrer(this);
    295         Node[] newNodes = new Node[nodes.length + 1];
    296         System.arraycopy(nodes, 0, newNodes, 0, nodes.length);
    297         newNodes[nodes.length] = n;
    298         nodes = newNodes;
    299         fireNodesChanged();
     319
     320        boolean locked = writeLock();
     321        try {
     322            if (isIncomplete())
     323                throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
     324            clearCached();
     325            n.addReferrer(this);
     326            Node[] newNodes = new Node[nodes.length + 1];
     327            System.arraycopy(nodes, 0, newNodes, 0, nodes.length);
     328            newNodes[nodes.length] = n;
     329            nodes = newNodes;
     330            fireNodesChanged();
     331        } finally {
     332            writeUnlock(locked);
     333        }
    300334    }
    301335
     
    311345    public void addNode(int offs, Node n) throws IllegalStateException, IndexOutOfBoundsException {
    312346        if (n==null) return;
    313         if (isIncomplete())
    314             throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
    315         clearCached();
    316         n.addReferrer(this);
    317         Node[] newNodes = new Node[nodes.length + 1];
    318         System.arraycopy(nodes, 0, newNodes, 0, offs);
    319         System.arraycopy(nodes, offs, newNodes, offs + 1, nodes.length - offs);
    320         newNodes[offs] = n;
    321         nodes = newNodes;
    322         fireNodesChanged();
     347
     348        boolean locked = writeLock();
     349        try {
     350            if (isIncomplete())
     351                throw new IllegalStateException(tr("Cannot add node {0} to incomplete way {1}.", n.getId(), getId()));
     352
     353            clearCached();
     354            n.addReferrer(this);
     355            Node[] newNodes = new Node[nodes.length + 1];
     356            System.arraycopy(nodes, 0, newNodes, 0, offs);
     357            System.arraycopy(nodes, offs, newNodes, offs + 1, nodes.length - offs);
     358            newNodes[offs] = n;
     359            nodes = newNodes;
     360            fireNodesChanged();
     361        } finally {
     362            writeUnlock(locked);
     363        }
    323364    }
    324365
    325366    @Override
    326367    public void setDeleted(boolean deleted) {
    327         for (Node n:nodes) {
    328             if (deleted) {
    329                 n.removeReferrer(this);
    330             } else {
    331                 n.addReferrer(this);
    332             }
    333         }
    334         fireNodesChanged();
    335         super.setDeleted(deleted);
     368        boolean locked = writeLock();
     369        try {
     370            for (Node n:nodes) {
     371                if (deleted) {
     372                    n.removeReferrer(this);
     373                } else {
     374                    n.addReferrer(this);
     375                }
     376            }
     377            fireNodesChanged();
     378            super.setDeleted(deleted);
     379        } finally {
     380            writeUnlock(locked);
     381        }
    336382    }
    337383
    338384    public boolean isClosed() {
    339385        if (isIncomplete()) return false;
    340         return nodes.length >= 3 && lastNode() == firstNode();
     386
     387        Node[] nodes = this.nodes;
     388        return nodes.length >= 3 && nodes[nodes.length-1] == nodes[0];
    341389    }
    342390
    343391    public Node lastNode() {
     392        Node[] nodes = this.nodes;
    344393        if (isIncomplete() || nodes.length == 0) return null;
    345394        return nodes[nodes.length-1];
     
    347396
    348397    public Node firstNode() {
     398        Node[] nodes = this.nodes;
    349399        if (isIncomplete() || nodes.length == 0) return null;
    350400        return nodes[0];
     
    352402
    353403    public boolean isFirstLastNode(Node n) {
     404        Node[] nodes = this.nodes;
    354405        if (isIncomplete() || nodes.length == 0) return false;
    355         return n == firstNode() || n == lastNode();
     406        return n == nodes[0] || n == nodes[nodes.length -1];
    356407    }
    357408
     
    368419        DataSet dataSet = getDataSet();
    369420        if (dataSet != null) {
     421            Node[] nodes = this.nodes;
    370422            for (Node n: nodes) {
    371423                if (n.getDataSet() != dataSet)
     
    412464
    413465    public boolean hasIncompleteNodes() {
     466        Node[] nodes = this.nodes;
    414467        for (Node node:nodes) {
    415468            if (node.isIncomplete())
Note: See TracChangeset for help on using the changeset viewer.