source: 1dwg/trunk/IgorPro/cansasXML.ipf @ 31

Last change on this file since 31 was 31, checked in by prjemian, 13 years ago

fixes #1
provides documentation for IgorPro? binding

File size: 39.1 KB
Line 
1#pragma rtGlobals=1             // Use modern global access method.
2#pragma version=1.05
3
4// file:        cansasXML.ipf
5// author:      Pete R. Jemian <jemian@anl.gov>
6// date:        2008-03-31
7// purpose:  implement an IgorPro file reader to read the canSAS 1-D reduced SAS data in XML files
8//                      adheres to the cansas1d/1.0 standard
9// URL: http://www.smallangles.net/wgwiki/index.php/cansas1d_documentation
10//
11// requires:    IgorPro (http://www.wavemetrics.com/)
12//                              XMLutils - XOP (http://www.igorexchange.com/project/XMLutils)
13
14FUNCTION CS_XmlReader(fileName)
15        //
16        // open a canSAS 1-D reduced SAS XML data file
17        //      returns:
18        //              0 : successful
19        //              -1: XML file not found
20        //              -2: root element is not <SASroot>
21        //              -3: <SASroot> version  is not 1.0
22        //              -4: no <SASentry> elements
23        //
24        STRING fileName
25        STRING origFolder
26        STRING workingFolder = "root:Packages:CS_XMLreader"
27        VARIABLE returnCode
28
29        //
30        // set up a work folder within root:Packages
31        // Clear out any progress/results from previous activities
32        //
33        origFolder = GetDataFolder(1)
34        SetDataFolder root:                                     // start in the root data folder
35        NewDataFolder/O  root:Packages          // good practice
36        KillDataFolder/Z  $workingFolder                // clear out any previous work
37        NewDataFolder/O/S  $workingFolder       // Do all our work in root:XMLreader
38
39        //
40        // Try to open the named XML file (clean-up and return if failure)
41        //
42        VARIABLE fileID
43        STRING/G errorMsg, xmlFile
44        MAKE/T/N=(0,3)/O metadata
45        CS_appendMetaData("xmlFile", "", fileName)
46        xmlFile = fileName
47        fileID = XmlOpenFile(fileName)                  // open and parse the XMLfile
48        IF ( fileID < 0 )
49                SWITCH(fileID)                                  // fileID holds the return code; check it
50                        CASE -1:
51                                errorMsg = fileName + ": failed to parse XML"
52                        BREAK
53                        CASE -2:
54                                errorMsg = fileName + " either not found or cannot be opened for reading"
55                        BREAK
56                ENDSWITCH
57                PRINT errorMsg
58                SetDataFolder $origFolder
59                RETURN(-1)                                              // could not find file
60        ENDIF
61
62        //
63        // index the XML element list
64        //
65        XmlElemList(fileID)                                     // load the elementlist into W_ElementList
66        WAVE/T  W_ElementList                           // This declaration comes _after_ XmlElemList() is called
67
68        // qualify the XML file, don't allow just any ole XML.
69        IF ( CmpStr(W_ElementList[0][3], "SASroot") != 0 )              // deftly avoid namespace
70                errorMsg = fileName + ": root element is not <SASroot>"
71                PRINT errorMsg
72                XmlCloseFile(fileID,0)
73                SetDataFolder $origFolder
74                RETURN(-2)                                              // not a canSAS XML file
75        ENDIF
76
77        // make an index to speed up searching through column 0
78        Make/O/T/N=(DimSize(W_ElementList, 0)) W_ElementList_Col0
79        W_ElementList_Col0 = W_ElementList[p][0]
80        STRING/G nsPre = "", nsStr = ""
81        CS_registerNameSpaces(fileID)                           // to assist XPath construction
82
83        // identify supported versions of canSAS XML standard
84        STRING version
85        version = StringByKey("version", W_ElementList[0][2])
86        CS_appendMetaData("cansas_version", CS_XPath_NS("/SASroot/@version"), version)
87        STRSWITCH(version)     
88        CASE "1.0":                                                     // version 1.0 of the canSAS 1-D reduced SAS data standard
89                PRINT fileName, "\t\t identified as: cansas1d/1.0 XML file"
90                returnCode = CS_1i_parseXml(fileID)
91                IF (returnCode != 0)
92                        IF (strlen(errorMsg) == 0)
93                                errorMsg = "error while parsing the XML"
94                        ENDIF
95                        PRINT errorMsg
96                        XmlCloseFile(fileID,0)
97                        SetDataFolder $origFolder
98                        RETURN(returnCode)                      // error while parsing the XML
99                ENDIF
100                BREAK
101        CASE "2.0a":                                            // unsupported
102        DEFAULT:                                                        // optional default expression executed
103                errorMsg = fileName + ": <SASroot> version (" + version + ") is not supported"
104                PRINT errorMsg
105                XmlCloseFile(fileID,0)
106                SetDataFolder $origFolder
107                RETURN(-3)                                              // attribute list must include version="1.0"
108        ENDSWITCH
109
110        XmlCloseFile(fileID,0)                                  // now close the file, without saving
111        fileID = -1
112
113        SetDataFolder $origFolder
114        RETURN(0)                                                       // execution finished OK
115END
116
117// ==================================================================
118
119FUNCTION CS_1i_parseXml(fileID)
120        VARIABLE fileID
121        WAVE/T W_ElementList
122        SVAR errorMsg, xmlFile
123        STRING/G Title
124        STRING XPathStr, Title_folder, SASdata_folder
125        STRING SASentryPath, SASdataPath, RunNum
126        VARIABLE i, j, index, SASdata_index, returnCode = 0
127
128        // locate all the SASentry elements
129        CS_simpleXmlListXpath(fileID, "", "/SASroot//SASentry")
130        WAVE/T  W_listXPath
131        // (safer to copy W_listxpath to a local variable and allow for other calls to XMLlistXpath)
132        DUPLICATE/O/T   W_listXPath, SASentryList
133
134        IF (numpnts(SASentryList) < 1)
135                errorMsg = "could not find any SASentry elements in XML file"
136                PRINT errorMsg
137                RETURN(-4)                                              // no <SASentry> elements
138        ENDIF
139
140        // Should we test here for all required elements?  That could be tedious.  And possibly unnecessary.
141
142        // process each SASentry element
143        DUPLICATE/O/T metadata, metadata_file
144        FOR (i = 0; i < numpnts(SASentryList); i += 1)
145                index = CS_findElementIndex(SASentryList[i])
146                DUPLICATE/O/T metadata_file, metadata
147                Title = CS_1i_locateTitle(fileID, CS_correctedXpathStr(SASentryList[i]))                // look in several places or use default title
148                Title_folder = CleanupName(Title, 0)
149                IF ( CheckName(Title_folder, 11) != 0 )
150                        Title_folder = UniqueName(Title_folder, 11, 0)
151                ENDIF
152                NewDataFolder/O  $Title_folder
153                STRING/G FolderList = ""
154                FolderList = AddListItem(":"+Title_folder+":", FolderList, ";", Inf)
155                //
156                // CS_correctedXpathStr
157                SASentryPath = CS_correctedXpathStr(SASentryList[i])
158                CS_1i_collectMetadata(fileID, SASentryPath)
159                //
160                // next, extract each SASdata block into a subfolder
161                CS_simpleXmlListXpath(fileID, SASentryPath, "//SASdata")        //output: W_listXPath
162                DUPLICATE/O/T   W_listXPath, SASdataList
163                IF (numpnts(SASdataList) == 1)
164                        // only one SASdata element, place data waves in Title folder
165                        SASdataPath = CS_correctedXpathStr(SASdataList[0])
166                        SASdata_folder = ":" + Title_folder + ":"
167                        RunNum = "/Run"
168                        CS_appendMetaData( "Run",                               SASentryPath+CS_XPath_NS(RunNum),                               "")
169                        CS_appendMetaData( "Run_name",                  SASentryPath+CS_XPath_NS(RunNum + "/@name"),    "")
170                        CS_appendMetaData( "SASdata_name",      SASdataPath+CS_XPath_NS("/@name"),                              "")
171                        PRINT "\t\t dataFolder:", SASdata_folder
172                        CS_1i_fillMetadataTable(fileID)
173                        IF (CS_1i_extractSasData(fileID, SASdataPath, SASdata_folder))
174                                // non-zero return code means an error, message is in errorMsg
175                                // What to do now?
176                                //      Proceed with that knowledge or discard the data folder?
177                                //      Go with the discard for now.
178                                returnCode += 1                         // for later
179                                PRINT "\t\t" + errorMsg
180                                KillDataFolder/Z $Title_folder          // only 1 SASdata
181                                // RETURN(1)                            // Can't return now.  What about other SASentry elements?
182                                BREAK
183                        ENDIF
184                ELSE
185                        // multiple SASdata elements, place data waves in subfolders
186                        DUPLICATE/O/T metadata, metadata_entry
187                        FOR (j = 0; j < numpnts(SASdataList); j += 1)
188                                DUPLICATE/O/T metadata_entry, metadata
189                                SASdata_index = CS_findElementIndex(SASdataList[j])
190                                SASdataPath = CS_correctedXpathStr(SASdataList[j])
191                                SASdata_folder = CleanupName(StringByKey("name", W_ElementList[SASdata_index][2]), 0)
192                                PRINT "\t\t dataFolder:", SASdata_folder
193                                RunNum = "/Run["+num2str(j+1)+"]"
194                                CS_appendMetaData( "Run"+num2str(j),                            SASdataPath+"/.."+CS_XPath_NS(RunNum),                          "")
195                                CS_appendMetaData( "Run"+num2str(j)+"_name",            SASdataPath+"/.."+CS_XPath_NS(RunNum + "/@name"),       "")
196                                CS_appendMetaData( "SASdata"+num2str(j)+"_name",        SASdataPath+CS_XPath_NS("/@name"),                                      "")
197                                SetDataFolder $Title_folder
198                                IF ( CheckName(SASdata_folder, 11) != 0 )
199                                        SASdata_folder = UniqueName(SASdata_folder, 11, 0)
200                                ENDIF
201                                NewDataFolder/O  $SASdata_folder
202                                SetDataFolder ::
203                                SASdata_folder =  ":" + Title_folder + ":" + SASdata_folder + ":"
204                                FolderList = AddListItem(SASdata_folder, FolderList, ";", Inf)
205                                //---
206                                CS_1i_fillMetadataTable(fileID)
207                                IF (CS_1i_extractSasData(fileID, SASdataPath, SASdata_folder))
208                                        // non-zero return code means an error, message is in errorMsg
209                                        // What to do now?
210                                        //      Proceed with that knowledge or discard the data folder?
211                                        //      Go with the discard for now.
212                                        returnCode += 1                                         // for later
213                                        PRINT "\t\t" + errorMsg
214                                        KillDataFolder/Z $SASdata_folder                // just this SASdata
215                                        // RETURN(1)                                            // Can't return now.  What about other SASentry elements?
216                                        BREAK
217                                ENDIF
218                        ENDFOR  // many SASdata
219                ENDIF                   // 1 or many SASdata
220                MAKE/O/T/N=(3,2) admin
221                admin[0][0] = "xmlFile"
222                admin[0][1] = xmlFile
223                admin[1][0] = "FolderList"
224                admin[1][1] = FolderList
225                admin[2][0] = "numSASdata"
226                admin[2][1] = num2str(numpnts(SASdataList))
227                Title_folder = ":" + Title_folder + ":"
228                MoveWave admin, $Title_folder
229                MoveString FolderList, $Title_folder
230        ENDFOR          // each SASentry
231
232        RETURN(returnCode)
233END
234
235// ==================================================================
236
237FUNCTION CS_1i_collectMetadata(fileID, sasEntryPath)
238        VARIABLE fileID
239        STRING sasEntryPath
240        VARIABLE i
241        WAVE/T metadata
242        STRING suffix = ""
243        STRING value, detailsPath, detectorPath, notePath
244        // collect some metadata
245        // first, fill a table with keywords, and XPath locations, 3rd column will be values
246
247        // handle most <SASsample> fields
248        CS_appendMetaData("sample_ID",                                          sasEntryPath+CS_XPath_NS("/SASsample/ID"), "")
249        CS_appendMetaData("sample_thickness",                           sasEntryPath+CS_XPath_NS("/SASsample/thickness"), "")
250        CS_appendMetaData("sample_thickness_unit",                      sasEntryPath+CS_XPath_NS("/SASsample/thickness/@unit"), "")
251        CS_appendMetaData("sample_transmission",                        sasEntryPath+CS_XPath_NS("/SASsample/transmission"), "")
252        CS_appendMetaData("sample_temperature",                         sasEntryPath+CS_XPath_NS("/SASsample/temperature"), "")
253        CS_appendMetaData("sample_temperature_unit",            sasEntryPath+CS_XPath_NS("/SASsample/temperature/@unit"), "")
254        CS_appendMetaData("sample_position_x",                          sasEntryPath+CS_XPath_NS("/SASsample/position/x"), "")
255        CS_appendMetaData("sample_position_x_unit",                     sasEntryPath+CS_XPath_NS("/SASsample/position/x/@unit"), "")
256        CS_appendMetaData("sample_position_y",                          sasEntryPath+CS_XPath_NS("/SASsample/position/y"), "")
257        CS_appendMetaData("sample_position_y_unit",                     sasEntryPath+CS_XPath_NS("/SASsample/position/y/@unit"), "")
258        CS_appendMetaData("sample_position_z",                          sasEntryPath+CS_XPath_NS("/SASsample/position/z"), "")
259        CS_appendMetaData("sample_position_z_unit",                     sasEntryPath+CS_XPath_NS("/SASsample/position/z/@unit"), "")
260        CS_appendMetaData("sample_orientation_roll",                    sasEntryPath+CS_XPath_NS("/SASsample/orientation/roll"), "")
261        CS_appendMetaData("sample_orientation_roll_unit",               sasEntryPath+CS_XPath_NS("/SASsample/orientation/roll/@unit"), "")
262        CS_appendMetaData("sample_orientation_pitch",           sasEntryPath+CS_XPath_NS("/SASsample/orientation/pitch"), "")
263        CS_appendMetaData("sample_orientation_pitch_unit",      sasEntryPath+CS_XPath_NS("/SASsample/orientation/pitch/@unit"), "")
264        CS_appendMetaData("sample_orientation_yaw",                     sasEntryPath+CS_XPath_NS("/SASsample/orientation/yaw"), "")
265        CS_appendMetaData("sample_orientation_yaw_unit",        sasEntryPath+CS_XPath_NS("/SASsample/orientation/yaw/@unit"), "")
266        // <SASsample><details> might appear multiple times, too!
267        CS_simpleXmlListXpath(fileID, sasEntryPath, "/SASsample//details")      //output: W_listXPath
268        DUPLICATE/O/T   W_listXPath, detailsList
269        suffix = ""
270        FOR (i = 0; i < numpnts(detailsList); i += 1)
271                IF (numpnts(detailsList) > 1)
272                        suffix = num2str(i)
273                ENDIF
274                detailsPath = CS_correctedXpathStr(detailsList[i])
275                CS_appendMetaData("sample_details"+suffix+"_name",      detailsPath+CS_XPath_NS("/@name"), "")
276                CS_appendMetaData("sample_details"+suffix,                      detailsPath, "")
277        ENDFOR
278
279
280        // <SASinstrument>
281        CS_appendMetaData("Instrument_name", sasEntryPath+CS_XPath_NS("/SASinstrument/name"), "")
282        CS_appendMetaData("Instrument_attr_name", sasEntryPath+CS_XPath_NS("/SASinstrument/@name"), "")
283
284        // <SASinstrument><SASsource>
285        CS_appendMetaData("source_name", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/@name"), "")
286        CS_appendMetaData("radiation", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/radiation"), "")
287        CS_appendMetaData("beam_size_name", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_size/@name"), "")
288        CS_appendMetaData("beam_size_x", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_size/x"), "")
289        CS_appendMetaData("beam_size_x_unit", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_size/x/@unit"), "")
290        CS_appendMetaData("beam_size_y", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_size/y"), "")
291        CS_appendMetaData("beam_size_y_unit", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_size/y/@unit"), "")
292        CS_appendMetaData("beam_size_z", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_size/z"), "")
293        CS_appendMetaData("beam_size_z_unit", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_size/z/@unit"), "")
294        CS_appendMetaData("beam_shape", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/beam_shape"), "")
295        CS_appendMetaData("wavelength", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength"), "")
296        CS_appendMetaData("wavelength_unit", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength/@unit"), "")
297        CS_appendMetaData("wavelength_min", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength_min"), "")
298        CS_appendMetaData("wavelength_min_unit", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength_min/@unit"), "")
299        CS_appendMetaData("wavelength_max", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength_max"), "")
300        CS_appendMetaData("wavelength_max_unit", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength_max/@unit"), "")
301        CS_appendMetaData("wavelength_spread", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength_spread"), "")
302        CS_appendMetaData("wavelength_spread_unit", sasEntryPath+CS_XPath_NS("/SASinstrument/SASsource/wavelength_spread/@unit"), "")
303
304        // ignore <SASinstrument><SAScollimation> for now
305        CS_simpleXmlListXpath(fileID, sasEntryPath, "/SASinstrument//SAScollimation")           //output: W_listXPath
306        DUPLICATE/O/T   W_listXPath, SAScollimationList
307        STRING collimationPath
308        suffix = ""
309        FOR (i = 0; i < numpnts(SAScollimationList); i += 1)
310                IF (numpnts(SAScollimationList) > 1)
311                        suffix = num2str(i)
312                ENDIF
313                collimationPath = CS_correctedXpathStr(SAScollimationList[i])
314                CS_appendMetaData("collimation_name"+suffix,    collimationPath+CS_XPath_NS("/@name"), "")
315                CS_appendMetaData("collimation_length"+suffix,  collimationPath+CS_XPath_NS("/length"), "")
316                CS_appendMetaData("collimation_length_unit"+suffix,     collimationPath+CS_XPath_NS("/length/@unit"), "")
317                CS_appendMetaData("collimation_aperture_name"+suffix,   collimationPath+CS_XPath_NS("/aperture/@name"), "")
318                CS_appendMetaData("collimation_aperture_type"+suffix,   collimationPath+CS_XPath_NS("/aperture/type"), "")
319                CS_appendMetaData("collimation_aperture_size_name"+suffix,      collimationPath+CS_XPath_NS("/aperture/size/@name"), "")
320                CS_appendMetaData("collimation_aperture_size_x"+suffix,         collimationPath+CS_XPath_NS("/aperture/size/x"), "")
321                CS_appendMetaData("collimation_aperture_size_x_unit"+suffix,    collimationPath+CS_XPath_NS("/aperture/size/x/@unit"), "")
322                CS_appendMetaData("collimation_aperture_size_y"+suffix,         collimationPath+CS_XPath_NS("/aperture/size/y"), "")
323                CS_appendMetaData("collimation_aperture_size_y_unit"+suffix,    collimationPath+CS_XPath_NS("/aperture/size/y/@unit"), "")
324                CS_appendMetaData("collimation_aperture_size_z"+suffix,         collimationPath+CS_XPath_NS("/aperture/size/z"), "")
325                CS_appendMetaData("collimation_aperture_size_z_unit"+suffix,    collimationPath+CS_XPath_NS("/aperture/size/z/@unit"), "")
326                CS_appendMetaData("collimation_aperture_distance"+suffix,       collimationPath+CS_XPath_NS("/aperture/distance"), "")
327                CS_appendMetaData("collimation_aperture_distance_unit"+suffix,  collimationPath+CS_XPath_NS("/aperture/distance/@unit"), "")
328        ENDFOR
329
330        // <SASinstrument><SASdetector> might appear multiple times
331        CS_simpleXmlListXpath(fileID, sasEntryPath, "/SASinstrument//SASdetector")      //output: W_listXPath
332        DUPLICATE/O/T   W_listXPath, SASdetectorList
333        suffix = ""
334        FOR (i = 0; i < numpnts(SASdetectorList); i += 1)
335                IF (numpnts(SASdetectorList) > 1)
336                        suffix = num2str(i)
337                ENDIF
338                detectorPath = CS_correctedXpathStr(SASdetectorList[i])
339                CS_appendMetaData("detector_name"+suffix,       detectorPath+CS_XPath_NS("/name"), "")
340                CS_appendMetaData("SDD"+suffix,                         detectorPath+CS_XPath_NS("/SDD"), "")
341                CS_appendMetaData("SDD"+suffix+"_unit",                 detectorPath+CS_XPath_NS("/SDD/@unit"), "")
342                CS_appendMetaData("detector_offset_name"+suffix,        detectorPath+CS_XPath_NS("/offset/@name"), "")
343                CS_appendMetaData("detector_offset_x"+suffix,           detectorPath+CS_XPath_NS("/offset/x"), "")
344                CS_appendMetaData("detector_offset_x_unit"+suffix,      detectorPath+CS_XPath_NS("/offset/x/@unit"), "")
345                CS_appendMetaData("detector_offset_y"+suffix,           detectorPath+CS_XPath_NS("/offset/y"), "")
346                CS_appendMetaData("detector_offset_y_unit"+suffix,      detectorPath+CS_XPath_NS("/offset/y/@unit"), "")
347                CS_appendMetaData("detector_offset_z"+suffix,           detectorPath+CS_XPath_NS("/offset/z"), "")
348                CS_appendMetaData("detector_offset_z_unit"+suffix,      detectorPath+CS_XPath_NS("/offset/z/@unit"), "")
349
350                CS_appendMetaData("detector_orientation_name"+suffix,           detectorPath+CS_XPath_NS("/orientation/@name"), "")
351                CS_appendMetaData("detector_orientation_roll"+suffix,           detectorPath+CS_XPath_NS("/orientation/roll"), "")
352                CS_appendMetaData("detector_orientation_roll_unit"+suffix,      detectorPath+CS_XPath_NS("/orientation/roll/@unit"), "")
353                CS_appendMetaData("detector_orientation_pitch"+suffix,          detectorPath+CS_XPath_NS("/orientation/pitch"), "")
354                CS_appendMetaData("detector_orientation_pitch_unit"+suffix,     detectorPath+CS_XPath_NS("/orientation/pitch/@unit"), "")
355                CS_appendMetaData("detector_orientation_yaw"+suffix,            detectorPath+CS_XPath_NS("/orientation/yaw"), "")
356                CS_appendMetaData("detector_orientation_yaw_unit"+suffix,       detectorPath+CS_XPath_NS("/orientation/yaw/@unit"), "")
357
358                CS_appendMetaData("detector_beam_center_name"+suffix,   detectorPath+CS_XPath_NS("/beam_center/@name"), "")
359                CS_appendMetaData("detector_beam_center_x"+suffix,              detectorPath+CS_XPath_NS("/beam_center/x"), "")
360                CS_appendMetaData("detector_beam_center_x_unit"+suffix,         detectorPath+CS_XPath_NS("/beam_center/x/@unit"), "")
361                CS_appendMetaData("detector_beam_center_y"+suffix,              detectorPath+CS_XPath_NS("/beam_center/y"), "")
362                CS_appendMetaData("detector_beam_center_y_unit"+suffix,         detectorPath+CS_XPath_NS("/beam_center/y/@unit"), "")
363                CS_appendMetaData("detector_beam_center_z"+suffix,              detectorPath+CS_XPath_NS("/beam_center/z"), "")
364                CS_appendMetaData("detector_beam_center_z_unit"+suffix,         detectorPath+CS_XPath_NS("/beam_center/z/@unit"), "")
365
366                CS_appendMetaData("detector_pixel_size_name"+suffix,    detectorPath+CS_XPath_NS("/pixel_size/@name"), "")
367                CS_appendMetaData("detector_pixel_size_x"+suffix,               detectorPath+CS_XPath_NS("/pixel_size/x"), "")
368                CS_appendMetaData("detector_pixel_size_x_unit"+suffix,  detectorPath+CS_XPath_NS("/pixel_size/x/@unit"), "")
369                CS_appendMetaData("detector_pixel_size_y"+suffix,               detectorPath+CS_XPath_NS("/pixel_size/y"), "")
370                CS_appendMetaData("detector_pixel_size_y_unit"+suffix,  detectorPath+CS_XPath_NS("/pixel_size/y/@unit"), "")
371                CS_appendMetaData("detector_pixel_size_z"+suffix,               detectorPath+CS_XPath_NS("/pixel_size/z"), "")
372                CS_appendMetaData("detector_pixel_size_z_unit"+suffix,  detectorPath+CS_XPath_NS("/pixel_size/z/@unit"), "")
373
374                CS_appendMetaData("slit_length"+suffix,                 detectorPath+CS_XPath_NS("/slit_length"), "")
375                CS_appendMetaData("slitLength"+suffix+"_unit",  detectorPath+CS_XPath_NS("/slit_length/@unit"), "")
376        ENDFOR
377
378        // ignore <SASprocess> for now
379
380        // <SASnote> might appear multiple times
381        CS_simpleXmlListXpath(fileID, sasEntryPath, "//SASnote")        //output: W_listXPath
382        DUPLICATE/O/T   W_listXPath, SASnoteList
383        suffix = ""
384        FOR (i = 0; i < numpnts(SASnoteList); i += 1)
385                IF (numpnts(SASnoteList) > 1)
386                        suffix = num2str(i)
387                ENDIF
388                notePath = CS_correctedXpathStr(SASnoteList[i])
389                CS_appendMetaData("SASnote"+suffix+"_name",     notePath+CS_XPath_NS("/@name"), "")
390                CS_appendMetaData("SASnote"+suffix,                             notePath, "")
391        ENDFOR
392        //CS_1i_fillMetadataTable(fileID)
393END
394
395// ==================================================================
396
397FUNCTION/S CS_1i_fillMetadataTable(fileID)
398        VARIABLE fileID
399        WAVE/T metadata
400        VARIABLE i
401        STRING value
402        // +++++++++++++++++++++++++                    // try to fill the value column from the XML data
403        FOR (i = 0; i < DimSize(metadata, 0); i += 1)
404                IF (strlen(metadata[i][1]) > 0)                         // XPathStr for this entry?
405                        IF (strlen(metadata[i][2])  == 0)                       // not defined yet?
406                                value = CS_XmlStrFmXpath(fileID, metadata[i][1], "")            // get it
407                                // What if the value string has a ";" embedded?
408                                //  This will complicate (?compromise?) the wavenote "key=value;" syntax.
409                                metadata[i][2] = ReplaceString(";", value, " :semicolon: ")
410                        ENDIF
411                ENDIF
412        ENDFOR
413END
414
415// ==================================================================
416
417FUNCTION/S CS_1i_locateTitle(fileID, SASentryPath)
418        VARIABLE fileID
419        STRING SASentryPath
420        WAVE/T metadata
421        STRING TitlePath, Title
422        // /SASroot/SASentry/Title is the expected location, but it could be empty
423        TitlePath = SASentryPath+CS_XPath_NS("/Title")
424        Title = CS_XmlStrFmXpath(fileID,  TitlePath, "")
425        // search harder for a title
426        IF (strlen(Title) == 0)
427                TitlePath = SASentryPath+CS_XPath_NS("/@name")
428                Title = CS_XmlStrFmXpath(fileID,  TitlePath, "")
429        ENDIF
430        IF (strlen(Title) == 0)
431                TitlePath = SASentryPath+CS_XPath_NS("/SASsample/ID")
432                Title = CS_XmlStrFmXpath(fileID,  TitlePath, "")
433        ENDIF
434        IF (strlen(Title) == 0)
435                TitlePath = SASentryPath+CS_XPath_NS("/SASsample/@name")
436                Title = CS_XmlStrFmXpath(fileID,  TitlePath, "")
437        ENDIF
438        IF (strlen(Title) == 0)
439                // last resort: make up a title
440                Title = "SASentry"
441                TitlePath = ""
442        ENDIF
443        PRINT "\t Title:", Title
444        CS_appendMetaData("title", TitlePath, Title)
445        RETURN(Title)
446END
447
448// ==================================================================
449
450FUNCTION CS_appendMetaData(key, xpath, value)
451        STRING key, xpath, value
452        WAVE/T metadata
453        VARIABLE last
454        last = DimSize(metadata, 0)
455        Redimension/N=(last+1, 3) metadata
456        metadata[last][0] = key
457        metadata[last][1] = xpath
458        metadata[last][2] = value
459END
460
461// ==================================================================
462
463FUNCTION CS_findElementIndex(matchStr)
464        STRING matchStr
465        //
466        // support the canSAS XML file reader
467        // return index where   g[index][0] == matchStr
468        // return -1 if not found
469        //
470        WAVE/T W_ElementList_Col0
471
472        FindValue/TEXT=matchStr    W_ElementList_Col0
473        RETURN(V_value)
474END
475
476// ==================================================================
477
478FUNCTION CS_registerNameSpaces(fileID)
479        VARIABLE fileID
480        //
481        // The canSAS 1-D namespace is defined in the <SASroot> element.
482        // We can use our own namespace prefix without further concern.
483        // Other namespaces may be used in the file but this does not
484        // look for foreign (non-canSAS) elements.
485        //
486        WAVE/T W_ElementList
487        SVAR nsPre
488        SVAR nsStr
489        VARIABLE i, j, index, row
490        STRING testStr
491        MAKE/T/N=(1,2)/O nsRegistry
492        //
493        nsStr = W_ElementList[0][1]             // this is the one to use
494        nsPre = ""
495        IF (strlen(nsStr))
496                nsPre = "cs:"
497                nsStr = "cs=" + nsStr
498        ENDIF
499        // 2008-03-14,PRJ: Now, add a workaround for the libxml2 support that affects MacOS.
500        //   When using namespaces
501        //              W_ElementList[][1] != "" (it shows a namespace for this node)
502        //   and with default libxml2 (v2.2) on MacOS,
503        //              W_ElementList[][0] != /SASroot/SASentry ...
504        //  BUT, on PCs and on MacOS with user-proveded libxml2 (?version?)
505        //              W_ElementList[][0] != /*/*[1]   or /*/* ...
506        //
507        // Need to handle either situation.
508        //  Add another column to W_ElementList that shows the corrected absolute
509        //      XPath query,  complete with namespace prefix as needed.
510        Redimension/N=(DimSize(W_ElementList,0),5) W_ElementList
511        W_ElementList[0,Inf][4] = ""                            // clear out the last column (useful only to developer)
512        W_ElementList[0][4] = CS_XPath_NS("/"+W_ElementList[0][3])
513        FOR (i = 1; i < DimSize(W_ElementList,0); i += 1)
514                IF (strlen(W_ElementList[i][4]) == 0)           // if not already set, then find absolute XPath string
515                        // For now, only handle items in the default namespace.
516                        // Takes more work to build in the capability to handle more namespaces
517                        //
518                        index = CS_findElementIndex(  CS_findLast(W_ElementList[i][0], "/", 1  )  )
519                        testStr = W_ElementList[index][4] + CS_XPath_NS("/"+W_ElementList[i][3])
520                        XMLlistXpath(fileID,testStr,nsStr)
521                        IF ( !EXISTS("W_listXPath") )
522                                PRINT i, testStr, " empty result from XmlListXpath()"
523                                BREAK
524                        ENDIF
525                        WAVE/T W_listXPath
526                        SWITCH(numpnts(W_listXPath))
527                                CASE 0:                 // What?  Can't find the node we just found?
528                                        //PRINT numpnts(W_listXPath), testStr, " !!! WARNING:  <Can't find the node we just found> in CS_registerNameSpaces()"
529                                        PRINT i, testStr, " Foreign element namespace detected and ignored"
530                                        BREAK
531                                CASE 1:                 // Only one node with this element
532                                        W_ElementList[i][4] = testStr
533                                        BREAK
534                                DEFAULT:                        // Multiple elements match this node; need to use indices
535                                        // PRINT numpnts(W_listXPath), testStr
536                                        FOR (j = 0; j < numpnts(W_listXPath); j += 1)
537                                                index = CS_findElementIndex(  W_listXPath[j]  )
538                                                W_ElementList[index][4] = testStr + "[" + num2str(j+1) + "]"
539                                        ENDFOR
540                                        BREAK
541                        ENDSWITCH
542                ENDIF
543        ENDFOR
544        // PRINT StringByKey("schemaLocation", W_ElementList[0][2])
545        //
546        // <code comes here>
547        RETURN(0)
548END
549
550// ==================================================================
551
552FUNCTION/S CS_findLast(src, matchStr, leftSide)
553        STRING src
554        STRING matchStr
555        VARIABLE leftSide
556        // given a source string such as:   /*/*/*[7]/*/*[39]/*[2]
557        // return the part on the $leftSide if the $matchStr
558        // Examples:
559        //       CS_findLast( "/*/*/*[7]/*/*[39]/*[2]"  , "/", 1)                 /*/*/*[7]/*/*[39]
560        //       CS_findLast( "/*/*/*[7]/*/*[39]/*[2]"  , "/", 0)                 /*[2]
561        //       CS_findLast( "/*/*/*[7]/*/*[39]/*[2]"  , "[", 1)                 /*/*/*[7]/*/*[39]/*
562        //       CS_findLast( "/*/*/*[7]/*/*[39]/*[2]"  , "[", 0)                 [2]
563        VARIABLE i
564        STRING result="", test
565        FOR (i=strlen(src)-1; i >= 0; i -= 1)
566                IF (stringmatch(src[i], matchStr ))
567                        IF (leftSide)
568                                result = src[0,i-1]
569                        ELSE
570                                result = src[i,Inf]
571                        ENDIF
572                        RETURN(result)
573                ENDIF
574        ENDFOR
575        RETURN(result)
576END
577
578// ==================================================================
579
580FUNCTION/S CS_XPath_NS(simpleStr)
581        // namespaces complicate the XPath description
582        // this function adds namespace info as necessary to simpleStr (an XPath)
583        STRING simpleStr
584        SVAR nsPre
585        STRING result = "", thisChar, lastChar = ""
586        VARIABLE i
587        FOR (i = 0; i < strlen(simpleStr); i += 1)
588                //PRINT simpleStr[i]
589                thisChar = simpleStr[i]
590                IF ( CmpStr(lastChar, "/") == 0 )
591                        STRSWITCH (thisChar)
592                                CASE "/":
593                                CASE ".":
594                                CASE "@":
595                                        BREAK
596                                DEFAULT:
597                                        result += nsPre
598                        ENDSWITCH
599                ENDIF
600                result += thisChar
601                lastChar = thisChar
602        ENDFOR
603        RETURN(result)
604END
605
606// ==================================================================
607
608FUNCTION/S CS_XmlStrFmXpath(fileID, prefix, value)
609        VARIABLE fileID
610        STRING prefix, value
611        SVAR nsStr
612        STRING result
613        result = TrimWS(XmlStrFmXpath(fileID, prefix + CS_XPath_NS(value), nsStr, ""))
614        RETURN( result )
615END
616
617// ==================================================================
618
619FUNCTION CS_simpleXmlWaveFmXpath(fileID, prefix, value)
620        VARIABLE fileID
621        STRING prefix, value
622        SVAR nsStr
623        XMLwaveFmXpath(fileID, prefix + CS_XPath_NS(value), nsStr, " ")
624        // output: M_xmlContent  W_xmlContentNodes
625END
626
627// ==================================================================
628
629FUNCTION/S CS_correctedXpathStr(indexPath)
630        STRING indexPath
631        VARIABLE index
632        STRING absolutePath
633        WAVE/T          W_ElementList
634        index  =  CS_findElementIndex(indexPath)
635        absolutePath = W_ElementList[index][4]  // use corrected XPath string
636        RETURN(absolutePath)
637END
638
639// ==================================================================
640
641FUNCTION CS_simpleXmlListXpath(fileID, prefix, value)
642        VARIABLE fileID
643        STRING prefix, value
644        SVAR nsStr
645        XMLlistXpath(fileID, prefix + CS_XPath_NS(value), nsStr)                // output: W_listXPath
646END
647
648// ==================================================================
649
650Function/T   TrimWS(str)
651    // TrimWhiteSpace (code from Jon Tischler)
652    String str
653    return TrimWSL(TrimWSR(str))
654End
655
656// ==================================================================
657
658Function/T   TrimWSL(str)
659    // TrimWhiteSpaceLeft (code from Jon Tischler)
660    String str
661    Variable i, N=strlen(str)
662    for (i=0;char2num(str[i])<=32 && i<N;i+=1)    // find first non-white space
663    endfor
664    return str[i,Inf]
665End
666
667// ==================================================================
668
669Function/T   TrimWSR(str)
670    // TrimWhiteSpaceRight (code from Jon Tischler)
671    String str
672    Variable i
673    for (i=strlen(str)-1; char2num(str[i])<=32 && i>=0; i-=1)    // find last non-white space
674    endfor
675    return str[0,i]
676End
677
678// ==================================================================
679
680FUNCTION CS_updateWaveNote(wavName, key, value)
681        STRING wavName, key, value
682        STRING wavenote
683        wavenote = ReplaceStringByKey(key, note($wavName), value)
684        Note /K $wavName, wavenote
685END
686
687// ==================================================================
688
689FUNCTION CS_1i_extractIdataColumn2Wave(fileID, basePath, colName, wavName)
690        //
691        // this function pulls one column of data from each <Idata> element
692        // easier to write this as a function than debug it all the times it is needed
693        //
694        VARIABLE fileID
695        STRING basePath, colName, wavName
696        STRING unit
697        WAVE/T metadata
698        VARIABLE i, numPts
699
700        //      Q values come out in multiple columns. Different nodes means different columns in M_xmlcontent
701        //      Multiple values in the SAME node (i.e. a vector) get put in different rows.
702        //      We are therefore going to transpose the wave
703        //      (Based on example from Andrew R.J. Nelson.)
704        CS_simpleXmlWaveFmXpath(fileID, basePath, "//Idata/" + colName)
705        WAVE/T  M_xmlcontent, W_xmlcontentnodes
706        numPts = numpnts(M_XMLcontent)
707        IF (numPts > 0)
708                MatrixTranspose M_XMLcontent
709                MAKE/O/D/N=(numpnts(M_XMLcontent)) $wavName = str2num(M_xmlcontent[p][0])
710                // don't forget the units!  Assume that all rows have the same "unit" as in the first row.
711                unit = CS_XmlStrFmXpath(fileID, basePath, "/Idata[1]/"+colName+"/@unit")
712                SetScale d 0, 1, unit, $wavName                         // update the wave's "UNITS" string
713                SetScale x 0, 1, unit, $wavName                         // put it here, too, for the Data Browser
714                // put unit directly into wavenote of _this_ wave
715                CS_updateWaveNote(wavName, "unit", unit)                // put UNIT in wavenote
716                // store all the metadata in the wavenote (for now, at least)
717                FOR (i = 0; i < DimSize(metadata, 0); i += 1)
718                        IF (strlen(metadata[i][2]) > 0)
719                                // only add defined metadata to the wavenote
720                                CS_updateWaveNote(wavName, metadata[i][0], metadata[i][2])
721                        ENDIF
722                ENDFOR
723        ELSE
724                // did not find any data
725                // no need to apply special handling here; do that in the caller
726        ENDIF
727        //IF (numPts)
728        //      PRINT "\t\t\t\t" + wavName + ": found " + num2str(numPts) + " points"
729        //ENDIF
730        RETURN(numPts)
731END
732
733// ==================================================================
734
735FUNCTION CS_1i_extractSasData(fileID, SASdataPath, SASdata_folder)
736        //
737        // extract data from the SASdata/Idata block in a canSAS1d/v1.0 XML file
738        //  (1i in the function name signifies this is a function that supports INPUT from version 1.0 XML files)
739        //
740        //      returns:
741        //              0       no error
742        //              1       number of points in waves is not the same as Qsas wave
743        //
744        VARIABLE fileID
745        STRING SASdataPath, SASdata_folder
746        WAVE/T metadata
747        VARIABLE numPts, numQ
748        SVAR errorMsg
749
750        // extract each Idata column into waves
751        // ignore the return codes here, check below
752        numQ    = CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "Q",                       "Qsas")
753        IF (numQ != CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "I",                     "Isas"))
754                errorMsg = "number of points in Qsas and Isas waves are not identical"
755                RETURN(1)
756        ENDIF
757        numPts = CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "Idev",             "Idev")
758        IF (numPts && (numQ != numPts) )
759                errorMsg = "number of points in Qsas and Idev waves are not identical"
760                RETURN(1)
761        ENDIF
762        numPts = CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "Qdev",             "Qdev")
763        IF (numPts && (numQ != numPts) )
764                errorMsg = "number of points in Qsas and Qdev waves are not identical"
765                RETURN(1)
766        ENDIF
767        numPts = CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "dQw",              "dQw")
768        IF (numPts && (numQ != numPts) )
769                errorMsg = "number of points in Qsas and dQw waves are not identical"
770                RETURN(1)
771        ENDIF
772        numPts = CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "dQl",                      "dQl")
773        IF (numPts && (numQ != numPts) )
774                errorMsg = "number of points in Qsas and dQl waves are not identical"
775                RETURN(1)
776        ENDIF
777        numPts = CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "Qmean",            "Qmean")
778        IF (numPts && (numQ != numPts) )
779                errorMsg = "number of points in Qsas and Qmean waves are not identical"
780                RETURN(1)
781        ENDIF
782        numPts = CS_1i_extractIdataColumn2Wave(fileID, SASdataPath, "Shadowfactor",     "Shadowfactor")
783        IF (numPts && (numQ != numPts) )
784                errorMsg = "number of points in Qsas and Shadowfactor waves are not identical"
785                RETURN(1)
786        ENDIF
787
788        PRINT "\t\t\t\t found " + num2str(numpnts(Qsas)) + " points"
789
790        // move the waves to the sample folder
791        // !!!!! Missing Qsas, Isas, Qdev, and/or Idev are a broken data set
792        //              This should produce an exception.  Should have been trapped by numPts tests.
793        //              Best to return an error code but the caller chain is not ready to pass that to the top level, yet.
794        MoveWave Qsas, $SASdata_folder                          // required wave
795        MoveWave Isas, $SASdata_folder                          // required wave
796        IF (exists("Idev") == 1)
797                MoveWave Idev, $SASdata_folder                  // optional wave
798        ENDIF
799        IF (exists("Qdev") == 1)
800                MoveWave Qdev, $SASdata_folder                  // optional wave
801        ENDIF
802        IF (exists("dQw") == 1)
803                MoveWave dQw, $SASdata_folder                   // optional wave
804        ENDIF
805        IF (exists("dQl") == 1)
806                MoveWave dQl, $SASdata_folder                   // optional wave
807        ENDIF
808        IF (exists("Qmean") == 1)
809                MoveWave Qmean, $SASdata_folder         // optional wave
810        ENDIF
811        IF (exists("ShadowFactor") == 1)
812                MoveWave ShadowFactor, $SASdata_folder  // optional wave
813        ENDIF
814        IF (exists("metadata") == 1)
815                Duplicate/O metadata, $SASdata_folder + "metadata"
816        ENDIF
817        RETURN(0)                       // no error
818END
819
820
821// ==================================================================
822// ==================================================================
823// ==================================================================
824
825
826FUNCTION prj_grabMyXmlData()
827        STRING srcDir = "root:Packages:CS_XMLreader"
828        STRING destDir = "root:PRJ_canSAS"
829        STRING srcFolder, destFolder, theFolder
830        Variable i
831        NewDataFolder/O  $destDir               // for all my imported data
832        FOR ( i = 0; i < CountObjects(srcDir, 4) ; i += 1 )
833                theFolder = GetIndexedObjName(srcDir, 4, i)
834                srcFolder = srcDir + ":" + theFolder
835                destFolder = destDir + ":" + theFolder
836                // PRINT srcFolder, destFolder
837                IF (DataFolderExists(destFolder))
838                        // !!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
839                        // need to find unique name for destination
840                        // Persons who implement this properly should be more elegant
841                        // For now, I will blast the existing and proceed blindly.
842                        KillDataFolder/Z  $destFolder           // clear out any previous work
843                        DuplicateDataFolder $srcFolder, $destFolder
844                ELSE
845                        DuplicateDataFolder $srcFolder, $destFolder
846                ENDIF
847        ENDFOR
848END
849
850FUNCTION prjTest_cansas1d()
851        // unit tests for the routines under prj-readXML.ipf
852        STRING theFile
853        STRING fList = ""
854        VARIABLE i, result, timerID, seconds
855        // build a table of test data sets
856        fList = AddListItem("elmo.xml",                                 fList, ";", Inf)                // non-existent file
857        fList = AddListItem("cansasXML.ipf",                    fList, ";", Inf)                // this file (should fail on XML parsing)
858        fList = AddListItem("book.xml",                                 fList, ";", Inf)                // good XML example file but not canSAS, not even close
859        fList = AddListItem("bimodal-test1.xml",                fList, ";", Inf)                // simple dataset
860        fList = AddListItem("bimodal-test2-vector.xml", fList, ";", Inf)                // version 2.0 file (no standard yet)
861        fList = AddListItem("test.xml",                                 fList, ";", Inf)                // cs_collagen.xml with no namespace
862        fList = AddListItem("test2.xml",                                fList, ";", Inf)                // version 2.0 file (no standard yet)
863        fList = AddListItem("ISIS_SANS_Example.xml",    fList, ";", Inf)                // from S. King, 2008-03-17
864        fList = AddListItem("W1W2.xml",                                 fList, ";", Inf)                // from S. King, 2008-03-17
865        fList = AddListItem("ill_sasxml_example.xml",   fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
866        fList = AddListItem("isis_sasxml_example.xml",  fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
867        fList = AddListItem("r586.xml",                                         fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
868        fList = AddListItem("r597.xml",                                         fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
869        fList = AddListItem("xg009036_001.xml",                 fList, ";", Inf)                // foreign elements with other namespaces
870        fList = AddListItem("cs_collagen.xml",                  fList, ";", Inf)                // another simple dataset, bare minimum info
871        fList = AddListItem("cs_collagen_full.xml",             fList, ";", Inf)                // more Q range than previous
872        fList = AddListItem("cs_af1410.xml",                    fList, ";", Inf)                // multiple SASentry and SASdata elements
873        fList = AddListItem("cansas1d-template.xml",    fList, ";", Inf)                // multiple SASentry and SASdata elements
874        //fList = AddListItem("1998spheres.xml",                        fList, ";", Inf)                // 2 SASentry, few thousand data points each
875        fList = AddListItem("does-not-exist-file.xml",          fList, ";", Inf)                // non-existent file
876        fList = AddListItem("cs_rr_polymers.xml",               fList, ";", Inf)                // Round Robin polymer samples from John Barnes @ NIST
877        // try to load each data set in the table
878        FOR ( i = 0; i < ItemsInList(fList) ; i += 1 )
879                theFile = StringFromList(i, fList)                                      // walk through all test files
880                // PRINT "file: ", theFile
881                pathInfo home
882                //IF (CS_XmlReader(theFile) == 0)                                       // did the XML reader return without an error code?
883                timerID = StartMStimer
884                result = CS_XmlReader(ParseFilePath(5,S_path,"*",0,0) + theFile)
885                seconds = StopMSTimer(timerID) * 1.0e-6
886                PRINT "\t Completed in ", seconds, " seconds"
887                IF (result == 0)    // did the XML reader return without an error code?
888                        prj_grabMyXmlData()                                             // move the data to my directory
889                ENDIF
890        ENDFOR
891END
892
893
894FUNCTION prjTest_writer(xmlFile)
895        STRING xmlFile
896        VARIABLE fileID
897        STRING nsStr = "cansas1d/1.0", prefixStr = ""
898        fileID = XMLcreateFile(xmlFile,"SASroot",nsStr,prefixStr)
899        XMLsetAttr(fileID,              "/SASroot",                             nsStr, "version", "1.0")
900        XMLsetAttr(fileID,              "/SASroot",                             nsStr, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
901        XMLsetAttr(fileID,              "/SASroot",                             nsStr, "xsi:schemaLocation", "cansas1d/1.0    http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd")
902        XMLaddNode(fileID,      "/SASroot",                             nsStr, "SASentry", "", 1)
903        XMLsetAttr(fileID,              "/SASroot/SASentry",    nsStr, "name", "something")
904        XMLaddNode(fileID,      "/SASroot/SASentry",    nsStr, "Title", "my very first title", 1)
905        XMLaddNode(fileID,      "/SASroot/SASentry",    nsStr, "Run", "2008-03-19", 1)
906        XMLsetAttr(fileID,              "/SASroot/SASentry/Run", nsStr, "name", "actually is a date")
907        XMLsaveFile(fileID)
908        XMLcloseFile(fileID,0)
909END
Note: See TracBrowser for help on using the repository browser.