@@ -30,75 +30,6 @@ const string XINCLUDE_URL = "http://www.w3.org/2001/XInclude";
3030
3131using ElementStack = vector<std::pair<ElementPtr, xml_node>>;
3232
33- void processXIncludes (DocumentPtr doc, xml_node& xmlNode, const FileSearchPath& searchPath, const XmlReadOptions* readOptions)
34- {
35- FileSearchPath includeSearchPath;
36- XmlReadFunction readXIncludeFunction = readOptions ? readOptions->readXIncludeFunction : readFromXmlFile;
37- xml_node xmlChild = xmlNode.first_child ();
38- while (xmlChild)
39- {
40- if (xmlChild.name () == XINCLUDE_TAG)
41- {
42- // Read XInclude references if requested.
43- if (readXIncludeFunction)
44- {
45- string filename = xmlChild.attribute (" href" ).value ();
46- const StringVec& parents = readOptions ? readOptions->parentXIncludes : StringVec ();
47-
48- // Validate XInclude state.
49- if (std::find (parents.begin (), parents.end (), filename) != parents.end ())
50- {
51- throw ExceptionParseError (" XInclude cycle detected." );
52- }
53- if (parents.size () >= MAX_XINCLUDE_DEPTH)
54- {
55- throw ExceptionParseError (" Maximum XInclude depth exceeded." );
56- }
57-
58- // Read the included file into a library document.
59- DocumentPtr library = createDocument ();
60- XmlReadOptions xiReadOptions = readOptions ? *readOptions : XmlReadOptions ();
61- xiReadOptions.parentXIncludes .push_back (filename);
62-
63- // Prepend the directory of the parent to accommodate
64- // includes relative to the parent file location.
65- if (includeSearchPath.isEmpty ())
66- {
67- string parentUri = doc->getSourceUri ();
68- if (!parentUri.empty ())
69- {
70- FilePath filePath = searchPath.find (parentUri);
71- if (!filePath.isEmpty ())
72- {
73- // Remove the file name from the path as we want the path to the containing folder.
74- includeSearchPath = searchPath;
75- includeSearchPath.prepend (filePath.getParentPath ());
76- }
77- }
78- // Set default search path if no parent path found
79- if (includeSearchPath.isEmpty ())
80- {
81- includeSearchPath = searchPath;
82- }
83- }
84- readXIncludeFunction (library, filename, includeSearchPath, &xiReadOptions);
85-
86- // Import the library document.
87- doc->importLibrary (library);
88- }
89-
90- // Remove include directive.
91- xml_node includeNode = xmlChild;
92- xmlChild = xmlChild.next_sibling ();
93- xmlNode.remove_child (includeNode);
94- }
95- else
96- {
97- xmlChild = xmlChild.next_sibling ();
98- }
99- }
100- }
101-
10233void documentToXml (DocumentPtr doc, xml_node& xmlRoot, const XmlWriteOptions* writeOptions)
10334{
10435 ElementStack elemStack;
@@ -183,68 +114,99 @@ void documentToXml(DocumentPtr doc, xml_node& xmlRoot, const XmlWriteOptions* wr
183114void documentFromXml (DocumentPtr doc, const xml_document& xmlDoc, const FileSearchPath& searchPath, const XmlReadOptions* readOptions)
184115{
185116 xml_node xmlRoot = xmlDoc.child (Document::CATEGORY.c_str ());
186- if (xmlRoot)
117+ if (! xmlRoot)
187118 {
188- processXIncludes (doc, xmlRoot, searchPath, readOptions);
189- ElementStack elemStack;
190- elemStack.emplace_back (doc, xmlRoot);
119+ throw ExceptionParseError (" No root MaterialX element found." );
120+ }
191121
192- while (!elemStack.empty ())
122+ // Process XInclude directives.
123+ XmlReadFunction readXIncludeFunction = readOptions ? readOptions->readXIncludeFunction : readFromXmlFile;
124+ if (readXIncludeFunction)
125+ {
126+ for (const xml_node& xmlChild : xmlRoot.children ())
193127 {
194- ElementPtr elem = elemStack.back ().first ;
195- xml_node xmlNode = elemStack.back ().second ;
196- elemStack.pop_back ();
197-
198- // Store attributes in element.
199- for (const xml_attribute& xmlAttr : xmlNode.attributes ())
128+ if (xmlChild.name () == XINCLUDE_TAG)
200129 {
201- if (xmlAttr.name () != Element::NAME_ATTRIBUTE)
130+ string filename = xmlChild.attribute (" href" ).value ();
131+ const StringVec& parents = readOptions ? readOptions->parentXIncludes : StringVec ();
132+
133+ // Validate XInclude state.
134+ if (std::find (parents.begin (), parents.end (), filename) != parents.end ())
135+ {
136+ throw ExceptionParseError (" XInclude cycle detected." );
137+ }
138+ if (parents.size () >= MAX_XINCLUDE_DEPTH)
202139 {
203- elem-> setAttribute (xmlAttr. name (), xmlAttr. value () );
140+ throw ExceptionParseError ( " Maximum XInclude depth exceeded. " );
204141 }
142+
143+ // Read the included file into a library document.
144+ DocumentPtr library = createDocument ();
145+ XmlReadOptions xiReadOptions = readOptions ? *readOptions : XmlReadOptions ();
146+ xiReadOptions.parentXIncludes .push_back (filename);
147+ readXIncludeFunction (library, filename, searchPath, &xiReadOptions);
148+
149+ // Import the library document.
150+ doc->importLibrary (library);
205151 }
152+ }
153+ }
206154
207- // Create child elements and recurse.
208- for (const xml_node& xmlChild : xmlNode.children ())
155+ // Build the element tree.
156+ ElementStack elemStack;
157+ elemStack.emplace_back (doc, xmlRoot);
158+ while (!elemStack.empty ())
159+ {
160+ ElementPtr elem = elemStack.back ().first ;
161+ xml_node xmlNode = elemStack.back ().second ;
162+ elemStack.pop_back ();
163+
164+ // Store attributes in element.
165+ for (const xml_attribute& xmlAttr : xmlNode.attributes ())
166+ {
167+ if (xmlAttr.name () != Element::NAME_ATTRIBUTE)
209168 {
210- string category = xmlChild.name ();
211- string name;
212- for (const xml_attribute& xmlAttr : xmlChild.attributes ())
213- {
214- if (xmlAttr.name () == Element::NAME_ATTRIBUTE)
215- {
216- name = xmlAttr.value ();
217- break ;
218- }
219- }
169+ elem->setAttribute (xmlAttr.name (), xmlAttr.value ());
170+ }
171+ }
220172
221- // Check for duplicate elements.
222- ConstElementPtr previous = elem->getChild (name);
223- if (previous)
224- {
225- continue ;
226- }
173+ // Create child elements and recurse.
174+ for (const xml_node& xmlChild : xmlNode.children ())
175+ {
176+ // Get child category.
177+ string category = xmlChild.name ();
178+ if (category == XINCLUDE_TAG)
179+ {
180+ continue ;
181+ }
227182
228- // Create the child element.
229- ElementPtr child = elem->addChildOfCategory (category, name);
183+ // Get child name and skip duplicates.
184+ string name = xmlChild.attribute (Element::NAME_ATTRIBUTE.c_str ()).value ();
185+ ConstElementPtr previous = elem->getChild (name);
186+ if (previous)
187+ {
188+ continue ;
189+ }
230190
231- // Handle the interpretation of XML comments and newlines.
232- if (readOptions && category.empty ())
191+ // Create the child element.
192+ ElementPtr child = elem->addChildOfCategory (category, name);
193+
194+ // Handle the interpretation of XML comments and newlines.
195+ if (readOptions && category.empty ())
196+ {
197+ if (readOptions->readComments && xmlChild.type () == node_comment)
233198 {
234- if (readOptions->readComments && xmlChild.type () == node_comment)
235- {
236- child = elem->changeChildCategory (child, CommentElement::CATEGORY);
237- child->setDocString (xmlChild.value ());
238- }
239- else if (readOptions->readNewlines && xmlChild.type () == node_newline)
240- {
241- child = elem->changeChildCategory (child, NewlineElement::CATEGORY);
242- }
199+ child = elem->changeChildCategory (child, CommentElement::CATEGORY);
200+ child->setDocString (xmlChild.value ());
201+ }
202+ else if (readOptions->readNewlines && xmlChild.type () == node_newline)
203+ {
204+ child = elem->changeChildCategory (child, NewlineElement::CATEGORY);
243205 }
244-
245- // Push child data onto the stack.
246- elemStack.emplace_back (child, xmlChild);
247206 }
207+
208+ // Push child data onto the stack.
209+ elemStack.emplace_back (child, xmlChild);
248210 }
249211 }
250212
@@ -355,16 +317,19 @@ void readFromXmlFile(DocumentPtr doc, FilePath filename, FileSearchPath searchPa
355317 xml_parse_result result = xmlDoc.load_file (filename.asString ().c_str (), getParseOptions (readOptions));
356318 validateParseResult (result, filename);
357319
358- // This must be done before parsing the XML as the source URI
359- // is used for searching for include files.
360- if (readOptions && !readOptions->parentXIncludes .empty ())
361- {
362- doc->setSourceUri (readOptions->parentXIncludes [0 ]);
363- }
364- else
320+ // Store the source URI of the document.
321+ FilePath sourcePath = (readOptions && !readOptions->parentXIncludes .empty ()) ?
322+ FilePath (readOptions->parentXIncludes [0 ]) :
323+ FilePath (filename);
324+ doc->setSourceUri (sourcePath);
325+
326+ // Enable XIncludes that are relative to this document.
327+ if (!sourcePath.isAbsolute ())
365328 {
366- doc-> setSourceUri (filename );
329+ sourcePath = searchPath. find (sourcePath );
367330 }
331+ searchPath.prepend (sourcePath.getParentPath ());
332+
368333 documentFromXml (doc, xmlDoc, searchPath, readOptions);
369334}
370335
0 commit comments