diff --git a/hoot-core/src/main/cpp/hoot/core/io/OsmApiChangeset.cpp b/hoot-core/src/main/cpp/hoot/core/io/OsmApiChangeset.cpp
index 3c1fde5..c63d49d 100644
--- a/hoot-core/src/main/cpp/hoot/core/io/OsmApiChangeset.cpp
+++ b/hoot-core/src/main/cpp/hoot/core/io/OsmApiChangeset.cpp
@@ -22,7 +22,7 @@
* This will properly maintain the copyright information. DigitalGlobe
* copyrights will be updated automatically.
*
- * @copyright Copyright (C) 2018, 2019 DigitalGlobe (http://www.digitalglobe.com/)
+ * @copyright Copyright (C) 2018, 2019, 2020 DigitalGlobe (http://www.digitalglobe.com/)
*/
#include "OsmApiChangeset.h"
@@ -324,6 +324,11 @@ void XmlChangeset::updateChangeset(const QString &changes)
QXmlStreamReader reader(changes);
// Make sure that the XML provided starts with the <diffResult> tag
QXmlStreamReader::TokenType type = reader.readNext();
+ if (type == QXmlStreamReader::Invalid)
+ {
+ LOG_ERROR("Invalid changeset response.");
+ return;
+ }
if (type == QXmlStreamReader::StartDocument)
type = reader.readNext();
if (type == QXmlStreamReader::StartElement && reader.name() != "diffResult")
@@ -1044,7 +1049,7 @@ void XmlChangeset::markBuffered(ChangesetElement* element)
element->setStatus(ChangesetElement::Buffering);
// Add to the buffer for lookup within this subset
_sendBuffer.push_back(element);
- _sentCount++;
+ ++_sentCount;
}
}
@@ -1098,14 +1103,14 @@ bool XmlChangeset::matchesPlaceholderFailure(const QString& hint,
long& element_id, ElementType::Type& element_type)
{
// Placeholder node not found for reference -145213 in way -5687
- // Placeholder Way not found for reference -12257 in relation -51
+ // Placeholder Way not found for reference -12257 in Relation -51
QRegularExpression reg("Placeholder (node|way|relation) not found for reference (-?[0-9]+) in (node|way|relation) (-?[0-9]+)",
QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = reg.match(hint);
if (match.hasMatch())
{
// Get the node/way/relation type and id that caused the failure
- member_type = ElementType::fromString(match.captured(1));
+ member_type = ElementType::fromString(match.captured(1).toLower());
bool success = false;
member_id = match.captured(2).toLong(&success);
if (!success)
@@ -1138,6 +1143,35 @@ bool XmlChangeset::matchesRelationFailure(const QString& hint, long& element_id,
return false;
}
+bool XmlChangeset::matchesMultiRelationFailure(const QString& hint,
+ long& element_id,
+ std::vector<long>& member_ids,
+ ElementType::Type& member_type)
+{
+ // Relation with id -2 requires the relations with id in 1707148,1707249, which either do not exist, or are not visible.
+ QRegularExpression reg("Relation (-?[0-9]+) requires the (nodes|ways|relations) with id in ((-?[0-9]+,)+) (.*)", //-?[0-9]+,).*", //+ which either do not exist, or are not visible.",
+ QRegularExpression::CaseInsensitiveOption);
+ QRegularExpressionMatch match = reg.match(hint);
+ if (match.hasMatch())
+ {
+ QString error = match.captured(1);
+ if (error != "")
+ element_id = error.toLong();
+ // Get the node/way/relation type (remove the 's') and id that failed
+ member_type = ElementType::fromString(match.captured(2).left(match.captured(2).length() - 1));
+ bool success = false;
+ QStringList ids = match.captured(3).split(",", QString::SkipEmptyParts);
+ for (int i = 0; i < ids.size(); ++i)
+ {
+ long id = ids[i].toLong(&success);
+ if (success)
+ member_ids.push_back(id);
+ }
+ return success;
+ }
+ return false;
+}
+
bool XmlChangeset::matchesChangesetPreconditionFailure(const QString& hint,
long& member_id, ElementType::Type& member_type,
long& element_id, ElementType::Type& element_type)
@@ -1186,6 +1220,16 @@ bool XmlChangeset::matchesChangesetConflictVersionMismatchFailure(const QString&
return false;
}
+bool XmlChangeset::matchesChangesetClosedFailure(const QString& hint)
+{
+ // Changeset conflict: The changeset 49514098 was closed at 2020-01-08 16:28:56 UTC
+ QRegularExpression reg(
+ "Changeset conflict: The changeset ([0-9]+) was closed.*",
+ QRegularExpression::CaseInsensitiveOption);
+ QRegularExpressionMatch match = reg.match(hint);
+ return match.hasMatch();
+}
+
bool XmlChangeset::writeErrorFile()
{
// Validate the pathname
@@ -1220,6 +1264,7 @@ ChangesetInfoPtr XmlChangeset::splitChangeset(ChangesetInfoPtr changeset, const
{
long member_id = 0;
long element_id = 0;
+ std::vector<long> member_ids;
ElementType::Type member_type = ElementType::Unknown;
ElementType::Type element_type = ElementType::Unknown;
// See if the hint is something like: Placeholder node not found for reference -145213 in way -5687
@@ -1257,7 +1302,7 @@ ChangesetInfoPtr XmlChangeset::splitChangeset(ChangesetInfoPtr changeset, const
// If there is a relation id, move just that relation to the split
for (int current_type = ChangesetType::TypeCreate; current_type != ChangesetType::TypeMax; ++current_type)
{
- if (changeset->contains(member_type, (ChangesetType)current_type, element_id))
+ if (changeset->contains(ElementType::Relation, (ChangesetType)current_type, element_id))
{
ChangesetRelation* relation = dynamic_cast<ChangesetRelation*>(_allRelations[element_id].get());
// Add the relation to the split and remove from the changeset
@@ -1288,6 +1333,22 @@ ChangesetInfoPtr XmlChangeset::splitChangeset(ChangesetInfoPtr changeset, const
}
}
}
+ // See if the hint is something like: Relation with id -2 requires the relations with id in 1707148,1707249, which either do not exist, or are not visible.
+ else if (matchesMultiRelationFailure(splitHint, element_id, member_ids, member_type))
+ {
+ // If there is a relation id, move just that relation to the split
+ for (int current_type = ChangesetType::TypeCreate; current_type != ChangesetType::TypeMax; ++current_type)
+ {
+ if (changeset->contains(ElementType::Relation, (ChangesetType)current_type, element_id))
+ {
+ ChangesetRelation* relation = dynamic_cast<ChangesetRelation*>(_allRelations[element_id].get());
+ // Add the relation to the split and remove from the changeset
+ split->add(ElementType::Relation, (ChangesetType)current_type, relation->id());
+ changeset->remove(ElementType::Relation, (ChangesetType)current_type, relation->id());
+ return split;
+ }
+ }
+ }
// See if the hint is something like: Changeset precondition failed: Precondition failed: Node 5 is still used by ways 67
else if (matchesChangesetPreconditionFailure(splitHint, member_id, member_type, element_id, element_type))
{
@@ -1692,9 +1753,70 @@ void XmlChangeset::writeRelations(const ChangesetInfoPtr& changeset, QTextStream
writeElements(changeset, ts, type, changeset_id, ElementType::Relation, _relations[type]);
}
+bool XmlChangeset::calculateRemainingChangeset(ChangesetInfoPtr &changeset)
+{
+ // Create the changeset info object if there isn't one
+ if (!changeset)
+ changeset.reset(new ChangesetInfo());
+ changeset->clear();
+ // This is the last changeset of the bunch because of the error state
+ changeset->setLast();
+ // Loop through all remaining elements
+ for (ChangesetType type = ChangesetType::TypeCreate; type != ChangesetType::TypeMax; type = static_cast<ChangesetType>(type + 1))
+ {
+ // Iterate all of the relations of "type" in the changeset
+ for (ChangesetElementMap::iterator it = _relations[type].begin(); it != _relations[type].end(); ++it)
+ {
+ ChangesetElement* relation = it->second.get();
+ if (relation->getStatus() == ChangesetElement::Available)
+ {
+ // Add the relation to the changeset
+ changeset->add(ElementType::Relation, type, relation->id());
+ // Mark the relation as buffered
+ markBuffered(relation);
+ // Mark the relation as sent
+ relation->setStatus(ChangesetElement::Sent);
+ }
+ }
+ // Iterate all of the ways of "type" in the changeset
+ for (ChangesetElementMap::iterator it = _ways[type].begin(); it != _ways[type].end(); ++it)
+ {
+ ChangesetElement* way = it->second.get();
+ if (way->getStatus() == ChangesetElement::Available)
+ {
+ // Add the way to the changeset
+ changeset->add(ElementType::Way, type, way->id());
+ // Mark the way as buffered
+ markBuffered(way);
+ // Mark the way as sent
+ way->setStatus(ChangesetElement::Sent);
+ }
+ }
+ // Iterate all of the nodes of "type" in the changeset
+ for (ChangesetElementMap::iterator it = _nodes[type].begin(); it != _nodes[type].end(); ++it)
+ {
+ ChangesetElement* node = it->second.get();
+ if (node->getStatus() == ChangesetElement::Available)
+ {
+ // Add the node to the changeset
+ changeset->add(ElementType::Node, type, node->id());
+ // Mark the node as buffered
+ markBuffered(node);
+ // Mark the node as sent
+ node->setStatus(ChangesetElement::Sent);
+ }
+ }
+ }
+ // Clear the send buffer
+ _sendBuffer.clear();
+ // Return true if there is anything in the changeset
+ return changeset->size() > 0;
+}
+
ChangesetInfo::ChangesetInfo()
: _attemptedResolveChangesetIssues(false),
- _numRetries(0)
+ _numRetries(0),
+ _last(false)
{
}