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

Last change on this file since 17 was 17, checked in by prjemian, 14 years ago

more metadata read by IgorPro? reader

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