Ignore:
Timestamp:
2009-06-23T22:03:37+02:00 (15 years ago)
Author:
Gubaer
Message:

new: MultiFetchServerObjectReader using APIs Multi Fetch method
update: now uses Multi Fetch to check for deleted primitives on the server
update: now uses Multi Fetch to update the selected primitives with the state from the server
fixed: cleaned up merging in MergeVisitor
new: conflict resolution dialog; now resolves conflicts due to different visibilities
new: replacement for realEqual() on OsmPrimitive and derived classes; realEqual now @deprecated
fixed: cleaning up OsmReader
fixed: progress indication in OsmApi

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

Legend:

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

    r1677 r1690  
    88import java.util.HashSet;
    99import java.util.HashMap;
     10import java.util.Iterator;
    1011import java.util.LinkedList;
    1112import java.util.List;
     
    8081        Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
    8182        for (OsmPrimitive osm : allPrimitives())
    82             if (!osm.deleted) {
     83            if (osm.visible && !osm.deleted) {
    8384                o.add(osm);
    8485            }
     
    8990        Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
    9091        for (OsmPrimitive osm : allPrimitives())
    91             if (!osm.deleted && !osm.incomplete) {
     92            if (osm.visible && !osm.deleted && !osm.incomplete) {
    9293                o.add(osm);
    9394            }
     
    9899        Collection<OsmPrimitive> o = new LinkedList<OsmPrimitive>();
    99100        for (OsmPrimitive osm : allPrimitives())
    100             if (!osm.deleted && !osm.incomplete && !(osm instanceof Relation)) {
     101            if (osm.visible && !osm.deleted && !osm.incomplete && !(osm instanceof Relation)) {
    101102                o.add(osm);
    102103            }
     
    311312        return ret;
    312313    }
     314
     315    protected void deleteWay(Way way) {
     316        way.nodes.clear();
     317        way.delete(true);
     318    }
     319
     320    /**
     321     * removes all references from ways in this dataset to a particular node
     322     *
     323     * @param node the node
     324     */
     325    public void unlinkNodeFromWays(Node node) {
     326        for (Way way: ways) {
     327            if (way.nodes.contains(node)) {
     328                way.nodes.remove(node);
     329                if (way.nodes.size() < 2) {
     330                    deleteWay(way);
     331                }
     332            }
     333        }
     334    }
     335
     336    /**
     337     * removes all references from relations in this dataset  to this primitive
     338     *
     339     * @param primitive the primitive
     340     */
     341    public void unlinkPrimitiveFromRelations(OsmPrimitive primitive) {
     342        for (Relation relation : relations) {
     343            Iterator<RelationMember> it = relation.members.iterator();
     344            while(it.hasNext()) {
     345                RelationMember member = it.next();
     346                if (member.member.equals(primitive)) {
     347                    it.remove();
     348                }
     349            }
     350        }
     351    }
     352
     353    /**
     354     * removes all references from from other primitives  to the
     355     * referenced primitive
     356     *
     357     * @param referencedPrimitive the referenced primitive
     358     */
     359    public void unlinkReferencesToPrimitive(OsmPrimitive referencedPrimitive) {
     360        if (referencedPrimitive instanceof Node) {
     361            unlinkNodeFromWays((Node)referencedPrimitive);
     362            unlinkPrimitiveFromRelations(referencedPrimitive);
     363        } else {
     364            unlinkPrimitiveFromRelations(referencedPrimitive);
     365        }
     366    }
    313367}
  • trunk/src/org/openstreetmap/josm/data/osm/Node.java

    r1640 r1690  
    3131
    3232    public final void setEastNorth(EastNorth eastNorth) {
    33        this.eastNorth = eastNorth;
    34        this.coor = Main.proj.eastNorth2latlon(eastNorth);
     33        this.eastNorth = eastNorth;
     34        this.coor = Main.proj.eastNorth2latlon(eastNorth);
    3535    }
    3636
     
    8787    }
    8888
     89    /**
     90     * @deprecated
     91     * @see #hasEqualSemanticAttributes(OsmPrimitive)
     92     * @see #hasEqualTechnicalAttributes(OsmPrimitive)
     93     */
     94    @Deprecated
    8995    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    9096        if (osm instanceof Node) {
     
    99105    }
    100106
     107    @Override
     108    public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
     109        if (other == null || ! (other instanceof Node) )
     110            return false;
     111        if (! super.hasEqualSemanticAttributes(other))
     112            return false;
     113        Node n = (Node)other;
     114        if (coor == null && n.coor == null)
     115            return true;
     116        else if (coor != null && n.coor != null)
     117            return coor.equals(n.coor);
     118        else
     119            return false;
     120    }
     121
    101122    public int compareTo(OsmPrimitive o) {
    102123        return o instanceof Node ? Long.valueOf(id).compareTo(o.id) : 1;
    103124    }
    104125
     126    @Override
    105127    public String getName() {
    106128        String name;
     
    109131        } else {
    110132            name = get("name");
    111             if (name == null)
     133            if (name == null) {
    112134                name = id == 0 ? tr("node") : ""+id;
     135            }
    113136            name += " (" + coor.latToString(mCord) + ", " + coor.lonToString(mCord) + ")";
    114137        }
  • trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java

    r1530 r1690  
    4343    public void putError(String text, Boolean isError)
    4444    {
    45         if(errors == null)
     45        if(errors == null) {
    4646            errors = new ArrayList<String>();
     47        }
    4748        String s = isError ? tr("Error: {0}", text) : tr("Warning: {0}", text);
    4849        errors.add(s);
     
    9192     * Visibility status as specified by the server. The visible attribute was
    9293     * introduced with the 0.4 API to be able to communicate deleted objects
    93      * (they will have visible=false). Currently JOSM does never deal with
    94      * these, so this is really for future use only.
     94     * (they will have visible=false).
    9595     */
    9696    public boolean visible = true;
     
    195195    @Override public boolean equals(Object obj) {
    196196        if (id == 0) return obj == this;
    197         if (obj instanceof OsmPrimitive) { // not null too
     197        if (obj instanceof OsmPrimitive)
    198198            return ((OsmPrimitive)obj).id == id && obj.getClass() == getClass();
    199         }
    200199        return false;
    201200    }
     
    226225     */
    227226    public final void put(String key, String value) {
    228         if (value == null)
     227        if (value == null) {
    229228            remove(key);
    230         else {
    231             if (keys == null)
     229        } else {
     230            if (keys == null) {
    232231                keys = new HashMap<String, String>();
     232            }
    233233            keys.put(key, value);
    234234        }
     
    241241        if (keys != null) {
    242242            keys.remove(key);
    243             if (keys.isEmpty())
     243            if (keys.isEmpty()) {
    244244                keys = null;
     245            }
    245246        }
    246247        mappaintStyle = null;
     
    280281        version = osm.version;
    281282        incomplete = osm.incomplete;
     283        visible = osm.visible;
    282284        clearCached();
    283285        clearErrors();
     
    288290     * but for the whole object (for conflict resolving)
    289291     * @param semanticOnly if <code>true</code>, modified flag and timestamp are not compared
    290      */
     292     *
     293     * @deprecated
     294     * @see #hasEqualSemanticAttributes(OsmPrimitive)
     295     * @see #hasEqualTechnicalAttributes(OsmPrimitive)
     296     */
     297    @Deprecated
    291298    public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    292299        return id == osm.id
    293300        && incomplete == osm.incomplete
    294301        && deleted == osm.deleted
    295         && (semanticOnly || (modified == osm.modified
    296          && timestamp == osm.timestamp
    297          && version == osm.version
    298          && visible == osm.visible
    299          && (user == null ? osm.user==null : user==osm.user)))
     302        && (semanticOnly || (
     303                modified == osm.modified
     304                && timestamp == osm.timestamp
     305                && version == osm.version
     306                && visible == osm.visible
     307                && (user == null ? osm.user==null : user==osm.user))
     308        )
    300309        && (keys == null ? osm.keys==null : keys.equals(osm.keys));
     310    }
     311
     312    /**
     313     * Replies true if this primitive and other are equal with respect to their
     314     * semantic attributes.
     315     * <ol>
     316     *   <li>equal id</ol>
     317     *   <li>both are complete or both are incomplete</li>
     318     *   <li>both have the same tags</li>
     319     * </ol>
     320     * @param other
     321     * @return true if this primitive and other are equal with respect to their
     322     * semantic attributes.
     323     */
     324    public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
     325        if (id != other.id)
     326            return false;
     327        if (incomplete && ! other.incomplete || !incomplete  && other.incomplete)
     328            return false;
     329        return (keys == null ? other.keys==null : keys.equals(other.keys));
     330    }
     331
     332    /**
     333     * Replies true if this primitive and other are equal with respect to their
     334     * technical attributes. The attributes:
     335     * <ol>
     336     *   <li>deleted</ol>
     337     *   <li>modified</ol>
     338     *   <li>timestamp</ol>
     339     *   <li>version</ol>
     340     *   <li>visible</ol>
     341     *   <li>user</ol>
     342     * </ol>
     343     * have to be equal
     344     * @param other the other primitive
     345     * @return true if this primitive and other are equal with respect to their
     346     * technical attributes
     347     */
     348    public boolean hasEqualTechnicalAttributes(OsmPrimitive other) {
     349        if (other == null) return false;
     350
     351        return
     352        deleted == other.deleted
     353        && modified == other.modified
     354        && timestamp == other.timestamp
     355        && version == other.version
     356        && visible == other.visible
     357        && (user == null ? other.user==null : user==other.user);
    301358    }
    302359
     
    312369        if (keys != null) {
    313370            for (Entry<String,String> e : keys.entrySet()) {
    314                 if (!uninteresting.contains(e.getKey())) {
     371                if (!uninteresting.contains(e.getKey()))
    315372                    return true;
    316                 }
    317373            }
    318374        }
     
    327383        if (keys != null) {
    328384            for (Entry<String,String> e : keys.entrySet()) {
    329                 if (directionKeys.contains(e.getKey())) {
     385                if (directionKeys.contains(e.getKey()))
    330386                    return true;
    331                 }
    332387            }
    333388        }
  • trunk/src/org/openstreetmap/josm/data/osm/Relation.java

    r1677 r1690  
    7070    }
    7171
     72    @Deprecated
    7273    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    7374        return osm instanceof Relation ? super.realEqual(osm, semanticOnly) && members.equals(((Relation)osm).members) : false;
     75    }
     76
     77    @Override
     78    public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
     79        if (other == null || ! (other instanceof Relation) )
     80            return false;
     81        if (! super.hasEqualSemanticAttributes(other))
     82            return false;
     83        Relation r = (Relation)other;
     84        return members.equals(r.members);
    7485    }
    7586
  • trunk/src/org/openstreetmap/josm/data/osm/RelationMember.java

    r1169 r1690  
    3333
    3434    @Override public boolean equals(Object other) {
    35         if (!(other instanceof RelationMember)) return false;
     35        if (other == null || !(other instanceof RelationMember)) return false;
    3636        RelationMember otherMember = (RelationMember) other;
    3737        return otherMember.role.equals(role) && otherMember.member.equals(member);
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r1677 r1690  
    3939    public void visitNodes(Visitor v) {
    4040        if (incomplete) return;
    41         for (Node n : this.nodes)
     41        for (Node n : this.nodes) {
    4242            v.visit(n);
     43        }
    4344    }
    4445
     
    100101    }
    101102
     103    @Deprecated
    102104    @Override public boolean realEqual(OsmPrimitive osm, boolean semanticOnly) {
    103105        return osm instanceof Way ? super.realEqual(osm, semanticOnly) && nodes.equals(((Way)osm).nodes) : false;
     106    }
     107
     108    @Override
     109    public boolean hasEqualSemanticAttributes(OsmPrimitive other) {
     110        if (other == null || ! (other instanceof Way) )
     111            return false;
     112        if (! super.hasEqualSemanticAttributes(other))
     113            return false;
     114        Way w = (Way)other;
     115        return nodes.equals(w.nodes);
    104116    }
    105117
     
    110122    }
    111123
     124    @Override
    112125    public String getName() {
    113126        String name;
     
    116129        } else {
    117130            name = get("name");
    118             if (name == null) name = get("ref");
     131            if (name == null) {
     132                name = get("ref");
     133            }
    119134            if (name == null) {
    120135                name =
    121136                    (get("highway") != null) ? tr("highway") :
    122                     (get("railway") != null) ? tr("railway") :
    123                     (get("waterway") != null) ? tr("waterway") :
    124                     (get("landuse") != null) ? tr("landuse") : "";
     137                        (get("railway") != null) ? tr("railway") :
     138                            (get("waterway") != null) ? tr("waterway") :
     139                                (get("landuse") != null) ? tr("landuse") : "";
    125140            }
    126141
     
    128143            String nodes = trn("{0} node", "{0} nodes", nodesNo, nodesNo);
    129144            name += (name.length() > 0) ? " ("+nodes+")" : nodes;
    130             if(errors != null)
     145            if(errors != null) {
    131146                name = "*"+name;
     147            }
    132148        }
    133149        return name;
     
    138154        boolean closed = (lastNode() == n && firstNode() == n);
    139155        int i;
    140         while ((i = nodes.indexOf(n)) >= 0)
     156        while ((i = nodes.indexOf(n)) >= 0) {
    141157            nodes.remove(i);
     158        }
    142159        i = nodes.size();
    143         if (closed && i > 2) // close again
     160        if (closed && i > 2) {
    144161            addNode(firstNode());
    145         // prevent closed ways with less than 3 different nodes
    146         else if (i >= 2 && i <= 3 && nodes.get(0) == nodes.get(i-1))
     162        } else if (i >= 2 && i <= 3 && nodes.get(0) == nodes.get(i-1)) {
    147163            nodes.remove(i-1);
     164        }
    148165    }
    149166
     
    151168        if (incomplete) return;
    152169        for(OsmPrimitive p : selection) {
    153            if (p instanceof Node) {
    154                removeNode((Node)p);
    155            }
    156        }
     170            if (p instanceof Node) {
     171                removeNode((Node)p);
     172            }
     173        }
    157174    }
    158175
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/MergeVisitor.java

    r1677 r1690  
    22package org.openstreetmap.josm.data.osm.visitor;
    33
     4import static org.openstreetmap.josm.tools.I18n.tr;
     5
    46import java.util.Collection;
    5 import java.util.Date;
    67import java.util.HashMap;
    78import java.util.Iterator;
    89import java.util.LinkedList;
    910import java.util.Map;
     11import java.util.logging.Logger;
    1012
    1113import org.openstreetmap.josm.data.osm.DataSet;
     14import org.openstreetmap.josm.data.osm.Node;
     15import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1216import org.openstreetmap.josm.data.osm.Relation;
    1317import org.openstreetmap.josm.data.osm.RelationMember;
    14 import org.openstreetmap.josm.data.osm.Node;
    15 import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1618import org.openstreetmap.josm.data.osm.Way;
    1719
    1820/**
    19  * A visitor that get a data set at construction time and merge every visited object
     21 * A visitor that gets a data set at construction time and merges every visited object
    2022 * into it.
    2123 *
    2224 * @author imi
     25 * @author Gubaer
    2326 */
    2427public class MergeVisitor extends AbstractVisitor {
     28    private static Logger logger = Logger.getLogger(MergeVisitor.class.getName());
    2529
    2630    /**
     
    2832     * round than merged)
    2933     */
    30     public Map<OsmPrimitive, OsmPrimitive> conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
    31 
    32     private final DataSet ds;
    33     private final DataSet mergeds;
     34    private Map<OsmPrimitive, OsmPrimitive> conflicts;
     35
     36    private final DataSet myDataSet;
     37    private final DataSet theirDataSet;
    3438
    3539    private final HashMap<Long, Node> nodeshash = new HashMap<Long, Node>();
     
    4246     * in ds.nodes instead.
    4347     */
    44     private final Map<OsmPrimitive, OsmPrimitive> merged
    45         = new HashMap<OsmPrimitive, OsmPrimitive>();
    46 
    47     public MergeVisitor(DataSet ds, DataSet mergeds) {
    48         this.ds = ds;
    49         this.mergeds = mergeds;
    50 
    51         for (Node n : ds.nodes) if (n.id != 0) nodeshash.put(n.id, n);
    52         for (Way w : ds.ways) if (w.id != 0) wayshash.put(w.id, w);
    53         for (Relation r : ds.relations) if (r.id != 0) relshash.put(r.id, r);
    54     }
    55 
    56     private <P extends OsmPrimitive> void genMerge(P other,
    57             Collection<P> myprims, Collection<P> mergeprims,
    58             HashMap<Long, P> primhash) {
    59         // 1. Try to find an identical prim with the same id.
    60         if (mergeById(myprims, primhash, other))
    61             return;
    62 
    63         // 2. Try to find a prim we can merge with the prim from the other ds.
    64         for (P my : myprims) {
    65             // LinkedList.contains calls equal, and OsmPrimitive.equal
    66             // compares just the id.
    67             if (match(my, other) && !mergeprims.contains(my)) {
    68                 merged.put(other, my);
    69                 mergeCommon(my, other);
     48    private Map<OsmPrimitive, OsmPrimitive> merged;
     49
     50    /**
     51     * constructor
     52     *
     53     * The visitor will merge <code>theirDataSet</code> onto <code>myDataSet</code>
     54     *
     55     * @param myDataSet  dataset with my primitives
     56     * @param theirDataSet dataset with their primitives.
     57     */
     58    public MergeVisitor(DataSet myDataSet, DataSet theirDataSet) {
     59        this.myDataSet = myDataSet;
     60        this.theirDataSet = theirDataSet;
     61
     62        for (Node n : myDataSet.nodes) if (n.id != 0) {
     63            nodeshash.put(n.id, n);
     64        }
     65        for (Way w : myDataSet.ways) if (w.id != 0) {
     66            wayshash.put(w.id, w);
     67        }
     68        for (Relation r : myDataSet.relations) if (r.id != 0) {
     69            relshash.put(r.id, r);
     70        }
     71        conflicts = new HashMap<OsmPrimitive, OsmPrimitive>();
     72        merged = new HashMap<OsmPrimitive, OsmPrimitive>();
     73    }
     74
     75    /**
     76     * Merges a primitive <code>other</code> of type <P> onto my primitives.
     77     *
     78     * If other.id != 0 it tries to merge it with an corresponding primitive from
     79     * my dataset with the same id. If this is not possible a conflict is remembered
     80     * in {@see #conflicts}.
     81     *
     82     * If other.id == 0 it tries to find a primitive in my dataset with id == 0 which
     83     * is semantically equal. If it finds one it merges its technical attributes onto
     84     * my primitive.
     85     *
     86     * @param <P>  the type of the other primitive
     87     * @param other  the other primitive
     88     * @param myPrimitives the collection of my relevant primitives (i.e. only my
     89     *    primitives of the same type)
     90     * @param otherPrimitives  the collection of the other primitives
     91     * @param primitivesWithDefinedIds the collection of my primitives with an
     92     *   assigned id (i.e. id != 0)
     93     */
     94    protected <P extends OsmPrimitive> void mergePrimitive(P other,
     95            Collection<P> myPrimitives, Collection<P> otherPrimitives,
     96            HashMap<Long, P> primitivesWithDefinedIds) {
     97
     98        if (other.id > 0 ) {
     99            // try to merge onto a matching primitive with the same
     100            // defined id
     101            //
     102            if (mergeById(myPrimitives, primitivesWithDefinedIds, other))
    70103                return;
    71             }
    72         }
    73 
    74         // 3. No idea how to merge that.  Simply add it unchanged.
    75         myprims.add(other);
     104        } else {
     105            // try to merge onto a primitive with which has no id assigned
     106            // yet but which is equal in its semantic attributes
     107            //
     108            for (P my : myPrimitives) {
     109                if (my.id >0 ) {
     110                    continue;
     111                }
     112                if (my.hasEqualSemanticAttributes(other)) {
     113                    // copy the technical attributes from their
     114                    // version
     115                    if (other.deleted) {
     116                        myDataSet.unlinkReferencesToPrimitive(my);
     117                        my.delete(true);
     118                    }
     119                    my.visible = other.visible;
     120                    my.user = other.user;
     121                    my.setTimestamp(other.getTimestamp());
     122                    my.modified = other.modified;
     123                    merged.put(other, my);
     124                    return;
     125                }
     126            }
     127        }
     128        // If we get here we didn't find a suitable primitive in
     129        // my dataset. Just add other to my dataset.
     130        //
     131        myPrimitives.add(other);
    76132    }
    77133
    78134    public void visit(Node other) {
    79         genMerge(other, ds.nodes, mergeds.nodes, nodeshash);
     135        mergePrimitive(other, myDataSet.nodes, theirDataSet.nodes, nodeshash);
    80136    }
    81137
    82138    public void visit(Way other) {
    83139        fixWay(other);
    84         genMerge(other, ds.ways, mergeds.ways, wayshash);
     140        mergePrimitive(other, myDataSet.ways, theirDataSet.ways, wayshash);
    85141    }
    86142
    87143    public void visit(Relation other) {
    88144        fixRelation(other);
    89         genMerge(other, ds.relations, mergeds.relations, relshash);
     145        mergePrimitive(other, myDataSet.relations, theirDataSet.relations, relshash);
    90146    }
    91147
     
    95151     */
    96152    public void fixReferences() {
    97         for (Way w : ds.ways) fixWay(w);
    98         for (Relation r : ds.relations) fixRelation(r);
     153        for (Way w : myDataSet.ways) {
     154            fixWay(w);
     155        }
     156        for (Relation r : myDataSet.relations) {
     157            fixRelation(r);
     158        }
    99159        for (OsmPrimitive osm : conflicts.values())
    100             if (osm instanceof Way)
     160            if (osm instanceof Way) {
    101161                fixWay((Way)osm);
    102             else if (osm instanceof Relation)
     162            } else if (osm instanceof Relation) {
    103163                fixRelation((Relation) osm);
     164            }
    104165    }
    105166
     
    110171            Node otherN = (Node) merged.get(n);
    111172            newNodes.add(otherN == null ? n : otherN);
    112             if (otherN != null)
     173            if (otherN != null) {
    113174                replacedSomething = true;
     175            }
    114176        }
    115177        if (replacedSomething) {
     
    139201    }
    140202
    141     private static <P extends OsmPrimitive> boolean match(P p1, P p2) {
    142         if ((p1.id == 0 || p2.id == 0) && !p1.incomplete && !p2.incomplete) {
    143             return realMatch(p1, p2);
    144         }
    145         return p1.id == p2.id;
    146     }
    147 
    148     /** @return true if the prims have pretty much the same data, i.e. the
    149      * same position, the same members, ...
    150      */
    151     // Java cannot dispatch on generics...
    152     private static boolean realMatch(OsmPrimitive p1, OsmPrimitive p2) {
    153         if (p1 instanceof Node && p2 instanceof Node) {
    154             return realMatch((Node) p1, (Node) p2);
    155         } else if (p1 instanceof Way && p2 instanceof Way) {
    156             return realMatch((Way) p1, (Way) p2);
    157         } else if (p1 instanceof Relation && p2 instanceof Relation) {
    158             return realMatch((Relation) p1, (Relation) p2);
    159         } else {
    160             throw new RuntimeException("arguments have unknown type");
    161         }
    162     }
    163 
    164     private static boolean realMatch(Node n1, Node n2) {
    165         return n1.getCoor().equalsEpsilon(n2.getCoor());
    166     }
    167 
    168     private static boolean realMatch(Way w1, Way w2) {
    169         if (w1.nodes.size() != w2.nodes.size())
    170             return false;
    171         Iterator<Node> it = w1.nodes.iterator();
    172         for (Node n : w2.nodes)
    173             if (!match(n, it.next()))
    174                 return false;
    175         return true;
    176     }
    177 
    178     private static boolean realMatch(Relation w1, Relation w2) {
    179         // FIXME this is not perfect yet...
    180         if (w1.members.size() != w2.members.size())
    181             return false;
    182         for (RelationMember em : w1.members) {
    183             if (!w2.members.contains(em)) {
    184                 return false;
    185             }
    186         }
    187         return true;
    188     }
    189 
    190     /**
    191      * Merge the common parts of an osm primitive.
    192      * @param my The object, the information gets merged into
    193      * @param other The object, the information gets merged from
    194      */
    195     private void mergeCommon(OsmPrimitive my, OsmPrimitive other) {
    196         if (other.deleted)
    197             my.delete(true);
    198         if (my.id == 0 || !my.modified || other.modified) {
    199             if (my.id == 0 && other.id != 0) {
    200                 my.id = other.id;
    201                 my.modified = other.modified; // match a new node
    202                 my.version = other.version;
    203             } else if (my.id != 0 && other.id != 0 && other.modified)
    204                 my.modified = true;
    205         }
    206         if (other.keys == null)
    207             return;
    208         if (my.keySet().containsAll(other.keys.keySet()))
    209             return;
    210         if (my.keys == null)
    211             my.keys = other.keys;
    212         else
    213             my.keys.putAll(other.keys);
    214 
    215         my.modified = true;
    216     }
    217 
    218203    /**
    219204     * Tries to merge a primitive <code>other</code> into an existing primitive with the same id.
    220205     *
    221206     * @param myPrimitives the complete set of my primitives (potential merge targets)
    222      * @param myPrimitivesWithID the map of primitives (potential merge targets) with an id <> 0, for faster lookup
     207     * @param myPrimitivesWithDefinedIds the map of primitives (potential merge targets) with an id <> 0, for faster lookup
    223208     *    by id. Key is the id, value the primitive with the given value. myPrimitives.valueSet() is a
    224209     *    subset of primitives.
    225      * @param other  the other primitive which is to be merged with a primitive in primitives if possible
     210     * @param other  the other primitive which is to be merged onto a primitive in my primitives
    226211     * @return true, if this method was able to merge <code>other</code> with an existing node; false, otherwise
    227212     */
    228213    private <P extends OsmPrimitive> boolean mergeById(
    229             Collection<P> myPrimitives, HashMap<Long, P> myPrimitivesWithID, P other) {
     214            Collection<P> myPrimitives, HashMap<Long, P> myPrimitivesWithDefinedIds, P other) {
    230215
    231216        // merge other into an existing primitive with the same id, if possible
    232217        //
    233         if (myPrimitivesWithID.containsKey(other.id)) {
    234             P my = myPrimitivesWithID.get(other.id);
    235             if (my.realEqual(other, true /* compare semantic fields only */)) {
    236                 // make sure the merge target becomes the higher version number
    237                 // and the later timestamp
    238                 //
    239                 my.version = Math.max(other.version, my.version);
    240                 if (other.getTimestamp().after(my.getTimestamp())) {
    241                     my.setTimestamp(other.getTimestamp());
     218        if (myPrimitivesWithDefinedIds.containsKey(other.id)) {
     219            P my = myPrimitivesWithDefinedIds.get(other.id);
     220            if (my.version <= other.version) {
     221                if (! my.visible && other.visible) {
     222                    // should not happen
     223                    //
     224                    logger.warning(tr("My primitive with id {0} and version {1} is visible although "
     225                            + "their primitive with lower version {2} is not visible. "
     226                            + "Can't deal with this inconsistency. Keeping my primitive. ",
     227                            Long.toString(my.id),Long.toString(my.version), Long.toString(other.version)
     228                    ));
     229                    merged.put(other, my);
     230                } else if (my.visible && ! other.visible) {
     231                    // this is always a conflict because the user has to decide whether
     232                    // he wants to create a clone of its local primitive or whether he
     233                    // wants to purge my from the local dataset. He can't keep it unchanged
     234                    // because it was deleted on the server.
     235                    //
     236                    conflicts.put(my,other);
     237                } else if (! my.modified && other.modified) {
     238                    // my not modified. We can assume that other is the most recent version.
     239                    // clone it onto my. But check first, whether other is deleted. if so,
     240                    // make sure that my is not references anymore in myDataSet.
     241                    //
     242                    if (other.deleted) {
     243                        myDataSet.unlinkReferencesToPrimitive(my);
     244                    }
     245                    my.cloneFrom(other);
     246                    merged.put(other, my);
     247                } else if (! my.modified && !other.modified) {
     248                    // nothing to merge
     249                    //
     250                    merged.put(other,my);
     251                } else if (my.deleted != other.deleted) {
     252                    // if we get here my is modified. Differences in deleted state
     253                    // have to be resolved manually
     254                    //
     255                    conflicts.put(my,other);
     256                } else if (! my.hasEqualSemanticAttributes(other)) {
     257                    // my is modified and is not semantically equal with other. Can't automatically
     258                    // resolve the differences
     259                    // =>  create a conflict
     260                    conflicts.put(my,other);
     261                } else {
     262                    // clone from other, but keep the modified flag. Clone will mainly copy
     263                    // technical attributes like timestamp or user information. Semantic
     264                    // attributes should already be equal if we get here.
     265                    //
     266                    my.cloneFrom(other);
     267                    my.modified = true;
     268                    merged.put(other, my);
    242269                }
     270            } else {
     271                // my.version > other.version => keep my version
    243272                merged.put(other, my);
    244                 return true;
    245             }
    246         }
    247 
    248         // try to merge into one of the existing primitives
    249         //
    250         for (P my : myPrimitives) {
    251             if (my.realEqual(other, false /* compare all fields */)) {
    252                 merged.put(other, my);
    253                 return true; // no merge needed.
    254             }
    255             if (my.realEqual(other, true)) {
    256                 // they differ in modified/version combination only. Auto-resolve it.
    257                 merged.put(other, my);
    258                 if (my.version < other.version) {
    259                     my.version = other.version;
    260                     my.modified = other.modified;
    261                     my.setTimestamp(other.getTimestamp());
    262                 }
    263                 return true; // merge done.
    264             }
    265             if (my.id == other.id && my.id != 0) {
    266                 if (my.incomplete || other.incomplete) {
    267                     if (my.incomplete) {
    268                         my.cloneFrom(other);
    269                     }
    270                 } else if (my.modified && other.modified) {
    271                     conflicts.put(my, other);
    272                 } else if (!my.modified && !other.modified) {
    273                     if (my.version < other.version) {
    274                         my.cloneFrom(other);
    275                     }
    276                 } else if (other.modified) {
    277                     if (my.version > other.version) {
    278                         conflicts.put(my, other);
    279                     } else {
    280                         my.cloneFrom(other);
    281                     }
    282                 } else if (my.modified) {
    283                     if (my.version < other.version) {
    284                         conflicts.put(my, other);
    285                     }
    286                 }
    287                 merged.put(other, my);
    288                 return true;
    289             }
     273            }
     274            return true;
    290275        }
    291276        return false;
    292277    }
     278
     279
     280    /**
     281     * Runs the merge operation. Successfully merged {@see OsmPrimitive}s are in
     282     * {@see #getMyDataSet()}.
     283     *
     284     * See {@see #getConflicts()} for a map of conflicts after the merge operation.
     285     */
     286    public void merge() {
     287        for (final OsmPrimitive primitive : theirDataSet.allPrimitives()) {
     288            primitive.visit(this);
     289        }
     290        fixReferences();
     291    }
     292
     293    /**
     294     * replies my dataset
     295     *
     296     * @return
     297     */
     298    public DataSet getMyDataSet() {
     299        return myDataSet;
     300    }
     301
     302
     303    /**
     304     * replies the map of conflicts
     305     *
     306     * @return the map of conflicts
     307     */
     308    public Map<OsmPrimitive, OsmPrimitive> getConflicts() {
     309        return conflicts;
     310    }
    293311}
Note: See TracChangeset for help on using the changeset viewer.