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

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

test for existence of XMLutils XOP

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