Ignore:
Timestamp:
2009-09-20T11:46:08+02:00 (15 years ago)
Author:
Gubaer
Message:

fixed #3304: Upload failed - Placeholder Relation not found
fixed #2353: Wrong relation upload order results in 512 Precondition Failed
Warning: this is going to break plugins which register Upload Hooks. These plugins will be fixed shortly.

File:
1 edited

Legend:

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

    r2039 r2168  
    22package org.openstreetmap.josm.data;
    33
     4import java.util.ArrayList;
     5import java.util.Collection;
     6import java.util.Collections;
     7import java.util.Comparator;
     8import java.util.HashMap;
     9import java.util.HashSet;
    410import java.util.LinkedList;
    511import java.util.List;
     12import java.util.Set;
     13import java.util.Stack;
     14import java.util.logging.Logger;
    615
    716import org.openstreetmap.josm.data.osm.DataSet;
     17import org.openstreetmap.josm.data.osm.Node;
    818import org.openstreetmap.josm.data.osm.OsmPrimitive;
     19import org.openstreetmap.josm.data.osm.Relation;
     20import org.openstreetmap.josm.data.osm.RelationMember;
     21import org.openstreetmap.josm.data.osm.Way;
     22
     23import org.openstreetmap.josm.actions.upload.CyclicUploadDependencyException;
    924
    1025/**
     
    1328 * The collection is derived from the modified primitives of an {@see DataSet}.
    1429 *
    15  * FIXME: use to optimize the upload order before uploading, see various tickets in trac
    1630 *
    1731 */
     
    113127        return ret;
    114128    }
     129
     130    /**
     131     * Adjusts the upload order for new relations. Child relations are uploaded first,
     132     * parent relations second.
     133     *
     134     * This method detects cyclic dependencies in new relation. Relations with cyclic
     135     * dependencies can't be uploaded.
     136     *
     137     * @throws CyclicUploadDependencyException thrown, if a cyclic dependency is detected
     138     */
     139    public void adjustRelationUploadOrder() throws CyclicUploadDependencyException{
     140        LinkedList<OsmPrimitive> newToAdd = new LinkedList<OsmPrimitive>();
     141        newToAdd.addAll(OsmPrimitive.getFilteredList(toAdd, Node.class));
     142        newToAdd.addAll(OsmPrimitive.getFilteredList(toAdd, Way.class));
     143
     144        List<Relation> relationsToAdd = OsmPrimitive.getFilteredList(toAdd, Relation.class);
     145        List<Relation> noProblemRelations = filterRelationsNotReferringToNewRelations(relationsToAdd);
     146        newToAdd.addAll(noProblemRelations);
     147        relationsToAdd.removeAll(noProblemRelations);
     148
     149        RelationUploadDependencyGraph graph = new RelationUploadDependencyGraph(relationsToAdd);
     150        newToAdd.addAll(graph.computeUploadOrder());
     151        toAdd = newToAdd;
     152    }
     153
     154    /**
     155     * Replies the subset of relations in <code>relations</code> which are not referring to any
     156     * new relation
     157     *
     158     * @param relations a list of relations
     159     * @return the subset of relations in <code>relations</code> which are not referring to any
     160     * new relation
     161     */
     162    protected List<Relation> filterRelationsNotReferringToNewRelations(Collection<Relation> relations) {
     163        List<Relation> ret = new LinkedList<Relation>();
     164        for (Relation relation: relations) {
     165            boolean refersToNewRelation = false;
     166            for (RelationMember m : relation.getMembers()) {
     167                if (m.isRelation() && m.getMember().getId() <= 0) {
     168                    refersToNewRelation = true;
     169                    break;
     170                }
     171            }
     172            if (!refersToNewRelation) {
     173                ret.add(relation);
     174            }
     175        }
     176        return ret;
     177    }
     178
     179    /**
     180     * Utility class to sort a collection of of new relations with their dependencies
     181     * topologically.
     182     *
     183     */
     184    private class RelationUploadDependencyGraph {
     185        private final Logger logger = Logger.getLogger(RelationUploadDependencyGraph.class.getName());
     186        private HashMap<Relation, Set<Relation>> children;
     187        private Collection<Relation> relations;
     188        private Set<Relation> visited;
     189        private List<Relation> uploadOrder;
     190
     191        public RelationUploadDependencyGraph() {
     192            this.children = new HashMap<Relation, Set<Relation>>();
     193            this.visited = new HashSet<Relation>();
     194        }
     195
     196        public RelationUploadDependencyGraph(Collection<Relation> relations) {
     197            this();
     198            build(relations);
     199        }
     200
     201        public void build(Collection<Relation> relations) {
     202            this.relations = new HashSet<Relation>();
     203            for(Relation relation: relations) {
     204                if (relation.getId() > 0 ) {
     205                    continue;
     206                }
     207                this.relations.add(relation);
     208                for (RelationMember m: relation.getMembers()) {
     209                    if (m.isRelation() && m.getMember().getId() == 0) {
     210                        addDependency(relation, (Relation)m.getMember());
     211                    }
     212                }
     213            }
     214        }
     215
     216        public Set<Relation> getChildren(Relation relation) {
     217            Set<Relation> p = children.get(relation);
     218            if (p == null) {
     219                p = new HashSet<Relation>();
     220                children.put(relation, p);
     221            }
     222            return p;
     223        }
     224
     225        public void addDependency(Relation relation, Relation child) {
     226            getChildren(relation).add(child);
     227        }
     228
     229        protected void visit(Stack<Relation> path, Relation current) throws CyclicUploadDependencyException{
     230            if (path.contains(current)) {
     231                path.push(current);
     232                throw new CyclicUploadDependencyException(path);
     233            }
     234            if (!visited.contains(current)) {
     235                path.push(current);
     236                visited.add(current);
     237                for (Relation dependent : getChildren(current)) {
     238                    visit(path,dependent);
     239                }
     240                uploadOrder.add(current);
     241                path.pop();
     242            }
     243        }
     244
     245        public List<Relation> computeUploadOrder() throws CyclicUploadDependencyException {
     246            visited = new HashSet<Relation>();
     247            uploadOrder = new LinkedList<Relation>();
     248            Stack<Relation> path = new Stack<Relation>();
     249            for (Relation relation: relations) {
     250                visit(path, relation);
     251            }
     252            ArrayList<Relation> ret = new ArrayList<Relation>(relations);
     253            Collections.sort(
     254                    ret,
     255                    new Comparator<Relation>() {
     256                        public int compare(Relation o1, Relation o2) {
     257                            return new Integer(uploadOrder.indexOf(o1)).compareTo(uploadOrder.indexOf(o2));
     258                        }
     259                    }
     260            );
     261            return ret;
     262        }
     263    }
    115264}
Note: See TracChangeset for help on using the changeset viewer.