src/lib/INIReader.cpp
branchv_0
changeset 16 b9a3c806468a
child 19 90f2b8ca32bf
equal deleted inserted replaced
15:f4fb07ed8753 16:b9a3c806468a
       
     1 /**
       
     2  * Relational pipes
       
     3  * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
       
     4  *
       
     5  * This program is free software: you can redistribute it and/or modify
       
     6  * it under the terms of the GNU General Public License as published by
       
     7  * the Free Software Foundation, version 3 of the License.
       
     8  *
       
     9  * This program is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       
    12  * GNU General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU General Public License
       
    15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
       
    16  */
       
    17 
       
    18 #include <vector>
       
    19 #include <regex>
       
    20 
       
    21 #include "INIReader.h"
       
    22 
       
    23 class INIReaderImpl : public INIReader {
       
    24 private:
       
    25 	std::istream& input;
       
    26 	std::vector<INIContentHandler*> handlers;
       
    27 public:
       
    28 
       
    29 	INIReaderImpl(std::istream& input) : input(input) {
       
    30 	}
       
    31 
       
    32 	void addHandler(INIContentHandler* handler) override {
       
    33 		handlers.push_back(handler);
       
    34 	}
       
    35 
       
    36 	void process() override {
       
    37 
       
    38 		for (INIContentHandler* handler : handlers) handler->startDocument();
       
    39 
       
    40 		std::regex whitespacePattrern("\\s*");
       
    41 		std::regex commentPattrern("\\s*(;|#)\\s*(.*)");
       
    42 		std::regex sectionPattrern("\\s*\\[\\s*([^\\]]+)\\s*\\]\\s*");
       
    43 		std::regex entryQuotesPattrern(/***/"\\s*(([^=\\]]+?[^=\\s\\]]*)(\\[([^\\]]+)\\])?)\\s*=\\s*\"([^']+)\"\\s*((;|#)\\s*(.*))?");
       
    44 		std::regex entryApostrophesPattrern("\\s*(([^=\\]]+?[^=\\s\\]]*)(\\[([^\\]]+)\\])?)\\s*=\\s*'([^']+)'\\s*((;|#)\\s*(.*))?");
       
    45 		std::regex entryPlainPattrern("\\s*(([^=\\]]+?[^=\\s\\]]*)(\\[([^\\]]+)\\])?)\\s*=\\s*(.*)");
       
    46 
       
    47 		std::smatch match;
       
    48 		bool inSection = false;
       
    49 		std::string line;
       
    50 		int lineNumber = 0;
       
    51 		int eventNumber = 0;
       
    52 
       
    53 
       
    54 		while (std::getline(input, line)) {
       
    55 			lineNumber++;
       
    56 
       
    57 			if (std::regex_match(line, match, whitespacePattrern)) {
       
    58 				// TODO: support also whitespace
       
    59 			} else if (std::regex_match(line, match, commentPattrern)) {
       
    60 				// TODO: support also comments + emit also the comment style (;/#)
       
    61 			} else if (std::regex_match(line, match, sectionPattrern)) {
       
    62 				if (inSection) for (INIContentHandler* handler : handlers) handler->endSection();
       
    63 				inSection = true;
       
    64 				INIContentHandler::SectionStartEvent event;
       
    65 				event.lineNumber = lineNumber;
       
    66 				event.eventNumber = ++eventNumber;
       
    67 				event.name = match[1];
       
    68 				// TODO: support also comments + emit also the comment style (;/#)
       
    69 				for (INIContentHandler* handler : handlers) handler->startSection(event);
       
    70 			} else if (std::regex_match(line, match, entryQuotesPattrern) || std::regex_match(line, match, entryApostrophesPattrern) || std::regex_match(line, match, entryPlainPattrern)) {
       
    71 				INIContentHandler::EntryEvent event;
       
    72 				event.lineNumber = lineNumber;
       
    73 				event.eventNumber = ++eventNumber;
       
    74 				event.key = match[2];
       
    75 				event.subKey = match[4];
       
    76 				event.fullKey = match[1];
       
    77 				event.value = match[5];
       
    78 				if (match.size() == 9) event.comment = match[8];
       
    79 				// TODO: emit also the quote style ('/"/) and surrounding whitespace
       
    80 				for (INIContentHandler* handler : handlers) handler->entry(event);
       
    81 			} else {
       
    82 				// TODO: warning, error, or support unknown content
       
    83 			}
       
    84 
       
    85 			// TODO: probably switch to state-machine approach instead of regular expressions
       
    86 			// TODO: warning/error handler
       
    87 			// TODO: support also multiline content (\ + \n)
       
    88 			// TODO: support also quoted or multiline keys?
       
    89 			// TODO: support also escaped characters
       
    90 			// TODO: support also Java .properties and manifest.mf formats?
       
    91 			// TODO: support also nested sections – hierarchy
       
    92 			// TODO: support also nested keys e.g. key.sub.subsub.subsubsub=value – translate them to nested sections
       
    93 			// TODO: support also option for alternative key-value separator (: instead of =)
       
    94 			// TODO: support also other encodings (currently only UTF-8 is supported)
       
    95 		}
       
    96 
       
    97 		if (inSection) for (INIContentHandler* handler : handlers) handler->endSection();
       
    98 
       
    99 		for (INIContentHandler* handler : handlers) handler->endDocument();
       
   100 	}
       
   101 };
       
   102 
       
   103 INIReader* INIReader::create(std::istream& input) {
       
   104 	return new INIReaderImpl(input);
       
   105 }