1 /*
2  * Copyright (C) 2004-2010 Geometer Plus <contact@geometerplus.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
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, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 #include <cctype>
21 #include <cstring>
22 
23 #include <ZLStringUtil.h>
24 #include <ZLInputStream.h>
25 
26 #include "StyleSheetParser.h"
27 
StyleSheetTableParser(StyleSheetTable & table)28 StyleSheetTableParser::StyleSheetTableParser(StyleSheetTable &table) : myTable(table) {
29 }
30 
storeData(const std::string & tagName,const std::string & className,const StyleSheetTable::AttributeMap & map)31 void StyleSheetTableParser::storeData(const std::string &tagName, const std::string &className, const StyleSheetTable::AttributeMap &map) {
32 	myTable.addMap(tagName, className, map);
33 }
34 
parseString(const char * text)35 shared_ptr<ZLTextStyleEntry> StyleSheetSingleStyleParser::parseString(const char *text) {
36 	myReadState = ATTRIBUTE_NAME;
37 	parse(text, strlen(text), true);
38 	shared_ptr<ZLTextStyleEntry> control = StyleSheetTable::createControl(myMap);
39 	reset();
40 	return control;
41 }
42 
StyleSheetParser()43 StyleSheetParser::StyleSheetParser() : myReadState(TAG_NAME), myInsideComment(false) {
44 }
45 
~StyleSheetParser()46 StyleSheetParser::~StyleSheetParser() {
47 }
48 
reset()49 void StyleSheetParser::reset() {
50 	myWord.erase();
51 	myAttributeName.erase();
52 	myReadState = TAG_NAME;
53 	myInsideComment = false;
54 	myTagName.erase();
55 	myClassName.erase();
56 	myMap.clear();
57 }
58 
parse(ZLInputStream & stream)59 void StyleSheetParser::parse(ZLInputStream &stream) {
60 	if (stream.open()) {
61 		char *buffer = new char[1024];
62 		while (true) {
63 			int len = stream.read(buffer, 1024);
64 			if (len == 0) {
65 				break;
66 			}
67 			parse(buffer, len);
68 		}
69 		delete[] buffer;
70 		stream.close();
71 	}
72 }
73 
parse(const char * text,int len,bool final)74 void StyleSheetParser::parse(const char *text, int len, bool final) {
75 	const char *start = text;
76 	const char *end = text + len;
77 	for (const char *ptr = start; ptr != end; ++ptr) {
78 		if (isspace(*ptr)) {
79 			if (start != ptr) {
80 				myWord.append(start, ptr - start);
81 			}
82 			processWord(myWord);
83 			myWord.erase();
84 			start = ptr + 1;
85 		} else if (isControlSymbol(*ptr)) {
86 			if (start != ptr) {
87 				myWord.append(start, ptr - start);
88 			}
89 			processWord(myWord);
90 			myWord.erase();
91 			processControl(*ptr);
92 			start = ptr + 1;
93 		}
94 	}
95 	if (start < end) {
96 		myWord.append(start, end - start);
97 		if (final) {
98 			processWord(myWord);
99 			myWord.erase();
100 		}
101 	}
102 }
103 
isControlSymbol(const char symbol)104 bool StyleSheetParser::isControlSymbol(const char symbol) {
105 	switch (symbol) {
106 		case '{':
107 		case '}':
108 		case ';':
109 		case ':':
110 			return true;
111 		default:
112 			return false;
113 	}
114 }
115 
storeData(const std::string &,const std::string &,const StyleSheetTable::AttributeMap &)116 void StyleSheetParser::storeData(const std::string&, const std::string&, const StyleSheetTable::AttributeMap&) {
117 }
118 
processControl(const char control)119 void StyleSheetParser::processControl(const char control) {
120 	switch (control) {
121 		case '{':
122 			myReadState = (myReadState == TAG_NAME) ? ATTRIBUTE_NAME : BROKEN;
123 			break;
124 		case '}':
125 			if (myReadState != BROKEN) {
126 				storeData(myTagName, myClassName, myMap);
127 			}
128 			myReadState = TAG_NAME;
129 			myTagName.erase();
130 			myClassName.erase();
131 			myMap.clear();
132 			break;
133 		case ';':
134 			myReadState =
135 				((myReadState == ATTRIBUTE_VALUE) ||
136 				 (myReadState == ATTRIBUTE_NAME)) ? ATTRIBUTE_NAME : BROKEN;
137 			break;
138 		case ':':
139 			myReadState = (myReadState == ATTRIBUTE_NAME) ? ATTRIBUTE_VALUE : BROKEN;
140 			break;
141 	}
142 }
143 
processWord(std::string & word)144 void StyleSheetParser::processWord(std::string &word) {
145 	while (!word.empty()) {
146 		int index = word.find(myInsideComment ? "*/" : "/*");
147 		if (!myInsideComment) {
148 			if (index == -1) {
149 				processWordWithoutComments(word);
150 			} else if (index > 0) {
151 				processWordWithoutComments(word.substr(0, index));
152 			}
153 		}
154 		if (index == -1) {
155 			break;
156 		}
157 		myInsideComment = !myInsideComment;
158 		word.erase(0, index + 2);
159 	}
160 }
161 
processWordWithoutComments(const std::string & word)162 void StyleSheetParser::processWordWithoutComments(const std::string &word) {
163 	switch (myReadState) {
164 		case TAG_NAME:
165 		{
166 			int index = word.find('.');
167 			if (index == -1) {
168 				if (myTagName.empty()) {
169 					myTagName = word;
170 				} else {
171 					myTagName += ' ' + word;
172 				}
173 			} else {
174 				if (myTagName.empty()) {
175 					myTagName = word.substr(0, index);
176 					myClassName = word.substr(index + 1);
177 				} else {
178 					myTagName += ' ' + word.substr(0, index);
179 					myClassName += ' ' + word.substr(index + 1);
180 				}
181 			}
182 			myMap.clear();
183 			break;
184 		}
185 		case ATTRIBUTE_NAME:
186 			myAttributeName = word;
187 			myMap[myAttributeName].clear();
188 			break;
189 		case ATTRIBUTE_VALUE:
190 			myMap[myAttributeName].push_back(word);
191 			break;
192 		case BROKEN:
193 			break;
194 	}
195 }
196