2011-09-25 09:53:22 +00:00
/********************************************************************************
* *
* This file is part of IfcOpenShell. *
* *
* IfcOpenShell is free software: you can redistribute it and/or modify *
* it under the terms of the Lesser GNU General Public License as published by *
* the Free Software Foundation, either version 3.0 of the License, or *
* (at your option) any later version. *
* *
* IfcOpenShell is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* Lesser GNU General Public License for more details. *
* *
* You should have received a copy of the Lesser GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
********************************************************************************/
/********************************************************************************
* *
* Implementation of character decoding as described in ISO 10303-21 table 2 and *
* table 4 *
* *
********************************************************************************/
# include <string>
# include <sstream>
# include <iomanip>
# include "../ifcparse/IfcCharacterDecoder.h"
# include "../ifcparse/IfcException.h"
# include "../ifcparse/IfcFile.h"
# define FIRST_SOLIDUS (1 << 1)
# define PAGE (1 << 2)
# define ALPHABET (1 << 3)
# define SECOND_SOLIDUS (1 << 4)
# define ALPHABET_DEFINITION (1 << 5)
# define APOSTROPHE (1 << 6)
# define ARBITRARY (1 << 7)
# define EXTENDED2 (1 << 8)
# define EXTENDED4 (1 << 9)
# define HEX(N) (1 << (9+N))
# define THIRD_SOLIDUS (1 << 18)
# define ENDEXTENDED_X (1 << 19)
# define ENDEXTENDED_0 (1 << 20)
# define FOURTH_SOLIDUS (1 << 21)
# define IGNORED_DIRECTIVE (1 << 22)
// FIXME: These probably need to be less forgiving in terms of wrongly defined sequences
# define EXPECTS_ALPHABET(S) (S & FIRST_SOLIDUS)
# define EXPECTS_PAGE(S) (S & FIRST_SOLIDUS)
# define EXPECTS_ARBITRARY(S) (S & FIRST_SOLIDUS)
# define EXPECTS_N_OR_F(S) (S & FIRST_SOLIDUS && !(S & ARBITRARY) )
# define EXPECTS_ARBITRARY2(S) (S & ARBITRARY && !(S & SECOND_SOLIDUS))
# define EXPECTS_ALPHABET_DEFINITION(S) (S & FIRST_SOLIDUS && S & ALPHABET)
# define EXPECTS_SOLIDUS(S) (S & ALPHABET_DEFINITION || S & PAGE || S & ARBITRARY || S & EXTENDED2 || S & EXTENDED4 || S & ENDEXTENDED_0 || S & IGNORED_DIRECTIVE || (S & EXTENDED4 && S & HEX(8)) || (S & EXTENDED2 && S & HEX(4)))
# define EXPECTS_CHARACTER(S) (S & PAGE && S & SECOND_SOLIDUS)
# define EXPECTS_HEX(S) (S & HEX(1) || S & HEX(3) || S & HEX(5) || S & HEX(6) || S & HEX(7) || (S & ARBITRARY && S & SECOND_SOLIDUS) || (S & EXTENDED2 && S & HEX(2)) || (S & EXTENDED4 && S & HEX(4)) )
# define EXPECTS_ENDEXTENDED_X(S) (S & THIRD_SOLIDUS)
# define EXPECTS_ENDEXTENDED_0(S) (S & ENDEXTENDED_X)
# define IS_VALID_ALPHABET_DEFINITION(C) (C >= 0x40 && C <= 0x4A)
# define IS_HEXADECIMAL(C) ((C >= 0x30 && C <= 0x39 ) || (C >= 0x41 && C <= 0x46 ))
# define HEX_TO_INT(C) ((C >= 0x30 && C <= 0x39 ) ? C - 0x30 : (C+10) - 0x41)
# define CLEAR_HEX(C) (C &= ~(HEX(1)&HEX(2)&HEX(3)&HEX(4)&HEX(5)&HEX(6)&HEX(7)&HEX(8)))
using namespace IfcParse ;
void IfcCharacterDecoder : : addChar ( std : : stringstream & s , const UChar32 & ch ) {
2011-09-27 19:30:53 +00:00
# ifdef HAVE_ICU
2011-09-25 09:53:22 +00:00
if ( destination ) {
2012-05-04 15:46:29 +00:00
/* Note: The extraction buffer is of size 5, because in the UTF-8 encoding the
maximum length in bytes is 4. We add 1 for the NUL character. In other encodings
the length could be higher, but we have not taken that into account. */
char extraction_buffer [ 5 ] = { } ;
UnicodeString ( ch ) . extract ( extraction_buffer , 5 , destination , status ) ;
extraction_buffer [ 4 ] = ' \0 ' ;
2011-09-25 09:53:22 +00:00
s < < extraction_buffer ;
} else {
std : : stringstream s2 ;
s2 < < " \\ u " < < std : : hex < < std : : setw ( 4 ) < < std : : setfill ( ' 0 ' ) < < ( int ) ch ;
s < < s2 . str ( ) ;
}
2011-09-27 19:30:53 +00:00
# else
s . put ( substitution_character ) ;
# endif
2011-09-25 09:53:22 +00:00
}
2012-06-27 04:22:51 +00:00
# include <iostream>
2011-09-25 09:53:22 +00:00
IfcCharacterDecoder : : IfcCharacterDecoder ( IfcParse : : File * f ) {
2012-06-27 04:22:51 +00:00
file = f ;
2011-09-27 19:30:53 +00:00
# ifdef HAVE_ICU
2012-06-27 04:22:51 +00:00
if ( destination ! = nullptr ) {
ucnv_close ( destination ) ;
}
destination = nullptr ;
if ( mode = = DEFAULT ) {
destination = ucnv_open ( nullptr , & status ) ;
} else if ( mode = = UTF8 ) {
destination = ucnv_open ( " utf-8 " , & status ) ;
} else if ( mode = = LATIN ) {
destination = ucnv_open ( " iso-8859-1 " , & status ) ;
}
2011-09-27 19:30:53 +00:00
# endif
2011-09-25 09:53:22 +00:00
}
IfcCharacterDecoder : : ~ IfcCharacterDecoder ( ) {
2011-09-27 19:30:53 +00:00
# ifdef HAVE_ICU
2012-06-27 04:22:51 +00:00
if ( destination ) ucnv_close ( destination ) ;
if ( converter ) ucnv_close ( converter ) ;
destination = nullptr ;
converter = nullptr ;
2011-09-27 19:30:53 +00:00
# endif
2011-09-25 09:53:22 +00:00
}
IfcCharacterDecoder : : operator std : : string ( ) {
unsigned int parse_state = 0 ;
std : : stringstream s ;
s . put ( ' \' ' ) ;
char current_char ;
int codepage = 1 ;
unsigned int hex = 0 ;
unsigned int hex_count = 0 ;
while ( current_char = file - > Peek ( ) ) {
if ( EXPECTS_CHARACTER ( parse_state ) ) {
2011-09-27 19:30:53 +00:00
# ifdef HAVE_ICU
2011-09-25 09:53:22 +00:00
if ( previous_codepage ! = codepage ) {
if ( converter ) ucnv_close ( converter ) ;
char encoder [ 11 ] = { ' i ' , ' s ' , ' o ' , ' - ' , ' 8 ' , ' 8 ' , ' 5 ' , ' 9 ' , ' - ' , codepage + 0x30 } ;
converter = ucnv_open ( encoder , & status ) ;
}
const char characters [ 2 ] = { current_char + 0x80 } ;
const char * char_array = & characters [ 0 ] ;
UChar32 ch = ucnv_getNextUChar ( converter , & char_array , char_array + 1 , & status ) ;
addChar ( s , ch ) ;
2011-09-27 19:30:53 +00:00
# else
UChar32 ch = 0 ;
addChar ( s , ch ) ;
# endif
2011-09-25 09:53:22 +00:00
parse_state = 0 ;
} else if ( current_char = = ' \' ' & & ! parse_state ) {
parse_state = APOSTROPHE ;
} else if ( current_char = = ' \\ ' & & ! parse_state ) {
parse_state = FIRST_SOLIDUS ;
} else if ( current_char = = ' \\ ' & & EXPECTS_SOLIDUS ( parse_state ) ) {
if ( parse_state & ALPHABET_DEFINITION | |
parse_state & IGNORED_DIRECTIVE | |
parse_state & ENDEXTENDED_0 ) parse_state = hex = hex_count = 0 ;
else if ( parse_state & HEX ( 3 ) ) parse_state + = THIRD_SOLIDUS ;
else parse_state + = SECOND_SOLIDUS ;
} else if ( current_char = = ' X ' & & EXPECTS_ENDEXTENDED_X ( parse_state ) ) {
parse_state + = ENDEXTENDED_X ;
} else if ( current_char = = ' 0 ' & & EXPECTS_ENDEXTENDED_0 ( parse_state ) ) {
parse_state + = ENDEXTENDED_0 ;
} else if ( current_char = = ' X ' & & EXPECTS_ARBITRARY ( parse_state ) ) {
parse_state + = ARBITRARY ;
} else if ( current_char = = ' 2 ' & & EXPECTS_ARBITRARY2 ( parse_state ) ) {
parse_state + = EXTENDED2 ;
} else if ( current_char = = ' 4 ' & & EXPECTS_ARBITRARY2 ( parse_state ) ) {
parse_state + = EXTENDED2 + EXTENDED4 ;
} else if ( current_char = = ' P ' & & EXPECTS_ALPHABET ( parse_state ) ) {
parse_state + = ALPHABET ;
} else if ( ( current_char = = ' N ' | | current_char = = ' F ' ) & & EXPECTS_N_OR_F ( parse_state ) ) {
parse_state + = IGNORED_DIRECTIVE ;
} else if ( IS_VALID_ALPHABET_DEFINITION ( current_char ) & & EXPECTS_ALPHABET_DEFINITION ( parse_state ) ) {
codepage = current_char - 0x40 ;
parse_state + = ALPHABET_DEFINITION ;
} else if ( current_char = = ' S ' & & EXPECTS_PAGE ( parse_state ) ) {
parse_state + = PAGE ;
} else if ( IS_HEXADECIMAL ( current_char ) & & EXPECTS_HEX ( parse_state ) ) {
hex < < = 4 ;
parse_state + = HEX ( ( + + hex_count ) ) ;
hex + = HEX_TO_INT ( current_char ) ;
if ( ( hex_count = = 2 & & ! ( parse_state & EXTENDED2 ) ) | |
( hex_count = = 4 & & ! ( parse_state & EXTENDED4 ) ) | |
( hex_count = = 8 ) ) {
addChar ( s , ( UChar32 ) hex ) ;
if ( hex_count = = 2 ) parse_state = 0 ;
else CLEAR_HEX ( parse_state ) ;
hex = hex_count = 0 ;
}
} else if ( parse_state & & ! (
( current_char = = ' \\ ' & & parse_state = = FIRST_SOLIDUS ) | |
( current_char = = ' \' ' & & parse_state = = APOSTROPHE )
) ) {
if ( parse_state = = APOSTROPHE & & current_char ! = ' \' ' ) break ;
throw IfcException ( " Invalid character encountered " ) ;
} else {
parse_state = hex = hex_count = 0 ;
2012-05-04 15:46:29 +00:00
// NOTE: this is in fact wrong, this ought to be the representation of the character.
// In UTF-8 this is the same, but we should not rely on that.
2011-09-25 09:53:22 +00:00
s . put ( current_char ) ;
}
file - > Inc ( ) ;
}
s . put ( ' \' ' ) ;
return s . str ( ) ;
}
void IfcCharacterDecoder : : dryRun ( ) {
unsigned int parse_state = 0 ;
char current_char ;
unsigned int hex_count = 0 ;
while ( current_char = file - > Peek ( ) ) {
if ( EXPECTS_CHARACTER ( parse_state ) ) {
parse_state = 0 ;
} else if ( current_char = = ' \' ' & & ! parse_state ) {
parse_state = APOSTROPHE ;
} else if ( current_char = = ' \\ ' & & ! parse_state ) {
parse_state = FIRST_SOLIDUS ;
} else if ( current_char = = ' \\ ' & & EXPECTS_SOLIDUS ( parse_state ) ) {
if ( parse_state & ALPHABET_DEFINITION | |
parse_state & IGNORED_DIRECTIVE | |
parse_state & ENDEXTENDED_0 ) parse_state = hex_count = 0 ;
else if ( parse_state & HEX ( 3 ) ) parse_state + = THIRD_SOLIDUS ;
else parse_state + = SECOND_SOLIDUS ;
} else if ( current_char = = ' X ' & & EXPECTS_ENDEXTENDED_X ( parse_state ) ) {
parse_state + = ENDEXTENDED_X ;
} else if ( current_char = = ' 0 ' & & EXPECTS_ENDEXTENDED_0 ( parse_state ) ) {
parse_state + = ENDEXTENDED_0 ;
} else if ( current_char = = ' X ' & & EXPECTS_ARBITRARY ( parse_state ) ) {
parse_state + = ARBITRARY ;
} else if ( current_char = = ' 2 ' & & EXPECTS_ARBITRARY2 ( parse_state ) ) {
parse_state + = EXTENDED2 ;
} else if ( current_char = = ' 4 ' & & EXPECTS_ARBITRARY2 ( parse_state ) ) {
parse_state + = EXTENDED2 + EXTENDED4 ;
} else if ( current_char = = ' P ' & & EXPECTS_ALPHABET ( parse_state ) ) {
parse_state + = ALPHABET ;
} else if ( ( current_char = = ' N ' | | current_char = = ' F ' ) & & EXPECTS_N_OR_F ( parse_state ) ) {
parse_state + = IGNORED_DIRECTIVE ;
} else if ( IS_VALID_ALPHABET_DEFINITION ( current_char ) & & EXPECTS_ALPHABET_DEFINITION ( parse_state ) ) {
parse_state + = ALPHABET_DEFINITION ;
} else if ( current_char = = ' S ' & & EXPECTS_PAGE ( parse_state ) ) {
parse_state + = PAGE ;
} else if ( IS_HEXADECIMAL ( current_char ) & & EXPECTS_HEX ( parse_state ) ) {
parse_state + = HEX ( ( + + hex_count ) ) ;
if ( ( hex_count = = 2 & & ! ( parse_state & EXTENDED2 ) ) | |
( hex_count = = 4 & & ! ( parse_state & EXTENDED4 ) ) | |
( hex_count = = 8 ) ) {
if ( hex_count = = 2 ) parse_state = 0 ;
else CLEAR_HEX ( parse_state ) ;
hex_count = 0 ;
}
} else if ( parse_state & & ! (
( current_char = = ' \\ ' & & parse_state = = FIRST_SOLIDUS ) | |
( current_char = = ' \' ' & & parse_state = = APOSTROPHE )
) ) {
if ( parse_state = = APOSTROPHE & & current_char ! = ' \' ' ) break ;
throw IfcException ( " Invalid character encountered " ) ;
} else {
parse_state = hex_count = 0 ;
}
file - > Inc ( ) ;
}
}
2011-09-27 19:30:53 +00:00
# ifdef HAVE_ICU
2011-09-25 09:53:22 +00:00
UConverter * IfcCharacterDecoder : : destination = 0 ;
UConverter * IfcCharacterDecoder : : converter = 0 ;
int IfcCharacterDecoder : : previous_codepage = - 1 ;
UErrorCode IfcCharacterDecoder : : status = U_ZERO_ERROR ;
2012-06-27 04:22:51 +00:00
# endif
//#ifdef HAVE_ICU
2011-09-25 09:53:22 +00:00
IfcCharacterDecoder : : ConversionMode IfcCharacterDecoder : : mode = IfcCharacterDecoder : : JSON ;
2012-06-27 04:22:51 +00:00
//#else
2011-09-27 19:30:53 +00:00
char IfcCharacterDecoder : : substitution_character = ' _ ' ;
2012-06-27 04:22:51 +00:00
//#endif