1 /*
2 # PostgreSQL Database Modeler (pgModeler)
3 #
4 # Copyright 2006-2020 - Raphael Araújo e Silva <raphael@pgmodeler.io>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation version 3.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # The complete text of GPLv3 is at LICENSE file on source code root directory.
16 # Also, you can get the complete GNU General Public License at <http://www.gnu.org/licenses/>
17 */
18 
19 #include "schemaparser.h"
20 #include "attributes.h"
21 
22 const char SchemaParser::CharComment='#';
23 const char SchemaParser::CharLineEnd='\n';
24 const char SchemaParser::CharTabulation='\t';
25 const char SchemaParser::CharSpace=' ';
26 const char SchemaParser::CharIniAttribute='{';
27 const char SchemaParser::CharEndAttribute='}';
28 const char SchemaParser::CharIniConditional='%';
29 const char SchemaParser::CharIniMetachar='$';
30 const char SchemaParser::CharIniPlainText='[';
31 const char SchemaParser::CharEndPlainText=']';
32 const char SchemaParser::CharIniCompExpr='(';
33 const char SchemaParser::CharEndCompExpr=')';
34 const char SchemaParser::CharValueDelim='"';
35 const char SchemaParser::CharValueOf='@';
36 
37 const QString SchemaParser::TokenIf=QString("if");
38 const QString SchemaParser::TokenThen=QString("then");
39 const QString SchemaParser::TokenElse=QString("else");
40 const QString SchemaParser::TokenEnd=QString("end");
41 const QString SchemaParser::TokenOr=QString("or");
42 const QString SchemaParser::TokenAnd=QString("and");
43 const QString SchemaParser::TokenNot=QString("not");
44 const QString SchemaParser::TokenSet=QString("set");
45 const QString SchemaParser::TokenUnset=QString("unset");
46 
47 const QString SchemaParser::TokenMetaSp=QString("sp");
48 const QString SchemaParser::TokenMetaBr=QString("br");
49 const QString SchemaParser::TokenMetaTb=QString("tb");
50 const QString SchemaParser::TokenMetaOb=QString("ob");
51 const QString SchemaParser::TokenMetaCb=QString("cb");
52 const QString SchemaParser::TokenMetaOc=QString("oc");
53 const QString SchemaParser::TokenMetaCc=QString("cc");
54 
55 const QString SchemaParser::TokenEqOper=QString("==");
56 const QString SchemaParser::TokenNeOper=QString("!=");
57 const QString SchemaParser::TokenGtOper=QString(">");
58 const QString SchemaParser::TokenLtOper=QString("<");
59 const QString SchemaParser::TokenGtEqOper=QString(">=");
60 const QString SchemaParser::TokenLtEqOper=QString("<=");
61 
62 const QRegExp SchemaParser::AttribRegExp=QRegExp("^([a-z])([a-z]*|(\\d)*|(\\-)*|(_)*)+", Qt::CaseInsensitive);
63 
SchemaParser()64 SchemaParser::SchemaParser()
65 {
66 	line=column=comment_count=0;
67 	ignore_unk_atribs=ignore_empty_atribs=false;
68 	pgsql_version=PgSqlVersions::DefaulVersion;
69 }
70 
setPgSQLVersion(const QString & pgsql_ver)71 void SchemaParser::setPgSQLVersion(const QString &pgsql_ver)
72 {
73 	unsigned curr_ver = QString(pgsql_ver).remove('.').toUInt(),
74 			version90 = QString(PgSqlVersions::PgSqlVersion90).remove('.').toUInt(),
75 			default_ver = QString(PgSqlVersions::DefaulVersion).remove('.').toUInt();
76 
77 	if(curr_ver != 0 && (curr_ver < version90))
78 		throw Exception(Exception::getErrorMessage(ErrorCode::InvPostgreSQLVersion)
79 						.arg(pgsql_ver)
80 						.arg(PgSqlVersions::PgSqlVersion90)
81 						.arg(PgSqlVersions::DefaulVersion),
82 						ErrorCode::InvPostgreSQLVersion,__PRETTY_FUNCTION__,__FILE__,__LINE__);
83 
84 	if(curr_ver > 0 && curr_ver <= default_ver)
85 		pgsql_version=pgsql_ver;
86 	else
87 		pgsql_version=PgSqlVersions::DefaulVersion;
88 }
89 
getPgSQLVersion()90 QString SchemaParser::getPgSQLVersion()
91 {
92 	return pgsql_version;
93 }
94 
extractAttributes()95 QStringList SchemaParser::extractAttributes()
96 {
97 	QStringList attribs;
98 	int start=0, end=0;
99 
100 	for(QString line : buffer)
101 	{
102 		//Find the first occurrence of '{' in the line
103 		start=line.indexOf(CharIniAttribute, start);
104 
105 		while(start >= 0 && start < line.size())
106 		{
107 			end=line.indexOf(CharEndAttribute, start);
108 			if(end >= 0)
109 			{
110 				//Extract the name between {} and push it into the list
111 				attribs.push_back(line.mid(start + 1, end - start -1));
112 				//Start searching new attribute start now from the last position
113 				start=line.indexOf(CharIniAttribute, end);
114 			}
115 			else
116 				break;
117 		}
118 
119 		start=end=0;
120 	}
121 
122 	attribs.removeDuplicates();
123 	return attribs;
124 }
125 
restartParser()126 void SchemaParser::restartParser()
127 {
128 	/* Clears the buffer and resets the counters for line,
129 		column and amount of comments */
130 	buffer.clear();
131 	attributes.clear();
132 	line=column=comment_count=0;
133 }
134 
loadBuffer(const QString & buf)135 void SchemaParser::loadBuffer(const QString &buf)
136 {
137 	QString buf_aux=buf, lin,
138 			escaped_comm_chr=QString("\\%1").arg(CharComment),
139 			placeholder = QString(QChar::ReplacementCharacter);
140 	QTextStream ts(&buf_aux);
141 	int pos=0;
142 	bool comm_holder_used = false;
143 
144 	//Prepares the parser to do new reading
145 	restartParser();
146 
147 	filename="[memory buffer]";
148 
149 	//While the input file doesn't reach the end
150 	while(!ts.atEnd())
151 	{
152 		//Get one line from stream (until the last char before \n)
153 		lin = ts.readLine();
154 
155 		/* Special treatment for escaped comment characters (e.g.: \#):
156 		 * In order to avoid removing wrongly the # from the the line where it appear in the form \#
157 		 * we need to replace it temporarily by a placeholder <?> and remove other portions of the line
158 		 * the is considered a real comment and then replace back that placeholder by the comment char again.
159 		 * This is useful if the user intend to represent the hash (#) char in the schema code and not use it as comment. */
160 		if(lin.indexOf(escaped_comm_chr) >= 0)
161 		{
162 			lin.replace(escaped_comm_chr, placeholder);
163 			comm_holder_used = true;
164 		}
165 
166 		/* Since the method getline discards the \n when the line was just a line break
167 		its needed to treat it in order to not lost it */
168 		if(lin.isEmpty()) lin+=CharLineEnd;
169 
170 		//If the entire line is commented out increases the comment lines counter
171 		if(lin[0]==CharComment) comment_count++;
172 
173 		//Looking for the position of other comment characters for deletion
174 		pos=lin.indexOf(CharComment);
175 
176 		//Removes the characters from the found position
177 		if(pos >= 0)
178 			lin.remove(pos, lin.size());
179 
180 		//Replacing the comment placeholder by the comment char causing that character to be printed to the code
181 		if(comm_holder_used)
182 		{
183 			lin.replace(placeholder, QString(CharComment));
184 			comm_holder_used = false;
185 		}
186 
187 		if(!lin.isEmpty())
188 		{
189 			//Add a line break in case the last character is not
190 			if(lin[lin.size()-1]!=CharLineEnd)
191 				lin+=CharLineEnd;
192 
193 			//Add the treated line in the buffer
194 			buffer.push_back(lin);
195 		}
196 	}
197 }
198 
loadFile(const QString & filename)199 void SchemaParser::loadFile(const QString &filename)
200 {
201 	if(!filename.isEmpty())
202 	{
203 		QFile input;
204 		QString buf;
205 
206 		//Open the file for reading
207 		input.setFileName(filename);
208 		input.open(QFile::ReadOnly);
209 
210 		if(!input.isOpen())
211 			throw Exception(Exception::getErrorMessage(ErrorCode::FileDirectoryNotAccessed).arg(filename),
212 							ErrorCode::FileDirectoryNotAccessed,__PRETTY_FUNCTION__,__FILE__,__LINE__);
213 
214 		buf=input.readAll();
215 		input.close();
216 
217 		//Loads the parser buffer
218 		loadBuffer(buf);
219 		SchemaParser::filename=filename;
220 	}
221 }
222 
getAttribute()223 QString SchemaParser::getAttribute()
224 {
225 	QString atrib, current_line;
226 	bool start_attrib, end_attrib, error=false;
227 
228 	//Get the current line from the buffer
229 	current_line=buffer[line];
230 
231 	/* Only start extracting an attribute if it starts with a {
232 		even if the current character is an attribute delimiter */
233 	if(current_line[column]!=CharIniAttribute)
234 		error=true;
235 	else
236 	{
237 		//Step to the next column in the line
238 		column++;
239 
240 		//Marks the flag indicating start of attribute
241 		start_attrib=true;
242 		//Unmarks the flag indicating end of attribute
243 		end_attrib=false;
244 
245 		/* Attempt to extract an attribute until a space, end of line
246 	  or attribute is encountered */
247 		while(current_line[column]!=CharLineEnd &&
248 			  current_line[column]!=CharSpace &&
249 			  current_line[column]!=CharTabulation &&
250 			  !end_attrib && !error)
251 		{
252 			if(current_line[column]!=CharEndAttribute)
253 				atrib+=current_line[column];
254 			else if(current_line[column]==CharEndAttribute && !atrib.isEmpty())
255 				end_attrib=true;
256 			else
257 				error=true;
258 			column++;
259 		}
260 
261 		/* If the attribute has been started but not finished
262 	  ie absence of the } in its statement (ie. {attr),
263 	  generates an error. */
264 		if(start_attrib && !end_attrib) error=true;
265 	}
266 
267 	if(error)
268 	{
269 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
270 						.arg(filename).arg((line + comment_count + 1)).arg((column+1)),
271 						ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
272 	}
273 	else if(!AttribRegExp.exactMatch(atrib))
274 	{
275 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidAttribute)
276 						.arg(atrib).arg(filename).arg((line + comment_count + 1)).arg((column+1)),
277 						ErrorCode::InvalidAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
278 	}
279 
280 	return atrib;
281 }
282 
getWord()283 QString SchemaParser::getWord()
284 {
285 	QString word, current_line;
286 
287 	//Gets the current line buffer
288 	current_line=buffer[line];
289 
290 	/* Attempt to extract a word if the first character is not
291 		a special character. */
292 	if(!isSpecialCharacter(current_line[column].toLatin1()))
293 	{
294 		/* Extract the word while it is not end of line, space or
295 		 special character */
296 		while(current_line[column]!=CharLineEnd &&
297 			  !isSpecialCharacter(current_line[column].toLatin1()) &&
298 			  current_line[column]!=CharSpace &&
299 			  current_line[column]!=CharTabulation)
300 		{
301 			word+=current_line[column];
302 			column++;
303 		}
304 	}
305 
306 	return word;
307 }
308 
getPureText()309 QString SchemaParser::getPureText()
310 {
311 	QString text, current_line;
312 	bool error=false;
313 
314 	current_line=buffer[line];
315 
316 	//Attempt to extract a pure text if the first character is a [
317 	if(current_line[column]==CharIniPlainText)
318 	{
319 		//Moves to the next character that contains the beginning of the text
320 		column++;
321 
322 		/* Extracts the text while the end of pure text (]), end of buffer or
323 		 beginning of other pure text ([) is reached */
324 		while(current_line[column]!=CharEndPlainText &&
325 			  line < buffer.size() &&
326 			  current_line[column]!=CharIniPlainText)
327 		{
328 			text+=current_line[column];
329 
330 			/* Special case to end of line. Unlike other elements of
331 			language, a pure text can be extracted until the end of the buffer,
332 			thus, this method also controls the lines transitions */
333 			if(current_line[column]==CharLineEnd)
334 			{
335 				//Step to the next line
336 				line++;
337 				column=0;
338 
339 				if(line < buffer.size())
340 					current_line=buffer[line];
341 			}
342 			else column++;
343 		}
344 
345 		if(current_line[column]==CharEndPlainText)
346 			column++;
347 		else
348 			error=true;
349 	}
350 	else error=true;
351 
352 	if(error)
353 	{
354 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
355 						.arg(filename).arg((line + comment_count + 1)).arg((column+1)),
356 						ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
357 	}
358 
359 	return text;
360 }
361 
getConditional()362 QString SchemaParser::getConditional()
363 {
364 	QString conditional, current_line;
365 	bool error=false;
366 
367 	current_line=buffer[line];
368 
369 	//Will initiate extraction if a % is found
370 	if(current_line[column]==CharIniConditional)
371 	{
372 		/* Passa para o próximo caractere que é o início do
373 		 do nome da palavra condicional */
374 		column++;
375 
376 		/* Moves to the next character that is the beginning of
377 		 the name of the conditional word */
378 		while(current_line[column]!=CharLineEnd &&
379 			  current_line[column]!=CharSpace &&
380 			  current_line[column]!=CharTabulation)
381 		{
382 			conditional+=current_line[column];
383 			column++;
384 		}
385 
386 		//If no word was extracted an error is raised
387 		if(conditional.isEmpty()) error=true;
388 	}
389 	else error=true;
390 
391 	if(error)
392 	{
393 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
394 						.arg(filename).arg(line + comment_count + 1).arg(column+1),
395 						ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
396 	}
397 
398 	return conditional;
399 }
400 
getMetaCharacter()401 QString SchemaParser::getMetaCharacter()
402 {
403 	QString meta, current_line;
404 	bool error=false;
405 
406 	current_line=buffer[line];
407 
408 	//Begins the extraction in case of a $ is found
409 	if(current_line[column]==CharIniMetachar)
410 	{
411 		//Moves to the next character that is the beginning of the metacharacter
412 		column++;
413 
414 		//Extracts the metacharacter until doesn't finds a space or end of line
415 		while(current_line[column]!=CharLineEnd &&
416 			  current_line[column]!=CharSpace &&
417 			  current_line[column]!=CharTabulation)
418 		{
419 			meta+=current_line[column];
420 			column++;
421 		}
422 
423 		//If no metacharacter was extracted an error is raised
424 		if(meta.isEmpty()) error=true;
425 	}
426 	else error=true;
427 
428 	if(error)
429 	{
430 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
431 						.arg(filename).arg(line + comment_count + 1).arg(column+1),
432 						ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
433 	}
434 
435 	return meta;
436 }
437 
isSpecialCharacter(char chr)438 bool SchemaParser::isSpecialCharacter(char chr)
439 {
440 	return chr==CharIniAttribute || chr==CharEndAttribute ||
441 			chr==CharIniConditional || chr==CharIniMetachar ||
442 			chr==CharIniPlainText || chr==CharEndPlainText;
443 }
444 
evaluateComparisonExpr()445 bool SchemaParser::evaluateComparisonExpr()
446 {
447 	QString curr_line, attrib, value, oper, valid_op_chrs="=!<>fi";
448 	bool error=false, end_eval=false, expr_is_true=true;
449 	static QStringList opers = { TokenEqOper, TokenNeOper, TokenGtOper,
450 															 TokenLtOper, TokenGtEqOper, TokenLtEqOper };
451 
452 	try
453 	{
454 		curr_line=buffer[line];
455 		column++;
456 
457 		while(!end_eval && !error)
458 		{
459 			ignoreBlankChars(curr_line);
460 
461 			/* If the scan reached the end of the line and the expression was not closed raises an syntax error
462 		 Comparison expr must start and end in the same line */
463 			if(curr_line[column]==CharLineEnd && !end_eval)
464 				error=true;
465 
466 			switch(curr_line[column].toLatin1())
467 			{
468 				case CharIniAttribute:
469 					/* Extract the attribute (the first element in the expression) only
470 			 if the comparison operator and values aren't extracted */
471 					if(attrib.isEmpty() && oper.isEmpty() && value.isEmpty())
472 						attrib=getAttribute();
473 					else
474 						error=true;
475 				break;
476 
477 				case CharValueDelim:
478 					/* Extract the value (the last element in the expression) only
479 			 if the attribute and operator were extracted */
480 					if(value.isEmpty() && !attrib.isEmpty() && !oper.isEmpty())
481 					{
482 						value+=curr_line[column++];
483 
484 						while(column < curr_line.size())
485 						{
486 							value+=curr_line[column++];
487 
488 							if(curr_line[column]==CharValueDelim)
489 							{
490 								value+=CharValueDelim;
491 								column++;
492 								break;
493 							}
494 						}
495 					}
496 					else
497 						error=true;
498 
499 				break;
500 
501 				case CharEndCompExpr:
502 					column++;
503 
504 					//If one of the elements are missing, raise an syntax error
505 					if(attrib.isEmpty() || oper.isEmpty() || value.isEmpty())
506 						error=true;
507 					else if(!opers.contains(QString(oper).remove('f').remove('i')))
508 					{
509 						throw Exception(Exception::getErrorMessage(ErrorCode::InvalidOperatorInExpression)
510 										.arg(oper).arg(filename).arg((line + comment_count + 1)).arg((column+1)),
511 										ErrorCode::InvalidOperatorInExpression,__PRETTY_FUNCTION__,__FILE__,__LINE__);
512 					}
513 					else if(attributes.count(attrib)==0 && !ignore_unk_atribs)
514 					{
515 						throw Exception(Exception::getErrorMessage(ErrorCode::UnkownAttribute)
516 										.arg(attrib).arg(filename).arg((line + comment_count +1)).arg((column+1)),
517 										ErrorCode::UnkownAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
518 					}
519 					else
520 					{
521 						QVariant left_val, right_val;
522 						value.remove(CharValueDelim);
523 
524 						//Evaluating the attribute value against the one captured on the expression without casting
525 						if(oper.endsWith('f'))
526 						{
527 							left_val = QVariant(attributes[attrib].toFloat());
528 							right_val = QVariant(value.toFloat());
529 							oper.remove('f');
530 							expr_is_true = getExpressionResult<float>(oper, left_val, right_val);
531 						}
532 						else if(oper.endsWith('i'))
533 						{
534 							left_val = QVariant(attributes[attrib].toInt());
535 							right_val = QVariant(value.toInt());
536 							oper.remove('i');
537 							expr_is_true = getExpressionResult<int>(oper, left_val, right_val);
538 						}
539 						else
540 						{
541 							left_val = QVariant(attributes[attrib]);
542 							right_val = QVariant(value);
543 							expr_is_true = getExpressionResult<QString>(oper, left_val, right_val);
544 						}
545 
546 						end_eval=true;
547 					}
548 				break;
549 
550 				default:
551 					/* Extract the operator (the second element in the expression) only
552 			 if the attribute was extracted and the value not */
553 					if(oper.size() <= 3 && !attrib.isEmpty() && value.isEmpty())
554 					{
555 						//If the current char is a valid operator capture it otherwise raise an error
556 						if(valid_op_chrs.indexOf(curr_line[column]) >= 0)
557 							oper+=curr_line[column++];
558 						else
559 							error=true;
560 					}
561 					else
562 						error=true;
563 
564 				break;
565 			}
566 		}
567 	}
568 	catch(Exception &e)
569 	{
570 		throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__,&e);
571 	}
572 
573 	if(error)
574 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
575 						.arg(filename).arg((line + comment_count + 1)).arg((column+1)),
576 						ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
577 
578 	return expr_is_true;
579 }
580 
defineAttribute()581 void SchemaParser::defineAttribute()
582 {
583 	QString curr_line, attrib, value, new_attrib;
584 	bool error=false, end_def=false, use_val_as_name=false;
585 
586 	try
587 	{
588 		curr_line=buffer[line];
589 
590 		while(!end_def && !error)
591 		{
592 			ignoreBlankChars(curr_line);
593 
594 			switch(curr_line[column].toLatin1())
595 			{
596 				case CharLineEnd:
597 					end_def=true;
598 				break;
599 
600 				case CharValueOf:
601 					if(!use_val_as_name)
602 					{
603 						use_val_as_name=true;
604 						column++;
605 						new_attrib=getAttribute();
606 					}
607 					else
608 						error=true;
609 				break;
610 
611 				case CharIniConditional:
612 					error=true;
613 				break;
614 
615 				case CharIniAttribute:
616 					if(new_attrib.isEmpty())
617 						new_attrib=getAttribute();
618 					else
619 					{
620 						//Get the attribute in the middle of the value
621 						attrib=getAttribute();
622 
623 						if(attributes.count(attrib)==0 && !ignore_unk_atribs)
624 						{
625 							throw Exception(Exception::getErrorMessage(ErrorCode::UnkownAttribute)
626 											.arg(attrib).arg(filename).arg((line + comment_count +1)).arg((column+1)),
627 											ErrorCode::UnkownAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
628 						}
629 
630 						value+=attributes[attrib];
631 					}
632 				break;
633 
634 				case CharIniPlainText:
635 					value+=getPureText();
636 				break;
637 
638 				case CharIniMetachar:
639 					value+=translateMetaCharacter(getMetaCharacter());
640 				break;
641 
642 				default:
643 					value+=getWord();
644 				break;
645 			}
646 
647 			//If the attribute name was not extracted yet returns a error
648 			if(new_attrib.isEmpty())
649 				error=true;
650 		}
651 	}
652 	catch(Exception &e)
653 	{
654 		throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__,&e);
655 	}
656 
657 	if(!error)
658 	{
659 		attrib=(use_val_as_name ? attributes[new_attrib] : new_attrib);
660 
661 		//Checking if the attribute has a valid name
662 		if(!AttribRegExp.exactMatch(attrib))
663 		{
664 			throw Exception(Exception::getErrorMessage(ErrorCode::InvalidAttribute)
665 							.arg(attrib).arg(filename).arg((line + comment_count + 1)).arg((column+1)),
666 							ErrorCode::InvalidAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
667 		}
668 
669 		/* Creates the attribute in the attribute map of the schema, making the attribute
670 	   available on the rest of the script being parsed */
671 
672 		attributes[attrib]=value;
673 	}
674 	else
675 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
676 						.arg(filename).arg((line + comment_count + 1)).arg((column+1)),
677 						ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
678 }
679 
unsetAttribute()680 void SchemaParser::unsetAttribute()
681 {
682 	QString curr_line, attrib;
683 	bool end_def=false;
684 
685 	try
686 	{
687 		curr_line=buffer[line];
688 
689 		while(!end_def)
690 		{
691 			ignoreBlankChars(curr_line);
692 
693 			switch(curr_line[column].toLatin1())
694 			{
695 				case CharLineEnd:
696 					end_def=true;
697 				break;
698 
699 				case CharIniAttribute:
700 					attrib=getAttribute();
701 
702 					if(attributes.count(attrib)==0 && !ignore_unk_atribs)
703 					{
704 						throw Exception(Exception::getErrorMessage(ErrorCode::UnkownAttribute)
705 										.arg(attrib).arg(filename).arg((line + comment_count +1)).arg((column+1)),
706 										ErrorCode::UnkownAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
707 					}
708 					else if(!AttribRegExp.exactMatch(attrib))
709 					{
710 						throw Exception(Exception::getErrorMessage(ErrorCode::InvalidAttribute)
711 										.arg(attrib).arg(filename).arg((line + comment_count + 1)).arg((column+1)),
712 										ErrorCode::InvalidAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
713 					}
714 
715 					attributes[attrib]="";
716 				break;
717 
718 				default:
719 					throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
720 									.arg(filename).arg((line + comment_count + 1)).arg((column+1)),
721 									ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
722 			}
723 		}
724 	}
725 	catch(Exception &e)
726 	{
727 		throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__,&e);
728 	}
729 }
evaluateExpression()730 bool SchemaParser::evaluateExpression()
731 {
732 	QString current_line, cond, attrib, prev_cond;
733 	bool error=false, end_eval=false, expr_is_true=true, attrib_true=true, comp_true=true;
734 	unsigned attrib_count=0, and_or_count=0;
735 
736 	try
737 	{
738 		current_line=buffer[line];
739 
740 		while(!end_eval && !error)
741 		{
742 			ignoreBlankChars(current_line);
743 
744 			if(current_line[column]==CharLineEnd)
745 			{
746 				line++;
747 				if(line < buffer.size())
748 				{
749 					current_line=buffer[line];
750 					column=0;
751 					ignoreBlankChars(current_line);
752 				}
753 				else if(!end_eval)
754 					error=true;
755 			}
756 
757 			switch(current_line[column].toLatin1())
758 			{
759 				//Extract the next conditional token
760 				case CharIniConditional:
761 					prev_cond=cond;
762 					cond=getConditional();
763 
764 					//Error 1: %if {a} %or %or %then
765 					error=(cond==prev_cond ||
766 						   //Error 2: %if {a} %and %or %then
767 						   (cond==TokenAnd && prev_cond==TokenOr) ||
768 						   //Error 3: %if {a} %or %and %then
769 						   (cond==TokenOr && prev_cond==TokenAnd) ||
770 						   //Error 4: %if %and {a} %then
771 						   (attrib_count==0 && (cond==TokenAnd || cond==TokenOr)));
772 
773 					if(cond==TokenThen)
774 					{
775 						/* Returns the parser to the token %then because additional
776 						operations is done whe this token is found */
777 						column-=cond.length()+1;
778 						end_eval=true;
779 
780 						//Error 1: %if {a} %not %then
781 						error=(prev_cond==TokenNot ||
782 							   //Error 2: %if %then
783 							   attrib_count==0 ||
784 							   //Error 3: %if {a} %and %then
785 							   (and_or_count!=attrib_count-1));
786 					}
787 					else if(cond==TokenOr || cond==TokenAnd)
788 						and_or_count++;
789 				break;
790 
791 				case CharIniAttribute:
792 					attrib=getAttribute();
793 
794 					//Raises an error if the attribute does is unknown
795 					if(attributes.count(attrib)==0 && !ignore_unk_atribs)
796 					{
797 						throw Exception(Exception::getErrorMessage(ErrorCode::UnkownAttribute)
798 										.arg(attrib).arg(filename).arg((line + comment_count +1)).arg((column+1)),
799 										ErrorCode::UnkownAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
800 					}
801 
802 					//Error 1: A conditional token other than %or %not %and if found on conditional expression
803 					error=(!cond.isEmpty() && cond!=TokenOr && cond!=TokenAnd && cond!=TokenNot) ||
804 						  //Error 2: A %not token if found after an attribute: %if {a} %not %then
805 						  (attrib_count > 0 && cond==TokenNot && prev_cond.isEmpty()) ||
806 						  //Error 3: Two attributes not separated by any conditional token: %if {a} {b} %then
807 						  (attrib_count > 0 && cond.isEmpty());
808 
809 					//Increments the extracted attribute counter
810 					attrib_count++;
811 
812 					if(!error)
813 					{
814 						//Appliyng the NOT operator if found
815 						attrib_true=(cond==TokenNot ? attributes[attrib].isEmpty() : !attributes[attrib].isEmpty());
816 
817 						//Executing the AND operation if the token is found
818 						if(cond==TokenAnd || prev_cond==TokenAnd)
819 							expr_is_true=(expr_is_true && attrib_true);
820 						else if(cond==TokenOr || prev_cond==TokenOr)
821 							expr_is_true=(expr_is_true || attrib_true);
822 						else
823 							expr_is_true=attrib_true;
824 
825 						cond.clear();
826 						prev_cond.clear();
827 					}
828 				break;
829 
830 				case CharIniCompExpr:
831 					comp_true=evaluateComparisonExpr();
832 
833 					//Appliyng the NOT operator if found
834 					if(cond==TokenNot) comp_true=!comp_true;
835 
836 					//Executing the AND operation if the token is found
837 					if(cond==TokenAnd || prev_cond==TokenAnd)
838 						expr_is_true=(expr_is_true && comp_true);
839 					else if(cond==TokenOr || prev_cond==TokenOr)
840 						expr_is_true=(expr_is_true || comp_true);
841 					else
842 						expr_is_true=comp_true;
843 
844 					//Consider the comparison expression as an attribute evaluation
845 					attrib_count++;
846 					cond.clear();
847 					prev_cond.clear();
848 				break;
849 
850 				default:
851 					error=true;
852 				break;
853 			}
854 		}
855 	}
856 	catch(Exception &e)
857 	{
858 		throw Exception(e.getErrorMessage(),e.getErrorCode(),	__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
859 	}
860 
861 	if(error)
862 	{
863 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
864 						.arg(filename).arg((line + comment_count + 1)).arg((column+1)),
865 						ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
866 	}
867 
868 	return expr_is_true;
869 }
870 
ignoreBlankChars(const QString & line)871 void SchemaParser::ignoreBlankChars(const QString &line)
872 {
873 	while(column < line.size() &&
874 		  (line[column]==CharSpace ||
875 		   line[column]==CharTabulation)) column++;
876 }
877 
translateMetaCharacter(const QString & meta)878 char SchemaParser::translateMetaCharacter(const QString &meta)
879 {
880 	static map<QString, char> metas={{ TokenMetaSp, CharSpace },
881 									 { TokenMetaTb, CharTabulation },
882 									 { TokenMetaBr, CharLineEnd },
883 									 { TokenMetaOb, CharIniPlainText },
884 									 { TokenMetaCb, CharEndPlainText },
885 									 { TokenMetaOc, CharIniAttribute },
886 									 { TokenMetaCc, CharEndAttribute }};
887 
888 	if(metas.count(meta)==0)
889 	{
890 		throw Exception(Exception::getErrorMessage(ErrorCode::InvalidMetacharacter)
891 						.arg(meta).arg(filename).arg(line + comment_count +1).arg(column+1),
892 						ErrorCode::InvalidMetacharacter,__PRETTY_FUNCTION__,__FILE__,__LINE__);
893 	}
894 
895 	return metas.at(meta);
896 }
897 
getCodeDefinition(const QString & obj_name,attribs_map & attribs,unsigned def_type)898 QString SchemaParser::getCodeDefinition(const QString & obj_name, attribs_map &attribs, unsigned def_type)
899 {
900 	try
901 	{
902 		QString filename;
903 
904 		if(def_type==SqlDefinition)
905 		{
906 			//Formats the filename
907 			filename = GlobalAttributes::getSchemaFilePath(GlobalAttributes::SQLSchemaDir, obj_name);
908 			attribs[Attributes::PgSqlVersion]=pgsql_version;
909 
910 			//Try to get the object definitin from the specified path
911 			return getCodeDefinition(filename, attribs);
912 		}
913 		else
914 		{
915 			filename = GlobalAttributes::getSchemaFilePath(GlobalAttributes::XMLSchemaDir, obj_name);
916 			return XmlParser::convertCharsToXMLEntities(getCodeDefinition(filename, attribs));
917 		}
918 	}
919 	catch(Exception &e)
920 	{
921 		throw Exception(e.getErrorMessage(),e.getErrorCode(),	__PRETTY_FUNCTION__,__FILE__,__LINE__,&e);
922 	}
923 }
924 
ignoreUnkownAttributes(bool ignore)925 void SchemaParser::ignoreUnkownAttributes(bool ignore)
926 {
927 	ignore_unk_atribs=ignore;
928 }
929 
ignoreEmptyAttributes(bool ignore)930 void SchemaParser::ignoreEmptyAttributes(bool ignore)
931 {
932 	ignore_empty_atribs=ignore;
933 }
934 
getCodeDefinition(attribs_map & attribs)935 QString SchemaParser::getCodeDefinition(attribs_map &attribs)
936 {
937 	QString object_def;
938 	unsigned end_cnt, if_cnt;
939 	int if_level, prev_if_level;
940 	QString atrib, cond, prev_cond, word, meta;
941 	bool error, if_expr;
942 	char chr;
943 	vector<bool> vet_expif, vet_tk_if, vet_tk_then, vet_tk_else;
944 	map<int, vector<QString> > if_map, else_map;
945 	vector<QString>::iterator itr, itr_end;
946 	vector<int> vet_prev_level;
947 	vector<QString> *vet_aux;
948 
949 	//In case the file was successfuly loaded
950 	if(buffer.size() > 0)
951 	{
952 		//Init the control variables
953 		attributes=attribs;
954 		error=if_expr=false;
955 		if_level=-1;
956 		end_cnt=if_cnt=0;
957 
958 		while(line < buffer.size())
959 		{
960 			chr=buffer[line][column].toLatin1();
961 			switch(chr)
962 			{
963 				/* Increments the number of rows causing the parser
964 				to get the next line buffer for analysis */
965 				case CharLineEnd:
966 					line++;
967 					column=0;
968 				break;
969 
970 				case CharTabulation:
971 				case CharSpace:
972 					//The parser will ignore the spaces that are not within pure texts
973 					while(buffer[line][column]==CharSpace ||
974 								buffer[line][column]==CharTabulation) column++;
975 				break;
976 
977 					//Metacharacter extraction
978 				case CharIniMetachar:
979 					meta=getMetaCharacter();
980 
981 					//Checks whether the metacharacter is part of the  'if' expression (this is an error)
982 					if(if_level>=0 && vet_tk_if[if_level] && !vet_tk_then[if_level])
983 					{
984 						throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
985 										.arg(filename).arg(line + comment_count +1).arg(column+1),
986 										ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
987 					}
988 					else
989 					{
990 						//Converting the metacharacter drawn to the character that represents this
991 						chr=translateMetaCharacter(meta);
992 						meta="";
993 						meta+=chr;
994 
995 						//If the parser is inside an 'if / else' extracting tokens
996 						if(if_level>=0)
997 						{
998 							/* If the parser is in 'if' section,
999 								 places the metacharacter on the word map of the current 'if' */
1000 							if(vet_tk_if[if_level] &&
1001 									vet_tk_then[if_level] &&
1002 									!vet_tk_else[if_level])
1003 								if_map[if_level].push_back(meta);
1004 
1005 							/* If the parser is in 'else' section,
1006 								 places the metacharacter on the word map of the current 'else'*/
1007 							else if(vet_tk_else[if_level])
1008 								else_map[if_level].push_back(meta);
1009 						}
1010 						else
1011 							/* If the parsers is not in a 'if / else', puts the metacharacter
1012 								 in the definition sql */
1013 							object_def+=meta;
1014 					}
1015 
1016 				break;
1017 
1018 					//Attribute extraction
1019 				case CharIniAttribute:
1020 				case CharEndAttribute:
1021 					atrib=getAttribute();
1022 
1023 					//Checks if the attribute extracted belongs to the passed list of attributes
1024 					if(attributes.count(atrib)==0)
1025 					{
1026 						if(!ignore_unk_atribs)
1027 						{
1028 							throw Exception(Exception::getErrorMessage(ErrorCode::UnkownAttribute)
1029 											.arg(atrib).arg(filename).arg((line + comment_count +1)).arg((column+1)),
1030 											ErrorCode::UnkownAttribute,__PRETTY_FUNCTION__,__FILE__,__LINE__);
1031 						}
1032 						else
1033 							attributes[atrib]="";
1034 					}
1035 
1036 					//If the parser is inside an 'if / else' extracting tokens
1037 					if(if_level>=0)
1038 					{
1039 						//If the parser evaluated the 'if' conditional and is inside the current if block
1040 						if(!(!if_expr && vet_tk_if[if_level] && !vet_tk_then[if_level]))
1041 						{
1042 							word=atrib;
1043 							atrib="";
1044 							atrib+=CharIniAttribute;
1045 							atrib+=word;
1046 							atrib+=CharEndAttribute;
1047 
1048 							//If the parser is in the 'if' section
1049 							if(vet_tk_if[if_level] &&
1050 									vet_tk_then[if_level] &&
1051 									!vet_tk_else[if_level])
1052 								//Inserts the attribute value in the map of the words of current the 'if' section
1053 								if_map[if_level].push_back(atrib);
1054 							else if(vet_tk_else[if_level])
1055 								//Inserts the attribute value in the map of the words of current the 'else' section
1056 								else_map[if_level].push_back(atrib);
1057 						}
1058 					}
1059 					else
1060 					{
1061 						/* If the attribute has no value set and parser must not ignore empty values
1062 						raises an exception */
1063 						if(attributes[atrib].isEmpty() && !ignore_empty_atribs)
1064 						{
1065 							throw Exception(Exception::getErrorMessage(ErrorCode::UndefinedAttributeValue)
1066 											.arg(atrib).arg(filename).arg(line + comment_count +1).arg(column+1),
1067 											ErrorCode::UndefinedAttributeValue,__PRETTY_FUNCTION__,__FILE__,__LINE__);
1068 						}
1069 
1070 						/* If the parser is not in an if / else, concatenates the value of the attribute
1071 							directly in definition in sql */
1072 						object_def+=attributes[atrib];
1073 					}
1074 				break;
1075 
1076 					//Conditional instruction extraction
1077 				case CharIniConditional:
1078 					prev_cond=cond;
1079 					cond=getConditional();
1080 
1081 					//Checks whether the extracted token is a valid conditional
1082 					if(cond!=TokenIf && cond!=TokenElse &&
1083 							cond!=TokenThen && cond!=TokenEnd &&
1084 							cond!=TokenOr && cond!=TokenNot &&
1085 							cond!=TokenAnd && cond!=TokenSet &&
1086 							cond!=TokenUnset)
1087 					{
1088 						throw Exception(Exception::getErrorMessage(ErrorCode::InvalidInstruction)
1089 										.arg(cond).arg(filename).arg(line + comment_count +1).arg(column+1),
1090 										ErrorCode::InvalidInstruction,__PRETTY_FUNCTION__,__FILE__,__LINE__);
1091 					}
1092 					else if(cond==TokenSet || cond==TokenUnset)
1093 					{
1094 						bool extract=false;
1095 
1096 						/* Extracts or unset the attribute only if the process is not in the middle of a 'if-then-else' or
1097 			   if the parser is inside the 'if' part and the expression is evaluated as true, or in the 'else' part
1098 			   and the related 'if' is false. Otherwise the line where %set is located will be completely ignored */
1099 						extract=(if_level < 0 || vet_expif.empty());
1100 
1101 						if(!extract && if_level >= 0)
1102 						{
1103 							//If in 'else' the related 'if' is false, extracts the attribute
1104 							if(prev_cond == TokenElse && !vet_expif[if_level])
1105 								extract=true;
1106 							else if(prev_cond != TokenElse)
1107 							{
1108 								//If in the 'if' part all the previous ifs until the current must be true
1109 								extract=true;
1110 								for(int i=0; i <= if_level && extract; i++)
1111 								{
1112 									extract=(vet_expif[i] && !vet_tk_else[i]) ||
1113 													(!vet_expif[i] && vet_tk_else[i]);
1114 								}
1115 							}
1116 						}
1117 
1118 						if(extract)
1119 						{
1120 							if(cond==TokenSet)
1121 								defineAttribute();
1122 							else
1123 								unsetAttribute();
1124 						}
1125 						else
1126 						{
1127 							column=0;
1128 							line++;
1129 						}
1130 					}
1131 					else
1132 					{
1133 						//If the toke is an 'if'
1134 						if(cond==TokenIf)
1135 						{
1136 							//Evaluates the if expression storing the result on the vector
1137 							if_expr=true;
1138 							vet_expif.push_back(evaluateExpression());
1139 
1140 							/* Inserts the value of the current 'if' level  in the previous 'if' levels vector.
1141 							 This vector is used to know which 'if' the parser was in before entering current if */
1142 							vet_prev_level.push_back(if_level);
1143 
1144 							/* Mark the flags indicating that an  'if' was found and a
1145 							 'then' and 'else' have not been found yet */
1146 							vet_tk_if.push_back(true);
1147 							vet_tk_then.push_back(false);
1148 							vet_tk_else.push_back(false);
1149 
1150 							//Defines the current if level as the size of list 'if' tokens found  -1
1151 							if_level=(vet_tk_if.size()-1);
1152 
1153 							//Increases the number of found 'if's
1154 							if_cnt++;
1155 						}
1156 						//If the parser is in 'if / else' and one 'then' token is found
1157 						else if(cond==TokenThen && if_level>=0)
1158 						{
1159 							//Marks the then token flag of the current 'if'
1160 							vet_tk_then[if_level]=true;
1161 
1162 							/* Clears the  expression extracted flag from the 'if - then',
1163 								 so that the parser does not generate an error when it encounters another
1164 								 'if - then' with an expression still unextracted */
1165 							if_expr=false;
1166 						}
1167 						//If the parser is in 'if / else' and a 'else' token is found
1168 						else if(cond==TokenElse && if_level>=0)
1169 							//Mark the  o flag do token else do if atual
1170 							vet_tk_else[if_level]=true;
1171 						//Case the parser is in 'if/else' and a 'end' token was found
1172 						else if(cond==TokenEnd && if_level>=0)
1173 						{
1174 							//Increments the number of 'end' tokes found
1175 							end_cnt++;
1176 
1177 							//Get the level of the previous 'if' where the parser was
1178 							prev_if_level=vet_prev_level[if_level];
1179 
1180 							//In case the current 'if' be internal (nested) (if_level > 0)
1181 							if(if_level > 0)
1182 							{
1183 								//In case the current 'if' doesn't in the 'else' section of the above 'if' (previous if level)
1184 								if(!vet_tk_else[prev_if_level])
1185 									//Get the extracted word vector on the above 'if'
1186 									vet_aux=&if_map[prev_if_level];
1187 								else
1188 									//Get the extracted word vector on the above 'else'
1189 									vet_aux=&else_map[prev_if_level];
1190 							}
1191 							else
1192 								vet_aux=nullptr;
1193 
1194 							/* Initializes the iterators to scan
1195 								 the auxiliary vector if necessary */
1196 							itr=itr_end=if_map[0].end();
1197 
1198 							/* In case the expression of the current 'if' has the value true
1199 								 then the parser will scan the list of words on the 'if' part of the
1200 								 current 'if' */
1201 							if(vet_expif[if_level])
1202 							{
1203 								itr=if_map[if_level].begin();
1204 								itr_end=if_map[if_level].end();
1205 							}
1206 
1207 							/* If there is a 'else' part on the current 'if'
1208 								 then the parser will scan the list of words on the 'else' part */
1209 							else if(else_map.count(if_level)>0)
1210 							{
1211 								itr=else_map[if_level].begin();
1212 								itr_end=else_map[if_level].end();
1213 							}
1214 
1215 							/* This iteration scans the list of words selected above
1216 								 inserting them in 'if' part  of the 'if' or 'else' superior to current.
1217 								 This is done so that only the words extracted based on
1218 								 ifs expressions of the buffer are embedded in defining sql */
1219 							while(itr!=itr_end)
1220 							{
1221 								//If the auxiliary vector is allocated, inserts the word on above 'if / else'
1222 								if(vet_aux)
1223 									vet_aux->push_back((*itr));
1224 								else
1225 								{
1226 									word=(*itr);
1227 
1228 									//Check if the word is not an attribute
1229 									if(!word.isEmpty() && word.startsWith(CharIniAttribute) && word.endsWith(CharEndAttribute))
1230 									{
1231 										//If its an attribute, extracts the name between { } and checks if the same has empty value
1232 										atrib=word.mid(1, word.size()-2);
1233 										word=attributes[atrib];
1234 
1235 										/* If the attribute has no value set and parser must not ignore empty values
1236 										raises an exception */
1237 										if(word.isEmpty() && !ignore_empty_atribs)
1238 										{
1239 											throw Exception(Exception::getErrorMessage(ErrorCode::UndefinedAttributeValue)
1240 															.arg(atrib).arg(filename).arg(line + comment_count +1).arg(column+1),
1241 															ErrorCode::UndefinedAttributeValue,__PRETTY_FUNCTION__,__FILE__,__LINE__);
1242 										}
1243 									}
1244 
1245 									//Else, insert the work directly on the object definition
1246 									object_def+=word;
1247 								}
1248 								itr++;
1249 							}
1250 
1251 							//Case the current if is nested (internal)
1252 							if(if_level > 0)
1253 								//Causes the parser to return to the earlier 'if'
1254 								if_level=prev_if_level;
1255 
1256 							/* In case the 'if' be the uppermost (level 0) indicates that all
1257 								 the if's  has already been checked, so the parser will clear the
1258 								 used auxiliary structures*/
1259 							else
1260 							{
1261 								if_map.clear();
1262 								else_map.clear();
1263 								vet_tk_if.clear();
1264 								vet_tk_then.clear();
1265 								vet_tk_else.clear();
1266 								vet_expif.clear();
1267 								vet_prev_level.clear();
1268 
1269 								//Resets the ifs levels
1270 								if_level=prev_if_level=-1;
1271 							}
1272 						}
1273 						else
1274 							error=true;
1275 
1276 						if(!error)
1277 						{
1278 							/* Verifying that the conditional words appear in a valid  order if not
1279 							 the parser generates an error. Correct order means IF before THEN,
1280 							 ELSE after IF and before END */
1281 							if((prev_cond==TokenIf && cond!=TokenThen) ||
1282 									(prev_cond==TokenElse && cond!=TokenIf && cond!=TokenEnd) ||
1283 									(prev_cond==TokenThen && cond==TokenThen))
1284 								error=true;
1285 						}
1286 
1287 						if(error)
1288 						{
1289 							throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
1290 											.arg(filename).arg(line + comment_count +1).arg(column+1),
1291 											ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
1292 						}
1293 					}
1294 				break;
1295 
1296 					//Extraction of pure text or simple words
1297 				default:
1298 					if(chr==CharIniPlainText ||
1299 							chr==CharEndPlainText)
1300 						word=getPureText();
1301 					else
1302 						word=getWord();
1303 
1304 					//Case the parser is in 'if/else'
1305 					if(if_level>=0)
1306 					{
1307 						/* In case the word/text be inside 'if' expression, the parser returns an error
1308 						 because only an attribute must be on the 'if' expression  */
1309 						if(vet_tk_if[if_level] && !vet_tk_then[if_level])
1310 						{
1311 							throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
1312 											.arg(filename).arg(line + comment_count +1).arg(column+1),
1313 											ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
1314 						}
1315 						//Case the parser is in 'if' section
1316 						else if(vet_tk_if[if_level] &&
1317 								vet_tk_then[if_level] &&
1318 								!vet_tk_else[if_level])
1319 							//Inserts the word on the words map extracted on 'if' section
1320 							if_map[if_level].push_back(word);
1321 						else if(vet_tk_else[if_level])
1322 							//Inserts the word on the words map extracted on 'else' section
1323 							else_map[if_level].push_back(word);
1324 					}
1325 					else
1326 						//Case the parser is not in 'if/else' concatenates the word/text directly on the object definition
1327 						object_def+=word;
1328 				break;
1329 			}
1330 		}
1331 
1332 		/* If has more 'if' toknes than  'end' tokens, this indicates that some 'if' in code
1333 		was not closed thus the parser returns an error */
1334 		if(if_cnt!=end_cnt)
1335 		{
1336 			throw Exception(Exception::getErrorMessage(ErrorCode::InvalidSyntax)
1337 							.arg(filename).arg(line + comment_count +1).arg(column+1),
1338 							ErrorCode::InvalidSyntax,__PRETTY_FUNCTION__,__FILE__,__LINE__);
1339 		}
1340 	}
1341 
1342 	restartParser();
1343 	ignore_unk_atribs=false;
1344 	ignore_empty_atribs=false;
1345 	return object_def;
1346 }
1347 
getCodeDefinition(const QString & filename,attribs_map & attribs)1348 QString SchemaParser::getCodeDefinition(const QString &filename, attribs_map &attribs)
1349 {
1350 	try
1351 	{
1352 		loadFile(filename);
1353 		attribs[Attributes::PgSqlVersion]=pgsql_version;
1354 		return getCodeDefinition(attribs);
1355 	}
1356 	catch(Exception &e)
1357 	{
1358 		throw Exception(e.getErrorMessage(),e.getErrorCode(),__PRETTY_FUNCTION__,__FILE__,__LINE__, &e);
1359 	}
1360 }
1361 
1362 template<typename Type>
getExpressionResult(const QString & oper,const QVariant & left_val,const QVariant & right_val)1363 bool SchemaParser::getExpressionResult(const QString &oper, const QVariant &left_val, const QVariant &right_val){
1364 	return ((oper==TokenEqOper && (left_val.value<Type>() == right_val.value<Type>())) ||
1365 					(oper==TokenNeOper && (left_val.value<Type>() != right_val.value<Type>())) ||
1366 					(oper==TokenGtOper && (left_val.value<Type>() > right_val.value<Type>())) ||
1367 					(oper==TokenLtOper && (left_val.value<Type>() < right_val.value<Type>())) ||
1368 					(oper==TokenGtEqOper && (left_val.value<Type>() >= right_val.value<Type>())) ||
1369 					(oper==TokenLtEqOper && (left_val.value<Type>() <= right_val.value<Type>())));
1370 }
1371