// Method Definitions ******************************************************** CommandLineOptions::CommandLineOptions (void) { // I am not using an intializer list here because of how many variables there are. m_helpMode = false; m_selectedUtility = ProgramMode::MAIN; m_inputText = ""; m_outputFilename = ""; m_minLength = 0; m_maxLength = 0; m_errorCode = ErrorCode::NONE; m_alphabet = CharacterSet (0); // Gets the number of usable cores/threads. m_threadCount = std::thread::hardware_concurrency (); }
namespace duct { const char* __ini_tokenName(const Token& token) { switch (token.getType()) { case NULL_TOKEN: return "NULLToken"; case StringToken: return "StringToken"; case QuotedStringToken: return "QuotedStringToken"; case NumberToken: return "NumberToken"; case DoubleToken: return "DoubleToken"; case EqualsToken: return "EqualsToken"; case NodeToken: return "NodeToken"; case CommentToken: return "CommentToken"; case EOFToken: return "EOFToken"; case EOLToken: return "EOLToken"; } return "UNKNOWNToken"; } // class IniParser implementation CharacterSet IniParser::_whitespaceset=CharacterSet("\t "); CharacterSet IniParser::_numberset=CharacterSet("0-9\\-+"); CharacterSet IniParser::_digitset=CharacterSet(".0-9\\-+"); IniParser::IniParser() : _handler(NULL) { reset(); } IniParser::IniParser(Stream* stream) : _handler(NULL) { initWithStream(stream); } IniParser::~IniParser() { reset(); } void IniParser::setHandler(ParserHandler* handler) { _handler=(IniParserHandler*)handler; } ParserHandler* IniParser::getHandler() { return _handler; } bool IniParser::parse() { //nextChar(); skipWhitespace(); nextToken(); readToken(); if (_curchar==CHAR_EOF) { _token.reset(EOFToken); _handler->handleToken(_token); // Just to make sure the EOF gets handled (data might not end with a newline, causing an EOFToken) return false; } else if (_token.getType()==EOFToken) { return false; } return true; } void IniParser::skipWhitespace() { while (_curchar!=CHAR_EOF && _whitespaceset.contains(_curchar)) nextChar(); } Token& IniParser::nextToken() { _token.reset(NULL_TOKEN); switch (_curchar) { case CHAR_QUOTE: _token.setType(QuotedStringToken); break; case CHAR_SEMICOLON: _token.setType(CommentToken); break; case CHAR_EOF: _token.setType(EOFToken); break; case CHAR_NEWLINE: _token.setType(EOLToken); break; case CHAR_DECIMALPOINT: _token.setType(DoubleToken); _token.addChar(_curchar); // Add the decimal break; case CHAR_EQUALSIGN: _token.setType(EqualsToken); break; case CHAR_OPENBRACKET: _token.setType(NodeToken); break; default: if (_numberset.contains(_curchar)) { _token.setType(NumberToken); } else { _token.setType(StringToken); } break; } _token.setPosition(_line, _column); return _token; } void IniParser::readToken() { //printf("(IniParser::readToken) token-type:%s line:%d, col:%d\n", __ini_tokenName(_token), _token.getLine(), _token.getColumn()); switch (_token.getType()) { case QuotedStringToken: readQuotedStringToken(); nextChar(); break; case StringToken: readStringToken(); break; case NumberToken: readNumberToken(); break; case DoubleToken: nextChar(); readDoubleToken(); break; case EqualsToken: nextChar(); break; case CommentToken: skipToEOL(); //nextChar(); // Bad to get the next char, as it could be the EOL needed to terminate the current identifier break; case NodeToken: readNodeToken(); nextChar(); break; case EOLToken: nextChar(); break; case EOFToken: // Do nothing break; default: throw IniParserException(PARSERERROR_PARSER, "IniParser::readToken", NULL, this, "Unhandled token: %s", __ini_tokenName(_token)); break; } _handler->handleToken(_token); } void IniParser::readNumberToken() { while (_curchar!=CHAR_EOF) { if (_curchar==CHAR_QUOTE) { throw IniParserException(PARSERERROR_PARSER, "IniParser::readNumberToken", &_token, this, "Unexpected quote"); } else if (_curchar==CHAR_NEWLINE || _whitespaceset.contains(_curchar) || _curchar==CHAR_SEMICOLON /*|| _curchar==CHAR_EQUALSIGN*/) { break; } else { if (_numberset.contains(_curchar)) { _token.addChar(_curchar); } else if (_curchar==CHAR_DECIMALPOINT) { _token.addChar(_curchar); nextChar(); _token.setType(DoubleToken); readDoubleToken(); return; } else { _token.setType(StringToken); readStringToken(); return; } } nextChar(); } } void IniParser::readDoubleToken() { while (_curchar!=CHAR_EOF) { if (_curchar==CHAR_QUOTE) { throw IniParserException(PARSERERROR_PARSER, "IniParser::readDoubleToken", &_token, this, "Unexpected quote"); } else if (_curchar==CHAR_NEWLINE || _whitespaceset.contains(_curchar) || _curchar==CHAR_SEMICOLON /*^^^|| _curchar==CHAR_EQUALSIGN*/) { break; } else { if (_numberset.contains(_curchar)) { _token.addChar(_curchar); } else { // (_curchar==CHAR_DECIMALPOINT) // The token should've already contained a decimal point, so it must be a string. _token.setType(StringToken); readStringToken(); return; } } nextChar(); } } void IniParser::readStringToken() { while (_curchar!=CHAR_EOF) { if (_curchar==CHAR_QUOTE) { throw IniParserException(PARSERERROR_PARSER, "IniParser::readStringToken", &_token, this, "Unexpected quote"); } else if (_curchar==CHAR_NEWLINE /*^^^|| _whitespaceset.contains(_curchar)*/ || _curchar==CHAR_SEMICOLON || _curchar==CHAR_EQUALSIGN) { break; } else { _token.addChar(_curchar); } nextChar(); } } void IniParser::readQuotedStringToken() { nextChar(); // Skip the first character (will be the initial quote) while (_curchar!=CHAR_QUOTE) { switch (_curchar) { case CHAR_EOF: throw IniParserException(PARSERERROR_PARSER, "IniParser::readQuotedStringToken", &_token, this, "Encountered EOF whilst reading quoted string"); break; case CHAR_NEWLINE: throw IniParserException(PARSERERROR_PARSER, "IniParser::readQuotedStringToken", &_token, this, "Unexpected EOL (expected quote)"); default: _token.addChar(_curchar); break; } nextChar(); } } void IniParser::readNodeToken() { nextChar(); // Skip initial bracket while (_curchar!=CHAR_EOF) { if (_curchar==CHAR_OPENBRACKET) { throw IniParserException(PARSERERROR_PARSER, "IniParser::readNodeToken", &_token, this, "Unexpected open bracket"); } else if (_curchar==CHAR_SEMICOLON) { throw IniParserException(PARSERERROR_PARSER, "IniParser::readNodeToken", &_token, this, "Unexpected semicolon"); } else if (_curchar==CHAR_NEWLINE) { throw IniParserException(PARSERERROR_PARSER, "IniParser::readNodeToken", &_token, this, "Unexpected end of line"); } else if (_curchar==CHAR_CLOSEBRACKET /*|| _whitespaceset.contains(_curchar)*/) { break; } else { _token.addChar(_curchar); } nextChar(); } } // class IniParserException implementation IniParserException::IniParserException(IniParserError error, const char* reporter, const Token* token, const IniParser* parser, const char* fmt, ...) { _error=error; _reporter=reporter; _token=token; _parser=parser; char temp[256]; va_list args; va_start(args, fmt); vsprintf(temp, fmt, args); va_end(args); temp[255]='\0'; if (_parser && !_token) { _token=&_parser->getToken(); } if (_token && _parser) { sprintf(_message, "(%s) [%s] from line: %d, col: %d to line: %d, col: %d: %s", _reporter, errorToString(_error), _token->getLine(), _token->getColumn(), _parser->getLine(), _parser->getColumn(), temp); } else if (_token) { sprintf(_message, "(%s) [%s] at line: %d, col: %d: %s", _reporter, errorToString(_error), _token->getLine(), _token->getColumn(), temp); } else if (_parser) { sprintf(_message, "(%s) [%s] at line: %d, col: %d: %s", _reporter, errorToString(_error), _parser->getLine(), _parser->getColumn(), temp); } else { sprintf(_message, "(%s) [%s]: %s", _reporter, errorToString(_error), temp); } _message[511]='\0'; } const char* IniParserException::what() const throw() { return _message; } const char* IniParserException::errorToString(IniParserError error) { switch (error) { case PARSERERROR_PARSER: return "ERROR_PARSER"; case PARSERERROR_HIERARCHY: return "ERROR_HIERARCHY"; case PARSERERROR_MEMALLOC: return "ERROR_MEMALLOC"; default: return "ERROR_UNKNOWN"; } } // class IniParserHandler implementation IniParserHandler::IniParserHandler(IniParser& parser) : _parser(parser), _equals(false), _rootnode(NULL), _currentnode(NULL) { _parser.setHandler(this); } void IniParserHandler::setParser(Parser& parser) { _parser=(IniParser&)parser; _parser.setHandler(this); } Parser& IniParserHandler::getParser() { return _parser; } void IniParserHandler::throwex(IniParserException e) { freeData(); throw e; } void IniParserHandler::clean() { _currentnode=NULL; _rootnode=NULL; _varname.remove(); _equals=false; } bool IniParserHandler::process() { _rootnode=new Node(NULL); _currentnode=_rootnode; while (_parser.parse()) { } finish(); return true; } Node* IniParserHandler::processFromStream(Stream* stream) { _parser.initWithStream(stream); process(); Node* node=_rootnode; // Store before cleaning clean(); _parser.reset(); return node; } void IniParserHandler::freeData() { if (_currentnode) { if (_rootnode==_currentnode || _currentnode->getParent()!=_rootnode) { // delete the root if the root and the current node are the same or if the current node has been parented delete _rootnode; } else if (_currentnode->getParent()==NULL) { // delete the root and the current node if the current node has not been parented delete _rootnode; delete _currentnode; } } else if (_rootnode) { delete _rootnode; } clean(); } void IniParserHandler::handleToken(Token& token) { switch (token.getType()) { case StringToken: case QuotedStringToken: { if (_varname.length()>0 && _equals) { if (token.getType()==StringToken) { int bv=Variable::stringToBool(token.toString()); if (bv!=-1) { addValueAndReset(new BoolVariable((bv==1) ? true : false, _varname)); return; } } addValueAndReset(new StringVariable(token.toString(), _varname)); } else if (_varname.length()>0) { throwex(IniParserException(PARSERERROR_PARSER, "IniParserHandler::handleToken", &token, &_parser, "Expected equals sign, got string")); } else { _varname.setTo(token.toString()).trim(); } } break; case NumberToken: if (_varname.length()>0 && _equals) { addValueAndReset(new IntVariable(token.toInt(), _varname)); } else { throwex(IniParserException(PARSERERROR_PARSER, "IniParserHandler::handleToken", &token, &_parser, "A number cannot be an identifier")); } break; case DoubleToken: if (_varname.length()>0 && _equals) { addValueAndReset(new FloatVariable(token.toFloat(), _varname)); } else { throwex(IniParserException(PARSERERROR_PARSER, "IniParserHandler::handleToken", &token, &_parser, "A number cannot be an identifier")); } break; case EqualsToken: if (_varname.length()==0) { throwex(IniParserException(PARSERERROR_PARSER, "IniParserHandler::handleToken", &token, &_parser, "Expected string, got equality sign")); } else if (_equals) { throwex(IniParserException(PARSERERROR_PARSER, "IniParserHandler::handleToken", &token, &_parser, "Expected value, got equality sign")); } else { _equals=true; } break; case NodeToken: { if (_varname.length()==0) { _varname.setTo(token.toString()).trim(); _currentnode=new Node(_varname, _rootnode); // Trim whitespace _varname.remove(); // clear the string _rootnode->add(_currentnode); } else { throwex(IniParserException(PARSERERROR_PARSER, "IniParserHandler::handleToken", &token, &_parser, "NodeToken: Unknown error. _varname length is>0")); } } break; case CommentToken: // Do nothing break; case EOLToken: case EOFToken: finish(); break; default: //printf("IniParserHandler::handleToken Unhandled token of type: %s\n", __ini_tokenName(token)); break; } } void IniParserHandler::finish() { if (_varname.length()>0 && _equals) { addValueAndReset(new StringVariable("", _varname)); } else if (_varname.length()>0) { throwex(IniParserException(PARSERERROR_PARSER, "IniParserHandler::finish", NULL, &_parser, "Expected equality sign, got EOL or EOF")); } } void IniParserHandler::reset() { _varname.remove(); _equals=false; } void IniParserHandler::addValueAndReset(ValueVariable* value) { _currentnode->add(value); reset(); } // class IniFormatter implementation IniParser IniFormatter::_parser=IniParser(); IniParserHandler IniFormatter::_handler=IniParserHandler(IniFormatter::_parser); bool IniFormatter::formatValue(const ValueVariable& value, UnicodeString& result, unsigned int nameformat, unsigned int varformat) { if (value.getName().length()>0) { value.getNameFormatted(result, nameformat); UnicodeString temp; value.getValueFormatted(temp, varformat); result.append('=').append(temp); return true; } else { result.remove(); // clear the result string debug_print("Value name is 0-length"); } return false; } Node* IniFormatter::loadFromFile(const char* path, const char* encoding) { Stream* stream=FileStream::readFile(path, encoding); if (stream) { Node* root=_handler.processFromStream(stream); stream->close(); delete stream; return root; } return NULL; } Node* IniFormatter::loadFromFile(const std::string& path, const char* encoding) { return loadFromFile(path.c_str(), encoding); } Node* IniFormatter::loadFromFile(const UnicodeString& path, const char* encoding) { std::string temp; path.toUTF8String(temp); return loadFromFile(temp.c_str(), encoding); } Node* IniFormatter::loadFromStream(Stream* stream) { if (stream) { return _handler.processFromStream(stream); } return NULL; } bool IniFormatter::writeToFile(const Node* root, const char* path, const char* encoding, unsigned int nameformat, unsigned int varformat) { Stream* stream=FileStream::writeFile(path, encoding); if (stream) { writeToStream(root, stream, 0, nameformat, varformat); stream->close(); return true; } return false; } bool IniFormatter::writeToFile(const Node* root, const std::string& path, const char* encoding, unsigned int nameformat, unsigned int varformat) { return writeToFile(root, path.c_str(), encoding, nameformat, varformat); } bool IniFormatter::writeToFile(const Node* root, const UnicodeString& path, const char* encoding, unsigned int nameformat, unsigned int varformat) { std::string temp; path.toUTF8String(temp); return writeToFile(root, temp.c_str(), encoding, nameformat, varformat); } bool IniFormatter::writeToStream(const Node* root, Stream* stream, unsigned int tcount, unsigned int nameformat, unsigned int varformat) { if (root && stream) { UnicodeString temp; if (root->getParent() && root->getName().length()>0) { // cheap way of saying the node is not a root node writeTabs(stream, tcount, false); root->getNameFormatted(temp, nameformat); stream->writeChar16('['); temp.append(']'); stream->writeLine(temp); } Node* node; ValueVariable* value; for (VarList::const_iterator iter=root->begin(); iter!=root->end(); ++iter) { value=dynamic_cast<ValueVariable*>(*iter); node=dynamic_cast<Node*>(*iter); if (node) { writeToStream(node, stream, tcount, nameformat, varformat); } else if (value) { if (formatValue(*value, temp, nameformat, varformat)) { writeTabs(stream, tcount, false); stream->writeLine(temp); } } } return true; } return false; } void IniFormatter::writeTabs(Stream* stream, unsigned int count, bool newline) { while (0<count--) { stream->writeChar16('\t'); } if (newline) stream->writeChar16('\n'); } } // namespace duct
bool _set(const StringName& p_name, const Variant& p_value) { String n = p_name; if (n=="extra_space/char") char_extra_spacing=p_value; else if (n=="extra_space/space") space_extra_spacing=p_value; else if (n=="extra_space/top") top_extra_spacing=p_value; else if (n=="extra_space/bottom") bottom_extra_spacing=p_value; else if (n=="character_set/mode") { character_set=CharacterSet(int(p_value)); _change_notify(); } else if (n=="character_set/custom") custom_file=p_value; else if (n=="shadow/enabled") { shadow=p_value; _change_notify(); }else if (n=="shadow/radius") shadow_radius=p_value; else if (n=="shadow/offset") shadow_offset=p_value; else if (n=="shadow/color") shadow_color=p_value; else if (n=="shadow/transition") shadow_transition=p_value; else if (n=="shadow2/enabled") { shadow2=p_value; _change_notify(); }else if (n=="shadow2/radius") shadow2_radius=p_value; else if (n=="shadow2/offset") shadow2_offset=p_value; else if (n=="shadow2/color") shadow2_color=p_value; else if (n=="shadow2/transition") shadow2_transition=p_value; else if (n=="color/mode") { color_type=ColorType(int(p_value)); _change_notify(); }else if (n=="color/color") color=p_value; else if (n=="color/begin") gradient_begin=p_value; else if (n=="color/end") gradient_end=p_value; else if (n=="color/image") gradient_image=p_value; else if (n=="advanced/round_advance") round_advance=p_value; else return false; emit_signal("changed"); return true; }
//**************************************************************************** void CommandLineOptions::parseCollideCommands (const unsigned int argc, const char* const argv[], const unsigned int currentArg) { std::string currentArgumentValue; unsigned int argumentIndex = currentArg; if (argumentIndex >= static_cast<unsigned int> (argc)) { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } currentArgumentValue = argv[argumentIndex]; if (currentArgumentValue == CommandLineOptions::COMMAND_HELP) { // Help can only be requested if no other flags are given. // Because of this we can easily check if help is valid by // checking the number of passed arguments. if (argc == 3) { m_helpMode = true; } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_INPUT_FILE) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_inputText.length () == 0) { // Load file. if (!readFileContents (argv[argumentIndex], m_inputText)) { m_errorCode = ErrorCode::FILE_READ_ERROR; return; } } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_OUTPUT_FILE) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_outputFilename.length () == 0) { m_outputFilename = argv[argumentIndex]; } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_MIN) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_minLength == 0) { if (!parseStringAsUint (argv[argumentIndex], m_minLength)) { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_MAX) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_maxLength == 0) { if (!parseStringAsUint (argv[argumentIndex], m_maxLength)) { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_THREAD_COUNT) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_threadCount == std::thread::hardware_concurrency ()) { if (!parseStringAsUint (argv[argumentIndex], m_threadCount)) { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_ALPHBET_FLAGS) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_alphabet.length () == 0) { try { m_alphabet = CharacterSet (0); m_alphabet.generateFromStringSubsetFlags (argv[argumentIndex]); } catch (...) { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_ALPHBET_FILE) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_alphabet.length () == 0) { m_alphabet = CharacterSet (0); if (!m_alphabet.loadFromFile (argv[argumentIndex])) { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (currentArgumentValue == CommandLineOptions::COMMAND_ALPHBET_CHARS) { if (!m_helpMode && ++argumentIndex < static_cast<unsigned int> (argc) && m_alphabet.length () == 0) { try { m_alphabet = CharacterSet (argv[argumentIndex]); } catch (...) { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } } else if (m_inputText.length () == 0) { // If there is no input text then assume that this unknown // argument is a string of text as input. m_inputText = argv[argumentIndex]; } else { m_errorCode = ErrorCode::COLLIDE_USAGE_ERROR; return; } argumentIndex++; if (argumentIndex < static_cast<unsigned int> (argc)) { parseCollideCommands (argc, argv, argumentIndex); return; } m_errorCode = ErrorCode::NONE; return; }
namespace duct { const char* __csv_tokenName(const Token& token) { switch (token.getType()) { case NULL_TOKEN: return "NULLToken"; case StringToken: return "StringToken"; case QuotedStringToken: return "QuotedStringToken"; case NumberToken: return "NumberToken"; case DoubleToken: return "DoubleToken"; case SeparatorToken: return "SeparatorToken"; case EOFToken: return "EOFToken"; case EOLToken: return "EOLToken"; } return "UNKNOWNToken"; } // class CSVRow implementation CSVRow::CSVRow(int index) : _index(index) { } CSVRow::CSVRow() : _index(0) { } CSVRow::~CSVRow() { clear(); } void CSVRow::setIndex(int index) { _index=index; } int CSVRow::getIndex() const { return _index; } CSVRecordMap::iterator CSVRow::begin() { return _values.begin(); } CSVRecordMap::const_iterator CSVRow::begin() const { return _values.begin(); } CSVRecordMap::iterator CSVRow::end() { return _values.end(); } CSVRecordMap::const_iterator CSVRow::end() const { return _values.end(); } CSVRecordMap::reverse_iterator CSVRow::rbegin() { return _values.rbegin(); } CSVRecordMap::const_reverse_iterator CSVRow::rbegin() const { return _values.rbegin(); } CSVRecordMap::reverse_iterator CSVRow::rend() { return _values.rend(); } CSVRecordMap::const_reverse_iterator CSVRow::rend() const { return _values.rend(); } CSVRecordMap::iterator CSVRow::find(int index) { return _values.find(index); } CSVRecordMap::const_iterator CSVRow::find(int index) const { return _values.find(index); } void CSVRow::erase(CSVRecordMap::iterator iter) { if (iter!=end()) { if (iter->second) { delete iter->second; } _values.erase(iter); } } void CSVRow::erase(CSVRecordMap::reverse_iterator iter) { if (iter!=rend()) { if (iter->second) { delete iter->second; } _values.erase(--(iter.base())); } } void CSVRow::set(int index, ValueVariable* value) { CSVRecordMap::iterator iter=find(index); if (iter!=end()) { if (iter->second) { delete iter->second; } iter->second=value; } else { _values[index]=value; } } ValueVariable* CSVRow::get(int index) { CSVRecordMap::iterator iter=find(index); if (iter!=end()) { return iter->second; } return NULL; } const ValueVariable* CSVRow::get(int index) const { CSVRecordMap::const_iterator iter=find(index); if (iter!=end()) { return iter->second; } return NULL; } IntVariable* CSVRow::getInt(int index) { ValueVariable* v=get(index); if (v && (VARTYPE_INTEGER&v->getType())) { return (IntVariable*)v; } return NULL; } const IntVariable* CSVRow::getInt(int index) const { const ValueVariable* v=get(index); if (v && (VARTYPE_INTEGER&v->getType())) { return (IntVariable*)v; } return NULL; } bool CSVRow::getIntValue(int& result, int index) const { const IntVariable* v=getInt(index); if (v) { result=v->get(); return true; } return false; } StringVariable* CSVRow::getString(int index) { ValueVariable* v=get(index); if (v && (VARTYPE_STRING&v->getType())) { return (StringVariable*)v; } return NULL; } const StringVariable* CSVRow::getString(int index) const { const ValueVariable* v=get(index); if (v && (VARTYPE_STRING&v->getType())) { return (StringVariable*)v; } return NULL; } const UnicodeString* CSVRow::getStringValue(int index) const { const StringVariable* v=getString(index); if (v) { return &v->get(); } return NULL; } bool CSVRow::getStringValue(UnicodeString& result, int index) const { const StringVariable* v=getString(index); if (v) { result.setTo(v->get()); return true; } return false; } FloatVariable* CSVRow::getFloat(int index) { ValueVariable* v=get(index); if (v && (VARTYPE_FLOAT&v->getType())) { return (FloatVariable*)v; } return NULL; } const FloatVariable* CSVRow::getFloat(int index) const { const ValueVariable* v=get(index); if (v && (VARTYPE_FLOAT&v->getType())) { return (FloatVariable*)v; } return NULL; } bool CSVRow::getFloatValue(float& result, int index) const { const FloatVariable* v=getFloat(index); if (v) { result=v->get(); return true; } return false; } BoolVariable* CSVRow::getBool(int index) { ValueVariable* v=get(index); if (v && (VARTYPE_BOOL&v->getType())) { return (BoolVariable*)v; } return NULL; } const BoolVariable* CSVRow::getBool(int index) const { const ValueVariable* v=get(index); if (v && (VARTYPE_BOOL&v->getType())) { return (BoolVariable*)v; } return NULL; } bool CSVRow::getBoolValue(bool& result, int index) const { const BoolVariable* v=getBool(index); if (v) { result=v->get(); return true; } return false; } bool CSVRow::getAsString(UnicodeString& result, int index) const { const ValueVariable* v=get(index); if (v) { v->valueAsString(result, false); return true; } return false; } bool CSVRow::remove(int index) { CSVRecordMap::iterator iter=find(index); if (iter!=end()) { if (iter->second) { delete iter->second; } _values.erase(iter); return true; } return false; } void CSVRow::clear() { CSVRecordMap::iterator iter=begin(); for (; iter!=end(); ++iter) { if (iter->second) { delete iter->second; } } _values.clear(); } bool CSVRow::has(int index) const { CSVRecordMap::const_iterator iter=find(index); if (iter!=end()) { return true; } return false; } size_t CSVRow::getCount(bool nulls) const { if (nulls) { return _values.size(); } else { size_t size=0; CSVRecordMap::const_iterator iter; CSVRecordMap::const_iterator iend=end(); for (iter=begin(); iter!=iend; ++iter) { if (iter->second) { size++; } } return size; } } size_t CSVRow::inRange(int start, int end, bool nulls) const { size_t size=0; int index; CSVRecordMap::const_iterator iter; CSVRecordMap::const_iterator iend=this->end(); for (index=start; index<=end; ++index) { iter=find(index); if (iter!=iend && (iter->second || nulls)) { size++; } } return size; } // class CSVMap CSVMap::CSVMap() { } CSVMap::~CSVMap() { clear(); } size_t CSVMap::getRowCount() const { return _rows.size(); } size_t CSVMap::getHeaderCount(int index, bool nulls) const { CSVRowMap::const_iterator iter=find(index); if (iter!=end()) { return iter->second->getCount(nulls); } return 0; } size_t CSVMap::getValueCount() const { size_t size=0; CSVRowMap::const_iterator iter=begin(); for (; iter!=end(); ++iter) { size+=iter->second->getCount(); } return size; } CSVRowMap::iterator CSVMap::begin() { return _rows.begin(); } CSVRowMap::const_iterator CSVMap::begin() const { return _rows.begin(); } CSVRowMap::iterator CSVMap::end() { return _rows.end(); } CSVRowMap::const_iterator CSVMap::end() const { return _rows.end(); } CSVRowMap::reverse_iterator CSVMap::rbegin() { return _rows.rbegin(); } CSVRowMap::const_reverse_iterator CSVMap::rbegin() const { return _rows.rbegin(); } CSVRowMap::reverse_iterator CSVMap::rend() { return _rows.rend(); } CSVRowMap::const_reverse_iterator CSVMap::rend() const { return _rows.rend(); } CSVRowMap::iterator CSVMap::find(int index) { return _rows.find(index); } CSVRowMap::const_iterator CSVMap::find(int index) const { return _rows.find(index); } void CSVMap::erase(CSVRowMap::iterator iter) { if (iter!=end()) { delete iter->second; _rows.erase(iter); } } void CSVMap::erase(CSVRowMap::reverse_iterator iter) { if (iter!=rend()) { delete iter->second; _rows.erase(--(iter.base())); } } bool CSVMap::set(CSVRow* row) { return set(row->getIndex(), row); } bool CSVMap::set(int index, CSVRow* row) { if (row) { CSVRowMap::iterator iter=find(index); if (iter!=end()) { delete iter->second; iter->second=row; } else { _rows[index]=row; } row->setIndex(index); return true; } return false; } CSVRow* CSVMap::get(int index) { CSVRowMap::iterator iter=find(index); if (iter!=end()) { return iter->second; } return NULL; } const CSVRow* CSVMap::get(int index) const { CSVRowMap::const_iterator iter=find(index); if (iter!=end()) { return iter->second; } return NULL; } bool CSVMap::moveRow(int src, int dest, bool swap) { if (src==dest) { return true; } CSVRowMap::iterator si=find(src); if (si!=end()) { CSVRow* srow=si->second; if (swap) { CSVRowMap::iterator di=find(dest); if (di!=end()) { si->second=di->second; si->second->setIndex(src); di->second=srow; srow->setIndex(dest); return true; } } _rows.erase(si); return set(dest, srow); } return false; } bool CSVMap::remove(int index) { CSVRowMap::iterator iter=find(index); if (iter!=end()) { delete iter->second; _rows.erase(iter); return true; } return false; } bool CSVMap::has(int row) const { CSVRowMap::const_iterator iter=find(row); if (iter!=end()) { return true; } return false; } void CSVMap::clear() { CSVRowMap::iterator iter; for (iter=begin(); iter!=end(); ++iter) { delete iter->second; } _rows.clear(); } bool CSVMap::setValue(int row, int column, ValueVariable* value, bool autocreate) { CSVRow* r=get(row); if (!r && autocreate) { r=new CSVRow(); set(row, r); } if (r) { r->set(column, value); return true; } return false; } ValueVariable* CSVMap::getValue(int row, int column) { CSVRow* r=get(row); if (r) { return r->get(column); } return NULL; } const ValueVariable* CSVMap::getValue(int row, int column) const { const CSVRow* r=get(row); if (r) { return r->get(column); } return NULL; } IntVariable* CSVMap::getInt(int row, int column) { CSVRow* r=get(row); if (r) { return r->getInt(column); } return NULL; } const IntVariable* CSVMap::getInt(int row, int column) const { const CSVRow* r=get(row); if (r) { r->getInt(column); } return NULL; } bool CSVMap::getIntValue(int& result, int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getIntValue(result, column); } return false; } StringVariable* CSVMap::getString(int row, int column) { CSVRow* r=get(row); if (r) { return r->getString(column); } return NULL; } const StringVariable* CSVMap::getString(int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getString(column); } return NULL; } const UnicodeString* CSVMap::getStringValue(int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getStringValue(column); } return NULL; } bool CSVMap::getStringValue(UnicodeString& result, int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getStringValue(result, column); } return false; } FloatVariable* CSVMap::getFloat(int row, int column) { CSVRow* r=get(row); if (r) { return r->getFloat(column); } return NULL; } const FloatVariable* CSVMap::getFloat(int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getFloat(column); } return NULL; } bool CSVMap::getFloatValue(float& result, int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getFloatValue(result, column); } return false; } BoolVariable* CSVMap::getBool(int row, int column) { CSVRow* r=get(row); if (r) { return r->getBool(column); } return NULL; } const BoolVariable* CSVMap::getBool(int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getBool(column); } return NULL; } bool CSVMap::getBoolValue(bool& result, int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getBoolValue(result, column); } return false; } bool CSVMap::getAsString(UnicodeString& result, int row, int column) const { const CSVRow* r=get(row); if (r) { return r->getAsString(result, column); } return false; } bool CSVMap::removeValue(int row, int column) { CSVRow* r=get(row); if (r) { return r->remove(column); } return false; } bool CSVMap::hasValue(int row, int column) const { CSVRowMap::const_iterator iter=find(row); if (iter!=end()) { return iter->second->has(column); } return false; } void CSVMap::clearValues() { CSVRowMap::iterator iter; for (iter=begin(); iter!=end(); ++iter) { iter->second->clear(); } } // class CSVParser implementation CharacterSet CSVParser::_numberset=CharacterSet("0-9\\-+"); CharacterSet CSVParser::_numeralset=CharacterSet("0-9"); CharacterSet CSVParser::_signset=CharacterSet("\\-+"); CharacterSet CSVParser::_whitespaceset=CharacterSet(); CSVParser::CSVParser() : _handler(NULL) { reset(); } CSVParser::CSVParser(Stream* stream) { initWithStream(stream); } CSVParser::~CSVParser() { } void CSVParser::setSeparator(UChar32 c) { _sepchar=c; _whitespaceset.clear(); if (_sepchar!='\t') { _whitespaceset.addRange('\t'); } if (_sepchar!=' ') { _whitespaceset.addRange(' '); } } UChar32 CSVParser::getSeparator() const { return _sepchar; } void CSVParser::setHandler(ParserHandler* handler) { _handler=(CSVParserHandler*)handler; } ParserHandler* CSVParser::getHandler() { return _handler; } Token& CSVParser::nextToken() { _token.reset(NULL_TOKEN); _token.setPosition(_line, _column); switch (_curchar) { case CHAR_QUOTE: _token.setType(QuotedStringToken); break; case CHAR_EOF: _token.setType(EOFToken); break; case CHAR_NEWLINE: _token.setType(EOLToken); break; case CHAR_DECIMALPOINT: _token.setType(DoubleToken); _token.addChar(_curchar); // add the decimal break; default: if (_curchar==_sepchar) { _token.setType(SeparatorToken); } else if (_numberset.contains(_curchar)) { _token.setType(NumberToken); _token.addChar(_curchar); // add the number/sign } else { _token.setType(StringToken); } break; } return _token; } void CSVParser::readToken() { switch (_token.getType()) { case QuotedStringToken: readQuotedStringToken(); nextChar(); break; case StringToken: readStringToken(); break; case NumberToken: nextChar(); readNumberToken(); break; case DoubleToken: nextChar(); readDoubleToken(); break; case SeparatorToken: nextChar(); break; case EOLToken: nextChar(); break; case EOFToken: // Do nothing break; default: throw CSVParserException(PARSERERROR_PARSER, "CSVParser::readToken", NULL, this, "Unhandled token: %s", __csv_tokenName(_token)); break; } // Special resolve when Number and Double tokens only contain signs or periods switch (_token.getType()) { case NumberToken: if (_token.compare(_signset)) _token.setType(StringToken); break; case DoubleToken: if (_token.compare(_signset) || _token.compare(CHAR_DECIMALPOINT)) _token.setType(StringToken); break; default: break; } _handler->handleToken(_token); } bool CSVParser::parse() { //skipWhitespace(); nextToken(); readToken(); if (_curchar==CHAR_EOF) { _token.reset(EOFToken); _handler->handleToken(_token); return false; } else if (_token.getType()==EOFToken) { return false; } return true; } void CSVParser::readNumberToken() { while (_curchar!=CHAR_EOF) { if (_curchar==CHAR_QUOTE) { throw CSVParserException(PARSERERROR_PARSER, "CSVParser::readNumberToken", &_token, this, "Unexpected quote"); } else if (_curchar==CHAR_NEWLINE || _curchar==_sepchar) { break; } else if (_numeralset.contains(_curchar)) { _token.addChar(_curchar); } else if (_curchar==CHAR_DECIMALPOINT) { _token.addChar(_curchar); nextChar(); _token.setType(DoubleToken); readDoubleToken(); return; } else { _token.setType(StringToken); readStringToken(); return; } nextChar(); } } void CSVParser::readDoubleToken() { while (_curchar!=CHAR_EOF) { if (_curchar==CHAR_QUOTE) { throw CSVParserException(PARSERERROR_PARSER, "CSVParser::readDoubleToken", &_token, this, "Unexpected quote"); } else if (_curchar==CHAR_NEWLINE || _curchar==_sepchar) { break; } else if (_numeralset.contains(_curchar)) { _token.addChar(_curchar); } else { // (_curchar==CHAR_DECIMALPOINT) // the token should've already contained a decimal point, so it must be a string. _token.setType(StringToken); readStringToken(); return; } nextChar(); } } void CSVParser::readStringToken() { while (_curchar!=CHAR_EOF) { if (_curchar==CHAR_QUOTE) { if (_token.compare(_whitespaceset)) { // valid; whitespace before a quoted string is thrown away _token.reset(QuotedStringToken); readQuotedStringToken(); nextChar(); // skip ending quote return; } else { throw CSVParserException(PARSERERROR_PARSER, "CSVParser::readStringToken", &_token, this, "Unexpected quote"); } } else if (_curchar==CHAR_BACKSLASH) { UChar32 c=CharUtils::getEscapeChar(nextChar()); if (c!=CHAR_EOF) { _token.addChar(c); } else { throw CSVParserException(PARSERERROR_PARSER, "CSVParser::readStringToken", &_token, this, "Unknown escape sequence: %c", _curchar); } } else if (_curchar==_sepchar || _curchar==CHAR_NEWLINE) { break; } else { _token.addChar(_curchar); } nextChar(); } } void CSVParser::readQuotedStringToken() { bool eolreached=false; nextChar(); // skip the first character (it will be the initial quote) while (_curchar!=CHAR_QUOTE) { if (_curchar==CHAR_EOF) { throw CSVParserException(PARSERERROR_PARSER, "CSVParser::readQuotedStringToken", &_token, this, "Encountered EOF whilst reading quoted string"); } else if (_curchar==CHAR_BACKSLASH) { UChar32 c=CharUtils::getEscapeChar(nextChar()); if (c!=CHAR_EOF) { _token.addChar(c); } else { throw CSVParserException(PARSERERROR_PARSER, "CSVParser::readQuotedStringToken", &_token, this, "Unknown escape sequence: %c", _curchar); } } else { if (!eolreached) { _token.addChar(_curchar); } if (_curchar==CHAR_NEWLINE) { eolreached=true; } else if (eolreached && !_whitespaceset.contains(_curchar)) { eolreached=false; _token.addChar(_curchar); } } nextChar(); } } // class CSVParserException implementation CSVParserException::CSVParserException(CSVParserError error, const char* reporter, const Token* token, const CSVParser* parser, const char* fmt, ...) { _error=error; _reporter=reporter; _token=token; _parser=parser; char temp[256]; va_list args; va_start(args, fmt); vsprintf(temp, fmt, args); va_end(args); temp[255]='\0'; if (_parser && !_token) { _token=&_parser->getToken(); } if (_token && _parser) { sprintf(_message, "(%s) [%s] from line: %d, col: %d to line: %d, col: %d: %s", _reporter, errorToString(_error), _token->getLine(), _token->getColumn(), _parser->getLine(), _parser->getColumn(), temp); } else if (_token) { sprintf(_message, "(%s) [%s] at line: %d, col: %d: %s", _reporter, errorToString(_error), _token->getLine(), _token->getColumn(), temp); } else if (_parser) { sprintf(_message, "(%s) [%s] at line: %d, col: %d: %s", _reporter, errorToString(_error), _parser->getLine(), _parser->getColumn(), temp); } else { sprintf(_message, "(%s) [%s]: %s", _reporter, errorToString(_error), temp); } _message[511]='\0'; } const char* CSVParserException::what() const throw() { return _message; } const char* CSVParserException::errorToString(CSVParserError error) { switch (error) { case PARSERERROR_PARSER: return "ERROR_PARSER"; case PARSERERROR_MEMALLOC: return "ERROR_MEMALLOC"; default: return "ERROR_UNKNOWN"; } } // class CSVParserHandler implementation CSVParserHandler::CSVParserHandler(CSVParser& parser) : _parser(parser), _map(NULL), _currentrow(NULL), _strow(0), _row(0), _column(0), _gtoken(false) { _parser.setHandler(this); } void CSVParserHandler::setParser(Parser& parser) { _parser=(CSVParser&)parser; _parser.setHandler(this); } Parser& CSVParserHandler::getParser() { return _parser; } void CSVParserHandler::throwex(CSVParserException e) { freeData(); throw e; } void CSVParserHandler::clean() { _currentrow=NULL; _row=_strow; _column=0; _gtoken=false; } bool CSVParserHandler::process() { _map=new CSVMap(); _currentrow=new CSVRow(); while (_parser.parse()) { } finish(); return true; } void CSVParserHandler::handleToken(Token& token) { switch (token.getType()) { case StringToken: { if (_gtoken) { if (token.compare(CSVParser::_whitespaceset)) { return; // ignore whitespace } else { throwex(CSVParserException(PARSERERROR_PARSER, "CSVParserHandler::handleToken", &token, &_parser, "Unexpected non-whitespace StringToken")); } } const UnicodeString& str=token.toString(); int bv=Variable::stringToBool(str); if (bv!=-1) { addToRow(new BoolVariable(bv==1 ? true : false)); } else { addToRow(new StringVariable(str)); } } break; case QuotedStringToken: if (_gtoken) { throwex(CSVParserException(PARSERERROR_PARSER, "CSVParserHandler::handleToken", &token, &_parser, "Unexpected QuotedStringToken")); } addToRow(new StringVariable(token.toString())); break; case NumberToken: if (_gtoken) { throwex(CSVParserException(PARSERERROR_PARSER, "CSVParserHandler::handleToken", &token, &_parser, "Unexpected NumberToken")); } addToRow(new IntVariable(token.toInt())); break; case DoubleToken: if (_gtoken) { throwex(CSVParserException(PARSERERROR_PARSER, "CSVParserHandler::handleToken", &token, &_parser, "Unexpected DoubleToken")); } addToRow(new FloatVariable(token.toFloat())); break; case SeparatorToken: if (!_gtoken) { addToRow(NULL); } _gtoken=false; _column++; break; case EOLToken: newRow(); break; case EOFToken: break; } } void CSVParserHandler::finish() { } CSVMap* CSVParserHandler::processFromStream(Stream* stream) { _parser.initWithStream(stream); clean(); // make sure the row index is reset process(); CSVMap* map=_map; // store before cleaning clean(); _parser.reset(); return map; } void CSVParserHandler::setup(UChar32 sepchar, unsigned int headercount) { _parser.setSeparator(sepchar); _strow=-(int)headercount; } void CSVParserHandler::freeData() { if (_currentrow) { delete _currentrow; } if (_map) { delete _map; } clean(); } void CSVParserHandler::addToRow(ValueVariable* val) { if (_currentrow==NULL) { newRow(); } _currentrow->set(_column, val); _gtoken=(val!=NULL); } void CSVParserHandler::newRow() { if (_currentrow!=NULL) { if (!_gtoken) { addToRow(NULL); } _map->set(_row++, _currentrow); } _currentrow=new CSVRow(_row); _column=0; _gtoken=false; } // class CSVFormatter implementation CSVParser CSVFormatter::_parser=CSVParser(); CSVParserHandler CSVFormatter::_handler=CSVParserHandler(CSVFormatter::_parser); void CSVFormatter::formatRow(const CSVRow& row, UnicodeString& result, UChar32 sepchar, unsigned int varformat) { result.remove(); UnicodeString formatted; int lastcolumn=0; CSVRecordMap::const_iterator iter; for (iter=row.begin(); iter!=row.end(); ++iter) { for (; lastcolumn<iter->first; ++lastcolumn) { result.append(sepchar); } if (iter->second) { iter->second->getValueFormatted(formatted, varformat); // result.append(iter->second->getTypeName()); // result.append(':'); result.append(formatted); //} else { // result.append("[null]"); } } } CSVMap* CSVFormatter::loadFromFile(const char* path, UChar32 sepchar, unsigned int headercount, const char* encoding) { Stream* stream=FileStream::readFile(path, encoding); if (stream) { _handler.setup(sepchar, headercount); CSVMap* map=_handler.processFromStream(stream); stream->close(); delete stream; return map; } return NULL; } CSVMap* CSVFormatter::loadFromFile(const std::string& path, UChar32 sepchar, unsigned int headercount, const char* encoding) { return loadFromFile(path.c_str(), sepchar, headercount, encoding); } CSVMap* CSVFormatter::loadFromFile(const UnicodeString& path, UChar32 sepchar, unsigned int headercount, const char* encoding) { std::string temp; path.toUTF8String(temp); return loadFromFile(temp.c_str(), sepchar, headercount, encoding); } CSVMap* CSVFormatter::loadFromStream(Stream* stream, UChar32 sepchar, unsigned int headercount) { if (stream) { _handler.setup(sepchar, headercount); return _handler.processFromStream(stream); } return NULL; } bool CSVFormatter::writeToFile(const CSVMap* map, const char* path, UChar32 sepchar, const char* encoding, unsigned int varformat) { Stream* stream=FileStream::writeFile(path, encoding); if (stream) { writeToStream(map, stream, sepchar, varformat); stream->close(); return true; } return false; } bool CSVFormatter::writeToFile(const CSVMap* map, const std::string& path, UChar32 sepchar, const char* encoding, unsigned int varformat) { return writeToFile(map, path.c_str(), sepchar, encoding, varformat); } bool CSVFormatter::writeToFile(const CSVMap* map, const UnicodeString& path, UChar32 sepchar, const char* encoding, unsigned int varformat) { std::string temp; path.toUTF8String(temp); return writeToFile(map, temp.c_str(), sepchar, encoding, varformat); } bool CSVFormatter::writeToStream(const CSVMap* map, Stream* stream, UChar32 sepchar, unsigned int varformat) { if (map!=NULL && stream!=NULL) { UnicodeString temp; bool first=false; int lastrow=0; CSVRowMap::const_iterator iter; for (iter=map->begin(); iter!=map->end(); ++iter) { if (!first) { lastrow=iter->first; first=true; } /*for (; lastrow!=iter->first; ++lastrow) { stream->writeChar('\n'); }*/ if (iter->second) { // check if record is NULL formatRow(*iter->second, temp, sepchar, varformat); stream->writeLine(temp); } } return true; } return false; } } // namespace duct