Ignore:
Timestamp:
2009-09-27T16:29:21+02:00 (10 years ago)
Author:
Gubaer
Message:

fixed #3249: Resolve conflicts between invisible and deleted primitives automatically

Location:
trunk/src/org/openstreetmap/josm/command
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/command/ConflictResolveCommand.java

    r2163 r2198  
    1212
    1313/**
    14  * This is the common basse class for {@see Command}s which manipulate {@see Conflict}s in
     14 * This is the common base class for {@see Command}s which manipulate {@see Conflict}s in
    1515 * addition to {@see OsmPrimitive}s.
    1616 *
    1717 * A ConflictResolverCommand can remember a collection of conflicts it resolves. Upon undoing
    18  * it reconstitutes these conflicts.
     18 * it reconstitutes them.
    1919 *
    2020 */
     
    3030    }
    3131
     32    public ConflictResolveCommand(OsmDataLayer layer) {
     33        super(layer);
     34        resolvedConflicts = new ConflictCollection();
     35    }
     36
    3237    /**
    3338     * remembers a conflict in the internal list of remembered conflicts
     
    3540     * @param c the remembered conflict
    3641     */
    37     protected void rememberConflict(Conflict c) {
     42    protected void rememberConflict(Conflict<?> c) {
    3843        if (! resolvedConflicts.hasConflictForMy(c.getMy())) {
    3944            resolvedConflicts.add(c);
     
    4853    protected void reconstituteConflicts() {
    4954        OsmDataLayer editLayer = getLayer();
    50         for(Conflict c : resolvedConflicts) {
     55        for(Conflict<?> c : resolvedConflicts) {
    5156            if (!editLayer.getConflicts().hasConflictForMy(c.getMy())) {
    5257                editLayer.getConflicts().add(c);
     
    6065
    6166        if (! Main.map.mapView.hasLayer(getLayer())) {
    62             logger.warning(tr("Can't undo command ''{0}'' because layer ''{1}'' is not present any more",
     67            logger.warning(tr("Can''t undo command ''{0}'' because layer ''{1}'' is not present any more",
    6368                    this.toString(),
    6469                    getLayer().toString()
     
    7075        reconstituteConflicts();
    7176    }
    72 
    73 
    74 
    7577}
  • trunk/src/org/openstreetmap/josm/command/PurgePrimitivesCommand.java

    r2163 r2198  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.util.ArrayList;
    76import java.util.Collection;
     7import java.util.Collections;
     8import java.util.HashSet;
    89import java.util.List;
     10import java.util.Set;
    911import java.util.logging.Logger;
    1012
     
    1517import org.openstreetmap.josm.Main;
    1618import org.openstreetmap.josm.data.conflict.ConflictCollection;
    17 import org.openstreetmap.josm.data.osm.DataSet;
     19import org.openstreetmap.josm.data.osm.BackreferencedDataSet;
    1820import org.openstreetmap.josm.data.osm.Node;
    1921import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2022import org.openstreetmap.josm.data.osm.Relation;
    21 import org.openstreetmap.josm.data.osm.RelationMember;
    2223import org.openstreetmap.josm.data.osm.Way;
     24import org.openstreetmap.josm.gui.DefaultNameFormatter;
     25import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    2326import org.openstreetmap.josm.tools.ImageProvider;
    2427
     
    3841    static private final Logger logger = Logger.getLogger(PurgePrimitivesCommand.class.getName());
    3942
    40     /**
    41      * Represents a pair of {@see OsmPrimitive} where the parent referrs to
    42      * the child, either because a {@see Way} includes a {@see Node} or
    43      * because a {@see Relation} refers to any other {@see OsmPrimitive}
    44      * via a relation member.
    45      *
    46      */
    47     static class OsmParentChildPair {
    48         private OsmPrimitive parent;
    49         private OsmPrimitive child;
    50 
    51 
    52         public OsmParentChildPair(OsmPrimitive parent, OsmPrimitive child) {
    53             this.parent = parent;
    54             this.child = child;
    55         }
    56 
    57         public OsmPrimitive getParent() {
    58             return parent;
    59         }
    60 
    61         public OsmPrimitive getChild() {
    62             return child;
    63         }
    64 
    65         @Override
    66         public int hashCode() {
    67             final int prime = 31;
    68             int result = 1;
    69             result = prime * result + ((child == null) ? 0 : child.hashCode());
    70             result = prime * result + ((parent == null) ? 0 : parent.hashCode());
    71             return result;
    72         }
    73 
    74         @Override
    75         public boolean equals(Object obj) {
    76             if (this == obj)
    77                 return true;
    78             if (obj == null)
    79                 return false;
    80             if (getClass() != obj.getClass())
    81                 return false;
    82             OsmParentChildPair other = (OsmParentChildPair) obj;
    83             if (child == null) {
    84                 if (other.child != null)
    85                     return false;
    86             } else if (child != other.child)
    87                 return false;
    88             if (parent == null) {
    89                 if (other.parent != null)
    90                     return false;
    91             } else if (parent != other.parent)
    92                 return false;
    93             return true;
    94         }
    95     }
    96 
    97     /**
    98      * creates a list of all {@see OsmParentChildPair}s for a given {@see OsmPrimitive}
    99      * as child and given set of parents. We don't use {@see CollectBackReferencesVisitor}
    100      * because it seems quite inefficient.
    101      *
    102      * @param parents  the set of potential parents
    103      * @param child the child
    104      * @return the list of {@see OsmParentChildPair}
    105      */
    106     protected List<OsmParentChildPair> getParentChildPairs(List<OsmPrimitive> parents, OsmPrimitive child) {
    107         ArrayList<OsmParentChildPair> pairs = new ArrayList<OsmParentChildPair>();
    108         for (OsmPrimitive parent : parents) {
    109             if (parent instanceof Way) {
    110                 Way w = (Way)parent;
    111                 for (OsmPrimitive node : w.getNodes()) {
    112                     if (node == child) {
    113                         OsmParentChildPair pair = new OsmParentChildPair(parent, node);
    114                         if (! pairs.contains(pair)) {
    115                             pairs.add(pair);
    116                         }
    117                     }
    118                 }
    119             } else if (parent instanceof Relation) {
    120                 Relation r = (Relation)parent;
    121                 for (RelationMember member : r.getMembers()) {
    122                     if (member.getMember() == child) {
    123                         OsmParentChildPair pair = new OsmParentChildPair(parent, member.getMember());
    124                         if (! pairs.contains(pair)) {
    125                             pairs.add(pair);
    126                         }
    127                     }
    128                 }
    129             }
    130         }
    131         return pairs;
    132     }
    133 
    134     /** the primitive to purge */
    135     private OsmPrimitive primitive;
     43    /** the primitives to purge */
     44    private Collection<OsmPrimitive> toPurge;
    13645
    13746    /** the set of primitives to purge as consequence of purging
    13847     * {@see #primitive}, including {@see #primitive}
    13948     */
    140     private ArrayList<OsmPrimitive> purgedPrimitives;
    141 
    142     /** the set of {@see OsmParentChildPair}. We keep a reference
    143      * to this set for the {@see #fillModifiedData(Collection, Collection, Collection)} operation
    144      */
    145     private ArrayList<OsmParentChildPair> pairs;
    146 
     49    private Set<OsmPrimitive> purgedPrimitives;
     50
     51    private Set<OsmPrimitive> origVersionsOfTouchedPrimitives;
     52
     53    /**
     54     * the data structure with child->parent references
     55     */
     56    private BackreferencedDataSet backreferenceDataSet;
     57
     58    protected void init(Collection<OsmPrimitive> toPurge) {
     59        this.toPurge = toPurge;
     60        this.purgedPrimitives = new HashSet<OsmPrimitive>();
     61        this.origVersionsOfTouchedPrimitives = new HashSet<OsmPrimitive>();
     62    }
    14763
    14864    /**
    14965     * constructor
    150      * @param node  the node to undelete
     66     * @param primitive the primitive to purge
     67     *
    15168     */
    15269    public PurgePrimitivesCommand(OsmPrimitive primitive) {
    153         this.primitive = primitive;
    154         purgedPrimitives = new ArrayList<OsmPrimitive>();
    155         pairs = new ArrayList<OsmParentChildPair>();
    156     }
    157 
    158     @Override
    159     public MutableTreeNode description() {
     70        init(Collections.singleton(primitive));
     71    }
     72
     73    /**
     74     * constructor
     75     * @param layer the OSM data layer
     76     * @param primitive the primitive to purge
     77     *
     78     */
     79    public PurgePrimitivesCommand(OsmDataLayer layer, OsmPrimitive primitive) {
     80        super(layer);
     81        init(Collections.singleton(primitive));
     82    }
     83
     84    /**
     85     * constructor
     86     * @param layer the OSM data layer
     87     * @param primitives the primitives to purge
     88     *
     89     */
     90    public PurgePrimitivesCommand(OsmDataLayer layer, Collection<OsmPrimitive> primitives) {
     91        super(layer);
     92        init(primitives);
     93    }
     94
     95    /**
     96     * Replies a collection with the purged primitives
     97     *
     98     * @return a collection with the purged primitives
     99     */
     100    public Collection<OsmPrimitive> getPurgedPrimitives() {
     101        return purgedPrimitives;
     102    }
     103
     104    protected MutableTreeNode getDescription(OsmPrimitive primitive) {
    160105        return new DefaultMutableTreeNode(
    161106                new JLabel(
    162                         tr("Purging 1 primitive"),
     107                        tr("Purged object ''{0}''", primitive.getDisplayName(DefaultNameFormatter.getInstance())),
    163108                        ImageProvider.get("data", "object"),
    164109                        JLabel.HORIZONTAL
     
    167112    }
    168113
    169     /**
    170      * Purges an {@see OsmPrimitive} <code>toPurge</code> from a {@see DataSet}.
     114    protected MutableTreeNode getDescription(Collection<OsmPrimitive> primitives) {
     115
     116        DefaultMutableTreeNode root = new DefaultMutableTreeNode(
     117                tr("Purged {0} objects", primitives.size())
     118        );
     119        for (OsmPrimitive p : primitives) {
     120            root.add(getDescription(p));
     121        }
     122        return root;
     123    }
     124
     125    @Override
     126    public MutableTreeNode description() {
     127        if (purgedPrimitives.size() == 1)
     128            return getDescription(purgedPrimitives.iterator().next());
     129        else
     130            return getDescription(purgedPrimitives);
     131    }
     132
     133    /**
     134     * Purges an {@see OsmPrimitive} <code>child</code> from a {@see DataSet}.
    171135     *
    172      * @param toPurge the primitive to purge
    173      * @param ds  the dataset to purge from
     136     * @param child the primitive to purge
    174137     * @param hive the hive of {@see OsmPrimitive}s we remember other {@see OsmPrimitive}
    175      * we have to purge because we purge <code>toPurge</code>.
     138     * we have to purge because we purge <code>child</code>.
    176139     *
    177140     */
    178     protected void purge(OsmPrimitive toPurge, DataSet ds, ArrayList<OsmPrimitive> hive) {
    179         ArrayList<OsmPrimitive> parents = new ArrayList<OsmPrimitive>();
    180         parents.addAll(getLayer().data.ways);
    181         parents.addAll(getLayer().data.relations);
    182         List<OsmParentChildPair> pairs = getParentChildPairs(parents, primitive);
    183         hive.remove(toPurge);
    184         for (OsmParentChildPair pair: pairs) {
    185             if (pair.getParent() instanceof Way) {
    186                 Way w = (Way)pair.getParent();
    187                 System.out.println(tr("removing reference from way {0}",w.getId()));
     141    protected void removeReferecesToPrimitive(OsmPrimitive child, Set<OsmPrimitive> hive) {
     142        hive.remove(child);
     143        for (OsmPrimitive parent: this.backreferenceDataSet.getParents(child)) {
     144            if (toPurge.contains(parent))
     145                // parent itself is to be purged. This method is going to be
     146                // invoked for parent later
     147                return;
     148            if (parent instanceof Way) {
     149                Way w = (Way)parent;
     150                if (!origVersionsOfTouchedPrimitives.contains(w)) {
     151                    origVersionsOfTouchedPrimitives.add(w);
     152                }
    188153                List<Node> wayNodes = w.getNodes();
    189                 wayNodes.remove(primitive);
     154                wayNodes.remove(child);
    190155                w.setNodes(wayNodes);
    191                 // if a way ends up with less than two node we
     156                // if a way ends up with less than two nodes we
    192157                // remember it on the "hive"
    193158                //
     
    195160                    System.out.println(tr("Warning: Purging way {0} because number of nodes dropped below 2. Current is {1}",
    196161                            w.getId(),w.getNodesCount()));
    197                     if (!hive.contains(w)) {
    198                         hive.add(w);
    199                     }
     162                    hive.add(w);
    200163                }
    201             } else if (pair.getParent() instanceof Relation) {
    202                 Relation r = (Relation)pair.getParent();
    203                 System.out.println(tr("removing reference from relation {0}",r.getId()));
    204                 r.removeMembersFor(primitive);
     164            } else if (parent instanceof Relation) {
     165                Relation r = (Relation)parent;
     166                if (!origVersionsOfTouchedPrimitives.contains(r)) {
     167                    origVersionsOfTouchedPrimitives.add(r);
     168                }
     169                System.out.println(tr("Removing reference from relation {0}",r.getId()));
     170                r.removeMembersFor(child);
     171            } else {
     172                // should not happen. parent can't be a node
    205173            }
    206174        }
     
    209177    @Override
    210178    public boolean executeCommand() {
    211         ArrayList<OsmPrimitive> hive = new ArrayList<OsmPrimitive>();
     179        if (backreferenceDataSet == null) {
     180            backreferenceDataSet = new BackreferencedDataSet(getLayer().data);
     181            backreferenceDataSet.build();
     182        }
     183        HashSet<OsmPrimitive> hive = new HashSet<OsmPrimitive>();
    212184
    213185        // iteratively purge the primitive and all primitives
    214         // which violate invariants after they loose a reference to
     186        // which violate invariants after they lose a reference to
    215187        // the primitive (i.e. ways which end up with less than two
    216188        // nodes)
    217         hive.add(primitive);
     189        hive.addAll(toPurge);
    218190        while(! hive.isEmpty()) {
    219             OsmPrimitive toPurge = hive.get(0);
    220             purge(toPurge, getLayer().data, hive);
    221             if (toPurge instanceof Node) {
    222                 getLayer().data.nodes.remove(toPurge);
    223             } else if (primitive instanceof Way) {
    224                 getLayer().data.ways.remove(toPurge);
    225             } else if (primitive instanceof Relation) {
    226                 getLayer().data.relations.remove(toPurge);
     191            OsmPrimitive p = hive.iterator().next();
     192            removeReferecesToPrimitive(p, hive);
     193            getLayer().data.removePrimitive(p);
     194            purgedPrimitives.add(p);
     195            ConflictCollection conflicts = getLayer().getConflicts();
     196            if (conflicts.hasConflictForMy(p)) {
     197                rememberConflict(conflicts.getConflictForMy(p));
     198                conflicts.remove(p);
    227199            }
    228             purgedPrimitives.add(toPurge);
    229             ConflictCollection conflicts = getLayer().getConflicts();
    230             if (conflicts.hasConflictForMy(toPurge)) {
    231                 rememberConflict(conflicts.getConflictForMy(toPurge));
    232                 conflicts.remove(toPurge);
    233             }
    234         }
     200        }
     201        // we don't need this any more
     202        backreferenceDataSet = null;
    235203        return super.executeCommand();
    236204    }
     
    239207    public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted,
    240208            Collection<OsmPrimitive> added) {
    241         for (OsmParentChildPair pair : pairs) {
    242             modified.add(pair.getParent());
    243         }
    244         // we don't need pairs anymore
    245         pairs = null;
     209        modified.addAll(origVersionsOfTouchedPrimitives);
    246210    }
    247211
     
    249213    public void undoCommand() {
    250214        if (! Main.map.mapView.hasLayer(getLayer())) {
    251             logger.warning(tr("Can't undo command ''{0}'' because layer ''{1}'' is not present any more",
     215            logger.warning(tr("Can''t undo command ''{0}'' because layer ''{1}'' is not present any more",
    252216                    this.toString(),
    253217                    getLayer().toString()
     
    263227        }
    264228        reconstituteConflicts();
    265         // will restore the former references to the purged nodes
    266         //
     229
     230        // will restore the primitives referring to one
     231        // of the purged primitives
    267232        super.undoCommand();
    268233    }
     234
     235    /**
     236     * Use to inject a backreference data set used when the command
     237     * is executed.
     238     *
     239     * @param ds the backreference data set
     240     */
     241    public void setBackreferenceDataSet(BackreferencedDataSet ds) {
     242        this.backreferenceDataSet = ds;
     243    }
    269244}
Note: See TracChangeset for help on using the changeset viewer.