/src/gdal/ogr/ogr_schema_override.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * Project: OGR_SCHEMA open options handling |
3 | | * Purpose: Class for representing a layer schema override. |
4 | | * Author: Alessandro Pasotti, elpaso@itopen.it |
5 | | * |
6 | | ****************************************************************************** |
7 | | * Copyright (c) 2024, Alessandro Pasotti <elpaso at itopen dot it> |
8 | | * |
9 | | * SPDX-License-Identifier: MIT |
10 | | ****************************************************************************/ |
11 | | |
12 | | //! @cond Doxygen_Suppress |
13 | | |
14 | | #include "ogr_schema_override.h" |
15 | | |
16 | | bool OGRSchemaOverride::LoadFromJSON(const std::string &osJSON) |
17 | 0 | { |
18 | 0 | std::string osFieldsSchemaOverride; |
19 | 0 | bool bFieldsSchemaOverrideIsFilePath{false}; |
20 | | |
21 | | // Try to load the content of the file |
22 | 0 | GByte *pabyRet = nullptr; |
23 | 0 | if (VSIIngestFile(nullptr, osJSON.c_str(), &pabyRet, nullptr, -1) == TRUE) |
24 | 0 | { |
25 | 0 | bFieldsSchemaOverrideIsFilePath = true; |
26 | 0 | osFieldsSchemaOverride = std::string(reinterpret_cast<char *>(pabyRet)); |
27 | 0 | VSIFree(pabyRet); |
28 | 0 | } |
29 | |
|
30 | 0 | if (!bFieldsSchemaOverrideIsFilePath) |
31 | 0 | { |
32 | 0 | osFieldsSchemaOverride = osJSON; |
33 | 0 | } |
34 | |
|
35 | 0 | CPLJSONDocument oSchemaDoc; |
36 | 0 | if (oSchemaDoc.LoadMemory(osFieldsSchemaOverride)) |
37 | 0 | { |
38 | 0 | const CPLJSONObject oRoot = oSchemaDoc.GetRoot(); |
39 | 0 | if (oRoot.IsValid()) |
40 | 0 | { |
41 | 0 | const auto aoLayers = oRoot.GetArray("layers"); |
42 | | // Loop through layer names and get the field details for each field. |
43 | 0 | for (const auto &oLayer : aoLayers) |
44 | 0 | { |
45 | 0 | if (oLayer.IsValid()) |
46 | 0 | { |
47 | 0 | const auto oLayerFields = oLayer.GetArray("fields"); |
48 | | // Parse fields |
49 | 0 | const auto osLayerName = oLayer.GetString("name"); |
50 | 0 | const auto osSchemaType = oLayer.GetString("schemaType"); |
51 | | // Default schemaType is "Patch" |
52 | 0 | const auto bSchemaFullOverride = |
53 | 0 | CPLString(osSchemaType).tolower() == "full"; |
54 | 0 | OGRLayerSchemaOverride oLayerOverride; |
55 | 0 | oLayerOverride.SetLayerName(osLayerName); |
56 | 0 | oLayerOverride.SetFullOverride(bSchemaFullOverride); |
57 | |
|
58 | 0 | if (oLayerFields.Size() > 0 && !osLayerName.empty()) |
59 | 0 | { |
60 | 0 | for (const auto &oField : oLayerFields) |
61 | 0 | { |
62 | 0 | const auto osFieldName = oField.GetString("name"); |
63 | 0 | if (osFieldName.empty()) |
64 | 0 | { |
65 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
66 | 0 | "Field name is missing"); |
67 | 0 | return false; |
68 | 0 | } |
69 | 0 | OGRFieldDefnOverride oFieldOverride; |
70 | |
|
71 | 0 | const CPLString oType( |
72 | 0 | CPLString(oField.GetString("type")).tolower()); |
73 | 0 | const CPLString oSubType( |
74 | 0 | CPLString(oField.GetString("subType")) |
75 | 0 | .tolower()); |
76 | 0 | const CPLString osNewName( |
77 | 0 | CPLString(oField.GetString("newName")) |
78 | 0 | .tolower()); |
79 | 0 | const auto nWidth = oField.GetInteger("width", 0); |
80 | 0 | const auto nPrecision = |
81 | 0 | oField.GetInteger("precision", 0); |
82 | |
|
83 | 0 | if (!osNewName.empty()) |
84 | 0 | { |
85 | 0 | oFieldOverride.SetFieldName(osNewName); |
86 | 0 | } |
87 | |
|
88 | 0 | if (!oType.empty()) |
89 | 0 | { |
90 | 0 | const OGRFieldType eType = |
91 | 0 | OGRFieldDefn::GetFieldTypeByName( |
92 | 0 | oType.c_str()); |
93 | | // Check if the field type is valid |
94 | 0 | if (eType == OFTString && oType != "string") |
95 | 0 | { |
96 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
97 | 0 | "Unsupported field type: %s " |
98 | 0 | "for field %s", |
99 | 0 | oType.c_str(), |
100 | 0 | osFieldName.c_str()); |
101 | 0 | return false; |
102 | 0 | } |
103 | 0 | oFieldOverride.SetFieldType(eType); |
104 | 0 | } |
105 | | |
106 | 0 | if (!oSubType.empty()) |
107 | 0 | { |
108 | 0 | const OGRFieldSubType eSubType = |
109 | 0 | OGRFieldDefn::GetFieldSubTypeByName( |
110 | 0 | oSubType.c_str()); |
111 | | // Check if the field subType is valid |
112 | 0 | if (eSubType == OFSTNone && oSubType != "none") |
113 | 0 | { |
114 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
115 | 0 | "Unsupported field subType: " |
116 | 0 | "%s for field %s", |
117 | 0 | oSubType.c_str(), |
118 | 0 | osFieldName.c_str()); |
119 | 0 | return false; |
120 | 0 | } |
121 | 0 | oFieldOverride.SetFieldSubType(eSubType); |
122 | 0 | } |
123 | | |
124 | 0 | if (nWidth != 0) |
125 | 0 | { |
126 | 0 | oFieldOverride.SetFieldWidth(nWidth); |
127 | 0 | } |
128 | |
|
129 | 0 | if (nPrecision != 0) |
130 | 0 | { |
131 | 0 | oFieldOverride.SetFieldPrecision(nPrecision); |
132 | 0 | } |
133 | |
|
134 | 0 | if (bSchemaFullOverride || oFieldOverride.IsValid()) |
135 | 0 | { |
136 | 0 | oLayerOverride.AddFieldOverride(osFieldName, |
137 | 0 | oFieldOverride); |
138 | 0 | } |
139 | 0 | else |
140 | 0 | { |
141 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
142 | 0 | "Field %s has no valid overrides " |
143 | 0 | "and schemaType is not \"Full\"", |
144 | 0 | osFieldName.c_str()); |
145 | 0 | return false; |
146 | 0 | } |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | 0 | if (oLayerOverride.IsValid()) |
151 | 0 | { |
152 | 0 | AddLayerOverride(osLayerName, oLayerOverride); |
153 | 0 | } |
154 | 0 | else |
155 | 0 | { |
156 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
157 | 0 | "Layer %s has no valid overrides", |
158 | 0 | osLayerName.c_str()); |
159 | 0 | return false; |
160 | 0 | } |
161 | 0 | } |
162 | 0 | else |
163 | 0 | { |
164 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
165 | 0 | "SCHEMA info is invalid JSON"); |
166 | 0 | return false; |
167 | 0 | } |
168 | 0 | } |
169 | 0 | return true; |
170 | 0 | } |
171 | 0 | else |
172 | 0 | { |
173 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
174 | 0 | "SCHEMA info is invalid JSON"); |
175 | 0 | return false; |
176 | 0 | } |
177 | 0 | } |
178 | 0 | else |
179 | 0 | { |
180 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "SCHEMA info is invalid JSON"); |
181 | 0 | return false; |
182 | 0 | } |
183 | 0 | } |
184 | | |
185 | | bool OGRSchemaOverride::IsValid() const |
186 | 0 | { |
187 | 0 | bool isValid = !m_moLayerOverrides.empty(); |
188 | 0 | for (const auto &oLayerOverride : m_moLayerOverrides) |
189 | 0 | { |
190 | 0 | isValid &= oLayerOverride.second.IsValid(); |
191 | 0 | } |
192 | 0 | return isValid; |
193 | 0 | } |
194 | | |
195 | | bool OGRLayerSchemaOverride::IsValid() const |
196 | 0 | { |
197 | 0 | bool isValid = !m_osLayerName.empty() && !m_moFieldOverrides.empty(); |
198 | 0 | for (const auto &oFieldOverride : m_moFieldOverrides) |
199 | 0 | { |
200 | 0 | isValid &= !oFieldOverride.first.empty(); |
201 | | // When schemaType is "full" override we don't need to check if the field |
202 | | // overrides are valid: a list of fields to keep is enough. |
203 | 0 | if (!m_bIsFullOverride) |
204 | 0 | { |
205 | 0 | isValid &= oFieldOverride.second.IsValid(); |
206 | 0 | } |
207 | 0 | } |
208 | 0 | return isValid; |
209 | 0 | } |
210 | | |
211 | | bool OGRFieldDefnOverride::IsValid() const |
212 | 0 | { |
213 | 0 | return m_osName.has_value() || m_eType.has_value() || |
214 | 0 | m_eSubType.has_value() || m_nWidth.has_value() || |
215 | 0 | m_nPrecision.has_value(); |
216 | 0 | } |
217 | | |
218 | | //! @endcond |