Ignore:
Timestamp:
2018-08-05T14:09:31+02:00 (6 years ago)
Author:
Don-vip
Message:

fix #16546 - Support Overpass API JSON format

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/io/AbstractReader.java

    r13197 r14086  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
     6import java.io.IOException;
    67import java.io.InputStream;
     8import java.io.InputStreamReader;
     9import java.text.MessageFormat;
    710import java.util.ArrayList;
    811import java.util.Collection;
     
    1114import java.util.Map;
    1215import java.util.Map.Entry;
    13 
     16import java.util.function.Consumer;
     17
     18import org.openstreetmap.josm.data.Bounds;
     19import org.openstreetmap.josm.data.DataSource;
     20import org.openstreetmap.josm.data.coor.LatLon;
     21import org.openstreetmap.josm.data.osm.AbstractPrimitive;
    1422import org.openstreetmap.josm.data.osm.Changeset;
    1523import org.openstreetmap.josm.data.osm.DataSet;
     24import org.openstreetmap.josm.data.osm.DownloadPolicy;
    1625import org.openstreetmap.josm.data.osm.Node;
     26import org.openstreetmap.josm.data.osm.NodeData;
    1727import org.openstreetmap.josm.data.osm.OsmPrimitive;
    1828import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
     29import org.openstreetmap.josm.data.osm.PrimitiveData;
    1930import org.openstreetmap.josm.data.osm.PrimitiveId;
    2031import org.openstreetmap.josm.data.osm.Relation;
     32import org.openstreetmap.josm.data.osm.RelationData;
    2133import org.openstreetmap.josm.data.osm.RelationMember;
    2234import org.openstreetmap.josm.data.osm.RelationMemberData;
    2335import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
     36import org.openstreetmap.josm.data.osm.Tagged;
     37import org.openstreetmap.josm.data.osm.UploadPolicy;
     38import org.openstreetmap.josm.data.osm.User;
    2439import org.openstreetmap.josm.data.osm.Way;
     40import org.openstreetmap.josm.data.osm.WayData;
     41import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
    2542import org.openstreetmap.josm.gui.progress.ProgressMonitor;
     43import org.openstreetmap.josm.tools.CheckParameterUtil;
    2644import org.openstreetmap.josm.tools.Logging;
     45import org.openstreetmap.josm.tools.Utils;
     46import org.openstreetmap.josm.tools.date.DateUtils;
    2747
    2848/**
    2949 * Abstract Reader, allowing other implementations than OsmReader (PbfReader in PBF plugin for example)
    3050 * @author Vincent
    31  *
     51 * @since 4490
    3252 */
    3353public abstract class AbstractReader {
     54
     55    /** Used by plugins to register themselves as data postprocessors. */
     56    private static volatile List<OsmServerReadPostprocessor> postprocessors;
     57
     58    protected boolean cancel;
     59
     60    /**
     61     * Register a new postprocessor.
     62     * @param pp postprocessor
     63     * @see #deregisterPostprocessor
     64     * @since xxx (moved from OsmReader)
     65     */
     66    public static void registerPostprocessor(OsmServerReadPostprocessor pp) {
     67        if (postprocessors == null) {
     68            postprocessors = new ArrayList<>();
     69        }
     70        postprocessors.add(pp);
     71    }
     72
     73    /**
     74     * Deregister a postprocessor previously registered with {@link #registerPostprocessor}.
     75     * @param pp postprocessor
     76     * @see #registerPostprocessor
     77     * @since xxx (moved from OsmReader)
     78     */
     79    public static void deregisterPostprocessor(OsmServerReadPostprocessor pp) {
     80        if (postprocessors != null) {
     81            postprocessors.remove(pp);
     82        }
     83    }
    3484
    3585    /**
     
    63113    public DataSet getDataSet() {
    64114        return ds;
     115    }
     116
     117    /**
     118     * Iterate over registered postprocessors and give them each a chance to modify the dataset we have just loaded.
     119     * @param progressMonitor Progress monitor
     120     */
     121    protected void callPostProcessors(ProgressMonitor progressMonitor) {
     122        if (postprocessors != null) {
     123            for (OsmServerReadPostprocessor pp : postprocessors) {
     124                pp.postprocessDataSet(getDataSet(), progressMonitor);
     125            }
     126        }
    65127    }
    66128
     
    207269
    208270    protected abstract DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor) throws IllegalDataException;
     271
     272    @FunctionalInterface
     273    protected interface ParserWorker {
     274       void accept(InputStreamReader ir) throws IllegalDataException, IOException;
     275    }
     276
     277    protected final DataSet doParseDataSet(InputStream source, ProgressMonitor progressMonitor, ParserWorker parserWorker)
     278            throws IllegalDataException {
     279        if (progressMonitor == null) {
     280            progressMonitor = NullProgressMonitor.INSTANCE;
     281        }
     282        ProgressMonitor.CancelListener cancelListener = () -> cancel = true;
     283        progressMonitor.addCancelListener(cancelListener);
     284        CheckParameterUtil.ensureParameterNotNull(source, "source");
     285        try {
     286            progressMonitor.beginTask(tr("Prepare OSM data...", 2));
     287            progressMonitor.indeterminateSubTask(tr("Parsing OSM data..."));
     288
     289            try (InputStreamReader ir = UTFInputStreamReader.create(source)) {
     290                parserWorker.accept(ir);
     291            }
     292            progressMonitor.worked(1);
     293
     294            boolean readOnly = getDataSet().isLocked();
     295
     296            progressMonitor.indeterminateSubTask(tr("Preparing data set..."));
     297            if (readOnly) {
     298                getDataSet().unlock();
     299            }
     300            prepareDataSet();
     301            if (readOnly) {
     302                getDataSet().lock();
     303            }
     304            progressMonitor.worked(1);
     305
     306            // iterate over registered postprocessors and give them each a chance
     307            // to modify the dataset we have just loaded.
     308            callPostProcessors(progressMonitor);
     309            // Make sure postprocessors did not change the read-only state
     310            if (readOnly && !getDataSet().isLocked()) {
     311                getDataSet().lock();
     312            }
     313            return getDataSet();
     314        } catch (IllegalDataException e) {
     315            throw e;
     316        } catch (IOException e) {
     317            throw new IllegalDataException(e);
     318        } finally {
     319            progressMonitor.finishTask();
     320            progressMonitor.removeCancelListener(cancelListener);
     321        }
     322    }
     323
     324    protected final long getLong(String name, String value) throws IllegalDataException {
     325        if (value == null) {
     326            throw new IllegalDataException(tr("Missing required attribute ''{0}''.", name));
     327        }
     328        try {
     329            return Long.parseLong(value);
     330        } catch (NumberFormatException e) {
     331            throw new IllegalDataException(tr("Illegal long value for attribute ''{0}''. Got ''{1}''.", name, value), e);
     332        }
     333    }
     334
     335    protected final void parseVersion(String version) throws IllegalDataException {
     336        validateVersion(version);
     337        ds.setVersion(version);
     338    }
     339
     340    private static void validateVersion(String version) throws IllegalDataException {
     341        if (version == null) {
     342            throw new IllegalDataException(tr("Missing mandatory attribute ''{0}''.", "version"));
     343        }
     344        if (!"0.6".equals(version)) {
     345            throw new IllegalDataException(tr("Unsupported version: {0}", version));
     346        }
     347    }
     348
     349    protected final void parseDownloadPolicy(String key, String downloadPolicy) throws IllegalDataException {
     350        parsePolicy(key, downloadPolicy, policy -> ds.setDownloadPolicy(DownloadPolicy.of(policy)));
     351    }
     352
     353    protected final void parseUploadPolicy(String key, String uploadPolicy) throws IllegalDataException {
     354        parsePolicy(key, uploadPolicy, policy -> ds.setUploadPolicy(UploadPolicy.of(policy)));
     355    }
     356
     357    private static void parsePolicy(String key, String policy, Consumer<String> consumer) throws IllegalDataException {
     358        if (policy != null) {
     359            try {
     360                consumer.accept(policy);
     361            } catch (IllegalArgumentException e) {
     362                throw new IllegalDataException(MessageFormat.format(
     363                        "Illegal value for attribute ''{0}''. Got ''{1}''.", key, policy), e);
     364            }
     365        }
     366    }
     367
     368    protected final void parseLocked(String locked) {
     369        if ("true".equalsIgnoreCase(locked)) {
     370            ds.lock();
     371        }
     372    }
     373
     374    protected final void parseBounds(String generator, String minlon, String minlat, String maxlon, String maxlat, String origin)
     375            throws IllegalDataException {
     376        if (minlon != null && maxlon != null && minlat != null && maxlat != null) {
     377            if (origin == null) {
     378                origin = generator;
     379            }
     380            Bounds bounds = new Bounds(
     381                    Double.parseDouble(minlat), Double.parseDouble(minlon),
     382                    Double.parseDouble(maxlat), Double.parseDouble(maxlon));
     383            if (bounds.isOutOfTheWorld()) {
     384                Bounds copy = new Bounds(bounds);
     385                bounds.normalize();
     386                Logging.info("Bbox " + copy + " is out of the world, normalized to " + bounds);
     387            }
     388            ds.addDataSource(new DataSource(bounds, origin));
     389        } else {
     390            throw new IllegalDataException(tr("Missing mandatory attributes on element ''bounds''. " +
     391                    "Got minlon=''{0}'',minlat=''{1}'',maxlon=''{2}'',maxlat=''{3}'', origin=''{4}''.",
     392                    minlon, minlat, maxlon, maxlat, origin
     393            ));
     394        }
     395    }
     396
     397    protected final void parseId(PrimitiveData current, long id) throws IllegalDataException {
     398        current.setId(id);
     399        if (current.getUniqueId() == 0) {
     400            throw new IllegalDataException(tr("Illegal object with ID=0."));
     401        }
     402    }
     403
     404    protected final void parseTimestamp(PrimitiveData current, String time) {
     405        if (time != null && !time.isEmpty()) {
     406            current.setRawTimestamp((int) (DateUtils.tsFromString(time)/1000));
     407        }
     408    }
     409
     410    private static User createUser(String uid, String name) throws IllegalDataException {
     411        if (uid == null) {
     412            if (name == null)
     413                return null;
     414            return User.createLocalUser(name);
     415        }
     416        try {
     417            return User.createOsmUser(Long.parseLong(uid), name);
     418        } catch (NumberFormatException e) {
     419            throw new IllegalDataException(MessageFormat.format("Illegal value for attribute ''uid''. Got ''{0}''.", uid), e);
     420        }
     421    }
     422
     423    protected final void parseUser(PrimitiveData current, String user, long uid) {
     424        current.setUser(User.createOsmUser(uid, user));
     425    }
     426
     427    protected final void parseUser(PrimitiveData current, String user, String uid) throws IllegalDataException {
     428        current.setUser(createUser(uid, user));
     429    }
     430
     431    protected final void parseVisible(PrimitiveData current, String visible) {
     432        if (visible != null) {
     433            current.setVisible(Boolean.parseBoolean(visible));
     434        }
     435    }
     436
     437    protected final void parseVersion(PrimitiveData current, String versionString) throws IllegalDataException {
     438        int version = 0;
     439        if (versionString != null) {
     440            try {
     441                version = Integer.parseInt(versionString);
     442            } catch (NumberFormatException e) {
     443                throw new IllegalDataException(
     444                        tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
     445                        Long.toString(current.getUniqueId()), versionString), e);
     446            }
     447            parseVersion(current, version);
     448        } else {
     449            // version expected for OSM primitives with an id assigned by the server (id > 0), since API 0.6
     450            if (!current.isNew() && ds.getVersion() != null && "0.6".equals(ds.getVersion())) {
     451                throw new IllegalDataException(
     452                        tr("Missing attribute ''version'' on OSM primitive with ID {0}.", Long.toString(current.getUniqueId())));
     453            }
     454        }
     455    }
     456
     457    protected final void parseVersion(PrimitiveData current, int version) throws IllegalDataException {
     458        switch (ds.getVersion()) {
     459        case "0.6":
     460            if (version <= 0 && !current.isNew()) {
     461                throw new IllegalDataException(
     462                        tr("Illegal value for attribute ''version'' on OSM primitive with ID {0}. Got {1}.",
     463                        Long.toString(current.getUniqueId()), version));
     464            } else if (version < 0 && current.isNew()) {
     465                Logging.warn(tr("Normalizing value of attribute ''version'' of element {0} to {2}, API version is ''{3}''. Got {1}.",
     466                        current.getUniqueId(), version, 0, "0.6"));
     467                version = 0;
     468            }
     469            break;
     470        default:
     471            // should not happen. API version has been checked before
     472            throw new IllegalDataException(tr("Unknown or unsupported API version. Got {0}.", ds.getVersion()));
     473        }
     474        current.setVersion(version);
     475    }
     476
     477    protected final void parseAction(PrimitiveData current, String action) {
     478        if (action == null) {
     479            // do nothing
     480        } else if ("delete".equals(action)) {
     481            current.setDeleted(true);
     482            current.setModified(current.isVisible());
     483        } else if ("modify".equals(action)) {
     484            current.setModified(true);
     485        }
     486    }
     487
     488    private static void handleIllegalChangeset(PrimitiveData current, IllegalArgumentException e, Object v)
     489            throws IllegalDataException {
     490        Logging.debug(e.getMessage());
     491        if (current.isNew()) {
     492            // for a new primitive we just log a warning
     493            Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
     494                    v, current.getUniqueId()));
     495            current.setChangesetId(0);
     496        } else {
     497            // for an existing primitive this is a problem
     498            throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v), e);
     499        }
     500    }
     501
     502    protected final void parseChangeset(PrimitiveData current, String v) throws IllegalDataException {
     503        if (v == null) {
     504            current.setChangesetId(0);
     505        } else {
     506            try {
     507                parseChangeset(current, Integer.parseInt(v));
     508            } catch (NumberFormatException e) {
     509                handleIllegalChangeset(current, e, v);
     510            }
     511        }
     512    }
     513
     514    protected final void parseChangeset(PrimitiveData current, int v) throws IllegalDataException {
     515        try {
     516            current.setChangesetId(v);
     517        } catch (IllegalArgumentException e) {
     518            handleIllegalChangeset(current, e, v);
     519        } catch (IllegalStateException e) {
     520            // thrown for positive changeset id on new primitives
     521            Logging.debug(e);
     522            Logging.info(e.getMessage());
     523            current.setChangesetId(0);
     524        }
     525        if (current.getChangesetId() <= 0) {
     526            if (current.isNew()) {
     527                // for a new primitive we just log a warning
     528                Logging.info(tr("Illegal value for attribute ''changeset'' on new object {1}. Got {0}. Resetting to 0.",
     529                        v, current.getUniqueId()));
     530                current.setChangesetId(0);
     531            } else if (current.getChangesetId() < 0) {
     532                // for an existing primitive this is a problem only for negative ids (GDPR extracts are set to 0)
     533                throw new IllegalDataException(tr("Illegal value for attribute ''changeset''. Got {0}.", v));
     534            }
     535        }
     536    }
     537
     538    protected final void parseTag(Tagged t, String key, String value) throws IllegalDataException {
     539        if (key == null || value == null) {
     540            throw new IllegalDataException(tr("Missing key or value attribute in tag."));
     541        } else if (Utils.isStripEmpty(key) && t instanceof AbstractPrimitive) {
     542            // #14199: Empty keys as ignored by AbstractPrimitive#put, but it causes problems to fix existing data
     543            // Drop the tag on import, but flag the primitive as modified
     544            ((AbstractPrimitive) t).setModified(true);
     545        } else {
     546            t.put(key.intern(), value.intern());
     547        }
     548    }
     549
     550    @FunctionalInterface
     551    protected interface CommonReader {
     552       void accept(PrimitiveData pd) throws IllegalDataException;
     553    }
     554
     555    @FunctionalInterface
     556    protected interface NodeReader {
     557       void accept(Node n) throws IllegalDataException;
     558    }
     559
     560    @FunctionalInterface
     561    protected interface WayReader {
     562       void accept(Way w, Collection<Long> nodeIds) throws IllegalDataException;
     563    }
     564
     565    @FunctionalInterface
     566    protected interface RelationReader {
     567       void accept(Relation r, Collection<RelationMemberData> members) throws IllegalDataException;
     568    }
     569
     570    private static boolean areLatLonDefined(String lat, String lon) {
     571        return lat != null && lon != null;
     572    }
     573
     574    private static boolean areLatLonDefined(double lat, double lon) {
     575        return lat != Double.NaN && lon != Double.NaN;
     576    }
     577
     578    private Node addNode(NodeData nd, NodeReader nodeReader) throws IllegalDataException {
     579        Node n = new Node(nd.getId(), nd.getVersion());
     580        n.setVisible(nd.isVisible());
     581        n.load(nd);
     582        nodeReader.accept(n);
     583        externalIdMap.put(nd.getPrimitiveId(), n);
     584        return n;
     585    }
     586
     587    protected final Node parseNode(double lat, double lon, CommonReader commonReader, NodeReader nodeReader)
     588            throws IllegalDataException {
     589        NodeData nd = new NodeData();
     590        LatLon ll = null;
     591        if (areLatLonDefined(lat, lon)) {
     592            try {
     593                ll = new LatLon(lat, lon);
     594                nd.setCoor(ll);
     595            } catch (NumberFormatException e) {
     596                Logging.trace(e);
     597            }
     598        }
     599        commonReader.accept(nd);
     600        if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) {
     601            throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.",
     602                    Long.toString(nd.getId()), lat, lon));
     603        }
     604        return addNode(nd, nodeReader);
     605    }
     606
     607    protected final Node parseNode(String lat, String lon, CommonReader commonReader, NodeReader nodeReader)
     608            throws IllegalDataException {
     609        NodeData nd = new NodeData();
     610        LatLon ll = null;
     611        if (areLatLonDefined(lat, lon)) {
     612            try {
     613                ll = new LatLon(Double.parseDouble(lat), Double.parseDouble(lon));
     614                nd.setCoor(ll);
     615            } catch (NumberFormatException e) {
     616                Logging.trace(e);
     617            }
     618        }
     619        commonReader.accept(nd);
     620        if (areLatLonDefined(lat, lon) && (ll == null || !ll.isValid())) {
     621            throw new IllegalDataException(tr("Illegal value for attributes ''lat'', ''lon'' on node with ID {0}. Got ''{1}'', ''{2}''.",
     622                    Long.toString(nd.getId()), lat, lon));
     623        }
     624        return addNode(nd, nodeReader);
     625    }
     626
     627    protected final Way parseWay(CommonReader commonReader, WayReader wayReader) throws IllegalDataException {
     628        WayData wd = new WayData();
     629        commonReader.accept(wd);
     630        Way w = new Way(wd.getId(), wd.getVersion());
     631        w.setVisible(wd.isVisible());
     632        w.load(wd);
     633        externalIdMap.put(wd.getPrimitiveId(), w);
     634
     635        Collection<Long> nodeIds = new ArrayList<>();
     636        wayReader.accept(w, nodeIds);
     637        if (w.isDeleted() && !nodeIds.isEmpty()) {
     638            Logging.info(tr("Deleted way {0} contains nodes", Long.toString(w.getUniqueId())));
     639            nodeIds = new ArrayList<>();
     640        }
     641        ways.put(wd.getUniqueId(), nodeIds);
     642        return w;
     643    }
     644
     645    protected final Relation parseRelation(CommonReader commonReader, RelationReader relationReader) throws IllegalDataException {
     646        RelationData rd = new RelationData();
     647        commonReader.accept(rd);
     648        Relation r = new Relation(rd.getId(), rd.getVersion());
     649        r.setVisible(rd.isVisible());
     650        r.load(rd);
     651        externalIdMap.put(rd.getPrimitiveId(), r);
     652
     653        Collection<RelationMemberData> members = new ArrayList<>();
     654        relationReader.accept(r, members);
     655        if (r.isDeleted() && !members.isEmpty()) {
     656            Logging.info(tr("Deleted relation {0} contains members", Long.toString(r.getUniqueId())));
     657            members = new ArrayList<>();
     658        }
     659        relations.put(rd.getUniqueId(), members);
     660        return r;
     661    }
     662
     663    protected final RelationMemberData parseRelationMember(Relation r, String ref, String type, String role) throws IllegalDataException {
     664        if (ref == null) {
     665            throw new IllegalDataException(tr("Missing attribute ''ref'' on member in relation {0}.",
     666                    Long.toString(r.getUniqueId())));
     667        }
     668        try {
     669            return parseRelationMember(r, Long.parseLong(ref), type, role);
     670        } catch (NumberFormatException e) {
     671            throw new IllegalDataException(tr("Illegal value for attribute ''ref'' on member in relation {0}. Got {1}",
     672                    Long.toString(r.getUniqueId()), ref), e);
     673        }
     674    }
     675
     676    protected final RelationMemberData parseRelationMember(Relation r, long id, String type, String role) throws IllegalDataException {
     677        if (id == 0) {
     678            throw new IllegalDataException(tr("Incomplete <member> specification with ref=0"));
     679        }
     680        if (type == null) {
     681            throw new IllegalDataException(tr("Missing attribute ''type'' on member {0} in relation {1}.",
     682                    Long.toString(id), Long.toString(r.getUniqueId())));
     683        }
     684        try {
     685            return new RelationMemberData(role, OsmPrimitiveType.fromApiTypeName(type), id);
     686        } catch (IllegalArgumentException e) {
     687            throw new IllegalDataException(tr("Illegal value for attribute ''type'' on member {0} in relation {1}. Got {2}.",
     688                    Long.toString(id), Long.toString(r.getUniqueId()), type), e);
     689        }
     690    }
    209691}
Note: See TracChangeset for help on using the changeset viewer.