From cb6884b48e6c6cecf6cb92d7d19ec34674616d82 Mon Sep 17 00:00:00 2001 From: Thomas Krijnen Date: Sun, 9 Sep 2012 11:55:53 +0000 Subject: [PATCH] Encode characters outside the valid range for IFC SPF files and escape backslashes when writing IFC files --- src/ifcparse/IfcCharacterDecoder.cpp | 59 ++++++++++++++++++++++++++++ src/ifcparse/IfcCharacterDecoder.h | 17 ++++++++ src/ifcparse/IfcParse.cpp | 5 ++- src/ifcparse/IfcWrite.cpp | 13 +++--- src/ifcparse/IfcWrite.h | 2 +- 5 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/ifcparse/IfcCharacterDecoder.cpp b/src/ifcparse/IfcCharacterDecoder.cpp index 4f869ddb0f..004085b0e7 100644 --- a/src/ifcparse/IfcCharacterDecoder.cpp +++ b/src/ifcparse/IfcCharacterDecoder.cpp @@ -67,6 +67,7 @@ #define CLEAR_HEX(C) (C &= ~(HEX(1)&HEX(2)&HEX(3)&HEX(4)&HEX(5)&HEX(6)&HEX(7)&HEX(8))) using namespace IfcParse; +using namespace IfcWrite; void IfcCharacterDecoder::addChar(std::stringstream& s,const UChar32& ch) { #ifdef HAVE_ICU @@ -296,3 +297,61 @@ std::string IfcCharacterDecoder::compatibility_charset = ""; #else char IfcCharacterDecoder::substitution_character = '_'; #endif + + +IfcCharacterEncoder::IfcCharacterEncoder(const std::string& input) { + if ( !converter) converter = ucnv_open("utf-8", &status); + str = input; +} + +IfcCharacterEncoder::~IfcCharacterEncoder() { + if ( !converter) ucnv_close(converter); + converter = 0; +} + +IfcCharacterEncoder::operator std::string() { +#ifdef HAVE_ICU + // Either 2 or 4 to uses \X2 or \X4 respectively. + // Currently hardcoded to 4, but \X2 might be + // sufficient for nearly all purposes. + const int num_bytes = 4; + const std::string num_bytes_str = std::string(1,num_bytes + 0x30); + + std::ostringstream oss; + UChar32 ch; + + const char* source = str.c_str(); + const char* limit = &*str.end(); + + bool in_extended = false; + + while(source < limit) { + ch = ucnv_getNextUChar(converter, &source, limit, &status); + const bool within_spf_range = ch >= 0x20 && ch <= 0x7e; + if ( in_extended && within_spf_range ) { + oss << "\\X0\\"; + } else if ( !in_extended && !within_spf_range ) { + oss << "\\X" << num_bytes_str << "\\"; + } + if ( within_spf_range ) { + oss.put(ch); + if ( ch == '\\' ) oss.put(ch); + } else { + oss << std::hex << std::setw(num_bytes*2) << std::uppercase << std::setfill('0') << (int) ch; + } + in_extended = !within_spf_range; + } + + if ( in_extended ) oss << "\\X0\\"; + + return oss.str(); +#else + return str; +#endif +} + + +#ifdef HAVE_ICU +UErrorCode IfcCharacterEncoder::status = U_ZERO_ERROR; +UConverter* IfcCharacterEncoder::converter = 0; +#endif \ No newline at end of file diff --git a/src/ifcparse/IfcCharacterDecoder.h b/src/ifcparse/IfcCharacterDecoder.h index e9e8c94dbd..ce640ebfc4 100644 --- a/src/ifcparse/IfcCharacterDecoder.h +++ b/src/ifcparse/IfcCharacterDecoder.h @@ -73,4 +73,21 @@ namespace IfcParse { } +namespace IfcWrite { + + class IfcCharacterEncoder { + private: +#ifdef HAVE_ICU + static UErrorCode status; + static UConverter* converter; + std::string str; +#endif + public: + IfcCharacterEncoder(const std::string& input); + ~IfcCharacterEncoder(); + operator std::string(); + }; + +} + #endif diff --git a/src/ifcparse/IfcParse.cpp b/src/ifcparse/IfcParse.cpp index b24fc388ff..39b9e2addc 100644 --- a/src/ifcparse/IfcParse.cpp +++ b/src/ifcparse/IfcParse.cpp @@ -468,7 +468,7 @@ TokenArgument::operator IfcUtil::IfcSchemaEntity() const { return token.first->f TokenArgument::operator IfcEntities() const { throw IfcException("Argument is not a list of entities"); } unsigned int TokenArgument::Size() const { return 1; } ArgumentPtr TokenArgument::operator [] (unsigned int i) const { throw IfcException("Argument is not a list of arguments"); } -std::string TokenArgument::toString(bool upper) const { return TokenFunc::toString(token); } +std::string TokenArgument::toString(bool upper) const { if ( upper && TokenFunc::isString(token) ) return IfcWrite::IfcCharacterEncoder(TokenFunc::toString(token)); else return TokenFunc::toString(token); } bool TokenArgument::isNull() const { return TokenFunc::isOperator(token,'$'); } // // Functions for casting the EntityArgument to other types @@ -488,10 +488,11 @@ ArgumentPtr EntityArgument::operator [] (unsigned int i) const { throw IfcExcept std::string EntityArgument::toString(bool upper) const { ArgumentPtr arg = entity->wrappedValue(); IfcParse::TokenArgument* token_arg = dynamic_cast(arg); - std::string token_string = ( token_arg ) ? TokenFunc::toString(token_arg->token) : ""; + std::string token_string = ( token_arg ) ? TokenFunc::toString(token_arg->token) : std::string(); std::string dt = Ifc2x3::Type::ToString(entity->type()); if ( upper ) { for (std::string::iterator p = dt.begin(); p != dt.end(); ++p ) *p = toupper(*p); + token_string = IfcWrite::IfcCharacterEncoder(token_string); } return dt + "(" + token_string + ")"; } diff --git a/src/ifcparse/IfcWrite.cpp b/src/ifcparse/IfcWrite.cpp index 8c594e4038..9c611151b6 100644 --- a/src/ifcparse/IfcWrite.cpp +++ b/src/ifcparse/IfcWrite.cpp @@ -20,6 +20,7 @@ #include "IfcParse.h" #include "IfcWrite.h" #include "IfcWritableEntity.h" +#include "IfcCharacterDecoder.h" using namespace IfcWrite; @@ -284,10 +285,12 @@ std::string IfcWriteIntegralArgument::toString(bool upper) const { case Argument_DOUBLE: ss << *(double*) data; break; - case Argument_STRING: - ss << '\'' << *(std::string*) data << '\''; - break; - case Argument_VECTOR_INT: + case Argument_STRING: { + std::string d = *(std::string*) data; + if ( upper ) d = IfcCharacterEncoder(d); + ss << '\'' << d << '\''; + break; + } case Argument_VECTOR_INT: ss << "("; {const std::vector& v = *(std::vector*) data; for ( std::vector::const_iterator it = v.begin(); it != v.end(); ++ it ) { @@ -361,7 +364,7 @@ IfcSelectHelper::IfcSelectHelper(int v, Ifc2x3::Type::Enum t) { IfcWriteArgument* a = new IfcWriteIntegralArgument(v); this->entity = new IfcSelectHelperEntity(t,a); } -IfcSelectHelper::IfcSelectHelper(float v, Ifc2x3::Type::Enum t) { +IfcSelectHelper::IfcSelectHelper(double v, Ifc2x3::Type::Enum t) { IfcWriteArgument* a = new IfcWriteIntegralArgument(v); this->entity = new IfcSelectHelperEntity(t,a); } diff --git a/src/ifcparse/IfcWrite.h b/src/ifcparse/IfcWrite.h index 565351f523..dc33159c47 100644 --- a/src/ifcparse/IfcWrite.h +++ b/src/ifcparse/IfcWrite.h @@ -170,7 +170,7 @@ namespace IfcWrite { IfcSelectHelper(const std::string& v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcText); IfcSelectHelper(const char* const v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcText); IfcSelectHelper(int v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcInteger); - IfcSelectHelper(float v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcReal); + IfcSelectHelper(double v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcReal); IfcSelectHelper(bool v, Ifc2x3::Type::Enum t=Ifc2x3::Type::IfcBoolean); bool is(Ifc2x3::Type::Enum t) const; Ifc2x3::Type::Enum type() const;