// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.io; import static org.openstreetmap.josm.tools.I18n.tr; import static org.openstreetmap.josm.tools.I18n.trn; import java.io.InputStream; import java.net.HttpURLConnection; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; import java.util.logging.Logger; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.DataSetMerger; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.OsmPrimitiveType; import org.openstreetmap.josm.data.osm.PrimitiveId; import org.openstreetmap.josm.data.osm.Relation; import org.openstreetmap.josm.data.osm.RelationMember; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.progress.NullProgressMonitor; import org.openstreetmap.josm.gui.progress.ProgressMonitor; import org.openstreetmap.josm.tools.CheckParameterUtil; /** * Retrieves a set of {@see OsmPrimitive}s from an OSM server using the so called * Multi Fetch API. * * Usage: *
* MultiFetchServerObjectReader reader = MultiFetchServerObjectReader() * .append(2345,2334,4444) * .append(new Node(72343)); * reader.parseOsm(); * if (!reader.getMissingPrimitives().isEmpty()) { * System.out.println("There are missing primitives: " + reader.getMissingPrimitives()); * } * if (!reader.getSkippedWays().isEmpty()) { * System.out.println("There are skipped ways: " + reader.getMissingPrimitives()); * } ** * */ public class MultiFetchServerObjectReader extends OsmServerReader{ static private Logger logger = Logger.getLogger(MultiFetchServerObjectReader.class.getName()); /** * the max. number of primitives retrieved in one step. Assuming IDs with 7 digits, * this leads to a max. request URL of ~ 1600 Bytes ((7 digits + 1 Separator) * 200), * which should be safe according to the * WWW FAQ. * */ static private int MAX_IDS_PER_REQUEST = 200; private HashSet
ds
must include
* an {@see OsmPrimitive} with id=id
. The id will
* later we fetched as part of a Multi Get request.
*
* Ignore the id if it id <= 0.
*
* @param ds the dataset (must not be null)
* @param id the id
* @exception IllegalArgumentException thrown, if ds is null
* @exception NoSuchElementException thrown, if ds doesn't include an {@see OsmPrimitive} with
* id=id
*/
protected void remember(DataSet ds, long id, OsmPrimitiveType type) throws IllegalArgumentException, NoSuchElementException{
CheckParameterUtil.ensureParameterNotNull(ds, "ds");
if (id <= 0) return;
OsmPrimitive primitive = ds.getPrimitiveById(id, type);
if (primitive == null)
throw new NoSuchElementException(tr("No primitive with id {0} in local dataset. Cannot infer primitive type.", id));
remember(primitive.getPrimitiveId());
return;
}
/**
* appends a {@see Node}s id to the list of ids which will be fetched from the server.
*
* @param node the node (ignored, if null)
* @return this
*
*/
public MultiFetchServerObjectReader append(DataSet ds, long id, OsmPrimitiveType type) {
switch(type) {
case NODE:
Node n = (Node)ds.getPrimitiveById(id,type);
append(n);
break;
case WAY:
Way w= (Way)ds.getPrimitiveById(id,type);
append(w);
break;
case RELATION:
Relation r = (Relation)ds.getPrimitiveById(id,type);
append(r);
break;
}
return this;
}
/**
* appends a {@see Node}s id to the list of ids which will be fetched from the server.
*
* @param node the node (ignored, if null)
* @return this
*
*/
public MultiFetchServerObjectReader append(Node node) {
if (node == null) return this;
remember(node.getPrimitiveId());
return this;
}
/**
* appends a {@see Way}s id and the list of ids of nodes the way refers to the list of ids which will be fetched from the server.
*
* @param way the way (ignored, if null)
* @return this
*
*/
public MultiFetchServerObjectReader append(Way way) {
if (way == null) return this;
if (way.isNew()) return this;
for (Node node: way.getNodes()) {
if (!node.isNew()) {
remember(node.getPrimitiveId());
}
}
remember(way.getPrimitiveId());
return this;
}
/**
* appends a {@see Relation}s id to the list of ids which will be fetched from the server.
*
* @param relation the relation (ignored, if null)
* @return this
*
*/
public MultiFetchServerObjectReader append(Relation relation) {
if (relation == null) return this;
if (relation.isNew()) return this;
remember(relation.getPrimitiveId());
for (RelationMember member : relation.getMembers()) {
if (OsmPrimitiveType.from(member.getMember()).equals(OsmPrimitiveType.RELATION)) {
// avoid infinite recursion in case of cyclic dependencies in relations
//
if (relations.contains(member.getMember().getId())) {
continue;
}
}
if (!member.getMember().isIncomplete()) {
appendGeneric(member.getMember());
}
}
return this;
}
protected MultiFetchServerObjectReader appendGeneric(OsmPrimitive primitive) {
if (OsmPrimitiveType.from(primitive).equals(OsmPrimitiveType.NODE))
return append((Node)primitive);
else if (OsmPrimitiveType.from(primitive).equals(OsmPrimitiveType.WAY))
return append((Way)primitive);
else if (OsmPrimitiveType.from(primitive).equals(OsmPrimitiveType.RELATION))
return append((Relation)primitive);
return this;
}
/**
* appends a list of {@see OsmPrimitive} to the list of ids which will be fetched from the server.
*
* @param primitives the list of primitives (ignored, if null)
* @return this
*
* @see #append(Node)
* @see #append(Way)
* @see #append(Relation)
*
*/
public MultiFetchServerObjectReader append(Collection extends OsmPrimitive> primitives) {
if (primitives == null) return this;
for (OsmPrimitive primitive : primitives) {
appendGeneric(primitive);
}
return this;
}
/**
* extracts a subset of max {@see #MAX_IDS_PER_REQUEST} ids from ids
and
* replies the subset. The extracted subset is removed from ids
.
*
* @param ids a set of ids
* @return the subset of ids
*/
protected Setfrom
to {@see #outputDataSet}.
*
* @param from the other dataset
*
*/
protected void merge(DataSet from) {
final DataSetMerger visitor = new DataSetMerger(outputDataSet,from);
visitor.merge();
}
/**
* fetches a set of ids of a given {@see OsmPrimitiveType} from the server
*
* @param ids the set of ids
* @param type the type
* @exception OsmTransferException thrown if an error occurs while communicating with the API server
*/
protected void fetchPrimitives(Set