22#include <boost/algorithm/string/split.hpp>
23#include <boost/algorithm/string/trim.hpp>
29void encode(std::string &data) {
31 buffer.reserve(data.size());
33 for (
auto const &element : data) {
36 buffer.append(
"&");
39 buffer.append(
""");
42 buffer.append(
"'");
45 buffer.append(
"<");
48 buffer.append(
">");
51 buffer.push_back(element);
72 std::make_shared<API::WorkspaceUnitValidator>(
"MomentumTransfer")),
73 "The input workspace, which must be in units of Q. Must be a 1D workspace.");
75 "The name of the xml file to save");
77 std::vector<std::string> radiation_source{
"Spallation Neutron Source",
78 "Pulsed Reactor Neutron Source",
79 "Reactor Neutron Source",
80 "Synchrotron X-ray Source",
82 "Rotating Anode X-ray",
88 declareProperty(
"RadiationSource",
"Spallation Neutron Source",
89 std::make_shared<Kernel::StringListValidator>(radiation_source),
"The type of radiation used.");
90 declareProperty(
"Append",
false,
91 "Selecting append allows the workspace to "
92 "be added to an existing canSAS 1-D file as "
94 declareProperty(
"Process",
"",
"Text to append to Process section");
95 declareProperty(
"DetectorNames",
"",
96 "Specify in a comma separated list, which detectors to store "
97 "information about; \nwhere each name must match a name "
98 "given for a detector in the [[IDF|instrument definition "
99 "file (IDF)]]. \nIDFs are located in the instrument "
100 "sub-directory of the Mantid install directory.");
103 std::vector<std::string> collimationGeometry{
104 "Cylinder",
"FlatPlate",
"Flat plate",
"Disc",
"Unknown",
106 declareProperty(
"Geometry",
"Unknown", std::make_shared<Kernel::StringListValidator>(collimationGeometry),
107 "The geometry type of the collimation.");
108 auto mustBePositiveOrZero = std::make_shared<Kernel::BoundedValidator<double>>();
109 mustBePositiveOrZero->setLower(0);
110 declareProperty(
"SampleHeight", 0.0, mustBePositiveOrZero,
111 "The height of the collimation element in mm. If specified "
112 "as 0 it will not be recorded.");
113 declareProperty(
"SampleWidth", 0.0, mustBePositiveOrZero,
114 "The width of the collimation element in mm. If specified as "
115 "0 it will not be recorded.");
118 declareProperty(
"SampleThickness", 0.0, mustBePositiveOrZero,
119 "The thickness of the sample in mm. If specified as 0 it "
120 "will not be recorded.");
130 const std::string &propertyValue,
int perioidNum) {
134 if ((propertyName ==
"Append") && (perioidNum > 1)) {
142 throw std::invalid_argument(
"Invalid inputworkspace ,Error in SaveCanSAS1D");
146 throw std::invalid_argument(
"Error in SaveCanSAS1D - more than one histogram.");
156 std::string sasTitle;
172 std::string sasSample;
178 std::string sasInstrument;
183 }
catch (std::runtime_error &) {
188 std::string sasProcess;
192 std::string sasNote =
"\n\t\t<SASnote>";
193 sasNote +=
"\n\t\t</SASnote>";
209 m_outFile.exceptions(std::ios::eofbit | std::ios::failbit | std::ios::badbit);
232 m_outFile.open(filename.c_str(), std::ios::out | std::ios::in);
240 }
catch (std::fstream::failure &) {
241 g_log.
information() <<
"File " << filename <<
" couldn't be opened for a appending, will try to create the file\n";
255 const int rootTagLen =
static_cast<int>(std::string(
"</SASroot>").length());
258 static const int LAST_TAG_LEN = 11;
262 m_outFile.seekg(-LAST_TAG_LEN - rootTagLen, std::ios::end);
263 char test_tag[LAST_TAG_LEN + 1];
266 static const char LAST_TAG[LAST_TAG_LEN + 1] =
"</SASentry>";
267 if (std::string(test_tag, LAST_TAG_LEN) != std::string(LAST_TAG, LAST_TAG_LEN)) {
270 bool tagFound(
false);
272 static const int UNCERT = 20;
273 for (
int i = 1; i < UNCERT; ++i) {
276 m_outFile.seekg(-i - LAST_TAG_LEN - rootTagLen, std::ios::end);
278 std::string read = std::string(test_tag, LAST_TAG_LEN);
279 if (read == std::string(LAST_TAG, LAST_TAG_LEN)) {
285 throw std::logic_error(
"Couldn't find the end of the existing data, "
286 "missing </SASentry> tag");
291 }
catch (std::fstream::failure &) {
293 throw std::logic_error(
"Trouble reading existing data in the output file, "
294 "are you appending to an invalid CanSAS1D file?");
304 m_outFile.open(fileName.c_str(), std::ios::out | std::ios::trunc);
307 <<
"<?xml-stylesheet type=\"text/xsl\" "
308 "href=\"cansasxml-html.xsl\" ?>\n";
312 }
catch (std::fstream::failure &) {
321 std::string specialchars =
"&<>'\"";
322 std::string::size_type searchIndex = 0;
323 std::string::size_type findIndex;
324 for (
char specialchar : specialchars) {
325 while (searchIndex < input.length()) {
326 findIndex = input.find(specialchar, searchIndex);
327 if (findIndex != std::string::npos) {
328 searchIndex = findIndex + 1;
346 std::basic_string<char>::reference str = input.at(
index);
349 input.replace(
index, 1,
"&");
352 input.replace(
index, 1,
"<");
355 input.replace(
index, 1,
">");
358 input.replace(
index, 1,
"'");
361 input.replace(
index, 1,
""");
370 rootElem =
"<SASroot version=\"1.0\"";
371 rootElem +=
"\n\t\t xmlns=\"cansas1d/1.0\"";
372 rootElem +=
"\n\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
373 rootElem +=
"\n\t\t xsi:schemaLocation=\"cansas1d/1.0 "
374 "http://svn.smallangles.net/svn/canSAS/1dwg/trunk/"
385 sasTitle =
"\n\t\t<Title>";
387 sasTitle +=
"</Title>";
397 if (
m_workspace->run().hasProperty(
"run_number")) {
401 g_log.
debug() <<
"Didn't find RunNumber log in workspace. Writing "
402 "<Run></Run> to the CANSAS file\n";
407 sasRun =
"\n\t\t<Run>";
428 if (dataUnit ==
"I(q) (cm-1)")
431 const auto intensities =
m_workspace->points(workspaceIndex);
432 auto intensityDeltas =
m_workspace->pointStandardDeviations(workspaceIndex);
433 if (!intensityDeltas)
434 intensityDeltas = HistogramData::PointStandardDeviations(intensities.size(), 0.0);
435 const auto &ydata =
m_workspace->y(workspaceIndex);
436 const auto &edata =
m_workspace->e(workspaceIndex);
437 sasData +=
"\n\t\t<SASdata>";
438 for (
size_t j = 0; j < ydata.size(); ++j) {
442 std::stringstream dx_str;
444 sasData +=
"\n\t\t\t<Idata><Q unit=\"1/A\">";
447 sasData +=
"<I unit=";
461 sasData +=
"<Idev unit=";
467 sasData +=
"</Idev>";
469 sasData +=
"<Qdev unit=\"1/A\">";
470 sasData += dx_str.str();
471 sasData +=
"</Qdev>";
473 sasData +=
"</Idata>";
475 sasData +=
"\n\t\t</SASdata>";
482 sasSample =
"\n\t\t<SASsample>";
484 std::string sasSampleId =
"\n\t\t\t<ID>";
488 sasSampleId += sampleid;
489 sasSampleId +=
"</ID>";
490 sasSample += sasSampleId;
495 std::string thicknessTag =
"\n\t\t\t<thickness unit=\"mm\">";
497 thicknessTag +=
"</thickness>";
498 sasSample += thicknessTag;
500 sasSample +=
"\n\t\t</SASsample>";
507 sasSource =
"\n\t\t\t<SASsource>";
511 std::string sasrad =
"\n\t\t\t\t<radiation>";
512 sasrad += radiation_source;
513 sasrad +=
"</radiation>";
516 sasSource +=
"\n\t\t\t</SASsource>";
523 const std::string detectorNames =
getProperty(
"DetectorNames");
525 if (detectorNames.empty()) {
526 sasDet +=
"\n\t\t\t<SASdetector>";
527 std::string sasDetname =
"\n\t\t\t\t<name/>";
528 sasDet += sasDetname;
529 sasDet +=
"\n\t\t\t</SASdetector>";
533 std::list<std::string> detList;
534 using std::placeholders::_1;
535 boost::algorithm::split(detList, detectorNames, std::bind(std::equal_to<char>(), _1,
','));
536 for (
auto detectorName : detList) {
537 boost::algorithm::trim(detectorName);
540 std::shared_ptr<const IComponent> comp =
m_workspace->getInstrument()->getComponentByName(detectorName);
541 if (comp != std::shared_ptr<const IComponent>()) {
542 sasDet +=
"\n\t\t\t<SASdetector>";
544 std::string sasDetname =
"\n\t\t\t\t<name>";
545 sasDetname += detectorName;
546 sasDetname +=
"</name>";
547 sasDet += sasDetname;
549 std::string sasDetUnit =
"\n\t\t\t\t<SDD unit=\"m\">";
551 std::stringstream sdd;
552 double distance = comp->getDistance(*
m_workspace->getInstrument()->getSample());
555 sasDetUnit += sdd.str();
556 sasDetUnit +=
"</SDD>";
558 sasDet += sasDetUnit;
559 sasDet +=
"\n\t\t\t</SASdetector>";
561 g_log.
notice() <<
"Detector with name " << detectorName
562 <<
" does not exist in the instrument of the workspace: " <<
m_workspace->getName() <<
'\n';
571 sasProcess =
"\n\t\t<SASprocess>";
574 std::string sasProcname =
"\n\t\t\t<name>";
575 sasProcname +=
"Mantid generated CanSAS1D XML";
576 sasProcname +=
"</name>";
577 sasProcess += sasProcname;
583 strftime(temp, 25,
"%d-%b-%Y %H:%M:%S", localtime(&rawtime));
584 std::string sasDate(temp);
586 std::string sasProcdate =
"\n\t\t\t<date>";
587 sasProcdate += sasDate;
588 sasProcdate +=
"</date>";
589 sasProcess += sasProcdate;
591 std::string sasProcsvn =
"\n\t\t\t<term name=\"svn\">";
593 sasProcsvn +=
"</term>";
594 sasProcess += sasProcsvn;
597 std::string user_file;
602 std::string sasProcuserfile =
"\n\t\t\t<term name=\"user_file\">";
603 sasProcuserfile += user_file;
604 sasProcuserfile +=
"</term>";
606 sasProcess += sasProcuserfile;
610 if (!process_xml.empty()) {
611 std::string processNote =
"\n\t\t\t<SASprocessnote>";
613 processNote += process_xml;
614 processNote +=
"</SASprocessnote>";
615 sasProcess += processNote;
617 sasProcess +=
"\n\t\t\t<SASprocessnote/>";
620 sasProcess +=
"\n\t\t</SASprocess>";
643 sasInstrument =
"\n\t\t<SASinstrument>";
646 std::string sasInstrName =
"\n\t\t\t<name>";
647 std::string instrname =
m_workspace->getInstrument()->getName();
650 sasInstrName += instrname;
651 sasInstrName +=
"</name>";
652 sasInstrument += sasInstrName;
655 std::string sasSource;
657 sasInstrument += sasSource;
662 double collimationHeight =
getProperty(
"SampleHeight");
663 double collimationWidth =
getProperty(
"SampleWidth");
664 std::string sasCollimation =
"\n\t\t\t<SAScollimation/>";
665 if (collimationHeight > 0 || collimationWidth > 0) {
666 sasCollimation =
"\n\t\t\t<SAScollimation>";
669 std::string collimationGeometry =
getProperty(
"Geometry");
670 sasCollimation +=
"\n\t\t\t\t<aperture name=\"" + collimationGeometry +
"\">";
673 sasCollimation +=
"\n\t\t\t\t\t<size>";
676 sasCollimation +=
"\n\t\t\t\t\t\t<x unit=\"mm\">" +
formatDouble(collimationWidth) +
"</x>";
678 sasCollimation +=
"\n\t\t\t\t\t\t<y unit=\"mm\">" +
formatDouble(collimationHeight) +
"</y>";
680 sasCollimation +=
"\n\t\t\t\t\t</size>";
681 sasCollimation +=
"\n\t\t\t\t</aperture>";
682 sasCollimation +=
"\n\t\t\t</SAScollimation>";
684 sasInstrument += sasCollimation;
689 sasInstrument += sasDet;
690 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 std::string & version()
The full version number.
Base class for properties.
virtual std::string value() const =0
Returns the value of the property as a string.
MANTID_DATAHANDLING_DLL std::string formatDouble(double const value)
@ Input
An input workspace.