source: sans/Dev/trunk/NCNR_User_Procedures/Common/canSASXML.ipf @ 838

Last change on this file since 838 was 670, checked in by srkline, 12 years ago

Corrections to NSORT and CombineFiles? to work with XML i/o.

CombineFiles? table hook now is properly active only in the upper table.

Some file filtering functions have been streamlined, but are still somewhat redundant, and could be consolidated. These are whenever listing of reduced files is requested (filter out what is known to be something else)

Commented out print statements from XML reader.

File size: 36.0 KB
Line 
1#pragma rtGlobals=1             // Use modern global access method.
2#pragma version=1.11
3#pragma IgorVersion=6.1
4
5// taken from smallangles.net on 02 APR 2010
6// changed Function/T to Function/S on (unused) TrimWS functions
7
8// file:        cansasXML.ipf
9// author:      Pete R. Jemian <jemian@anl.gov>
10// SVN date:    $Date$
11// SVN rev.:    $Revision$
12// SVN URL:     $HeadURL$
13// SVN ID:      $Id$
14// purpose:  implement an IgorPro file reader to read the canSAS 1-D reduced SAS data in XML files
15//                      adheres to the cansas1d/1.0 standard
16// readme:    http://www.smallangles.net/wgwiki/index.php/cansas1d_binding_IgorPro
17// URL: http://www.smallangles.net/wgwiki/index.php/cansas1d_documentation
18//
19// requires:    IgorPro (http://www.wavemetrics.com/)
20//                              XMLutils - XOP (http://www.igorexchange.com/project/XMLutils)
21// provides:  CS_CmlReader(String fileName)
22//                              all other functions in this file should not be relied upon
23
24// ==================================================================
25// CS_XmlReader("bimodal-test1.xml")
26// CS_XmlReader("1998spheres.xml")
27// CS_XmlReader("xg009036_001.xml")
28// CS_XmlReader("s81-polyurea.xml")
29// CS_XmlReader("cs_af1410.xml")
30//  testCollette();  prjTest_cansas1d()
31// ==================================================================
32
33
34#if( Exists("XmlOpenFile") )
35        // BEFORE we do anything else, check that XMLutils XOP is available.
36
37
38FUNCTION CS_XmlReader(fileName)
39        //
40        // open a canSAS 1-D reduced SAS XML data file
41        //      returns:
42        //              0 : successful
43        //              -1: XML file not found
44        //              -2: root element is not <SASroot> with valid canSAS namespace
45        //              -3: <SASroot> version  is not 1.0
46        //              -4: no <SASentry> elements
47        //              -5: XMLutils XOP needs upgrade
48        //              -6: XMLutils XOP not found
49        //
50        STRING fileName
51        STRING origFolder
52        STRING workingFolder = "root:Packages:CS_XMLreader"
53        VARIABLE returnCode
54
55
56        //
57        // set up a work folder within root:Packages
58        // Clear out any progress/results from previous activities
59        //
60        origFolder = GetDataFolder(1)
61        SetDataFolder root:                                     // start in the root data folder
62        NewDataFolder/O  root:Packages          // good practice
63        KillDataFolder/Z  $workingFolder                // clear out any previous work
64        NewDataFolder/O/S  $workingFolder       // Do all our work in root:XMLreader
65
66        //
67        // Try to open the named XML file (clean-up and return if failure)
68        //
69        VARIABLE fileID
70        STRING/G errorMsg, xmlFile
71        xmlFile = fileName
72        fileID = XmlOpenFile(fileName)                  // open and parse the XMLfile
73        IF ( fileID < 0 )
74                SWITCH(fileID)                                  // fileID holds the return code; check it
75                        CASE -1:
76                                errorMsg = fileName + ": failed to parse XML"
77                        BREAK
78                        CASE -2:
79                                errorMsg = fileName + " either not found or cannot be opened for reading"
80                        BREAK
81                ENDSWITCH
82                PRINT errorMsg
83                SetDataFolder $origFolder
84                RETURN(-1)                                              // could not find file
85        ENDIF
86
87        //
88        //      test to see if XMLutils has the needed upgrade
89        //
90        XMLlistXpath(fileID, "/*", "") 
91        IF ( EXISTS( "M_listXPath" ) == 0 )
92                XmlCloseFile(fileID,0)
93                errorMsg = "XMLutils needs an upgrade:  http://www.igorexchange.com/project/XMLutils"
94                PRINT errorMsg
95                SetDataFolder $origFolder
96                RETURN(-5)                                              // XOPutils needs an upgrade
97        ENDIF
98        WAVE/T  M_listXPath
99
100        // check for canSAS namespace string, returns "" if not valid or not found
101        STRING/G ns = CS_getDefaultNamespace(fileID)
102        IF (strlen(ns) == 0 )
103                XmlCloseFile(fileID,0)
104                errorMsg = "root element is not <SASroot> with valid canSAS namespace"
105                PRINT errorMsg
106                SetDataFolder $origFolder
107                RETURN(-2)                                              // root element is not <SASroot> with valid canSAS namespace
108        ENDIF
109        STRING/G nsPre = "cs:"
110        STRING/G nsStr = "cs=" + ns
111
112        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
113        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
114       
115        STRSWITCH(ns)   
116        CASE "cansas1d/1.0":                                                    // version 1.0 of the canSAS 1-D reduced SAS data standard
117//              PRINT fileName, "\t\t identified as: cansas1d/1.0 XML file"
118                returnCode = CS_1i_parseXml(fileID)                     //  This is where the action happens!
119                IF (returnCode != 0)
120                        IF (strlen(errorMsg) == 0)
121                                errorMsg = "error while parsing the XML"
122                        ENDIF
123                        PRINT errorMsg
124                        XmlCloseFile(fileID,0)
125                        SetDataFolder $origFolder
126                        RETURN(returnCode)                      // error while parsing the XML
127                ENDIF
128                BREAK
129        CASE "cansas1d/2.0a":                                           // unsupported
130        DEFAULT:                                                        // optional default expression executed
131                errorMsg = fileName + ": <SASroot>, namespace (" + ns + ") is not supported"
132                PRINT errorMsg
133                XmlCloseFile(fileID,0)
134                SetDataFolder $origFolder
135                RETURN(-3)                                              // attribute list must include version="1.0"
136        ENDSWITCH
137
138        XmlCloseFile(fileID,0)                                  // now close the file, without saving
139        fileID = -1
140
141        SetDataFolder root:Packages:CS_XMLreader
142        KillWaves/Z M_listXPath, SASentryList
143        SetDataFolder $origFolder
144        RETURN(0)                                                       // execution finished OK
145END
146
147FUNCTION/S CS_getDefaultNamespace(fileID)
148        // Test here (by guessing) for the various known namespaces.
149        // Return the one found in the "schemaLocation" attribute
150        // since the XMLutils XOP does not provide any xmlns attributes.
151        // It is possible to call XMLelemList and get the namespace directly
152        // but that call can be expensive (time) when there are lots of elements.
153        VARIABLE fileID
154        STRING ns = "", thisLocation
155        VARIABLE i, item
156        MAKE/T/N=(1)/O nsList           // list of all possible namespaces
157        nsList[0] = "cansas1d/1.0"              // first version of canSAS 1-D reduced SAS
158
159        FOR (item = 0; item < DimSize(nsList, 0); item += 1)            // loop over all possible namespaces
160                XMLlistAttr(fileID, "/cs:SASroot", "cs="+nsList[item])
161                WAVE/T M_listAttr
162                FOR (i = 0; i < DimSize(M_listAttr,0); i+=1)                    // loop over all available attributes
163                        // Expect the required canSAS XML header (will fail if "schemalocation" is not found)
164                        IF ( CmpStr(  LowerStr(M_listAttr[i][1]),  LowerStr("schemaLocation") ) == 0 )
165                                thisLocation = TrimWS(M_listAttr[i][2])
166                                IF ( StringMatch(thisLocation, nsList[item] + "*") )
167                                        ns = nsList[item]
168                                        BREAK           // found it!
169                                ENDIF
170                        ENDIF
171                ENDFOR
172                IF (strlen(ns))
173                        BREAK           // found it!
174                ENDIF
175        ENDFOR
176
177        KillWaves/Z nsList, M_listAttr
178        RETURN ns
179END
180
181// ==================================================================
182
183FUNCTION CS_1i_parseXml(fileID)
184        VARIABLE fileID
185        SVAR errorMsg, xmlFile
186        STRING/G Title, Title_folder
187        VARIABLE i, j, index, SASdata_index, returnCode = 0
188
189        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
190        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
191
192        // locate all the SASentry elements
193        //      assume nsPre = "cs" otherwise
194        // "/"+nsPre+":SASroot//"+nsPre+":SASentry"
195        XmlListXpath(fileID, "/cs:SASroot//cs:SASentry", nsStr)
196        WAVE/T  M_listXPath
197        STRING          SASentryPath
198        DUPLICATE/O/T   M_listXPath, SASentryList
199
200        FOR (i=0; i < DimSize(SASentryList, 0); i += 1)
201                SASentryPath = "/cs:SASroot/cs:SASentry["+num2str(i+1)+"]"
202                SetDataFolder root:Packages:CS_XMLreader
203               
204                title =  CS_1i_locateTitle(fileID, SASentryPath)
205                Title_folder = CS_cleanFolderName(Title)
206                NewDataFolder/O/S  $Title_folder
207
208                XmlListXpath(fileID, SASentryPath + "//cs:SASdata", nsStr)
209                WAVE/T  M_listXPath
210                IF ( DimSize(M_listXPath, 0) == 1)
211                        CS_1i_getOneSASdata(fileID, Title, SASentryPath+"/cs:SASdata")
212                        CS_1i_collectMetadata(fileID, SASentryPath)
213                ELSE
214                        FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)
215                                // Could make this new behavior optional
216                                STRING SASdata_item = "SASdata_" + num2str(j)
217                                STRING SASdata_node = SASentryPath+"/cs:SASdata["+num2str(j+1)+"]"
218                                // Preferred name obtained from the SASentry/SASdata/@name attribute, if present
219                                STRING SASdata_name = XMLstrFmXpath(fileID,  SASdata_node + "/@name", nsStr, "")
220                                IF (strlen(SASdata_name) == 0)
221                                        IF (DimSize(M_listXPath, 0) == 1)
222                                                // Alternative if only one SASdata block is to use the SASentry/Title
223                                                SASdata_name = XMLstrFmXpath(fileID,  SASentryPath+"/cs:Title", nsStr, "")
224                                        ELSE
225                                                // the original behavior: SASdata_0, SASdata_1, ...
226                                                SASdata_name = SASdata_item
227                                        ENDIF
228                                        // the original behavior: SASdata_0, SASdata_1, ...
229                                        SASdata_name = SASdata_item
230                                ENDIF
231                                STRING SASdataFolder = CS_cleanFolderName(SASdata_name)
232                                NewDataFolder/O/S  $SASdataFolder
233                                CS_1i_getOneSASdata(fileID, Title, SASdata_node)
234                                CS_1i_collectMetadata(fileID, SASentryPath)
235                                SetDataFolder ::                        // back up to parent directory
236                        ENDFOR
237                ENDIF
238                KillWaves/Z M_listXPath
239        ENDFOR
240
241        SetDataFolder root:Packages:CS_XMLreader
242        KillWaves/Z M_listXPath, SASentryList
243        RETURN(returnCode)
244END
245
246// ==================================================================
247
248FUNCTION/S CS_cleanFolderName(proposal)
249        STRING proposal
250        STRING result
251        result = CleanupName(proposal, 0)
252        IF ( CheckName(result, 11) != 0 )
253                result = UniqueName(result, 11, 0)
254        ENDIF
255        RETURN result
256END
257
258// ==================================================================
259
260FUNCTION CS_1i_getOneSASdata(fileID, Title, SASdataPath)
261        VARIABLE fileID
262        STRING Title, SASdataPath
263        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
264        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
265        VARIABLE i
266        STRING SASdata_name, suffix = ""
267
268        //grab the data and put it in the working data folder
269        CS_1i_GetReducedSASdata(fileID, SASdataPath)
270
271        //start the metadata
272        MAKE/O/T/N=(0,2) metadata
273
274        SVAR xmlFile = root:Packages:CS_XMLreader:xmlFile
275        CS_appendMetaData(fileID, "xmlFile", "", xmlFile)
276
277        SVAR ns = root:Packages:CS_XMLreader:ns
278        CS_appendMetaData(fileID, "namespace", "", ns)
279        CS_appendMetaData(fileID, "Title", "", Title)
280       
281        XmlListXpath(fileID, SASdataPath + "/..//cs:Run", nsStr)
282        WAVE/T  M_listXPath
283        FOR (i=0; i < DimSize(M_listXPath, 0); i += 1)
284                IF ( DimSize(M_listXPath, 0) > 1 )
285                        suffix = "_" + num2str(i)
286                ENDIF
287                CS_appendMetaData(fileID, "Run" + suffix,  SASdataPath + "/../cs:Run["+num2str(i+1)+"]", "")
288                CS_appendMetaData(fileID, "Run/@name" + suffix,  SASdataPath + "/../cs:Run["+num2str(i+1)+"]/@name", "")
289        ENDFOR
290
291        SASdata_name = TrimWS(XMLstrFmXpath(fileID,  SASdataPath + "/@name", nsStr, ""))
292        CS_appendMetaData(fileID, "SASdata/@name", "", SASdata_name)
293
294        KillWaves/Z M_listXPath
295END
296
297// ==================================================================
298
299FUNCTION CS_1i_getOneVector(file,prefix,XML_name,Igor_name)
300        VARIABLE file
301        STRING prefix,XML_name,Igor_name
302        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
303        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
304
305        XmlWaveFmXpath(file,prefix+XML_name,nsStr,"")                   //this loads ALL the vector's nodes at the same time
306        WAVE/T M_xmlcontent
307        WAVE/T W_xmlContentNodes
308        IF (DimSize(M_xmlcontent, 0))                   // test to see if the nodes exist.  not strictly necessary if you know the nodes are there
309                IF (DimSize(M_xmlcontent,1)>DimSize(M_xmlcontent,0))    //if you're not in vector mode
310                        MatrixTranspose M_xmlcontent
311                ENDIF
312                MAKE/O/D/N=(DimSize(M_xmlcontent, 0)) $Igor_name
313                WAVE vect = $Igor_name
314                vect=str2num(M_xmlcontent)
315        ENDIF
316        KILLWAVES/Z M_xmlcontent, W_xmlContentNodes
317END
318
319// ==================================================================
320
321FUNCTION CS_1i_GetReducedSASdata(fileID, SASdataPath)
322        VARIABLE fileID
323        STRING SASdataPath
324        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
325        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
326        STRING prefix = ""
327        VARIABLE pos
328
329        VARIABLE cansasStrict = 1               // !!!software developer's choice!!!
330        IF (cansasStrict)               // only get known canSAS data vectors
331                prefix = SASdataPath + "//cs:"
332                // load ALL nodes of each vector (if exists) at the same time
333                CS_1i_getOneVector(fileID, prefix, "Q",                 "Qsas")
334                CS_1i_getOneVector(fileID, prefix, "I",                 "Isas")
335                CS_1i_getOneVector(fileID, prefix, "Idev",              "Idev")
336                CS_1i_getOneVector(fileID, prefix, "Qdev",              "Qdev")
337                CS_1i_getOneVector(fileID, prefix, "dQw",       "dQw")
338                CS_1i_getOneVector(fileID, prefix, "dQl",               "dQl")
339                CS_1i_getOneVector(fileID, prefix, "Qmean",     "Qmean")
340                CS_1i_getOneVector(fileID, prefix, "Shadowfactor",      "Shadowfactor")
341                // check them for common length
342        ELSE                            // search for _ANY_ data vectors
343                // find the names of all the data columns and load them as vectors
344                // this gets tricky if we want to avoid namespace references
345                XmlListXpath(fileID, SASdataPath+"//cs:Idata[1]/*", nsStr)
346                WAVE/T M_listXPath
347                STRING xmlElement, xPathStr
348                STRING igorWave
349                VARIABLE j
350                FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)        // loop over all columns in SASdata/Idata[1]
351                        xmlElement = M_listXPath[j][1]
352                        STRSWITCH(xmlElement)
353                                CASE "Q":               // IgorPro does not allow a variable named Q
354                                CASE "I":                       // or I
355                                        igorWave = xmlElement + "sas"
356                                        BREAK
357                                DEFAULT:
358                                        igorWave = xmlElement           // can we trust this one?
359                        ENDSWITCH
360                        //
361//                      // This will need some work to support foreign namespaces here
362//                      //
363//                      //
364//                      xPathStr = M_listXPath[j][0]                                                    // clear name reference
365//                      pos = strsearch(xPathStr, "/", Inf, 3)                                  // peel off the tail of the string and reform
366//                      xmlElement = xPathStr[pos,Inf]                                          // find last element on the path
367//                      prefix = xPathStr[0, pos-1-4]+"/*"                                              // ALL Idata elements
368//                      CS_1i_getOneVector(fileID,prefix, xmlElement, igorWave)         // loads ALL rows (Idata) of the column at the same time
369                        //
370                        //  Could there be a problem with a foreign namespace here?
371                        prefix = SASdataPath+"//cs:Idata"                                               // ALL Idata elements
372                        xmlElement = "cs:" + M_listXPath[j][1]                                  // just this column
373                        CS_1i_getOneVector(fileID,prefix, xmlElement, igorWave)         // loads ALL rows (Idata) of the column at the same time
374                ENDFOR
375                // check them for common length
376        ENDIF
377 
378        //get rid of any mess
379        KILLWAVES/z M_listXPath
380END
381
382// ==================================================================
383
384FUNCTION CS_1i_collectMetadata(fileID, sasEntryPath)
385        VARIABLE fileID
386        STRING sasEntryPath
387        VARIABLE i, j
388        WAVE/T metadata
389        STRING suffix = "", preMeta = "", preXpath = ""
390        STRING value, detailsPath, detectorPath, notePath
391
392        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
393        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
394
395        // collect some metadata
396        // first, fill a table with keywords, and XPath locations, 3rd column will be values
397
398        // handle most <SASsample> fields
399        CS_appendMetaData(fileID, "SASsample/@name",                            sasEntryPath + "/cs:SASsample/@name", "")
400        CS_appendMetaData(fileID, "SASsample/ID",                                       sasEntryPath + "/cs:SASsample/cs:ID", "")
401        CS_appendMetaData(fileID, "SASsample/thickness",                                sasEntryPath + "/cs:SASsample/cs:thickness", "")
402        CS_appendMetaData(fileID, "SASsample/thickness/@unit",                  sasEntryPath + "/cs:SASsample/cs:thickness/@unit", "")
403        CS_appendMetaData(fileID, "SASsample/transmission",                     sasEntryPath + "/cs:SASsample/cs:transmission", "")
404        CS_appendMetaData(fileID, "SASsample/temperature",                      sasEntryPath + "/cs:SASsample/cs:temperature", "")
405        CS_appendMetaData(fileID, "SASsample/temperature/@unit",           sasEntryPath + "/cs:SASsample/cs:temperature/@unit", "")
406        CS_appendMetaData(fileID, "SASsample/position/x",                          sasEntryPath + "/cs:SASsample/cs:position/cs:x", "")
407        CS_appendMetaData(fileID, "SASsample/position/x/@unit",            sasEntryPath + "/cs:SASsample/cs:position/cs:x/@unit", "")
408        CS_appendMetaData(fileID, "SASsample/position/y",                          sasEntryPath + "/cs:SASsample/cs:position/cs:y", "")
409        CS_appendMetaData(fileID, "SASsample/position/y/@unit",            sasEntryPath + "/cs:SASsample/cs:position/cs:y/@unit", "")
410        CS_appendMetaData(fileID, "SASsample/position/z",                          sasEntryPath + "/cs:SASsample/cs:position/cs:z", "")
411        CS_appendMetaData(fileID, "SASsample/position/z/@unit",            sasEntryPath + "/cs:SASsample/cs:position/cs:z/@unit", "")
412        CS_appendMetaData(fileID, "SASsample/orientation/roll",                    sasEntryPath + "/cs:SASsample/cs:orientation/cs:roll", "")
413        CS_appendMetaData(fileID, "SASsample/orientation/roll/@unit",      sasEntryPath + "/cs:SASsample/cs:orientation/cs:roll/@unit", "")
414        CS_appendMetaData(fileID, "SASsample/orientation/pitch",           sasEntryPath + "/cs:SASsample/cs:orientation/cs:pitch", "")
415        CS_appendMetaData(fileID, "SASsample/orientation/pitch/@unit",     sasEntryPath + "/cs:SASsample/cs:orientation/cs:pitch/@unit", "")
416        CS_appendMetaData(fileID, "SASsample/orientation/yaw",                     sasEntryPath + "/cs:SASsample/cs:orientation/cs:yaw", "")
417        CS_appendMetaData(fileID, "SASsample/orientation/yaw/@unit",       sasEntryPath + "/cs:SASsample/cs:orientation/cs:yaw/@unit", "")
418        // <SASsample><details> might appear multiple times, too!
419        XmlListXpath(fileID, sasEntryPath+"/cs:SASsample//cs:details", nsStr)   //output: M_listXPath
420        WAVE/T  M_listXPath
421        DUPLICATE/O/T   M_listXPath, detailsList
422        suffix = ""
423        FOR (i = 0; i < DimSize(detailsList, 0); i += 1)
424                IF (DimSize(detailsList, 0) > 1)
425                        suffix = "_" + num2str(i)
426                ENDIF
427                detailsPath = sasEntryPath+"/cs:SASsample/cs:details["+num2str(i+1)+"]"
428                CS_appendMetaData(fileID, "SASsample/details"+suffix+"/@name",  detailsPath + "/@name", "")
429                CS_appendMetaData(fileID, "SASsample/details"+suffix,           detailsPath, "")
430        ENDFOR
431
432
433        // <SASinstrument>
434        CS_appendMetaData(fileID, "SASinstrument/name",         sasEntryPath + "/cs:SASinstrument/cs:name", "")
435        CS_appendMetaData(fileID, "SASinstrument/@name",        sasEntryPath + "/cs:SASinstrument/@name", "")
436
437        // <SASinstrument><SASsource>
438        preMeta = "SASinstrument/SASsource"
439        preXpath = sasEntryPath + "/cs:SASinstrument/cs:SASsource"
440        CS_appendMetaData(fileID, preMeta + "/@name",                      preXpath + "/@name", "")
441        CS_appendMetaData(fileID, preMeta + "/radiation",                  preXpath + "/cs:radiation", "")
442        CS_appendMetaData(fileID, preMeta + "/beam/size/@name",            preXpath + "/cs:beam_size/@name", "")
443        CS_appendMetaData(fileID, preMeta + "/beam/size/x",                preXpath + "/cs:beam_size/cs:x", "")
444        CS_appendMetaData(fileID, preMeta + "/beam/size/x@unit",           preXpath + "/cs:beam_size/cs:x/@unit", "")
445        CS_appendMetaData(fileID, preMeta + "/beam/size/y",                preXpath + "/cs:beam_size/cs:y", "")
446        CS_appendMetaData(fileID, preMeta + "/beam/size/y@unit",           preXpath + "/cs:beam_size/cs:y/@unit", "")
447        CS_appendMetaData(fileID, preMeta + "/beam/size/z",                preXpath + "/cs:beam_size/cs:z", "")
448        CS_appendMetaData(fileID, preMeta + "/beam/size/z@unit",           preXpath + "/cs:beam_size/cs:z/@unit", "")
449        CS_appendMetaData(fileID, preMeta + "/beam/shape",                 preXpath + "/cs:beam_shape", "")
450        CS_appendMetaData(fileID, preMeta + "/wavelength",                 preXpath + "/cs:wavelength", "")
451        CS_appendMetaData(fileID, preMeta + "/wavelength/@unit",           preXpath + "/cs:wavelength/@unit", "")
452        CS_appendMetaData(fileID, preMeta + "/wavelength_min",             preXpath + "/cs:wavelength_min", "")
453        CS_appendMetaData(fileID, preMeta + "/wavelength_min/@unit",       preXpath + "/cs:wavelength_min/@unit", "")
454        CS_appendMetaData(fileID, preMeta + "/wavelength_max",             preXpath + "/cs:wavelength_max", "")
455        CS_appendMetaData(fileID, preMeta + "/wavelength_max/@unit",       preXpath + "/cs:wavelength_max/@unit", "")
456        CS_appendMetaData(fileID, preMeta + "/wavelength_spread",          preXpath + "/cs:wavelength_spread", "")
457        CS_appendMetaData(fileID, preMeta + "/wavelength_spread/@unit",    preXpath + "/cs:wavelength_spread/@unit", "")
458
459        // <SASinstrument><SAScollimation> might appear multiple times
460        XmlListXpath(fileID, sasEntryPath+"/cs:SASinstrument//cs:SAScollimation", nsStr)        //output: M_listXPath
461        WAVE/T  M_listXPath
462        DUPLICATE/O/T   M_listXPath, SAScollimationList
463        STRING collimationPath
464        FOR (i = 0; i < DimSize(SAScollimationList, 0); i += 1)
465                preMeta = "SASinstrument/SAScollimation"
466                IF (DimSize(SAScollimationList, 0) > 1)
467                        preMeta += "_" + num2str(i)
468                ENDIF
469                collimationPath = sasEntryPath+"/cs:SASinstrument/cs:SAScollimation["+num2str(i+1)+"]"
470                CS_appendMetaData(fileID, preMeta + "/@name",               collimationPath + "/@name", "")
471                CS_appendMetaData(fileID, preMeta + "/length",              collimationPath + "/cs:length", "")
472                CS_appendMetaData(fileID, preMeta + "/length_unit",         collimationPath + "/cs:length/@unit", "")
473                FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)        // aperture may be repeated!
474                        IF (DimSize(M_listXPath, 0) == 1)
475                                preMeta = "SASinstrument/SAScollimation/aperture"
476                        ELSE
477                                preMeta = "SASinstrument/SAScollimation/aperture_" + num2str(j)
478                        ENDIF
479                        preXpath = collimationPath + "/cs:aperture["+num2str(j+1)+"]"
480                        CS_appendMetaData(fileID, preMeta + "/@name",         preXpath + "/@name", "")
481                        CS_appendMetaData(fileID, preMeta + "/type",          preXpath + "/cs:type", "")
482                        CS_appendMetaData(fileID, preMeta + "/size/@name",     preXpath + "/cs:size/@name", "")
483                        CS_appendMetaData(fileID, preMeta + "/size/x",        preXpath + "/cs:size/cs:x", "")
484                        CS_appendMetaData(fileID, preMeta + "/size/x/@unit",   preXpath + "/cs:size/cs:x/@unit", "")
485                        CS_appendMetaData(fileID, preMeta + "/size/y",        preXpath + "/cs:size/cs:y", "")
486                        CS_appendMetaData(fileID, preMeta + "/size/y/@unit",   preXpath + "/cs:size/cs:y/@unit", "")
487                        CS_appendMetaData(fileID, preMeta + "/size/z",        preXpath + "/cs:size/cs:z", "")
488                        CS_appendMetaData(fileID, preMeta + "/size/z/@unit",   preXpath + "/cs:size/cs:z/@unit", "")
489                        CS_appendMetaData(fileID, preMeta + "/distance",       preXpath + "/cs:distance", "")
490                        CS_appendMetaData(fileID, preMeta + "/distance/@unit", preXpath + "/cs:distance/@unit", "")
491                ENDFOR
492        ENDFOR
493
494        // <SASinstrument><SASdetector> might appear multiple times
495        XmlListXpath(fileID, sasEntryPath+"/cs:SASinstrument//cs:SASdetector", nsStr)   //output: M_listXPath
496        WAVE/T  M_listXPath
497        DUPLICATE/O/T   M_listXPath, SASdetectorList
498        FOR (i = 0; i < DimSize(SASdetectorList, 0); i += 1)
499                preMeta = "SASinstrument/SASdetector"
500                IF (DimSize(SASdetectorList, 0) > 1)
501                        preMeta += "_" + num2str(i)
502                ENDIF
503                detectorPath = sasEntryPath+"/cs:SASinstrument/cs:SASdetector["+num2str(i+1)+"]"
504                CS_appendMetaData(fileID, preMeta + "/@name",                    detectorPath + "/cs:name", "")
505                CS_appendMetaData(fileID, preMeta + "/SDD",                              detectorPath + "/cs:SDD", "")
506                CS_appendMetaData(fileID, preMeta + "/SDD/@unit",                        detectorPath + "/cs:SDD/@unit", "")
507                CS_appendMetaData(fileID, preMeta + "/offset/@name",             detectorPath + "/cs:offset/@name", "")
508                CS_appendMetaData(fileID, preMeta + "/offset/x",                 detectorPath + "/cs:offset/cs:x", "")
509                CS_appendMetaData(fileID, preMeta + "/offset/x/@unit",           detectorPath + "/cs:offset/cs:x/@unit", "")
510                CS_appendMetaData(fileID, preMeta + "/offset/y",                 detectorPath + "/cs:offset/cs:y", "")
511                CS_appendMetaData(fileID, preMeta + "/offset/y/@unit",           detectorPath + "/cs:offset/cs:y/@unit", "")
512                CS_appendMetaData(fileID, preMeta + "/offset/z",                 detectorPath + "/cs:offset/cs:z", "")
513                CS_appendMetaData(fileID, preMeta + "/offset/z/@unit",           detectorPath + "/cs:offset/cs:z/@unit", "")
514
515                CS_appendMetaData(fileID, preMeta + "/orientation/@name",        detectorPath + "/cs:orientation/@name", "")
516                CS_appendMetaData(fileID, preMeta + "/orientation/roll",         detectorPath + "/cs:orientation/cs:roll", "")
517                CS_appendMetaData(fileID, preMeta + "/orientation/roll/@unit",   detectorPath + "/cs:orientation/cs:roll/@unit", "")
518                CS_appendMetaData(fileID, preMeta + "/orientation/pitch",        detectorPath + "/cs:orientation/cs:pitch", "")
519                CS_appendMetaData(fileID, preMeta + "/orientation/pitch/@unit",   detectorPath + "/cs:orientation/cs:pitch/@unit", "")
520                CS_appendMetaData(fileID, preMeta + "/orientation/yaw",          detectorPath + "/cs:orientation/cs:yaw", "")
521                CS_appendMetaData(fileID, preMeta + "/orientation/yaw/@unit",    detectorPath + "/cs:orientation/cs:yaw/@unit", "")
522
523                CS_appendMetaData(fileID, preMeta + "/beam_center/@name",        detectorPath + "/cs:beam_center/@name", "")
524                CS_appendMetaData(fileID, preMeta + "/beam_center/x",            detectorPath + "/cs:beam_center/cs:x", "")
525                CS_appendMetaData(fileID, preMeta + "/beam_center/x/@unit",      detectorPath + "/cs:beam_center/cs:x/@unit", "")
526                CS_appendMetaData(fileID, preMeta + "/beam_center/y",            detectorPath + "/cs:beam_center/cs:y", "")
527                CS_appendMetaData(fileID, preMeta + "/beam_center/y/@unit",      detectorPath + "/cs:beam_center/cs:y/@unit", "")
528                CS_appendMetaData(fileID, preMeta + "/beam_center/z",            detectorPath + "/cs:beam_center/cs:z", "")
529                CS_appendMetaData(fileID, preMeta + "/beam_center/z/@unit",      detectorPath + "/cs:beam_center/cs:z/@unit", "")
530
531                CS_appendMetaData(fileID, preMeta + "/pixel_size/@name",         detectorPath + "/cs:pixel_size/@name", "")
532                CS_appendMetaData(fileID, preMeta + "/pixel_size/x",             detectorPath + "/cs:pixel_size/cs:x", "")
533                CS_appendMetaData(fileID, preMeta + "/pixel_size/x/@unit",       detectorPath + "/cs:pixel_size/cs:x/@unit", "")
534                CS_appendMetaData(fileID, preMeta + "/pixel_size/y",             detectorPath + "/cs:pixel_size/cs:y", "")
535                CS_appendMetaData(fileID, preMeta + "/pixel_size/y/@unit",       detectorPath + "/cs:pixel_size/cs:y/@unit", "")
536                CS_appendMetaData(fileID, preMeta + "/pixel_size/z",             detectorPath + "/cs:pixel_size/cs:z", "")
537                CS_appendMetaData(fileID, preMeta + "/pixel_size/z/@unit",       detectorPath + "/cs:pixel_size/cs:z/@unit", "")
538
539                CS_appendMetaData(fileID, preMeta + "/slit_length",                    detectorPath + "/cs:slit_length", "")
540                CS_appendMetaData(fileID, preMeta + "/slit_length/@unit",              detectorPath + "/cs:slit_length/@unit", "")
541        ENDFOR
542
543        // <SASprocess> might appear multiple times
544        XmlListXpath(fileID, sasEntryPath+"//cs:SASprocess", nsStr)     //output: M_listXPath
545        WAVE/T  M_listXPath
546        DUPLICATE/O/T   M_listXPath, SASprocessList
547        STRING SASprocessPath, prefix
548        FOR (i = 0; i < DimSize(SASprocessList, 0); i += 1)
549                preMeta = "SASprocess"
550                IF (DimSize(SASprocessList, 0) > 1)
551                        preMeta += "_" + num2str(i)
552                ENDIF
553                SASprocessPath = sasEntryPath+"/cs:SASprocess["+num2str(i+1)+"]"
554                CS_appendMetaData(fileID, preMeta+"/@name",        SASprocessPath + "/@name", "")
555                CS_appendMetaData(fileID, preMeta+"/name",         SASprocessPath + "/cs:name", "")
556                CS_appendMetaData(fileID, preMeta+"/date",                 SASprocessPath + "/cs:date", "")
557                CS_appendMetaData(fileID, preMeta+"/description",   SASprocessPath + "/cs:description", "")
558                XmlListXpath(fileID, SASprocessPath+"//cs:term", nsStr)
559                FOR (j = 0; j < DimSize(M_listXPath, 0); j += 1)
560                        prefix = SASprocessPath + "/cs:term[" + num2str(j+1) + "]"
561                        CS_appendMetaData(fileID, preMeta+"/term_"+num2str(j)+"/@name",     prefix + "/@name", "")
562                        CS_appendMetaData(fileID, preMeta+"/term_"+num2str(j)+"/@unit",           prefix + "/@unit", "")
563                        CS_appendMetaData(fileID, preMeta+"/term_"+num2str(j),                            prefix, "")
564                ENDFOR
565                // ignore <SASprocessnote>
566        ENDFOR
567
568        // <SASnote> might appear multiple times
569        XmlListXpath(fileID, sasEntryPath+"//cs:SASnote", nsStr)        //output: M_listXPath
570        WAVE/T  M_listXPath
571        DUPLICATE/O/T   M_listXPath, SASnoteList
572        FOR (i = 0; i < DimSize(SASnoteList, 0); i += 1)
573                preMeta = "SASnote"
574                IF (DimSize(SASnoteList, 0) > 1)
575                        preMeta += "_" + num2str(i)
576                ENDIF
577                notePath = sasEntryPath+"//cs:SASnote["+num2str(i+1)+"]"
578                CS_appendMetaData(fileID, preMeta+"/@name",     notePath + "/@name", "")
579                CS_appendMetaData(fileID, preMeta,              notePath, "")
580        ENDFOR
581
582        KillWaves/Z M_listXPath, detailsList, SAScollimationList, SASdetectorList, SASprocessList, SASnoteList
583END
584
585// ==================================================================
586
587FUNCTION/S CS_1i_locateTitle(fileID, SASentryPath)
588        VARIABLE fileID
589        STRING SASentryPath
590        STRING TitlePath, Title
591        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
592        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
593
594        // /cs:SASroot/cs:SASentry/cs:Title is the expected location, but it could be empty
595        TitlePath = SASentryPath + "/cs:Title"
596        Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
597        // search harder for a title
598        IF (strlen(Title) == 0)
599                TitlePath = SASentryPath + "/@name"
600                Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
601        ENDIF
602        IF (strlen(Title) == 0)
603                TitlePath = SASentryPath + "/cs:SASsample/cs:ID"
604                Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
605        ENDIF
606        IF (strlen(Title) == 0)
607                TitlePath = SASentryPath + "/cs:SASsample/@name"
608                Title = XMLstrFmXpath(fileID,  TitlePath, nsStr, "")
609        ENDIF
610        IF (strlen(Title) == 0)
611                // last resort: make up a title
612                Title = "SASentry"
613                TitlePath = ""
614        ENDIF
615//      PRINT "\t Title:", Title
616        RETURN(Title)
617END
618
619// ==================================================================
620
621FUNCTION CS_appendMetaData(fileID, key, xpath, value)
622        VARIABLE fileID
623        STRING key, xpath, value
624        WAVE/T metadata
625        STRING k, v
626
627        SVAR nsPre = root:Packages:CS_XMLreader:nsPre
628        SVAR nsStr = root:Packages:CS_XMLreader:nsStr
629
630        k = TrimWS(key)
631        IF (  strlen(k) > 0 )
632                IF ( strlen(xpath) > 0 )
633                        value = XMLstrFmXpath(fileID,  xpath, nsStr, "")
634                ENDIF
635                // What if the value string has a ";" embedded?
636                //  This could complicate (?compromise?) the wavenote "key=value;" syntax.
637                //  But let the caller deal with it.
638                v = TrimWS(ReplaceString(";", value, " :semicolon: "))
639                IF ( strlen(v) > 0 )
640                        VARIABLE last
641                        last = DimSize(metadata, 0)
642                        Redimension/N=(last+1, 2) metadata
643                        metadata[last][0] = k
644                        metadata[last][1] = v
645                ENDIF
646        ENDIF
647END
648
649// ==================================================================
650
651Function/S   TrimWS(str)
652    // TrimWhiteSpace (code from Jon Tischler)
653    String str
654    return TrimWSL(TrimWSR(str))
655End
656
657// ==================================================================
658
659Function/S   TrimWSL(str)
660    // TrimWhiteSpaceLeft (code from Jon Tischler)
661    String str
662    Variable i, N=strlen(str)
663    for (i=0;char2num(str[i])<=32 && i<N;i+=1)    // find first non-white space
664    endfor
665    return str[i,Inf]
666End
667
668// ==================================================================
669
670Function/S   TrimWSR(str)
671    // TrimWhiteSpaceRight (code from Jon Tischler)
672    String str
673    Variable i
674    for (i=strlen(str)-1; char2num(str[i])<=32 && i>=0; i-=1)    // find last non-white space
675    endfor
676    return str[0,i]
677End
678
679// ==================================================================
680// ==================================================================
681// ==================================================================
682
683
684FUNCTION prj_grabMyXmlData()
685        STRING srcDir = "root:Packages:CS_XMLreader"
686        STRING destDir = "root:PRJ_canSAS"
687        STRING srcFolder, destFolder, theFolder
688        Variable i
689        NewDataFolder/O  $destDir               // for all my imported data
690        FOR ( i = 0; i < CountObjects(srcDir, 4) ; i += 1 )
691                theFolder = GetIndexedObjName(srcDir, 4, i)
692                srcFolder = srcDir + ":" + theFolder
693                destFolder = destDir + ":" + theFolder
694                // PRINT srcFolder, destFolder
695                IF (DataFolderExists(destFolder))
696                        // !!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
697                        // need to find unique name for destination
698                        // Persons who implement this properly should be more elegant
699                        // For now, I will blast the existing and proceed blindly.
700                        KillDataFolder/Z  $destFolder           // clear out any previous work
701                        DuplicateDataFolder $srcFolder, $destFolder
702                ELSE
703                        DuplicateDataFolder $srcFolder, $destFolder
704                ENDIF
705        ENDFOR
706END
707
708FUNCTION prjTest_cansas1d()
709        // unit tests for the routines under prj-readXML.ipf
710        STRING theFile
711        STRING fList = ""
712        VARIABLE i, result, timerID, seconds
713        // build a table of test data sets
714        fList = AddListItem("elmo.xml",                                 fList, ";", Inf)                // non-existent file
715        fList = AddListItem("cansasXML.ipf",                    fList, ";", Inf)                // this file (should fail on XML parsing)
716        fList = AddListItem("book.xml",                                 fList, ";", Inf)                // good XML example file but not canSAS, not even close
717        fList = AddListItem("bimodal-test1.xml",                fList, ";", Inf)                // simple dataset
718        fList = AddListItem("bimodal-test2-vector.xml", fList, ";", Inf)                // version 2.0 file (no standard yet)
719        fList = AddListItem("test.xml",                                 fList, ";", Inf)                // cs_collagen.xml with no namespace
720        fList = AddListItem("test2.xml",                                fList, ";", Inf)                // version 2.0 file (no standard yet)
721        fList = AddListItem("ISIS_SANS_Example.xml",    fList, ";", Inf)                // from S. King, 2008-03-17
722        fList = AddListItem("W1W2.xml",                                 fList, ";", Inf)                // from S. King, 2008-03-17
723        fList = AddListItem("ill_sasxml_example.xml",   fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
724        fList = AddListItem("isis_sasxml_example.xml",  fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
725        fList = AddListItem("r586.xml",                                         fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
726        fList = AddListItem("r597.xml",                                         fList, ";", Inf)                // from canSAS 2007 meeting, reformatted
727        fList = AddListItem("xg009036_001.xml",                 fList, ";", Inf)                // foreign elements with other namespaces
728        fList = AddListItem("cs_collagen.xml",                  fList, ";", Inf)                // another simple dataset, bare minimum info
729        fList = AddListItem("cs_collagen_full.xml",             fList, ";", Inf)                // more Q range than previous
730        fList = AddListItem("cs_af1410.xml",                    fList, ";", Inf)                // multiple SASentry and SASdata elements
731        fList = AddListItem("cansas1d-template.xml",    fList, ";", Inf)                // multiple SASentry and SASdata elements
732        fList = AddListItem("1998spheres.xml",                  fList, ";", Inf)                // 2 SASentry, few thousand data points each
733        fList = AddListItem("does-not-exist-file.xml",          fList, ";", Inf)                // non-existent file
734        fList = AddListItem("cs_rr_polymers.xml",               fList, ";", Inf)                // Round Robin polymer samples from John Barnes @ NIST
735        fList = AddListItem("s81-polyurea.xml",                         fList, ";", Inf)                // polyurea from APS/USAXS/Indra (with extra metadata)
736       
737        // try to load each data set in the table
738        FOR ( i = 0; i < ItemsInList(fList) ; i += 1 )
739                theFile = StringFromList(i, fList)                                      // walk through all test files
740                // PRINT "file: ", theFile
741                pathInfo home
742                //IF (CS_XmlReader(theFile) == 0)                                       // did the XML reader return without an error code?
743                timerID = StartMStimer
744                result = CS_XmlReader(ParseFilePath(5,S_path,"*",0,0) + theFile)
745                seconds = StopMSTimer(timerID) * 1.0e-6
746                PRINT "\t Completed in ", seconds, " seconds"
747                IF (result == 0)    // did the XML reader return without an error code?
748                        prj_grabMyXmlData()                                             // move the data to my directory
749                ENDIF
750        ENDFOR
751END
752
753
754FUNCTION testCollette()
755                                        // !!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
756                                        //          THIS IS JUST AN EXAMPLE
757
758// suggestions from ISIS users
759        // 3.   Loading actual data from LOQ caused some problems.
760        //      Data created by Colette names files with run number.
761        //      When entering full path to load the data if you use "\example\31531.X" Igor will read \3 as a character.
762        //      A simple fix which has worked for this is to use / instead of \ e.g. "\example/31531.X".
763       
764        //4.    Once data is loaded in Igor it is relatively easy to work with but would be nicer if the SASdata
765        //      was loaded into root directory (named using run number rather than generically as it is at the moment) rather than another folder.
766        //This becomes more problematic when two samples are being loaded for comparison.
767        //      Although still relatively easy to work with, changing the folders can lead to mistakes being made.
768
769        //Say, for Run=31531, then Qsas_31531
770
771        CS_XmlReader("W1W2.XML")
772        STRING srcDir = "root:Packages:CS_XMLreader"
773        STRING destDir = "root", importFolder, target
774        Variable i, j
775        FOR ( i = 0; i < CountObjects(srcDir, 4) ; i += 1 )
776                SetDataFolder $srcDir
777                importFolder = GetIndexedObjName(srcDir, 4, i)
778                SetDataFolder $importFolder
779                IF ( EXISTS( "metadata" ) == 1 )
780                        // looks like a SAS data folder
781                        WAVE/T metadata
782                        STRING Run = ""
783                        FOR (j = 0; j < DimSize(metadata, 0); j += 1)
784                                IF ( CmpStr( "Run", metadata[j][0]) == 0 )
785                                        // get the Run number and "clean" it up a bit
786                                        Run = TrimWS(  ReplaceString("\\", metadata[j][1], "/")  )
787                                        // !!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
788                                        // need to find unique name for destination waves       
789                                        //          THIS IS JUST AN EXAMPLE
790                                        // Persons who implement this properly should be more elegant
791                                        // For now, I will blast any existing and proceed blindly.
792                                        target = "root:Qsas_" + Run
793                                        Duplicate/O Qsas, $target
794                                        target = "root:Isas_" + Run
795                                        Duplicate/O Isas, $target
796                                        IF ( exists( "Idev" ) == 1 )
797                                                target = "root:Idev_" + Run
798                                                Duplicate/O Idev, $target
799                                        ENDIF
800                                        IF ( exists( "Qdev" ) == 1 )
801                                                target = "root:Qdev_" + Run
802                                                Duplicate/O Qdev, $target
803                                        ENDIF
804                                        IF ( exists( "dQw" ) == 1 )
805                                                target = "root:QdQw_" + Run
806                                                Duplicate/O dQw, $target
807                                        ENDIF
808                                        IF ( exists( "dQl" ) == 1 )
809                                                target = "root:dQl_" + Run
810                                                Duplicate/O dQl, $target
811                                        ENDIF
812                                        IF ( exists( "Qmean" ) == 1 )
813                                                target = "root:Qmean_" + Run
814                                                Duplicate/O Qmean, $target
815                                        ENDIF
816                                        IF ( exists( "Shadowfactor" ) == 1 )
817                                                target = "root:Shadowfactor_" + Run
818                                                Duplicate/O Shadowfactor, $target
819                                        ENDIF
820                                        target = "root:metadata_" + Run
821                                        Duplicate/O/T metadata, $target
822                                        BREAK
823                                ENDIF
824                        ENDFOR
825                ENDIF
826        ENDFOR
827
828        SetDataFolder root:
829END
830
831#else   // if( Exists("XmlOpenFile") )
832        // No XMLutils XOP: provide dummy function so that IgorPro can compile dependent support code
833        FUNCTION CS_XmlReader(fileName)
834            String fileName
835            Abort  "XML function provided by XMLutils XOP is not available, get the XOP from : http://www.igorexchange.com/project/XMLutils (see http://www.smallangles.net/wgwiki/index.php/cansas1d_binding_IgorPro for details)"
836            RETURN(-6)
837        END
838#endif  // if( Exists("XmlOpenFile") )
Note: See TracBrowser for help on using the repository browser.