Line data Source code
1 : /*
2 : * Software License Agreement (BSD License)
3 : *
4 : * Copyright (c) 2006, ScalingWeb.com
5 : * All rights reserved.
6 : *
7 : * Redistribution and use of this software in source and binary forms, with or without modification, are
8 : * permitted provided that the following conditions are met:
9 : *
10 : * * Redistributions of source code must retain the above
11 : * copyright notice, this list of conditions and the
12 : * following disclaimer.
13 : *
14 : * * Redistributions in binary form must reproduce the above
15 : * copyright notice, this list of conditions and the
16 : * following disclaimer in the documentation and/or other
17 : * materials provided with the distribution.
18 : *
19 : * * Neither the name of ScalingWeb.com nor the names of its
20 : * contributors may be used to endorse or promote products
21 : * derived from this software without specific prior
22 : * written permission of ScalingWeb.com.
23 :
24 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
25 : * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
26 : * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
27 : * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
28 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 : * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
30 : * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 : * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 : */
33 :
34 : #include "MorkParser.hxx"
35 : #include <boost/io/ios_state.hpp>
36 : #include <stdlib.h>
37 : #include <sstream>
38 : #include <string>
39 : #include <string.h>
40 : #include <stdexcept>
41 : #include <fstream>
42 : #include <iostream>
43 : #include <algorithm>
44 :
45 1 : std::string g_Empty = "";
46 :
47 : // Mork header of supported format version
48 : const char *MorkMagicHeader = "// <!-- <mdb:mork:z v=\"1.4\"/> -->";
49 :
50 : const char *MorkDictColumnMeta = "<(a=c)>";
51 :
52 :
53 6 : MorkParser::MorkParser( int DefaultScope ) :
54 : columns_(),
55 : values_(),
56 : mork_(),
57 : currentCells_(0),
58 : error_(NoError),
59 : morkData_(),
60 : morkPos_(0),
61 : nextAddValueId_(0x7fffffff),
62 : defaultScope_(DefaultScope),
63 : defaultListScope_(0x81),
64 : defaultTableId_(1),
65 6 : nowParsing_(NPValues)
66 : {
67 6 : }
68 :
69 3 : bool MorkParser::open( const std::string &path )
70 : {
71 3 : initVars();
72 3 : std::string line;
73 6 : std::ifstream infile(path.c_str(), std::ios_base::in);
74 3 : if(!infile.is_open())
75 : {
76 0 : error_ = FailedToOpen;
77 0 : return false;
78 : }
79 :
80 483 : while (getline(infile, line, '\n'))
81 : {
82 477 : morkData_.append(line);
83 477 : morkData_.append("\n");
84 : }
85 :
86 : // Parse mork
87 6 : return parse();
88 : }
89 :
90 : inline MorkErrors MorkParser::error()
91 : {
92 : return error_;
93 : }
94 :
95 3 : void MorkParser::initVars()
96 : {
97 3 : error_ = NoError;
98 3 : morkPos_ = 0;
99 3 : nowParsing_ = NPValues;
100 3 : currentCells_ = 0;
101 3 : nextAddValueId_ = 0x7fffffff;
102 3 : }
103 :
104 3 : bool MorkParser::parse()
105 : {
106 3 : bool Result = true;
107 :
108 : // Run over mork chars and parse each term
109 3 : char cur = nextChar();
110 :
111 3 : int i = 0;
112 :
113 306 : while ( Result && cur )
114 : {
115 300 : if ( !isWhiteSpace( cur ) )
116 : {
117 132 : i++;
118 : // Figure out what a term
119 132 : switch ( cur )
120 : {
121 : case '<':
122 : // Dict
123 24 : Result = parseDict();
124 24 : break;
125 : case '/':
126 : // Comment
127 3 : Result = parseComment();
128 3 : break;
129 : case '{':
130 6 : Result = parseTable();
131 : // Table
132 6 : break;
133 : case '[':
134 33 : Result = parseRow( 0, 0 );
135 : // Row
136 33 : break;
137 : case '@':
138 66 : Result = parseGroup();
139 : // Group
140 66 : break;
141 : default:
142 0 : error_ = DefectedFormat;
143 0 : Result = false;
144 0 : break;
145 : }
146 : }
147 :
148 : // Get next char
149 300 : cur = nextChar();
150 : }
151 :
152 3 : return Result;
153 : }
154 :
155 4368 : bool MorkParser::isWhiteSpace( char c )
156 : {
157 4368 : switch ( c )
158 : {
159 : case ' ':
160 : case '\t':
161 : case '\r':
162 : case '\n':
163 : case '\f':
164 1425 : return true;
165 : default:
166 2943 : return false;
167 : }
168 : }
169 :
170 21927 : inline char MorkParser::nextChar()
171 : {
172 21927 : char cur = 0;
173 :
174 :
175 21927 : if ( morkPos_ < morkData_.length() )
176 : {
177 21924 : cur = morkData_[ morkPos_ ];
178 21924 : morkPos_++;
179 : }
180 :
181 21927 : if ( !cur )
182 : {
183 3 : cur = 0;
184 : }
185 :
186 21927 : return cur;
187 : }
188 :
189 24 : bool MorkParser::parseDict()
190 : {
191 24 : char cur = nextChar();
192 24 : bool Result = true;
193 24 : nowParsing_ = NPValues;
194 :
195 813 : while ( Result && cur != '>' && cur )
196 : {
197 765 : if ( !isWhiteSpace( cur ) )
198 : {
199 441 : switch ( cur )
200 : {
201 : case '<':
202 : {
203 :
204 18 : if ( morkData_.substr( morkPos_ - 1, strlen( MorkDictColumnMeta ) ) == MorkDictColumnMeta )
205 : {
206 18 : nowParsing_ = NPColumns;
207 18 : morkPos_ += strlen( MorkDictColumnMeta ) - 1;
208 : }
209 :
210 :
211 18 : break;
212 : }
213 : case '(':
214 405 : Result = parseCell();
215 405 : break;
216 : case '/':
217 18 : Result = parseComment();
218 18 : break;
219 :
220 : }
221 : }
222 :
223 765 : cur = nextChar();
224 : }
225 :
226 24 : return Result;
227 : }
228 :
229 21 : inline bool MorkParser::parseComment()
230 : {
231 21 : char cur = nextChar();
232 21 : if ( '/' != cur ) return false;
233 :
234 426 : while ( cur != '\r' && cur != '\n' && cur )
235 : {
236 384 : cur = nextChar();
237 : }
238 :
239 21 : return true;
240 : }
241 :
242 2427 : bool MorkParser::parseCell()
243 : {
244 2427 : bool Result = true;
245 2427 : bool bValueOid = false;
246 2427 : bool bColumn = true;
247 2427 : int Corners = 0;
248 :
249 : // Column = Value
250 2427 : std::string Column;
251 4854 : std::string Text;
252 2427 : Column.reserve( 4 );
253 2427 : Text.reserve( 32 );
254 :
255 2427 : char cur = nextChar();
256 :
257 : // Process cell start with column (bColumn == true)
258 18972 : while ( Result && cur != ')' && cur )
259 : {
260 14118 : switch ( cur )
261 : {
262 : case '^':
263 : // Oids
264 2274 : Corners++;
265 2274 : if ( 1 == Corners )
266 : {
267 : }
268 252 : else if ( 2 == Corners )
269 : {
270 252 : bColumn = false;
271 252 : bValueOid = true;
272 : }
273 : else
274 : {
275 0 : Text += cur;
276 : }
277 :
278 2274 : break;
279 : case '=':
280 : // From column to value
281 2175 : if ( bColumn )
282 : {
283 2175 : bColumn = false;
284 : }
285 : else
286 : {
287 0 : Text += cur;
288 : }
289 2175 : break;
290 : case '\\':
291 : {
292 : // Get next two chars
293 0 : char NextChar= nextChar();
294 0 : if ( '\r' != NextChar && '\n' != NextChar )
295 : {
296 0 : Text += NextChar;
297 : }
298 0 : else nextChar();
299 : }
300 0 : break;
301 : case '$':
302 : {
303 : // Get next two chars
304 0 : std::string HexChar;
305 0 : HexChar += nextChar();
306 0 : HexChar += nextChar();
307 0 : Text += (char)strtoul(HexChar.c_str(), 0, 16);
308 : }
309 0 : break;
310 : default:
311 : // Just a char
312 9669 : if ( bColumn )
313 : {
314 4869 : Column += cur;
315 : }
316 : else
317 : {
318 4800 : Text += cur;
319 : }
320 9669 : break;
321 : }
322 :
323 14118 : cur = nextChar();
324 : }
325 :
326 : // Apply column and text
327 2427 : int ColumnId = strtoul(Column.c_str(), 0, 16);
328 :
329 2427 : if ( NPRows != nowParsing_ )
330 : {
331 : // Dicts
332 405 : if ( "" != Text )
333 : {
334 402 : if ( nowParsing_ == NPColumns )
335 : {
336 252 : columns_[ ColumnId ] = Text;
337 : }
338 : else
339 : {
340 150 : values_[ ColumnId ] = Text;
341 : }
342 : }
343 : }
344 : else
345 : {
346 2022 : if ( "" != Text )
347 : {
348 : // Rows
349 : //int ValueId = string( Text.c_str() ).toInt( 0, 16 );
350 630 : int ValueId = strtoul(Text.c_str(), 0, 16);
351 :
352 630 : if ( bValueOid )
353 : {
354 252 : ( *currentCells_ )[ ColumnId ] = ValueId;
355 : }
356 : else
357 : {
358 378 : nextAddValueId_--;
359 378 : values_[ nextAddValueId_ ] = Text;
360 378 : ( *currentCells_ )[ ColumnId ] = nextAddValueId_;
361 : }
362 : }
363 : }
364 :
365 4854 : return Result;
366 : }
367 :
368 6 : bool MorkParser::parseTable()
369 : {
370 6 : bool Result = true;
371 6 : std::string TextId;
372 6 : int Id = 0, Scope = 0;
373 :
374 6 : char cur = nextChar();
375 :
376 : // Get id
377 48 : while ( cur != '{' && cur != '[' && cur != '}' && cur )
378 : {
379 36 : if ( !isWhiteSpace( cur ) )
380 : {
381 30 : TextId += cur;
382 : }
383 :
384 36 : cur = nextChar();
385 : }
386 :
387 6 : parseScopeId( TextId, &Id, &Scope );
388 :
389 : // Parse the table
390 174 : while ( Result && cur != '}' && cur )
391 : {
392 162 : if ( !isWhiteSpace( cur ) )
393 : {
394 45 : switch ( cur )
395 : {
396 : case '{':
397 6 : Result = parseMeta( '}' );
398 6 : break;
399 : case '[':
400 39 : Result = parseRow( Id, Scope );
401 39 : break;
402 : case '-':
403 : case '+':
404 0 : break;
405 : default:
406 : {
407 0 : std::string JustId;
408 0 : while ( !isWhiteSpace( cur ) && cur )
409 : {
410 0 : JustId += cur;
411 0 : cur = nextChar();
412 :
413 0 : if ( cur == '}' )
414 : {
415 0 : return Result;
416 : }
417 : }
418 :
419 0 : int JustIdNum = 0, JustScopeNum = 0;
420 0 : parseScopeId( JustId, &JustIdNum, &JustScopeNum );
421 :
422 0 : setCurrentRow( Scope, Id, JustScopeNum, JustIdNum );
423 : }
424 0 : break;
425 : }
426 : }
427 :
428 162 : cur = nextChar();
429 : }
430 :
431 6 : return Result;
432 : }
433 :
434 78 : void MorkParser::parseScopeId( const std::string &TextId, int *Id, int *Scope )
435 : {
436 78 : int Pos = 0;
437 :
438 78 : if ( ( Pos = TextId.find( ':' ) ) >= 0 )
439 : {
440 48 : std::string tId = TextId.substr( 0, Pos );
441 96 : std::string tSc = TextId.substr( Pos + 1, TextId.length() - Pos );
442 :
443 48 : if ( tSc.length() > 1 && '^' == tSc[ 0 ] )
444 : {
445 : // Delete '^'
446 48 : tSc.erase( 0, 1 );
447 : }
448 :
449 48 : *Id = strtoul(tId.c_str(), 0, 16);
450 :
451 96 : *Scope = strtoul(tSc.c_str(), 0, 16);
452 : }
453 : else
454 : {
455 30 : *Id = strtoul(TextId.c_str(), 0, 16);
456 : }
457 78 : }
458 :
459 72 : inline void MorkParser::setCurrentRow( int TableScope, int TableId, int RowScope, int RowId )
460 : {
461 72 : if ( !RowScope )
462 : {
463 30 : RowScope = defaultScope_;
464 : }
465 :
466 72 : if ( !TableScope )
467 : {
468 33 : TableScope = defaultScope_;
469 : }
470 :
471 : // 01.08.2012 davido
472 : // TableId 0 is wrong here.
473 : // Straying rows (rows that defined outside the table) belong to the default scope and table is the last was seen: 1:^80
474 : // (at least i read so the specification)
475 72 : if (TableId)
476 : {
477 39 : defaultTableId_ = TableId;
478 : }
479 :
480 72 : if (!TableId)
481 : {
482 33 : TableId = defaultTableId_;
483 : }
484 :
485 72 : currentCells_ = &( mork_[ abs( TableScope ) ][ abs( TableId ) ][ abs( RowScope ) ][ abs( RowId ) ] );
486 72 : }
487 :
488 72 : bool MorkParser::parseRow( int TableId, int TableScope )
489 : {
490 72 : bool Result = true;
491 72 : std::string TextId;
492 72 : int Id = 0, Scope = 0;
493 72 : nowParsing_ = NPRows;
494 :
495 72 : char cur = nextChar();
496 :
497 : // Get id
498 417 : while ( cur != '(' && cur != ']' && cur != '[' && cur )
499 : {
500 273 : if ( !isWhiteSpace( cur ) )
501 : {
502 273 : TextId += cur;
503 : }
504 :
505 273 : cur = nextChar();
506 : }
507 :
508 72 : parseScopeId( TextId, &Id, &Scope );
509 72 : setCurrentRow( TableScope, TableId, Scope, Id );
510 :
511 : // Parse the row
512 2976 : while ( Result && cur != ']' && cur )
513 : {
514 2832 : if ( !isWhiteSpace( cur ) )
515 : {
516 2022 : switch ( cur )
517 : {
518 : case '(':
519 2022 : Result = parseCell();
520 2022 : break;
521 : case '[':
522 0 : Result = parseMeta( ']' );
523 0 : break;
524 : default:
525 0 : Result = false;
526 0 : break;
527 : }
528 : }
529 :
530 2832 : cur = nextChar();
531 : }
532 :
533 72 : return Result;
534 : }
535 :
536 66 : bool MorkParser::parseGroup()
537 : {
538 66 : return parseMeta( '@' );
539 : }
540 :
541 72 : bool MorkParser::parseMeta( char c )
542 : {
543 72 : char cur = nextChar();
544 :
545 576 : while ( cur != c && cur )
546 : {
547 432 : cur = nextChar();
548 : }
549 :
550 72 : return true;
551 : }
552 :
553 87 : MorkTableMap *MorkParser::getTables( int TableScope )
554 : {
555 87 : TableScopeMap::iterator iter;
556 87 : iter = mork_.find( TableScope );
557 :
558 87 : if ( iter == mork_.end() )
559 : {
560 0 : return 0;
561 : }
562 :
563 87 : return &iter->second;
564 : }
565 :
566 84 : MorkRowMap *MorkParser::getRows( int RowScope, RowScopeMap *table )
567 : {
568 84 : RowScopeMap::iterator iter;
569 84 : iter = table->find( RowScope );
570 :
571 84 : if ( iter == table->end() )
572 : {
573 0 : return 0;
574 : }
575 :
576 84 : return &iter->second;
577 : }
578 :
579 364 : std::string &MorkParser::getValue( int oid )
580 : {
581 364 : MorkDict::iterator foundIter = values_.find( oid );
582 :
583 364 : if ( values_.end() == foundIter )
584 : {
585 0 : return g_Empty;
586 : }
587 :
588 364 : return foundIter->second;
589 : }
590 :
591 195 : std::string &MorkParser::getColumn( int oid )
592 : {
593 195 : MorkDict::iterator foundIter = columns_.find( oid );
594 :
595 195 : if ( columns_.end() == foundIter )
596 : {
597 0 : return g_Empty;
598 : }
599 :
600 195 : return foundIter->second;
601 : }
602 :
603 81 : void MorkParser::retrieveLists(std::set<std::string>& lists)
604 : {
605 : #ifdef VERBOSE
606 : boost::io::ios_all_saver ias(std::cout);
607 : std::cout << std::hex << std::uppercase;
608 : #endif
609 :
610 81 : MorkTableMap* tables = getTables(defaultScope_);
611 81 : if (!tables) return;
612 486 : for (MorkTableMap::iterator TableIter = tables->begin();
613 324 : TableIter != tables->end(); ++TableIter )
614 : {
615 : #ifdef VERBOSE
616 : std::cout << "\t Table:"
617 : << ( ( int ) TableIter->first < 0 ? "-" : " " )
618 : << TableIter->first << std::endl;
619 : #endif
620 81 : MorkRowMap* rows = getRows( defaultListScope_, &TableIter->second );
621 81 : if (!rows) return;
622 729 : for ( MorkRowMap::iterator RowIter = rows->begin();
623 486 : RowIter != rows->end(); ++RowIter )
624 : {
625 : #ifdef VERBOSE
626 : std::cout << "\t\t\t Row Id:"
627 : << ( ( int ) RowIter->first < 0 ? "-" : " ")
628 : << RowIter->first << std::endl;
629 : std::cout << "\t\t\t\t Cells:\r\n";
630 : #endif
631 : // Get cells
632 1944 : for ( MorkCells::iterator cellsIter = RowIter->second.begin();
633 1296 : cellsIter != RowIter->second.end(); ++cellsIter )
634 : {
635 648 : if (cellsIter->first == 0xC1)
636 : {
637 162 : lists.insert(getValue( cellsIter->second ));
638 162 : break;
639 : }
640 : }
641 : }
642 : }
643 : }
644 :
645 1 : void MorkParser::getRecordKeysForListTable(std::string& listName, std::set<int>& records)
646 : {
647 : #ifdef VERBOSE
648 : boost::io::ios_all_saver ias(std::cout);
649 : std::cout << std::hex << std::uppercase;
650 : #endif
651 :
652 1 : MorkTableMap* tables = getTables(defaultScope_);
653 1 : if (!tables) return;
654 6 : for (MorkTableMap::iterator TableIter = tables->begin();
655 4 : TableIter != tables->end(); ++TableIter )
656 : {
657 : #ifdef VERBOSE
658 : std::cout << "\t Table:"
659 : << ( ( int ) TableIter->first < 0 ? "-" : " " )
660 : << TableIter->first << std::endl;
661 : #endif
662 1 : MorkRowMap* rows = getRows( 0x81, &TableIter->second );
663 1 : if (!rows) return;
664 9 : for ( MorkRowMap::iterator RowIter = rows->begin();
665 6 : RowIter != rows->end(); ++RowIter )
666 : {
667 : #ifdef VERBOSE
668 : std::cout << "\t\t\t Row Id:"
669 : << ( ( int ) RowIter->first < 0 ? "-" : " ")
670 : << RowIter->first << std::endl;
671 : std::cout << "\t\t\t\t Cells:\r\n";
672 : #endif
673 : // Get cells
674 2 : bool listFound = false;
675 60 : for ( MorkCells::iterator cellsIter = RowIter->second.begin();
676 40 : cellsIter != RowIter->second.end(); ++cellsIter )
677 : {
678 18 : if (listFound)
679 : {
680 5 : if (cellsIter->first >= 0xC7)
681 : {
682 5 : std::string value = getValue(cellsIter->second);
683 5 : int id = strtoul(value.c_str(), 0, 16);
684 5 : records.insert(id);
685 : }
686 : }
687 15 : else if ((cellsIter->first == 0xC1) &&
688 2 : listName == getValue( cellsIter->second ))
689 : {
690 1 : listFound = true;
691 : }
692 : }
693 :
694 : }
695 : }
696 : }
697 :
698 0 : void MorkParser::dump()
699 : {
700 0 : boost::io::ios_all_saver ias(std::cout);
701 0 : std::cout << std::hex << std::uppercase;
702 :
703 0 : std::cout << "Column Dict:\r\n";
704 0 : std::cout << "=============================================\r\n\r\n";
705 :
706 : //// columns dict
707 0 : for ( MorkDict::iterator iter = columns_.begin();
708 0 : iter != columns_.end(); ++iter )
709 : {
710 0 : std::cout << iter->first
711 0 : << " : "
712 0 : << iter->second
713 0 : << std::endl;
714 : }
715 :
716 : //// values dict
717 0 : std::cout << "\r\nValues Dict:\r\n";
718 0 : std::cout << "=============================================\r\n\r\n";
719 :
720 0 : for ( MorkDict::iterator iter = values_.begin();
721 0 : iter != values_.end(); ++iter )
722 : {
723 0 : if (iter->first >= nextAddValueId_) {
724 0 : continue;
725 : }
726 :
727 0 : std::cout << iter->first
728 0 : << " : "
729 0 : << iter->second
730 0 : << "\r\n";
731 : }
732 :
733 0 : std::cout << std::endl << "Data:" << std::endl;
734 0 : std::cout << "============================================="
735 0 : << std::endl << std::endl;
736 :
737 : //// Mork data
738 0 : for ( TableScopeMap::iterator iter = mork_.begin();
739 0 : iter != mork_.end(); ++iter )
740 : {
741 0 : std::cout << "\r\n Scope:" << iter->first << std::endl;
742 :
743 0 : for ( MorkTableMap::iterator TableIter = iter->second.begin();
744 0 : TableIter != iter->second.end(); ++TableIter )
745 : {
746 0 : std::cout << "\t Table:"
747 0 : << ( ( int ) TableIter->first < 0 ? "-" : " " )
748 0 : << TableIter->first << std::endl;
749 :
750 0 : for (RowScopeMap::iterator RowScopeIter = TableIter->second.begin();
751 0 : RowScopeIter != TableIter->second.end(); ++RowScopeIter )
752 : {
753 0 : std::cout << "\t\t RowScope:"
754 0 : << RowScopeIter->first << std::endl;
755 :
756 0 : for (MorkRowMap::iterator RowIter = RowScopeIter->second.begin();
757 0 : RowIter != RowScopeIter->second.end(); ++RowIter )
758 : {
759 0 : std::cout << "\t\t\t Row Id:"
760 0 : << ((int) RowIter->first < 0 ? "-" : " ")
761 0 : << RowIter->first << std::endl;
762 0 : std::cout << "\t\t\t\t Cells:" << std::endl;
763 :
764 0 : for (MorkCells::iterator CellsIter = RowIter->second.begin();
765 0 : CellsIter != RowIter->second.end(); ++CellsIter )
766 : {
767 : // Write ids
768 0 : std::cout << "\t\t\t\t\t"
769 0 : << CellsIter->first
770 0 : << " : "
771 0 : << CellsIter->second
772 0 : << " => ";
773 :
774 0 : MorkDict::iterator FoundIter = values_.find( CellsIter->second );
775 0 : if ( FoundIter != values_.end() )
776 : {
777 : // Write string values
778 0 : std::cout << columns_[ CellsIter->first ].c_str()
779 0 : << " : "
780 0 : << FoundIter->second.c_str()
781 0 : << std::endl;
782 : }
783 : }
784 : }
785 : }
786 : }
787 0 : }
788 3 : }
|