1 /*
2  * Copyright 2006-2008 The FLWOR Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "stdafx.h"
17 
18 #include "runtime/parsing_and_serializing/parse_fragment.h"
19 
20 #include <iostream>
21 
22 #include "zorbatypes/URI.h"
23 #include "context/static_context.h"
24 
25 #include "store/api/store.h"
26 #include "store/api/item.h"
27 #include "store/api/item_factory.h"
28 #include "store/api/load_properties.h"
29 
30 #include "system/globalenv.h"
31 
32 #include "types/schema/schema.h"
33 #include "types/schema/validate.h"
34 
35 
36 namespace zorba
37 {
38 
39 /*******************************************************************************
40   14.9.1 fn-zorba-xml:parse
41 ********************************************************************************/
42 
getFirstAttribute(store::Item_t node)43 store::Item_t getFirstAttribute(store::Item_t node)
44 {
45   store::Item_t attr;
46   store::Iterator_t attributes = node->getAttributes();
47   attributes->open();
48   attributes->next(attr);
49   attributes->close();
50   return attr;
51 }
52 
processOptions(store::Item_t item,store::LoadProperties & props,static_context * theSctx,const QueryLoc & loc)53 void processOptions(store::Item_t item, store::LoadProperties& props, static_context* theSctx, const QueryLoc& loc)
54 {
55   URI lValidatedBaseUri;
56   store::Item_t child, tempItem;
57 
58   if (item.getp() == NULL)
59     return;
60 
61 #ifndef ZORBA_NO_XMLSCHEMA
62   if (item->isValidated())
63   {
64     if (item->getNodeName() == NULL
65         ||
66         item->getNodeName()->getNamespace() != static_context::ZORBA_XML_FN_OPTIONS_NS)
67     {
68       throw XQUERY_EXCEPTION(zerr::ZXQD0003_INCONSISTENT_PARSE_FRAGMENT_OPTIONS,
69                              ERROR_PARAMS(ZED(ParseFragmentInvalidOptions)), ERROR_LOC( loc ));
70     }
71   }
72   else
73   {
74     tempItem = NULL; // used as the effectiveValidationValue()'s typeName
75     Validator::effectiveValidationValue(
76         item,
77         item,
78         tempItem,
79         theSctx->get_typemanager(),
80         ParseConstants::val_strict,
81         theSctx,
82         loc);
83   }
84 #endif
85 
86   store::Iterator_t children = item->getChildren();
87   children->open();
88 
89   while (children->next(child))
90   {
91     if (child->getNodeKind() != store::StoreConsts::elementNode)
92       continue;
93 
94     if (child->getNodeName()->getLocalName() == "base-uri")
95     {
96       store::Item_t attr = getFirstAttribute(child);
97 
98       try {
99         lValidatedBaseUri = URI(attr->getStringValue());
100       } catch (ZorbaException const& /* e */) {
101         throw XQUERY_EXCEPTION(
102           err::FODC0007,
103           ERROR_PARAMS( attr->getStringValue() ),
104           ERROR_LOC( loc )
105         );
106       }
107 
108       if (!lValidatedBaseUri.is_absolute()) {
109         throw XQUERY_EXCEPTION(
110           err::FODC0007,
111           ERROR_PARAMS( lValidatedBaseUri.toString() ),
112           ERROR_LOC( loc )
113         );
114       }
115 
116       props.setBaseUri(attr->getStringValue());
117     }
118     else if (child->getNodeName()->getLocalName() == "no-error")
119       props.setNoError(true);
120     else if (child->getNodeName()->getLocalName() == "strip-boundary-space")
121       props.setStripWhitespace(true);
122     else if (child->getNodeName()->getLocalName() == "schema-validate")
123     {
124       store::Item_t attr = getFirstAttribute(child);
125       if (attr->getStringValue() == "strict")
126         props.setSchemaStrictValidate(true);
127       else
128         props.setSchemaLaxValidate(true);
129     }
130     else if (child->getNodeName()->getLocalName() == "DTD-validate")
131       props.setDTDValidate(true);
132     else if (child->getNodeName()->getLocalName() == "DTD-load")
133       props.setDTDLoad(true);
134     else if (child->getNodeName()->getLocalName() == "default-DTD-attributes")
135       props.setDefaultDTDAttributes(true);
136     else if (child->getNodeName()->getLocalName() == "parse-external-parsed-entity")
137     {
138       props.setParseExternalParsedEntity(true);
139       store::Item_t attr;
140       store::Iterator_t attribs = child->getAttributes();
141       attribs->open();
142       while (attribs->next(attr))
143       {
144         if (attr->getNodeName()->getLocalName() == "skip-root-nodes")
145           props.setSkipRootNodes(ztd::aton<xs_int>(attr->getStringValue().c_str()));
146         else if (attr->getNodeName()->getLocalName() == "skip-top-level-text-nodes")
147           props.setSkipTopLevelTextNodes(true);
148         else if (attr->getNodeName()->getLocalName() == "error-on-doctype")
149           props.setErrorOnDoctype(true);
150       }
151       attribs->close();
152     }
153     else if (child->getNodeName()->getLocalName() == "substitute-entities")
154       props.setSubstituteEntities(true);
155     else if (child->getNodeName()->getLocalName() == "xinclude-substitutions")
156       props.setXincludeSubstitutions(true);
157     else if (child->getNodeName()->getLocalName() == "remove-redundant-ns")
158       props.setRemoveRedundantNS(true);
159     else if (child->getNodeName()->getLocalName() == "no-CDATA")
160       props.setNoCDATA(true);
161     else if (child->getNodeName()->getLocalName() == "no-xinclude-nodes")
162       props.setNoXIncludeNodes(true);
163   }
164 
165   children->close();
166 
167   if (props.getSchemaLaxValidate() + props.getSchemaStrictValidate() +
168       props.getDTDValidate() + props.getParseExternalParsedEntity() > 1)
169   {
170     throw XQUERY_EXCEPTION(zerr::ZXQD0003_INCONSISTENT_PARSE_FRAGMENT_OPTIONS,
171                            ERROR_PARAMS(ZED(ParseFragmentOptionCombinationNotAllowed)), ERROR_LOC( loc ));
172   }
173 }
174 
reset(PlanState & planState)175 void FnZorbaParseXmlFragmentIteratorState::reset(PlanState& planState)
176 {
177   PlanIteratorState::reset(planState);
178   theFragmentStream.reset();
179   theProperties.reset();
180   theProperties.setStoreDocument(false);
181   baseUri = "";
182   docUri = "";
183 }
184 
nextImpl(store::Item_t & result,PlanState & planState) const185 bool FnZorbaParseXmlFragmentIterator::nextImpl(store::Item_t& result, PlanState& planState) const
186 {
187   store::Store& lStore = GENV.getStore();
188   zstring docString;
189   store::Item_t tempItem;
190   bool validated = true;
191 
192   FnZorbaParseXmlFragmentIteratorState* state;
193   DEFAULT_STACK_INIT(FnZorbaParseXmlFragmentIteratorState, state, planState);
194 
195   if (consumeNext(result, theChildren[0].getp(), planState))
196   {
197     if (result->isStreamable())
198     {
199       state->theFragmentStream.theStream = &result->getStream();
200       state->theFragmentStream.setStreamReleaser(result->getStreamReleaser());
201       result->setStreamReleaser(nullptr);
202     }
203     else
204     {
205       result->getStringValue2(docString);
206       state->theFragmentStream.theIss = new std::istringstream(docString.c_str());
207       state->theFragmentStream.theStream = state->theFragmentStream.theIss;
208     }
209 
210     // read options
211     consumeNext(tempItem, theChildren[1].getp(), planState);
212     state->theProperties.setBaseUri(theSctx->get_base_uri());
213     state->theProperties.setStoreDocument(false);
214     processOptions(tempItem, state->theProperties, theSctx, loc);
215     state->theProperties.setCreateDocParentLink(false);
216 
217     // baseURI serves both as the base URI used by the XML parser
218     // to resolve relative entity references within the document,
219     // and as the base URI of the document node that is returned.
220     state->baseUri = state->theProperties.getBaseUri();
221     state->docUri = state->theProperties.getBaseUri();
222 
223 
224     ////////////////////////////////////////////////////////////////////////
225     // External parsed entity processing
226     ////////////////////////////////////////////////////////////////////////
227     if (state->theProperties.getParseExternalParsedEntity())
228     {
229       state->theFragmentStream.root_elements_to_skip = state->theProperties.getSkipRootNodes();
230 
231       while ( ! state->theFragmentStream.stream_is_consumed())
232       {
233         try {
234           result = lStore.loadDocument(state->baseUri, state->docUri, state->theFragmentStream, state->theProperties);
235         } catch (ZorbaException const& e) {
236           if ( ! state->theProperties.getNoError())
237             throw XQUERY_EXCEPTION( err::FODC0006, ERROR_PARAMS("parse-xml:parse()", e.what()), ERROR_LOC( loc ));
238           else
239             result = NULL;
240         }
241 
242         if (result == NULL)
243           continue;
244 
245         // Return the children of document node
246         state->theFragmentStream.children = result->getChildren();
247         while (state->theFragmentStream.children->next(result) && result != NULL)
248         {
249           if (state->theProperties.getSkipTopLevelTextNodes() && result->getNodeKind() == store::StoreConsts::textNode)
250             continue;
251 
252           STACK_PUSH(true, state);
253         }
254       }
255     }
256     ////////////////////////////////////////////////////////////////////////
257     // XML document processing
258     ////////////////////////////////////////////////////////////////////////
259     else  // if (!state->theProperties.getEnableExtParsedEntity())
260     {
261       try {
262         result = lStore.loadDocument(state->baseUri, state->docUri, *state->theFragmentStream.theStream, state->theProperties);
263       } catch (ZorbaException const& e) {
264         if ( ! state->theProperties.getNoError())
265           throw XQUERY_EXCEPTION( err::FODC0006, ERROR_PARAMS("parse-xml:parse()", e.what()), ERROR_LOC( loc ));
266         else
267           result = NULL;
268       }
269 
270       if (result != NULL)
271       {
272 #ifndef ZORBA_NO_XMLSCHEMA
273         if (state->theProperties.getSchemaLaxValidate() || state->theProperties.getSchemaStrictValidate())
274         {
275           try
276           {
277             tempItem = NULL; // used as the effectiveValidationValue()'s typeName
278             validated = Validator::effectiveValidationValue(
279                           result,
280                           result,
281                           tempItem,
282                           theSctx->get_typemanager(),
283                           state->theProperties.getSchemaLaxValidate() ? ParseConstants::val_lax : ParseConstants::val_strict,
284                           theSctx,
285                           this->loc);
286           }
287           catch (ZorbaException& /*e*/)
288           {
289             if ( ! state->theProperties.getNoError())
290               throw;
291             else
292             {
293               result = NULL;
294               validated = false;
295             }
296           }
297         }
298 #endif
299         // Ignore the schema validation options if Zorba is built without schema support
300 
301         STACK_PUSH(validated, state);
302       } // if (result != NULL)
303     } // if (state->theProperties.getEnableExtParsedEntity())
304   } // if (consumeNext(result, theChildren[0].getp(), planState))
305 
306   STACK_END(state);
307 }
308 
309 
310 /*******************************************************************************
311   14.9.2 fn:parse-xml-fragment
312 ********************************************************************************/
nextImpl(store::Item_t & result,PlanState & planState) const313 bool FnParseXmlFragmentIterator::nextImpl(store::Item_t& result, PlanState& planState) const
314 {
315   zstring docString;
316 
317   FnParseXmlFragmentIteratorState* state;
318   DEFAULT_STACK_INIT(FnParseXmlFragmentIteratorState, state, planState);
319 
320   if (consumeNext(result, theChildren[0].getp(), planState))
321   {
322     if (result->isStreamable())
323     {
324       state->theFragmentStream.theStream = &result->getStream();
325     }
326     else
327     {
328       result->getStringValue2(docString);
329       state->theFragmentStream.theIss = new std::istringstream(docString.c_str());
330       state->theFragmentStream.theStream = state->theFragmentStream.theIss;
331     }
332 
333     state->theProperties.setBaseUri(theSctx->get_base_uri());
334     state->baseUri = state->theProperties.getBaseUri();
335     state->theProperties.setParseExternalParsedEntity(true);
336     state->theFragmentStream.only_one_doc_node = 1; // create only one document node holding all fragment nodes
337 
338     try {
339       state->theProperties.setStoreDocument(false);
340       result = GENV.getStore().loadDocument(state->baseUri, state->docUri, state->theFragmentStream, state->theProperties);
341     } catch (ZorbaException const& e) {
342       if( ! state->theProperties.getNoError())
343         throw XQUERY_EXCEPTION(err::FODC0006, ERROR_PARAMS("fn:parse-xml-fragment()", e.what() ), ERROR_LOC(loc));
344       else
345         result = NULL;
346     }
347 
348     if (result != NULL)
349       STACK_PUSH(true, state);
350   } // if
351 
352   STACK_END(state);
353 }
354 
reset(PlanState & planState)355 void FnParseXmlFragmentIteratorState::reset(PlanState& planState)
356 {
357   PlanIteratorState::reset(planState);
358   theFragmentStream.reset();
359   theProperties.reset();
360   theProperties.setStoreDocument(false);
361   baseUri = "";
362   docUri = "";
363 }
364 
365 } /* namespace zorba */
366