diff --git a/modules/io/doc/mmcif.rst b/modules/io/doc/mmcif.rst index 0a648960cdc83894b41016847d88b3ff26caaea9..e6c85eea7ffb54e96b6c85e5488422583e6353ff 100644 --- a/modules/io/doc/mmcif.rst +++ b/modules/io/doc/mmcif.rst @@ -1549,9 +1549,16 @@ The content of the file written: Static constructor from a string value, stores input as is with the exception of the following processing: - * encapsulate string in brackets if *string_val* contains space character * set to "?" if *string_val* is an empty string (in mmCIF, "?" marks "unknown" values) + * encapsulate string in quotes if *string_val* contains space character + * encapsulate string in quotes if *string_val* starts with any of the + following special characters: _, #, $, ', ", [, ], ; + * encapsulate string in quotes if *string_val* starts with any of the + following special strings: "data\_" (case insensitive), + "save\_" (case insensitive) + * encapsulate string in quotes if *string_val* is equal to any of the + following reserved words (case insensitive): "loop\_", "stop\_", "global\_" :param string_val: The value :type string_val: :class:`str` diff --git a/modules/io/src/mol/star_writer.hh b/modules/io/src/mol/star_writer.hh index 1a7d49f2d596e95c0915ad252c2de4ee6f95e7c6..53923e957bbea13eb0f20d450c30984f98d78bea 100644 --- a/modules/io/src/mol/star_writer.hh +++ b/modules/io/src/mol/star_writer.hh @@ -100,33 +100,136 @@ public: StarWriterValue value; fts(float_value, decimals, value.value_); return value; - } + } static StarWriterValue FromString(const String& string_value) { StarWriterValue value; - // cases we still need to deal with: - // - special characters in strings (put in quotation marks) - // - long strings (semicolon based syntax) - // see https://mmcif.wwpdb.org/docs/tutorials/mechanics/pdbx-mmcif-syntax.html - bool has_space = false; - for(char c: string_value) { - if(isspace(c)) { - has_space = true; - break; - } - } + if(string_value == "") { value.value_ = "?"; - } else if(has_space) { - value.value_ = "'" + string_value + "'"; - } - else { - value.value_ = string_value; + } else { + // string requires quotes if any of the following is True + // information from https://www.iucr.org/resources/cif/spec/version1.1/cifsyntax + // * space in string + // * any string that starts with any of the following strings + // * _ + // * # + // * $ + // * ' + // * " + // * [ + // * ] + // * ; + // * data_ (case insensitive) + // * save_ (case insensitive) + // * any string that is equal to any of the following reserved words + // * loop_ (case insensitive) + // * stop_ (case insensitive) + // * global_ (case insensitive) + bool needs_quotes = false; + + // space in string + for(char c: string_value) { + if(isspace(c)) { + needs_quotes = true; + break; + } + } + + // any string that starts with any of the special single characters + if(!needs_quotes) { + switch(string_value[0]) { + case '_': { + needs_quotes = true; + break; + } + case '#': { + needs_quotes = true; + break; + } + case '$': { + needs_quotes = true; + break; + } + case '\'': { + needs_quotes = true; + break; + } + case '\"': { + needs_quotes = true; + break; + } + case '[': { + needs_quotes = true; + break; + } + case ']': { + needs_quotes = true; + break; + } + case ';': { + needs_quotes = true; + break; + } + } + } + + // any string that starts with any of the special multi character thingies + if(!needs_quotes && string_value.size() >= 5 && string_value[4] == '_') { + // need to do case insensitive checking + if((string_value[0] == 'd' || string_value[0] == 'D') && + (string_value[1] == 'a' || string_value[1] == 'A') && + (string_value[2] == 't' || string_value[2] == 'T') && + (string_value[3] == 'a' || string_value[3] == 'A')) { + needs_quotes = true; + } + if((string_value[0] == 's' || string_value[0] == 'S') && + (string_value[1] == 'a' || string_value[1] == 'A') && + (string_value[2] == 'v' || string_value[2] == 'V') && + (string_value[3] == 'e' || string_value[3] == 'E')) { + needs_quotes = true; + } + } + + // any string that is exactly one of the reserved words + if(!needs_quotes && string_value.size() == 5 && string_value[4] == '_') { + // need to do case insensitive checking + if((string_value[0] == 'l' || string_value[0] == 'L') && + (string_value[1] == 'o' || string_value[1] == 'O') && + (string_value[2] == 'o' || string_value[2] == 'O') && + (string_value[3] == 'p' || string_value[3] == 'P')) { + needs_quotes = true; + } + if((string_value[0] == 's' || string_value[0] == 'S') && + (string_value[1] == 't' || string_value[1] == 'T') && + (string_value[2] == 'o' || string_value[2] == 'O') && + (string_value[3] == 'p' || string_value[3] == 'P')) { + needs_quotes = true; + } + } + + if(!needs_quotes && string_value.size() == 7 && string_value[6] == '_') { + // need to do case insensitive checking + if((string_value[0] == 'g' || string_value[0] == 'G') && + (string_value[1] == 'l' || string_value[1] == 'L') && + (string_value[2] == 'o' || string_value[2] == 'O') && + (string_value[3] == 'b' || string_value[3] == 'B') && + (string_value[4] == 'a' || string_value[4] == 'A') && + (string_value[5] == 'l' || string_value[5] == 'L')) { + needs_quotes = true; + } + } + + if(needs_quotes) { + value.value_ = "\"" + string_value + "\""; + } else { + value.value_ = string_value; + } } return value; } const String& GetValue() const { return value_; } private: -String value_; + String value_; };