21#include <boost/algorithm/string/split.hpp>
22#include <boost/algorithm/string/trim.hpp>
28void encode(std::string &data) {
30 buffer.reserve(data.size());
32 for (
auto const &element : data) {
35 buffer.append(
"&");
38 buffer.append(
""");
41 buffer.append(
"'");
44 buffer.append(
"<");
47 buffer.append(
">");
50 buffer.push_back(element);
71 std::make_shared<API::WorkspaceUnitValidator>(
"MomentumTransfer")),
72 "The input workspace, which must be in units of Q. Must be a 1D workspace.");
74 "The name of the xml file to save");
76 std::vector<std::string> radiation_source{
"Spallation Neutron Source",
77 "Pulsed Reactor Neutron Source",
78 "Reactor Neutron Source",
79 "Synchrotron X-ray Source",
81 "Rotating Anode X-ray",
87 declareProperty(
"RadiationSource",
"Spallation Neutron Source",
88 std::make_shared<Kernel::StringListValidator>(radiation_source),
"The type of radiation used.");
89 declareProperty(
"Append",
false,
90 "Selecting append allows the workspace to "
91 "be added to an existing canSAS 1-D file as "
93 declareProperty(
"Process",
"",
"Text to append to Process section");
94 declareProperty(
"DetectorNames",
"",
95 "Specify in a comma separated list, which detectors to store "
96 "information about; \nwhere each name must match a name "
97 "given for a detector in the [[IDF|instrument definition "
98 "file (IDF)]]. \nIDFs are located in the instrument "
99 "sub-directory of the Mantid install directory.");
102 std::vector<std::string> collimationGeometry{
103 "Cylinder",
"FlatPlate",
"Flat plate",
"Disc",
"Unknown",
105 declareProperty(
"Geometry",
"Unknown", std::make_shared<Kernel::StringListValidator>(collimationGeometry),
106 "The geometry type of the collimation.");
107 auto mustBePositiveOrZero = std::make_shared<Kernel::BoundedValidator<double>>();
108 mustBePositiveOrZero->setLower(0);
109 declareProperty(
"SampleHeight", 0.0, mustBePositiveOrZero,
110 "The height of the collimation element in mm. If specified "
111 "as 0 it will not be recorded.");
112 declareProperty(
"SampleWidth", 0.0, mustBePositiveOrZero,
113 "The width of the collimation element in mm. If specified as "
114 "0 it will not be recorded.");
117 declareProperty(
"SampleThickness", 0.0, mustBePositiveOrZero,
118 "The thickness of the sample in mm. If specified as 0 it "
119 "will not be recorded.");
129 const std::string &propertyValue,
int perioidNum) {
133 if ((propertyName ==
"Append") && (perioidNum > 1)) {
141 throw std::invalid_argument(
"Invalid inputworkspace ,Error in SaveCanSAS1D");
145 throw std::invalid_argument(
"Error in SaveCanSAS1D - more than one histogram.");
155 std::string sasTitle;
171 std::string sasSample;
177 std::string sasInstrument;
182 }
catch (std::runtime_error &) {
187 std::string sasProcess;
191 std::string sasNote =
"\n\t\t<SASnote>";
192 sasNote +=
"\n\t\t</SASnote>";
208 m_outFile.exceptions(std::ios::eofbit | std::ios::failbit | std::ios::badbit);
231 m_outFile.open(filename.c_str(), std::ios::out | std::ios::in);
239 }
catch (std::fstream::failure &) {
240 g_log.
information() <<
"File " << filename <<
" couldn't be opened for a appending, will try to create the file\n";
254 const int rootTagLen =
static_cast<int>(std::string(
"</SASroot>").length());
257 static const int LAST_TAG_LEN = 11;
261 m_outFile.seekg(-LAST_TAG_LEN - rootTagLen, std::ios::end);
262 char test_tag[LAST_TAG_LEN + 1];
265 static const char LAST_TAG[LAST_TAG_LEN + 1] =
"</SASentry>";
266 if (std::string(test_tag, LAST_TAG_LEN) != std::string(LAST_TAG, LAST_TAG_LEN)) {
269 bool tagFound(
false);
271 static const int UNCERT = 20;
272 for (
int i = 1; i < UNCERT; ++i) {
275 m_outFile.seekg(-i - LAST_TAG_LEN - rootTagLen, std::ios::end);
277 std::string read = std::string(test_tag, LAST_TAG_LEN);
278 if (read == std::string(LAST_TAG, LAST_TAG_LEN)) {
284 throw std::logic_error(
"Couldn't find the end of the existing data, "
285 "missing </SASentry> tag");
290 }
catch (std::fstream::failure &) {
292 throw std::logic_error(
"Trouble reading existing data in the output file, "
293 "are you appending to an invalid CanSAS1D file?");
303 m_outFile.open(fileName.c_str(), std::ios::out | std::ios::trunc);
306 <<
"<?xml-stylesheet type=\"text/xsl\" "
307 "href=\"cansasxml-html.xsl\" ?>\n";
311 }
catch (std::fstream::failure &) {
320 std::string specialchars =
"&<>'\"";
321 std::string::size_type searchIndex = 0;
322 std::string::size_type findIndex;
323 for (
char specialchar : specialchars) {
324 while (searchIndex < input.length()) {
325 findIndex = input.find(specialchar, searchIndex);
326 if (findIndex != std::string::npos) {
327 searchIndex = findIndex + 1;
345 std::basic_string<char>::reference str = input.at(
index);
348 input.replace(
index, 1,
"&");
351 input.replace(
index, 1,
"<");
354 input.replace(
index, 1,
">");
357 input.replace(
index, 1,
"'");
360 input.replace(
index, 1,
""");
369 rootElem =
"<SASroot version=\"1.0\"";
370 rootElem +=
"\n\t\t xmlns=\"cansas1d/1.0\"";
371 rootElem +=
"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
372 rootElem +=
"\n\t\t xsi:schemaLocation=\"cansas1d/1.0 "
373 "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/"
384 sasTitle =
"\n\t\t<Title>";
386 sasTitle +=
"</Title>";
396 if (
m_workspace->run().hasProperty(
"run_number")) {
400 g_log.
debug() <<
"Didn't find RunNumber log in workspace. Writing "
401 "<Run></Run> to the CANSAS file\n";
406 sasRun =
"\n\t\t<Run>";
427 if (dataUnit ==
"I(q) (cm-1)")
430 const auto intensities =
m_workspace->points(workspaceIndex);
431 auto intensityDeltas =
m_workspace->pointStandardDeviations(workspaceIndex);
432 if (!intensityDeltas)
433 intensityDeltas = HistogramData::PointStandardDeviations(intensities.size(), 0.0);
434 const auto &ydata =
m_workspace->y(workspaceIndex);
435 const auto &edata =
m_workspace->e(workspaceIndex);
436 sasData +=
"\n\t\t<SASdata>";
437 for (
size_t j = 0; j < ydata.size(); ++j) {
441 std::stringstream dx_str;
442 dx_str << intensityDeltas[j];
443 sasData +=
"\n\t\t\t<Idata><Q unit=\"1/A\">";
446 sasData +=
"<I unit=";
460 sasData +=
"<Idev unit=";
466 sasData +=
"</Idev>";
468 sasData +=
"<Qdev unit=\"1/A\">";
469 sasData += dx_str.str();
470 sasData +=
"</Qdev>";
472 sasData +=
"</Idata>";
474 sasData +=
"\n\t\t</SASdata>";
481 sasSample =
"\n\t\t<SASsample>";
483 std::string sasSampleId =
"\n\t\t\t<ID>";
487 sasSampleId += sampleid;
488 sasSampleId +=
"</ID>";
489 sasSample += sasSampleId;
494 std::string thicknessTag =
"\n\t\t\t<thickness unit=\"mm\">";
496 thicknessTag +=
"</thickness>";
497 sasSample += thicknessTag;
499 sasSample +=
"\n\t\t</SASsample>";
506 sasSource =
"\n\t\t\t<SASsource>";
510 std::string sasrad =
"\n\t\t\t\t<radiation>";
511 sasrad += radiation_source;
512 sasrad +=
"</radiation>";
515 sasSource +=
"\n\t\t\t</SASsource>";
522 const std::string detectorNames =
getProperty(
"DetectorNames");
524 if (detectorNames.empty()) {
525 sasDet +=
"\n\t\t\t<SASdetector>";
526 std::string sasDetname =
"\n\t\t\t\t<name/>";
527 sasDet += sasDetname;
528 sasDet +=
"\n\t\t\t</SASdetector>";
532 std::list<std::string> detList;
533 using std::placeholders::_1;
534 boost::algorithm::split(detList, detectorNames, std::bind(std::equal_to<char>(), _1,
','));
535 for (
auto detectorName : detList) {
536 boost::algorithm::trim(detectorName);
539 std::shared_ptr<const IComponent> comp =
m_workspace->getInstrument()->getComponentByName(detectorName);
540 if (comp != std::shared_ptr<const IComponent>()) {
541 sasDet +=
"\n\t\t\t<SASdetector>";
543 std::string sasDetname =
"\n\t\t\t\t<name>";
544 sasDetname += detectorName;
545 sasDetname +=
"</name>";
546 sasDet += sasDetname;
548 std::string sasDetUnit =
"\n\t\t\t\t<SDD unit=\"m\">";
550 std::stringstream sdd;
551 double distance = comp->getDistance(*
m_workspace->getInstrument()->getSample());
554 sasDetUnit += sdd.str();
555 sasDetUnit +=
"</SDD>";
557 sasDet += sasDetUnit;
558 sasDet +=
"\n\t\t\t</SASdetector>";
560 g_log.
notice() <<
"Detector with name " << detectorName
561 <<
" does not exist in the instrument of the workspace: " <<
m_workspace->getName() <<
'\n';
570 sasProcess =
"\n\t\t<SASprocess>";
573 std::string sasProcname =
"\n\t\t\t<name>";
574 sasProcname +=
"Mantid generated CanSAS1D XML";
575 sasProcname +=
"</name>";
576 sasProcess += sasProcname;
582 strftime(temp, 25,
"%d-%b-%Y %H:%M:%S", localtime(&rawtime));
583 std::string sasDate(temp);
585 std::string sasProcdate =
"\n\t\t\t<date>";
586 sasProcdate += sasDate;
587 sasProcdate +=
"</date>";
588 sasProcess += sasProcdate;
590 std::string sasProcsvn =
"\n\t\t\t<term name=\"svn\">";
592 sasProcsvn +=
"</term>";
593 sasProcess += sasProcsvn;
596 std::string user_file;
601 std::string sasProcuserfile =
"\n\t\t\t<term name=\"user_file\">";
602 sasProcuserfile += user_file;
603 sasProcuserfile +=
"</term>";
605 sasProcess += sasProcuserfile;
609 if (!process_xml.empty()) {
610 std::string processNote =
"\n\t\t\t<SASprocessnote>";
612 processNote += process_xml;
613 processNote +=
"</SASprocessnote>";
614 sasProcess += processNote;
616 sasProcess +=
"\n\t\t\t<SASprocessnote/>";
619 sasProcess +=
"\n\t\t</SASprocess>";
642 sasInstrument =
"\n\t\t<SASinstrument>";
645 std::string sasInstrName =
"\n\t\t\t<name>";
646 std::string instrname =
m_workspace->getInstrument()->getName();
649 sasInstrName += instrname;
650 sasInstrName +=
"</name>";
651 sasInstrument += sasInstrName;
654 std::string sasSource;
656 sasInstrument += sasSource;
661 double collimationHeight =
getProperty(
"SampleHeight");
662 double collimationWidth =
getProperty(
"SampleWidth");
663 std::string sasCollimation =
"\n\t\t\t<SAScollimation/>";
664 if (collimationHeight > 0 || collimationWidth > 0) {
665 sasCollimation =
"\n\t\t\t<SAScollimation>";
668 std::string collimationGeometry =
getProperty(
"Geometry");
669 sasCollimation +=
"\n\t\t\t\t<aperture name=\"" + collimationGeometry +
"\">";
672 sasCollimation +=
"\n\t\t\t\t\t<size>";
675 sasCollimation +=
"\n\t\t\t\t\t\t<x unit=\"mm\">" +
std::to_string(collimationWidth) +
"</x>";
677 sasCollimation +=
"\n\t\t\t\t\t\t<y unit=\"mm\">" +
std::to_string(collimationHeight) +
"</y>";
679 sasCollimation +=
"\n\t\t\t\t\t</size>";
680 sasCollimation +=
"\n\t\t\t\t</aperture>";
681 sasCollimation +=
"\n\t\t\t</SAScollimation>";
683 sasInstrument += sasCollimation;
688 sasInstrument += sasDet;
689 sasInstrument +=
"\n\t\t</SASinstrument>";
#define DECLARE_ALGORITHM(classname)
std::map< DeltaEMode::Type, std::string > index
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual void setOtherProperties(IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue, int periodNum)
Virtual method to set the non workspace properties for this algorithm.
@ Save
to specify a file to write to, the file may or may not exist
IAlgorithm is the interface implemented by the Algorithm base class.
bool hasProperty(const std::string &name) const
Does the property exist on the object.
Kernel::Property * getLogData(const std::string &name) const
Access a single log entry.
This class stores information regarding an experimental run as a series of log entries.
A property class for workspaces.
bool openForAppending(const std::string &filename)
opens the named file if possible or returns false
void createSASSourceElement(std::string &sasSource)
this method creates a sasSource element
std::fstream m_outFile
an fstream object is used to write the xml manually as the user requires a specific format with new l...
void findEndofLastEntry()
Moves to the end of the last entry in the file.
void createSASTitleElement(std::string &sasTitle)
this method creates a sasTitle element
void createSASInstrument(std::string &sasInstrument)
this method creates a sasInstrument element
virtual void createSASRootElement(std::string &rootElem)
sasroot element
void setOtherProperties(API::IAlgorithm *alg, const std::string &propertyName, const std::string &propertyValue, int perioidNum) override
overriden method sets appending for workspace groups
void searchandreplaceSpecialChars(std::string &input)
this method searches for xml special characters and replace with entity references
void createSASProcessElement(std::string &sasProcess)
this method creates a sasProcess element
void createSASDataElement(std::string &sasData, size_t workspaceIndex)
this method creates a sasData element
void createSASDetectorElement(std::string &sasDet)
this method creates a sasDetector element
void createSASSampleElement(std::string &sasSample)
this method creates a sasSample element
virtual void writeHeader(const std::string &fileName)
Write xml header tags.
void prepareFileToWriteEntry(const std::string &fileName)
Opens the output file and, as necessary blanks it, writes the file header and moves the file pointer.
void createSASRunElement(std::string &sasRun)
this method creates a sasRun Element
API::MatrixWorkspace_const_sptr m_workspace
points to the workspace that will be written to file
void replacewithEntityReference(std::string &input, const std::string::size_type &index)
replaces the charcter at index in the input string with xml entity reference(eg.replace '&' with "&")
void exec() override
Overwrites Algorithm method.
Records the filename and the description of failure.
Exception for when an item is not found in a collection.
virtual void setPropertyValue(const std::string &name, const std::string &value)=0
Sets property value from a string.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
void information(const std::string &msg)
Logs at information level.
static const char * version()
The full version number.
Base class for properties.
virtual std::string value() const =0
Returns the value of the property as a string.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.