diff --git a/ogr/ogr_xerces_headers.h b/ogr/ogr_xerces_headers.h index 0c0dc42a1e3f..dde4fe6a08e1 100644 --- a/ogr/ogr_xerces_headers.h +++ b/ogr/ogr_xerces_headers.h @@ -36,6 +36,7 @@ #include #include +#include #include #include #include diff --git a/ogr/ogrsf_frmts/nas/nashandler.cpp b/ogr/ogrsf_frmts/nas/nashandler.cpp index d159eee40e6b..94e2fefea6e2 100644 --- a/ogr/ogrsf_frmts/nas/nashandler.cpp +++ b/ogr/ogrsf_frmts/nas/nashandler.cpp @@ -33,6 +33,17 @@ #include "cpl_string.h" #include "ogr_xerces.h" +#if DEBUG_VERBOSE +#define NASDebug(fmt, ...) \ + CPLDebugOnly("NAS", "%s:%d %s " fmt, __FILE__, __LINE__, __FUNCTION__, \ + __VA_ARGS__) +#else +#define NASDebug(fmt, ...) \ + do \ + { \ + } while (0) +#endif + /* Update modes: @@ -123,9 +134,7 @@ NASHandler::NASHandler(NASReader *poReader) : m_poReader(poReader), m_pszCurField(nullptr), m_pszGeometry(nullptr), m_nGeomAlloc(0), m_nGeomLen(0), m_nGeometryDepth(0), m_nGeometryPropertyIndex(-1), m_nDepth(0), m_nDepthFeature(0), - m_bIgnoreFeature(false), m_bInUpdate(false), m_bInUpdateProperty(false), - m_nUpdateOrDeleteDepth(0), m_nUpdatePropertyDepth(0), - m_nNameOrValueDepth(0) + m_bIgnoreFeature(false) { } @@ -151,14 +160,24 @@ CPLString NASHandler::GetAttributes(const Attributes *attrs) for (unsigned int i = 0; i < attrs->getLength(); i++) { osRes += " "; - osRes += transcode(attrs->getQName(i), m_osAttrName); + osRes += transcode(attrs->getQName(i)); osRes += "=\""; - osRes += transcode(attrs->getValue(i), m_osAttrValue); + osRes += transcode(attrs->getValue(i)); osRes += "\""; } return osRes; } +/************************************************************************/ +/* setDocumentLocator() */ +/************************************************************************/ + +void NASHandler::setDocumentLocator(const Locator *locator) +{ + m_Locator = locator; + return DefaultHandler::setDocumentLocator(locator); +} + /************************************************************************/ /* startElement() */ /************************************************************************/ @@ -172,362 +191,229 @@ void NASHandler::startElement(const XMLCh *const /* uri */, GMLReadState *poState = m_poReader->GetState(); transcode(localname, m_osElementName); -#ifdef DEBUG_TRACE_ELEMENTS - for (int k = 0; k < m_nDepth; k++) - printf(" "); /*ok*/ - printf(">%s\n", m_osElementName.c_str()); /*ok*/ -#endif - if (m_bIgnoreFeature && m_nDepth >= m_nDepthFeature) - { - m_nDepth++; + m_nDepth++; + if (m_bIgnoreFeature && m_nDepth > m_nDepthFeature) return; - } - -#ifdef DEBUG_VERBOSE - CPLDebug("NAS", - "[%d] startElement %s m_bIgnoreFeature:%d m_nDepth:%d " - "m_nDepthFeature:%d featureClass:%s lastComponent:%s", - m_nDepth, m_osElementName.c_str(), m_bIgnoreFeature, m_nDepth, - m_nDepthFeature, - poState->m_poFeature - ? poState->m_poFeature->GetClass()->GetElementName() - : "(no feature)", - m_poReader->GetState()->GetLastComponent()); -#endif - /* -------------------------------------------------------------------- */ - /* If we are in the midst of collecting a feature attribute */ - /* value, then this must be a complex attribute which we don't */ - /* try to collect for now, so just terminate the field */ - /* collection. */ - /* -------------------------------------------------------------------- */ - if (m_pszCurField != nullptr) + if (m_nDepthFeature == 0) { - CPLFree(m_pszCurField); - m_pszCurField = nullptr; - } - - /* -------------------------------------------------------------------- */ - /* If we are collecting geometry, or if we determine this is a */ - /* geometry element then append to the geometry info. */ - /* -------------------------------------------------------------------- */ - const char *pszLast = nullptr; - - if (m_pszGeometry != nullptr || IsGeometryElement(m_osElementName)) - { - if (m_nGeometryPropertyIndex == -1 && poState->m_poFeature && - poState->m_poFeature->GetClass()) + if (m_osElementName == "Replace") { - GMLFeatureClass *poClass = poState->m_poFeature->GetClass(); - m_nGeometryPropertyIndex = - poClass->GetGeometryPropertyIndexBySrcElement( - poState->osPath.c_str()); - } - - const int nLNLen = static_cast(m_osElementName.size()); - CPLString osAttributes = GetAttributes(&attrs); - - /* should save attributes too! */ - - if (m_pszGeometry == nullptr) - m_nGeometryDepth = poState->m_nPathLength; + int nIndex = attrs.getIndex(u"safeToIgnore"); + if (nIndex != -1) + transcode(attrs.getValue(nIndex), m_osSafeToIgnore); + else + m_osSafeToIgnore = "true"; + m_osReplacingFID = ""; - if (m_pszGeometry == nullptr || - m_nGeomLen + nLNLen + 4 + (int)osAttributes.size() > m_nGeomAlloc) - { - m_nGeomAlloc = - (int)(m_nGeomAlloc * 1.3 + nLNLen + osAttributes.size() + 1000); - m_pszGeometry = (char *)CPLRealloc(m_pszGeometry, m_nGeomAlloc); + CPLAssert(m_osDeleteContext == ""); + m_osDeleteContext = m_osElementName; } - - strcpy(m_pszGeometry + m_nGeomLen, "<"); - strcpy(m_pszGeometry + m_nGeomLen + 1, m_osElementName); - - if (!osAttributes.empty()) + else if (m_osElementName == "Update" || m_osElementName == "Delete") { - strcat(m_pszGeometry + m_nGeomLen, " "); - strcat(m_pszGeometry + m_nGeomLen, osAttributes); - } - - strcat(m_pszGeometry + m_nGeomLen, ">"); - m_nGeomLen += static_cast(strlen(m_pszGeometry + m_nGeomLen)); - } + int nIndex = attrs.getIndex(u"typeNames"); + if (nIndex == -1) + nIndex = attrs.getIndex(u"typeName"); - /* -------------------------------------------------------------------- */ - /* Is this the ogc:Filter element in a update operation */ - /* (wfs:Delete, wfsext:Replace or wfs:Update)? */ - /* specialized sort of feature. */ - /* Issue a "Delete" feature for each ResourceId */ - /* -------------------------------------------------------------------- */ - else if (m_nDepthFeature == 0 && - (m_osElementName == "Filter" || m_osElementName == "ResourceId") && - (pszLast = m_poReader->GetState()->GetLastComponent()) != - nullptr && - (EQUAL(pszLast, "Delete") || EQUAL(pszLast, "Replace") || - EQUAL(pszLast, "Update"))) - { - const char *pszFilteredClassName = m_poReader->GetFilteredClassName(); - if (pszFilteredClassName != nullptr && - strcmp("Delete", pszFilteredClassName) != 0) - { - m_bIgnoreFeature = true; - m_nDepthFeature = m_nDepth; - m_nDepth++; + if (nIndex == -1) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "NAS: expected type name missing at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); + return; + } - return; - } + transcode(attrs.getValue(nIndex), m_osTypeName); - if (m_osLastTypeName == "") - { - CPLError(CE_Failure, CPLE_AssertionFailed, - "m_osLastTypeName == \"\""); + const char *pszTypeName = strchr(m_osTypeName.c_str(), ':'); + pszTypeName = pszTypeName ? pszTypeName + 1 : m_osTypeName.c_str(); + m_osTypeName = pszTypeName; - m_bIgnoreFeature = true; - m_nDepthFeature = m_nDepth; - m_nDepth++; - return; + CPLAssert(m_osDeleteContext == ""); + m_osDeleteContext = m_osElementName; } - - if (EQUAL(pszLast, "Replace") && - (m_osLastReplacingFID == "" || m_osLastSafeToIgnore == "")) + else if (m_osDeleteContext == "Update" && + (m_osElementName == "Name" || + m_osElementName == "ValueReference" || + m_osElementName == "Value")) { - CPLError(CE_Failure, CPLE_AssertionFailed, - "m_osLastReplacingFID == \"\" || " - "m_osLastSafeToIgnore == \"\""); - - m_bIgnoreFeature = true; - m_nDepthFeature = m_nDepth; - m_nDepth++; - return; + // fetch value + CPLFree(m_pszCurField); + m_pszCurField = CPLStrdup(""); } - - if (EQUAL(pszLast, "Update") && m_osLastEnded == "") + else if (m_osDeleteContext != "" && (m_osElementName == "ResourceId" || + m_osElementName == "FeatureId")) { - CPLError(CE_Failure, CPLE_AssertionFailed, "m_osLastEnded == \"\""); - - m_bIgnoreFeature = true; - m_nDepthFeature = m_nDepth; - m_nDepth++; - return; - } - - m_bIgnoreFeature = false; - - m_poReader->PushFeature("Delete", attrs); + const char *pszFilteredClassName = + m_poReader->GetFilteredClassName(); + if (!pszFilteredClassName || EQUAL(pszFilteredClassName, "Delete")) + { + if (m_osTypeName == "") + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "NAS: type name(s) missing at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); + return; + } - m_nDepthFeature = m_nDepth; - m_nDepth++; + int nIndex = attrs.getIndex( + m_osElementName == "ResourceId" ? u"rid" : u"fid"); + if (nIndex == -1) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "NAS: expected feature id missing at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); + return; + } - m_poReader->SetFeaturePropertyDirectly("typeName", - CPLStrdup(m_osLastTypeName)); - m_poReader->SetFeaturePropertyDirectly("context", CPLStrdup(pszLast)); + CPLString osFeatureId; + transcode(attrs.getValue(nIndex), osFeatureId); - if (EQUAL(pszLast, "Delete") && m_osElementName == "ResourceId") - { - char *rid = CPLStrdup(""); + m_poReader->PushFeature("Delete", attrs); + m_poReader->SetFeaturePropertyDirectly("typeName", + CPLStrdup(m_osTypeName)); + m_poReader->SetFeaturePropertyDirectly( + "context", CPLStrdup(m_osDeleteContext)); + m_poReader->SetFeaturePropertyDirectly("FeatureId", + CPLStrdup(osFeatureId)); - m_poReader->CheckForRID(attrs, &rid); + if (m_osDeleteContext == "Replace") + { + if (m_osReplacingFID == "") + { + CPLError( + CE_Failure, CPLE_AssertionFailed, + "NAS: replacing feature id not set at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); + return; + } - m_poReader->SetFeaturePropertyDirectly("FeatureId", rid); - } + m_poReader->SetFeaturePropertyDirectly( + "replacedBy", CPLStrdup(m_osReplacingFID)); + m_poReader->SetFeaturePropertyDirectly( + "safeToIgnore", CPLStrdup(m_osSafeToIgnore)); + m_osReplacingFID = ""; + m_osSafeToIgnore = ""; + } + else if (m_osDeleteContext == "Update") + { + m_poReader->SetFeaturePropertyDirectly( + "endet", CPLStrdup(m_osUpdateEnds)); + for (std::list::iterator it = + m_UpdateOccasions.begin(); + it != m_UpdateOccasions.end(); ++it) + { + m_poReader->SetFeaturePropertyDirectly("anlass", + CPLStrdup(*it)); + } - if (EQUAL(pszLast, "Replace")) - { - // CPLAssert( m_osLastReplacingFID != "" ); - // CPLAssert( m_osLastSafeToIgnore != "" ); - m_poReader->SetFeaturePropertyDirectly( - "replacedBy", CPLStrdup(m_osLastReplacingFID)); - m_poReader->SetFeaturePropertyDirectly( - "safeToIgnore", CPLStrdup(m_osLastSafeToIgnore)); - } - else if (EQUAL(pszLast, "Update")) - { - m_poReader->SetFeaturePropertyDirectly("endet", - CPLStrdup(m_osLastEnded)); + m_osUpdateEnds = ""; + m_UpdateOccasions.clear(); + } - for (std::list::iterator it = m_LastOccasions.begin(); - it != m_LastOccasions.end(); ++it) + return; + } + else { - m_poReader->SetFeaturePropertyDirectly("anlass", - CPLStrdup(*it)); + // we don't issue Delete features + m_osDeleteContext = ""; } - - m_osLastEnded = ""; - m_LastOccasions.clear(); } - - return; - } - - /* -------------------------------------------------------------------- */ - /* Is it a feature? If so push a whole new state, and return. */ - /* -------------------------------------------------------------------- */ - else if (!m_bInUpdateProperty && m_nDepthFeature == 0 && - m_poReader->IsFeatureElement(m_osElementName)) - { - m_osLastTypeName = m_osElementName; - - const char *pszFilteredClassName = m_poReader->GetFilteredClassName(); - - pszLast = m_poReader->GetState()->GetLastComponent(); - if (pszLast != nullptr && EQUAL(pszLast, "Replace")) + else if (m_poReader->IsFeatureElement(m_osElementName)) { - const XMLCh achFID[] = {'g', 'm', 'l', ':', 'i', 'd', '\0'}; - int nIndex = attrs.getIndex(achFID); + m_nDepthFeature = m_nDepth - 1; - if (nIndex == -1 || m_osLastReplacingFID != "") + // record id of replacing feature + if (m_osDeleteContext == "Replace") { - CPLError(CE_Failure, CPLE_AssertionFailed, - "nIndex == -1 || m_osLastReplacingFID !=\"\""); - - m_bIgnoreFeature = true; - m_nDepthFeature = m_nDepth; - m_nDepth++; + int nIndex = attrs.getIndex(u"gml:id"); + if (nIndex == -1) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "NAS: id of replacing feature not set at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); + m_bIgnoreFeature = true; + return; + } - return; + CPLAssert(m_osReplacingFID == ""); + transcode(attrs.getValue(nIndex), m_osReplacingFID); } - // Capture "gml:id" attribute as part of the property value - - // primarily this is for the wfsext:Replace operation's attribute. - transcode(attrs.getValue(nIndex), m_osLastReplacingFID); + m_osTypeName = m_osElementName; -#ifdef DEBUG_VERBOSE - CPLDebug("NAS", "[%d] ### Replace typeName=%s replacedBy=%s", - m_nDepth, m_osLastTypeName.c_str(), - m_osLastReplacingFID.c_str()); -#endif - } + const char *pszFilteredClassName = + m_poReader->GetFilteredClassName(); + m_bIgnoreFeature = pszFilteredClassName && + !EQUAL(m_osElementName, pszFilteredClassName); - if (pszFilteredClassName != nullptr && - m_osElementName != pszFilteredClassName) - { - m_bIgnoreFeature = true; - m_nDepthFeature = m_nDepth; - m_nDepth++; + if (!m_bIgnoreFeature) + m_poReader->PushFeature(m_osElementName, attrs); return; } - - m_bIgnoreFeature = false; - - m_poReader->PushFeature(m_osElementName, attrs); - - m_nDepthFeature = m_nDepth; - m_nDepth++; - - return; } - - /* -------------------------------------------------------------------- */ - /* If it is the wfs:Delete or wfs:Update element, then remember */ - /* the typeName attribute so we can assign it to the feature that */ - /* will be produced when we process the Filter element. */ - /* -------------------------------------------------------------------- */ - else if (m_nUpdateOrDeleteDepth == 0 && - (m_osElementName == "Delete" || m_osElementName == "Update")) + else if (m_pszGeometry != nullptr || IsGeometryElement(m_osElementName)) { - const XMLCh Name0[] = {'t', 'y', 'p', 'e', 'N', 'a', 'm', 'e', '\0'}; - - int nIndex0 = attrs.getIndex(Name0); - if (nIndex0 != -1) + if (m_nGeometryPropertyIndex == -1 && poState->m_poFeature && + poState->m_poFeature->GetClass()) { - transcode(attrs.getValue(nIndex0), m_osLastTypeName); + GMLFeatureClass *poClass = poState->m_poFeature->GetClass(); + m_nGeometryPropertyIndex = + poClass->GetGeometryPropertyIndexBySrcElement( + poState->osPath.c_str()); } - else - { - const XMLCh Name1[] = {'t', 'y', 'p', 'e', 'N', - 'a', 'm', 'e', 's', '\0'}; - int nIndex1 = attrs.getIndex(Name1); - if (nIndex1 != -1) - { - transcode(attrs.getValue(nIndex1), m_osLastTypeName); - } - } + const int nLNLen = static_cast(m_osElementName.size()); + CPLString osAttributes = GetAttributes(&attrs); - m_osLastSafeToIgnore = ""; - m_osLastReplacingFID = ""; + /* should save attributes too! */ - if (m_osElementName == "Update") + if (m_pszGeometry == nullptr) + m_nGeometryDepth = poState->m_nPathLength; + + if (m_pszGeometry == nullptr || + m_nGeomLen + nLNLen + 4 + (int)osAttributes.size() > m_nGeomAlloc) { - m_bInUpdate = true; + m_nGeomAlloc = + (int)(m_nGeomAlloc * 1.3 + nLNLen + osAttributes.size() + 1000); + m_pszGeometry = (char *)CPLRealloc(m_pszGeometry, m_nGeomAlloc); } - m_nUpdateOrDeleteDepth = m_nDepth; - } - - else if (m_nUpdatePropertyDepth == 0 && m_bInUpdate && - m_osElementName == "Property") - { - m_bInUpdateProperty = true; - m_nUpdatePropertyDepth = m_nDepth; - } - - else if (m_nNameOrValueDepth == 0 && m_bInUpdateProperty && - (m_osElementName == "Name" || m_osElementName == "Value" || - m_osElementName == "ValueReference")) - { - // collect attribute name or value - CPLFree(m_pszCurField); - m_pszCurField = CPLStrdup(""); - m_nNameOrValueDepth = m_nDepth; - } - - /* -------------------------------------------------------------------- */ - /* If it is the wfsext:Replace element, then remember the */ - /* safeToIgnore attribute so we can assign it to the feature */ - /* that will be produced when we process the Filter element. */ - /* -------------------------------------------------------------------- */ - else if (m_osElementName == "Replace") - { - const XMLCh Name[] = {'s', 'a', 'f', 'e', 'T', 'o', 'I', - 'g', 'n', 'o', 'r', 'e', '\0'}; - int nIndex = attrs.getIndex(Name); + strcpy(m_pszGeometry + m_nGeomLen, "<"); + strcpy(m_pszGeometry + m_nGeomLen + 1, m_osElementName); - if (nIndex != -1) - { - transcode(attrs.getValue(nIndex), m_osLastSafeToIgnore); - } - else + if (!osAttributes.empty()) { - CPLError(CE_Warning, CPLE_AppDefined, - "NAS: safeToIgnore attribute missing"); - m_osLastSafeToIgnore = "false"; + strcat(m_pszGeometry + m_nGeomLen, " "); + strcat(m_pszGeometry + m_nGeomLen, osAttributes); } - m_osLastReplacingFID = ""; + strcat(m_pszGeometry + m_nGeomLen, ">"); + m_nGeomLen += static_cast(strlen(m_pszGeometry + m_nGeomLen)); } - - /* -------------------------------------------------------------------- */ - /* If it is (or at least potentially is) a simple attribute, */ - /* then start collecting it. */ - /* -------------------------------------------------------------------- */ else if (m_poReader->IsAttributeElement(m_osElementName, attrs)) { + m_poReader->DealWithAttributes( + m_osElementName, static_cast(m_osElementName.length()), attrs); CPLFree(m_pszCurField); m_pszCurField = CPLStrdup(""); - - m_poReader->DealWithAttributes(m_osElementName, m_osElementName.length(), attrs); - - // Capture "fid" attribute as part of the property value - - // primarily this is for wfs:Delete operation's FeatureId attribute. - if (m_osElementName == "FeatureId") - m_poReader->CheckForFID(attrs, &m_pszCurField); - else if (m_osElementName == "ResourceId") - m_poReader->CheckForRID(attrs, &m_pszCurField); } - /* -------------------------------------------------------------------- */ - /* Push the element onto the current state's path. */ - /* -------------------------------------------------------------------- */ poState->PushPath(m_osElementName); - m_nDepth++; - if (poState->osPath.size() > 512) { - CPLError(CE_Failure, CPLE_AppDefined, "Too long path. Stop parsing"); + CPLError(CE_Failure, CPLE_AssertionFailed, + "NAS: Too long path. Stop parsing at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); m_poReader->StopParsing(); } } @@ -545,12 +431,6 @@ void NASHandler::endElement(const XMLCh *const /* uri */, transcode(localname, m_osElementName); m_nDepth--; -#ifdef DEBUG_TRACE_ELEMENTS - for (int k = 0; k < m_nDepth; k++) - printf(" "); /*ok*/ - printf("<%s\n", m_osElementName.c_str()); /*ok*/ -#endif - if (m_bIgnoreFeature && m_nDepth >= m_nDepthFeature) { if (m_nDepth == m_nDepthFeature) @@ -561,100 +441,48 @@ void NASHandler::endElement(const XMLCh *const /* uri */, return; } -#ifdef DEBUG_VERBOSE - CPLDebug("NAS", - "[%d] endElement %s m_bIgnoreFeature:%d m_nDepth:%d " - "m_nDepthFeature:%d featureClass:%s", - m_nDepth, m_osElementName.c_str(), m_bIgnoreFeature, m_nDepth, - m_nDepthFeature, - poState->m_poFeature - ? poState->m_poFeature->GetClass()->GetElementName() - : "(no feature)"); -#endif - - if (m_bInUpdateProperty) + if (m_osDeleteContext == "Update") { - if (m_nDepth == m_nNameOrValueDepth && - (m_osElementName == "Name" || m_osElementName == "ValueReference")) + if (m_osElementName == "Name" || m_osElementName == "ValueReference") { - m_osLastPropertyName = m_pszCurField ? m_pszCurField : ""; + const char *pszName = m_pszCurField; + pszName = strrchr(m_pszCurField, '/'); + pszName = pszName ? pszName + 1 : m_pszCurField; + pszName = strrchr(pszName, ':'); + pszName = pszName ? pszName + 1 : m_pszCurField; + + CPLAssert(m_osUpdatePropertyName == ""); + m_osUpdatePropertyName = pszName; CPLFree(m_pszCurField); m_pszCurField = nullptr; - m_nNameOrValueDepth = 0; - } - else if (m_osElementName == "Value" && m_nDepth == m_nNameOrValueDepth) - { - m_osLastPropertyValue = m_pszCurField ? m_pszCurField : ""; - CPLFree(m_pszCurField); - m_pszCurField = nullptr; - m_nNameOrValueDepth = 0; - } - else if (m_nDepth == m_nUpdatePropertyDepth && - m_osElementName == "Property") - { - if (EQUAL(m_osLastPropertyName, - "adv:lebenszeitintervall/adv:AA_Lebenszeitintervall/" - "adv:endet") || - EQUAL(m_osLastPropertyName, - "lebenszeitintervall/AA_Lebenszeitintervall/endet")) - { - CPLAssert(m_osLastPropertyValue != ""); - m_osLastEnded = m_osLastPropertyValue; - } - else if (EQUAL(m_osLastPropertyName, "adv:anlass") || - EQUAL(m_osLastPropertyName, "anlass")) - { - CPLAssert(m_osLastPropertyValue != ""); - m_LastOccasions.push_back(m_osLastPropertyValue); - } - else + + if (m_osUpdatePropertyName != "endet" && + m_osUpdatePropertyName != "anlass") { - CPLError(CE_Warning, CPLE_AppDefined, - "NAS: Expected property name or value instead of %s", - m_osLastPropertyName.c_str()); + CPLError(CE_Failure, CPLE_AppDefined, + "NAS: Unexpected property name %s at %d:%d", + m_osUpdatePropertyName.c_str(), + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); + m_osUpdatePropertyName = ""; } - - m_osLastPropertyName = ""; - m_osLastPropertyValue = ""; - m_bInUpdateProperty = false; - m_nUpdatePropertyDepth = 0; } - - poState->PopPath(); - - return; - } - - if (m_nUpdateOrDeleteDepth > 0 && - (m_osElementName == "Delete" || m_osElementName == "Update")) - { - if (m_bInUpdate && m_osElementName == "Update") + else if (m_osElementName == "Value") { - m_bInUpdate = false; + CPLAssert(m_osUpdatePropertyName != ""); + if (m_osUpdatePropertyName == "endet") + m_osUpdateEnds = m_pszCurField; + else if (m_osUpdatePropertyName == "anlass") + m_UpdateOccasions.push_back(m_pszCurField); + m_osUpdatePropertyName = ""; + CPLFree(m_pszCurField); + m_pszCurField = nullptr; } - m_nUpdateOrDeleteDepth = 0; } - - /* -------------------------------------------------------------------- */ - /* Is this closing off an attribute value? We assume so if */ - /* we are collecting an attribute value and got to this point. */ - /* We don't bother validating that the closing tag matches the */ - /* opening tag. */ - /* -------------------------------------------------------------------- */ - if (m_pszCurField != nullptr) + else if (m_pszCurField != nullptr && poState->m_poFeature != nullptr) { - CPLAssert(poState->m_poFeature != nullptr); - - // keep using "featureid" for GID 7 - const char *pszPath; - if (EQUAL(poState->m_poFeature->GetClass()->GetElementName(), - "Delete") && - poState->osPath == "ResourceId") - pszPath = "FeatureId"; - else - pszPath = poState->osPath.c_str(); - - m_poReader->SetFeaturePropertyDirectly(pszPath, m_pszCurField); + m_poReader->SetFeaturePropertyDirectly(poState->osPath.c_str(), + m_pszCurField); m_pszCurField = nullptr; } @@ -719,23 +547,28 @@ void NASHandler::endElement(const XMLCh *const /* uri */, poState->m_poFeature ->GetGeometryList()[m_nGeometryPropertyIndex]); - CPLDebug("NAS", - "Overwriting other geometry (%s; replace:%s; " - "with:%s)", - poIdProp && poIdProp->nSubProperties > 0 && - poIdProp->papszSubProperties[0] - ? poIdProp->papszSubProperties[0] - : "(null)", - m_pszGeometry, pszOldGeom); + NASDebug( + "Overwriting other geometry (%s; replace:%s; " + "with:%s) at %d:%d", + poIdProp && poIdProp->nSubProperties > 0 && + poIdProp->papszSubProperties[0] + ? poIdProp->papszSubProperties[0] + : "(null)", + m_pszGeometry, pszOldGeom, + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); CPLFree(pszOldGeom); #else - CPLError(CE_Warning, CPLE_AppDefined, - "NAS: Overwriting other geometry (%s)", - poIdProp && poIdProp->nSubProperties > 0 && - poIdProp->papszSubProperties[0] - ? poIdProp->papszSubProperties[0] - : "(null)"); + CPLError( + CE_Warning, CPLE_AppDefined, + "NAS: Overwriting other geometry (%s) at %d:%d", + poIdProp && poIdProp->nSubProperties > 0 && + poIdProp->papszSubProperties[0] + ? poIdProp->papszSubProperties[0] + : "(null)", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); #endif } @@ -758,21 +591,28 @@ void NASHandler::endElement(const XMLCh *const /* uri */, else { - CPLError(CE_Warning, CPLE_AppDefined, - "NAS: Unexpected geometry skipped (class:%s " - "path:%s geom:%s)", - poState->m_poFeature->GetClass()->GetName(), - poState->osPath.c_str(), m_pszGeometry); + CPLError( + CE_Warning, CPLE_AssertionFailed, + "NAS: Unexpected geometry skipped (class:%s " + "path:%s geom:%s) at %d:%d", + poState->m_poFeature->GetClass()->GetName(), + poState->osPath.c_str(), m_pszGeometry, + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); CPLDestroyXMLNode(psNode); } } else CPLError(CE_Warning, CPLE_AppDefined, - "NAS: Invalid geometry skipped"); + "NAS: Invalid geometry skipped at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); } else CPLError(CE_Warning, CPLE_AppDefined, - "NAS: Skipping geometry without feature"); + "NAS: Skipping geometry without feature at %d:%d", + static_cast(m_Locator->getLineNumber()), + static_cast(m_Locator->getColumnNumber())); CPLFree(m_pszGeometry); m_pszGeometry = nullptr; @@ -781,56 +621,22 @@ void NASHandler::endElement(const XMLCh *const /* uri */, } } - /* -------------------------------------------------------------------- */ - /* If we are collecting a feature, and this element tag matches */ - /* element name for the class, then we have finished the */ - /* feature, and we pop the feature read state. */ - /* -------------------------------------------------------------------- */ - const char *pszLast = nullptr; - - if (m_nDepth == m_nDepthFeature && poState->m_poFeature != nullptr && - m_osElementName == poState->m_poFeature->GetClass()->GetElementName()) + // Finished actual feature or ResourceId/FeatureId of Delete/Replace/Update operation + if ((m_nDepth == m_nDepthFeature && poState->m_poFeature != nullptr && + EQUAL(m_osElementName, + poState->m_poFeature->GetClass()->GetElementName())) || + (m_osDeleteContext != "" && + (m_osElementName == "ResourceId" || m_osElementName == "FeatureId"))) { m_nDepthFeature = 0; m_poReader->PopState(); } - - /* -------------------------------------------------------------------- */ - /* Ends of a wfs:Delete or wfs:Update should be triggered on the */ - /* close of the element. */ - /* -------------------------------------------------------------------- */ - else if (m_nDepth == m_nDepthFeature && poState->m_poFeature != nullptr && - m_osElementName == "Filter" && - (pszLast = poState->m_poFeature->GetClass()->GetElementName()) != - nullptr && - (EQUAL(pszLast, "Delete") || EQUAL(pszLast, "Update"))) - { - m_nDepthFeature = 0; - m_poReader->PopState(); - } - - else if (m_nDepth >= m_nDepthFeature && poState->m_poFeature != nullptr && - m_osElementName == "ResourceId" && - (pszLast = poState->m_poFeature->GetClass()->GetElementName()) != - nullptr && - EQUAL(pszLast, "Delete")) - { - m_nDepthFeature = 0; - m_poReader->PopState(); - } - - /* -------------------------------------------------------------------- */ - /* Otherwise, we just pop the element off the local read states */ - /* element stack. */ - /* -------------------------------------------------------------------- */ else + poState->PopPath(); + + if (m_osDeleteContext == m_osElementName) { - if (m_osElementName == poState->GetLastComponent()) - poState->PopPath(); - else - { - CPLAssert(false); - } + m_osDeleteContext = ""; } } @@ -899,8 +705,8 @@ void NASHandler::fatalError(const SAXParseException &exception) CPLString osErrMsg; transcode(exception.getMessage(), osErrMsg); CPLError(CE_Failure, CPLE_AppDefined, - "XML Parsing Error: %s at line %d, column %d\n", osErrMsg.c_str(), - static_cast(exception.getLineNumber()), + "NAS: XML Parsing Error: %s at line %d, column %d\n", + osErrMsg.c_str(), static_cast(exception.getLineNumber()), static_cast(exception.getColumnNumber())); } @@ -926,4 +732,4 @@ bool NASHandler::IsGeometryElement(const char *pszElement) strcmp(pszElement, "LineString") == 0; } -// vim: set sw=4 expandtab : +// vim: set sw=4 expandtab ai : diff --git a/ogr/ogrsf_frmts/nas/nasreader.cpp b/ogr/ogrsf_frmts/nas/nasreader.cpp index bb76b31eeaf5..03047e5742ab 100644 --- a/ogr/ogrsf_frmts/nas/nasreader.cpp +++ b/ogr/ogrsf_frmts/nas/nasreader.cpp @@ -181,7 +181,7 @@ bool NASReader::SetupParser() XMLString::release(&xmlUriNS); CPLError(CE_Warning, CPLE_AppDefined, - "Exception initializing Xerces based GML reader.\n"); + "NAS: Exception initializing Xerces based GML reader.\n"); return false; } @@ -351,8 +351,7 @@ void NASReader::PushFeature(const char *pszElement, const Attributes &attrs) /* Check for gml:id, and if found push it as an attribute named */ /* gml_id. */ /* -------------------------------------------------------------------- */ - const XMLCh achFID[] = {'g', 'm', 'l', ':', 'i', 'd', '\0'}; - int nFIDIndex = attrs.getIndex(achFID); + int nFIDIndex = attrs.getIndex(u"gml:id"); if (nFIDIndex != -1) { char *pszFID = CPLStrdup(transcode(attrs.getValue(nFIDIndex))); @@ -370,6 +369,9 @@ void NASReader::PushFeature(const char *pszElement, const Attributes &attrs) bool NASReader::IsFeatureElement(const char *pszElement) { + if (EQUAL(pszElement, "Delete")) + return false; + CPLAssert(m_poState != nullptr); const char *pszLast = m_poState->GetLastComponent(); @@ -384,11 +386,6 @@ bool NASReader::IsFeatureElement(const char *pszElement) (nLen < 7 || !EQUAL(pszLast + nLen - 7, "Replace"))) return false; - // If the class list isn't locked, any element that is a featureMember - // will do. - if (!IsClassListLocked()) - return true; - // otherwise, find a class with the desired element name. for (int i = 0; i < GetClassCount(); i++) { @@ -403,7 +400,8 @@ bool NASReader::IsFeatureElement(const char *pszElement) /* IsAttributeElement() */ /************************************************************************/ -bool NASReader::IsAttributeElement(const char *pszElement, const Attributes &attrs) +bool NASReader::IsAttributeElement(const char *pszElement, + const Attributes &attrs) { if (m_poState->m_poFeature == nullptr) @@ -429,8 +427,8 @@ bool NASReader::IsAttributeElement(const char *pszElement, const Attributes &att osElemPath += pszElement; } - if( poClass->GetPropertyIndexBySrcElement( - osElemPath.c_str(), static_cast(osElemPath.size())) >= 0 ) + if (poClass->GetPropertyIndexBySrcElement( + osElemPath.c_str(), static_cast(osElemPath.size())) >= 0) return true; for (unsigned int idx = 0; idx < attrs.getLength(); ++idx) @@ -439,18 +437,19 @@ bool NASReader::IsAttributeElement(const char *pszElement, const Attributes &att CPLString osAttrPath; const char *pszName = strchr(osAttrName.c_str(), ':'); - if ( pszName ) + if (pszName) { - osAttrPath = osElemPath + "@" + (pszName+1); - if ( poClass->GetPropertyIndexBySrcElement( - osAttrPath.c_str(), static_cast(osAttrPath.size())) >= 0 ) - return true; + osAttrPath = osElemPath + "@" + (pszName + 1); + if (poClass->GetPropertyIndexBySrcElement( + osAttrPath.c_str(), static_cast(osAttrPath.size())) >= + 0) + return true; } osAttrPath = osElemPath + "@" + osAttrName; - if( poClass->GetPropertyIndexBySrcElement( - osAttrPath.c_str(), static_cast(osAttrPath.size())) >= 0 ) - return true; + if (poClass->GetPropertyIndexBySrcElement( + osAttrPath.c_str(), static_cast(osAttrPath.size())) >= 0) + return true; } return false; @@ -597,7 +596,7 @@ void NASReader::SetFeaturePropertyDirectly(const char *pszElement, { if (poClass->IsSchemaLocked()) { - CPLDebug("NAS", "Encountered property missing from class schema."); + // CPLDebug("NAS", "Encountered property %s missing from class %s schema [%s].", pszElement, poClass->GetName(), pszValue); CPLFree(pszValue); return; } @@ -639,7 +638,7 @@ void NASReader::SetFeaturePropertyDirectly(const char *pszElement, const GMLProperty *poIdProp = poFeature->GetProperty(iId); CPLError(CE_Warning, CPLE_AppDefined, - "Overwriting existing property %s.%s of value '%s' " + "NAS: Overwriting existing property %s.%s of value '%s' " "with '%s' (gml_id: %s; type:%d).", poClass->GetName(), pszElement, poProp->papszSubProperties[0], pszValue, @@ -696,7 +695,7 @@ bool NASReader::LoadClasses(const char *pszFile) if (fp == nullptr) { - CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.", + CPLError(CE_Failure, CPLE_OpenFailed, "NAS: Failed to open file %s.", pszFile); return false; } @@ -709,7 +708,7 @@ bool NASReader::LoadClasses(const char *pszFile) if (pszWholeText == nullptr) { CPLError(CE_Failure, CPLE_AppDefined, - "Failed to allocate %d byte buffer for %s,\n" + "NAS: Failed to allocate %d byte buffer for %s,\n" "is this really a GMLFeatureClassList file?", nLength, pszFile); VSIFCloseL(fp); @@ -720,7 +719,8 @@ bool NASReader::LoadClasses(const char *pszFile) { VSIFree(pszWholeText); VSIFCloseL(fp); - CPLError(CE_Failure, CPLE_AppDefined, "Read failed on %s.", pszFile); + CPLError(CE_Failure, CPLE_AppDefined, "NAS: Read failed on %s.", + pszFile); return false; } pszWholeText[nLength] = '\0'; @@ -731,7 +731,7 @@ bool NASReader::LoadClasses(const char *pszFile) { VSIFree(pszWholeText); CPLError(CE_Failure, CPLE_AppDefined, - "File %s does not contain a GMLFeatureClassList tree.", + "NAS: File %s does not contain a GMLFeatureClassList tree.", pszFile); return false; } @@ -750,7 +750,8 @@ bool NASReader::LoadClasses(const char *pszFile) !EQUAL(psRoot->pszValue, "GMLFeatureClassList")) { CPLError(CE_Failure, CPLE_AppDefined, - "File %s is not a GMLFeatureClassList document.", pszFile); + "NAS: File %s is not a GMLFeatureClassList document.", + pszFile); return false; } @@ -967,11 +968,11 @@ bool NASReader::PrescanForSchema(bool bGetExtents, bool /*bOnlyDetectSRS*/) m_nClassCount = j; - CPLDebug("NAS", "%d remaining classes after prescan.\n", m_nClassCount); + CPLDebug("NAS", "%d remaining classes after prescan.", m_nClassCount); for (int i = 0; i < m_nClassCount; i++) { - CPLDebug("NAS", "%s: " CPL_FRMT_GIB " features.\n", + CPLDebug("NAS", "%s: " CPL_FRMT_GIB " features.", m_papoClass[i]->GetName(), m_papoClass[i]->GetFeatureCount()); } @@ -989,52 +990,6 @@ void NASReader::ResetReading() SetFilteredClassName(nullptr); } -/************************************************************************/ -/* CheckForFID() */ -/* */ -/* Merge the fid attribute into the current field text. */ -/************************************************************************/ - -void NASReader::CheckForFID(const Attributes &attrs, char **ppszCurField) - -{ - const XMLCh Name[] = {'f', 'i', 'd', '\0'}; - int nIndex = attrs.getIndex(Name); - - if (nIndex != -1) - { - CPLString osCurField = *ppszCurField; - - osCurField += transcode(attrs.getValue(nIndex)); - - CPLFree(*ppszCurField); - *ppszCurField = CPLStrdup(osCurField); - } -} - -/************************************************************************/ -/* CheckForRID() */ -/* */ -/* Merge the rid attribute into the current field text. */ -/************************************************************************/ - -void NASReader::CheckForRID(const Attributes &attrs, char **ppszCurField) - -{ - const XMLCh Name[] = {'r', 'i', 'd', '\0'}; - int nIndex = attrs.getIndex(Name); - - if (nIndex != -1) - { - CPLString osCurField = *ppszCurField; - - osCurField += transcode(attrs.getValue(nIndex)); - - CPLFree(*ppszCurField); - *ppszCurField = CPLStrdup(osCurField); - } -} - /************************************************************************/ /* GetAttributeElementIndex() */ /************************************************************************/ @@ -1045,11 +1000,6 @@ int NASReader::GetAttributeElementIndex(const char *pszElement, int nLen, { GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass(); - // If the schema is not yet locked, then any simple element - // is potentially an attribute. - if (!poClass->IsSchemaLocked()) - return INT_MAX; - // Otherwise build the path to this element into a single string // and compare against known attributes. if (m_poState->m_nPathLength == 0) @@ -1087,13 +1037,16 @@ int NASReader::GetAttributeElementIndex(const char *pszElement, int nLen, return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(), nFullLen); } + + return -1; } /************************************************************************/ /* DealWithAttributes() */ /************************************************************************/ -void NASReader::DealWithAttributes(const char *pszName, int nLenName, const Attributes &attrs) +void NASReader::DealWithAttributes(const char *pszName, int nLenName, + const Attributes &attrs) { GMLFeature *poFeature = GetState()->m_poFeature; @@ -1107,18 +1060,23 @@ void NASReader::DealWithAttributes(const char *pszName, int nLenName, const Attr int nAttrIndex = 0; const char *pszAttrKeyNoNS = strchr(osAttrKey, ':'); if (pszAttrKeyNoNS) - pszAttrKeyNoNS++; + pszAttrKeyNoNS++; if ((pszAttrKeyNoNS && - (nAttrIndex = GetAttributeElementIndex(pszName, nLenName, pszAttrKeyNoNS)) != -1) || - ((nAttrIndex = GetAttributeElementIndex(pszName, nLenName, osAttrKey)) != -1)) + (nAttrIndex = GetAttributeElementIndex(pszName, nLenName, + pszAttrKeyNoNS)) != -1) || + ((nAttrIndex = GetAttributeElementIndex(pszName, nLenName, + osAttrKey)) != -1)) { const char *pszAttrVal = osAttrVal; - if ( strcmp(osAttrKey, "xlink:href") == 0 ) + if (osAttrKey == "xlink:href" || + (pszAttrKeyNoNS && EQUAL(pszAttrKeyNoNS, "href"))) { - if (STARTS_WITH_CI(pszAttrVal , "urn:adv:oid:")) + if (STARTS_WITH_CI(pszAttrVal, "urn:adv:oid:")) pszAttrVal += 12; - else if (STARTS_WITH_CI(pszAttrVal , "https://registry.gdi-de.org/codelist/")) + else if (STARTS_WITH_CI( + pszAttrVal, + "https://registry.gdi-de.org/codelist/")) pszAttrVal = strrchr(pszAttrVal, '/') + 1; } diff --git a/ogr/ogrsf_frmts/nas/nasreaderp.h b/ogr/ogrsf_frmts/nas/nasreaderp.h index c27dec259258..5947dab4bf83 100644 --- a/ogr/ogrsf_frmts/nas/nasreaderp.h +++ b/ogr/ogrsf_frmts/nas/nasreaderp.h @@ -69,26 +69,21 @@ class NASHandler final : public DefaultHandler int m_nDepth; int m_nDepthFeature; bool m_bIgnoreFeature; - bool m_bInUpdate; - bool m_bInUpdateProperty; - int m_nUpdateOrDeleteDepth; - int m_nUpdatePropertyDepth; - int m_nNameOrValueDepth; - CPLString m_osLastTypeName; - CPLString m_osLastReplacingFID; - CPLString m_osLastSafeToIgnore; - CPLString m_osLastPropertyName; - CPLString m_osLastPropertyValue; - CPLString m_osLastEnded; - - std::list m_LastOccasions; + CPLString m_osDeleteContext; + CPLString m_osTypeName; + CPLString m_osReplacingFID; + CPLString m_osSafeToIgnore; + CPLString m_osUpdatePropertyName; + CPLString m_osUpdateEnds; + CPLString m_osFeatureId; + std::list m_UpdateOccasions; CPLString m_osElementName; - CPLString m_osAttrName; - CPLString m_osAttrValue; CPLString m_osCharacters; + const Locator *m_Locator; + public: explicit NASHandler(NASReader *poReader); virtual ~NASHandler(); @@ -106,6 +101,8 @@ class NASHandler final : public DefaultHandler void fatalError(const SAXParseException &) override; + void setDocumentLocator(const Locator *locator) override; + CPLString GetAttributes(const Attributes *attr); }; @@ -248,11 +245,10 @@ class NASReader final : public IGMLReader return m_bStopParsing; } - int GetAttributeElementIndex(const char *pszElement, int nLen, const char *pszAttrKey); - - void CheckForFID(const Attributes &attrs, char **ppszCurField); - void CheckForRID(const Attributes &attrs, char **ppszCurField); - void DealWithAttributes(const char *pszElement, int nLenName, const Attributes &attrs); + int GetAttributeElementIndex(const char *pszElement, int nLen, + const char *pszAttrKey); + void DealWithAttributes(const char *pszElement, int nLenName, + const Attributes &attrs); virtual const char *GetGlobalSRSName() override {