Ticket #22726: 22726.patch

File 22726.patch, 8.9 KB (added by taylor.smock, 2 years ago)
  • src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java

    Subject: [PATCH] Fix #22726: Child objects do not need to get full history all the time
    ---
    IDEA additional info:
    Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
    <+>UTF-8
    diff --git a/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java b/src/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmChangeTask.java
    a b  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.io.IOException;
    67import java.time.Instant;
    78import java.util.Arrays;
    89import java.util.HashMap;
     
    1213import java.util.Optional;
    1314import java.util.concurrent.Future;
    1415import java.util.concurrent.RejectedExecutionException;
     16import java.util.function.Function;
    1517import java.util.regex.Matcher;
     18import java.util.stream.Collectors;
    1619
    1720import org.openstreetmap.josm.data.Bounds;
    1821import org.openstreetmap.josm.data.osm.AbstractPrimitive;
     
    2326import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    2427import org.openstreetmap.josm.data.osm.PrimitiveData;
    2528import org.openstreetmap.josm.data.osm.PrimitiveId;
     29import org.openstreetmap.josm.data.osm.Relation;
    2630import org.openstreetmap.josm.data.osm.RelationData;
     31import org.openstreetmap.josm.data.osm.Way;
    2732import org.openstreetmap.josm.data.osm.WayData;
    2833import org.openstreetmap.josm.data.osm.history.History;
    2934import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
     
    3641import org.openstreetmap.josm.gui.history.HistoryLoadTask;
    3742import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    3843import org.openstreetmap.josm.io.Compression;
     44import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
    3945import org.openstreetmap.josm.io.OsmApi;
    4046import org.openstreetmap.josm.io.OsmServerLocationReader;
    4147import org.openstreetmap.josm.io.OsmServerReader;
    4248import org.openstreetmap.josm.io.OsmTransferException;
    4349import org.openstreetmap.josm.io.UrlPatterns.OsmChangeUrlPattern;
    4450import org.openstreetmap.josm.tools.Logging;
     51import org.xml.sax.SAXException;
    4552
    4653/**
    4754 * Task allowing to download OsmChange data (http://wiki.openstreetmap.org/wiki/OsmChange).
     
    103110                    compression);
    104111        }
    105112
     113        @Override
     114        public void realRun() throws IOException, SAXException, OsmTransferException {
     115            super.realRun();
     116            final Map<OsmPrimitive, Instant> toLoadNext = new HashMap<>();
     117            final Map<OsmPrimitive, Instant> toLoad = getToLoad(dataSet);
     118            while (!toLoad.isEmpty()) {
     119                loadLastVersions(toLoad, toLoadNext);
     120                toLoad.putAll(toLoadNext);
     121                toLoadNext.clear();
     122            }
     123        }
     124
     125        /**
     126         * This gets the last versions of references primitives. This may enough for many of the primitives.
     127         * @param toLoad The primitives to load
     128         * @param toLoadNext The primitives to load next (filled by this method)
     129         */
     130        private void loadLastVersions(Map<OsmPrimitive, Instant> toLoad, Map<OsmPrimitive, Instant> toLoadNext) throws OsmTransferException {
     131            final Map<OsmPrimitiveType, Map<OsmPrimitive, Instant>> typeMap = toLoad.entrySet().stream()
     132                    .collect(Collectors.groupingBy(entry -> entry.getKey().getType(), Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)));
     133            final Map<PrimitiveId, OsmPrimitive> idMap = toLoad.keySet().stream()
     134                    .collect(Collectors.toMap(OsmPrimitive::getPrimitiveId, Function.identity()));
     135            for (OsmPrimitiveType type : Arrays.asList(OsmPrimitiveType.NODE, OsmPrimitiveType.WAY, OsmPrimitiveType.RELATION)) {
     136                if (!typeMap.containsKey(type)) {
     137                    continue;
     138                }
     139                final MultiFetchServerObjectReader reader = MultiFetchServerObjectReader.create();
     140                typeMap.get(type).forEach((primitive, instant) -> reader.append(primitive));
     141                final DataSet ds = reader.parseOsm(this.progressMonitor.createSubTaskMonitor(1, false));
     142                switch (type) {
     143                    case NODE:
     144                        for (Node node : ds.getNodes()) {
     145                            Node original = (Node) idMap.get(node.getPrimitiveId());
     146                            if (original != null && toLoad.get(original).isAfter(node.getInstant())) {
     147                                original.load(node.save());
     148                            }
     149                            toLoad.remove(original);
     150                        }
     151                        break;
     152                    case WAY:
     153                        for (Way way : ds.getWays()) {
     154                            Way original = (Way) idMap.get(way.getPrimitiveId());
     155                            if (original != null && toLoad.get(original).isAfter(way.getInstant())) {
     156                                Instant date = toLoad.get(original);
     157                                original.load(way.save());
     158                                for (Long nodeId : way.getNodeIds()) {
     159                                    if (way.getDataSet().getPrimitiveById(nodeId, OsmPrimitiveType.NODE) == null) {
     160                                        Node n = new Node(nodeId);
     161                                        way.getDataSet().addPrimitive(n);
     162                                        toLoadNext.put(n, date);
     163                                    }
     164                                }
     165                            }
     166                            toLoad.remove(original);
     167                        }
     168                        break;
     169                    case RELATION:
     170                        for (Relation relation : ds.getRelations()) {
     171                            Relation original = (Relation) idMap.get(relation.getPrimitiveId());
     172                            if (original != null && toLoad.get(original).isAfter(relation.getInstant())) {
     173                                original.load(relation.save());
     174                            }
     175                            toLoad.remove(relation);
     176                        }
     177                        break;
     178                    default:
     179                        throw new IllegalStateException("Only Node, Ways, and Relations should be returned by the API");
     180                }
     181            }
     182        }
     183
    106184        @Override
    107185        protected void finish() {
    108186            super.finish();
     
    111189            try {
    112190                // A changeset does not contain all referred primitives, this is the map of incomplete ones
    113191                // For each incomplete primitive, we'll have to get its state at date it was referred
    114                 Map<OsmPrimitive, Instant> toLoad = new HashMap<>();
    115                 for (OsmPrimitive p : downloadedData.allNonDeletedPrimitives()) {
    116                     if (p.isIncomplete()) {
    117                         Instant timestamp = p.getReferrers().stream()
    118                                 .filter(ref -> !ref.isTimestampEmpty())
    119                                 .findFirst()
    120                                 .map(AbstractPrimitive::getInstant)
    121                                 .orElse(null);
    122                         toLoad.put(p, timestamp);
    123                     }
    124                 }
    125                 if (isCanceled()) return;
    126                 // Let's load all required history
    127                 MainApplication.worker.submit(new HistoryLoaderAndListener(toLoad));
    128             } catch (RejectedExecutionException e) {
    129                 rememberException(e);
    130                 setFailed(true);
     192                Map<OsmPrimitive, Instant> toLoad = getToLoad(downloadedData);
     193                if (isCanceled()) return;
     194                // Let's load all required history
     195                MainApplication.worker.submit(new HistoryLoaderAndListener(toLoad));
     196            } catch (RejectedExecutionException e) {
     197                rememberException(e);
     198                setFailed(true);
     199            }
     200        }
     201    }
     202
     203    /**
     204     * Get the primitives to load more information
     205     * @param ds The dataset to look for incomplete primitives from
     206     * @return The objects that still need to be loaded
     207     */
     208    private static Map<OsmPrimitive, Instant> getToLoad(DataSet ds) {
     209        Map<OsmPrimitive, Instant> toLoad = new HashMap<>();
     210        for (OsmPrimitive p : ds.allNonDeletedPrimitives()) {
     211            if (p.isIncomplete()) {
     212                Instant timestamp = p.getReferrers().stream()
     213                        .filter(ref -> !ref.isTimestampEmpty())
     214                        .findFirst()
     215                        .map(AbstractPrimitive::getInstant)
     216                        .orElse(null);
     217                toLoad.put(p, timestamp);
    131218            }
    132219        }
     220        return toLoad;
    133221    }
    134222
    135223    /**