PageRenderTime 44ms CodeModel.GetById 19ms app.highlight 13ms RepoModel.GetById 2ms app.codeStats 0ms

/src/lib/xml/dtd/xml_dtd_validator.e

http://github.com/tybor/Liberty
Specman e | 807 lines | 660 code | 93 blank | 54 comment | 23 complexity | 300b3a11ce3ac6e7217ef6e4e43b2fcd MD5 | raw file
  1-- See the Copyright notice at the end of this file.
  2--
  3class XML_DTD_VALIDATOR
  4   --
  5   -- Helps the parser to validate an XML file using a DTD
  6   --
  7
  8inherit
  9   XML_VALIDATOR
 10   BACKTRACKING
 11      rename
 12         current_node as backtrack_node,
 13         set_current_node as set_backtrack_node
 14      end
 15
 16insert
 17   XML_DTD_MEMORY
 18   RECYCLABLE
 19
 20create {XML_DTD_MEMORY}
 21   make
 22
 23feature {XML_PARSER}
 24   attributes: HASHED_DICTIONARY[UNICODE_STRING, UNICODE_STRING]
 25
 26   root: XML_DTD_NODE
 27
 28   point: XML_DTD_NODE
 29
 30   with_attribute (attribute_name, attribute_value: UNICODE_STRING; line, column: INTEGER)
 31      do
 32         attributes.add(new_string(attribute_value), new_string(attribute_name))
 33      end
 34
 35   is_valid_open_node (node_name: UNICODE_STRING; line, column: INTEGER): BOOLEAN
 36      do
 37         if point = Void then
 38            Result := root_element.name.is_equal(node_name)
 39         else
 40            Result := point.is_valid_child(Current, node_name)
 41         end
 42         if Result then
 43            Result := get_element(node_name).is_valid_attributes(attributes)
 44         end
 45      end
 46
 47   is_valid_close_node (node_name: UNICODE_STRING; line, column: INTEGER): BOOLEAN
 48      do
 49         if point = Void then
 50            -- not valid!
 51         else
 52            Result := point.name.is_equal(node_name)
 53               and then point.is_valid_child(Current, Void)
 54         end
 55      end
 56
 57   is_valid_open_close_node (node_name: UNICODE_STRING; line, column: INTEGER): BOOLEAN
 58      do
 59         Result := is_valid_open_node(node_name, line, column)
 60      end
 61
 62   current_node: UNICODE_STRING
 63      do
 64         if point /= Void then
 65            Result := point.name
 66         end
 67      end
 68
 69   open_node (node_name: UNICODE_STRING; line, column: INTEGER)
 70      local
 71         e: XML_DTD_ELEMENT
 72      do
 73         clear_attributes
 74         e := elements.at(node_name)
 75         point := new_node(e, point)
 76         if root = Void then
 77            root := point
 78         end
 79      end
 80
 81   close_node (node_name: UNICODE_STRING; line, column: INTEGER)
 82      local
 83         p: like point
 84      do
 85         p := point.parent
 86         if p = Void then
 87            check
 88               root = point
 89            end
 90            free_node(root)
 91            root := Void
 92         end
 93         point := p
 94      end
 95
 96   open_close_node (node_name: UNICODE_STRING; line, column: INTEGER)
 97      do
 98         open_node(node_name, line, column)
 99         close_node(node_name, line, column)
100      end
101
102   entity (a_entity: UNICODE_STRING; line, column: INTEGER): UNICODE_STRING
103      do
104         Result := entities.reference_at(a_entity)
105      end
106
107   entity_url (a_entity: UNICODE_STRING; line, column: INTEGER): UNICODE_STRING
108         -- When the parser reads an '''&entity;'''. Returns the entity URL if it is a SYSTEM entity.
109      do
110         Result := entity_urls.reference_at(a_entity)
111      end
112
113   is_valid_data (a_data: UNICODE_STRING; line, column: INTEGER): BOOLEAN
114      do
115         if point /= Void then
116            Result := point.is_valid_data(Current, a_data)
117         end
118      end
119
120   data (a_data: UNICODE_STRING; line, column: INTEGER)
121      do
122         -- DTD does not validate data, so we don't keep it
123      end
124
125   the_end
126      do
127         recycle_dtd_validator(Current)
128      end
129
130feature {} -- Nodes management, for validation
131   nodes_pool: RECYCLING_POOL[XML_DTD_NODE]
132      once
133         create Result.make
134      end
135
136   new_node (element: XML_DTD_ELEMENT; parent: XML_DTD_NODE): XML_DTD_NODE
137      do
138         if nodes_pool.is_empty then
139            create Result.make
140         else
141            Result := nodes_pool.item
142         end
143         check
144            Result.parent = Void
145         end
146         Result.element := element
147         if parent /= Void then
148            Result.parent := parent
149         end
150      ensure
151         Result.is_empty
152         Result.element = element
153         Result.parent = parent
154      end
155
156   free_node (a_node: XML_DTD_NODE)
157      local
158         node: XML_DTD_NODE
159      do
160         from
161         until
162            a_node.is_empty
163         loop
164            node := a_node.last
165            free_node(node)
166            check
167               not a_node.fast_has(node)
168            end
169         end
170         nodes_pool.recycle(a_node)
171      end
172
173feature {} -- Attributes string management
174   clear_attributes
175      local
176         key, item: UNICODE_STRING
177      do
178         from
179         until
180            attributes.is_empty
181         loop
182            key := attributes.key(attributes.lower)
183            item := attributes.item(attributes.lower)
184            attributes.fast_remove(key)
185            free_string(key)
186            free_string(item)
187         end
188      end
189
190feature {XML_DTD_PARSER}
191   parse_done
192         -- Called when done parsing the DTD
193      do
194         debug
195            io.put_string(root_element.out)
196            io.put_new_line
197         end
198      end
199
200feature {XML_DTD_PARSER} -- <!ELEMENT . . .>
201   elements: HASHED_DICTIONARY[XML_DTD_ELEMENT, UNICODE_STRING]
202
203   current_element: XML_DTD_ELEMENT
204
205   element_built (element_name: UNICODE_STRING): BOOLEAN
206      do
207         Result := get_element(element_name).is_built
208      end
209
210   adding_element (element_name: UNICODE_STRING): BOOLEAN
211      do
212         Result := current_element /= Void and then current_element.name.is_equal(element_name)
213      ensure
214         Result implies not element_built(element_name)
215         Result implies building_element
216      end
217
218   building_element: BOOLEAN
219      do
220         Result := current_element /= Void
221      end
222
223   add_element (element_name: UNICODE_STRING)
224      require
225         not element_built(element_name)
226         not building_element
227      do
228         current_element := get_element(element_name)
229         current_element.build
230      ensure
231         adding_element(element_name)
232      end
233
234   commit_element (element_name: UNICODE_STRING)
235      require
236         adding_element(element_name)
237      do
238         current_element.commit
239         current_element := Void
240      ensure
241         element_built(element_name)
242         not building_element
243      end
244
245   close_fix
246      require
247         building_element
248      do
249         current_element.close_fix
250      end
251
252   close_exactly_one
253      require
254         building_element
255      do
256         current_element.close_exactly_one
257      end
258
259   close_zero_or_one
260      require
261         building_element
262      do
263         current_element.close_zero_or_one
264      end
265
266   close_zero_or_more
267      require
268         building_element
269      do
270         current_element.close_zero_or_more
271      end
272
273   close_one_or_more
274      require
275         building_element
276      do
277         current_element.close_one_or_more
278      end
279
280   add_list
281      require
282         building_element
283      do
284         current_element.add_list
285      end
286
287   add_alt
288      require
289         building_element
290      do
291         current_element.add_alt
292      end
293
294   child_pcdata
295      require
296         building_element
297      do
298         current_element.child_pcdata
299      end
300
301   child_any
302      require
303         building_element
304      do
305         current_element.child_any
306      end
307
308   child_empty
309      require
310         building_element
311      do
312         current_element.child_empty
313      end
314
315   child_one_or_more (node: UNICODE_STRING)
316      require
317         building_element
318      do
319         current_element.child_one_or_more(get_element(node))
320      end
321
322   child_zero_or_more (node: UNICODE_STRING)
323      require
324         building_element
325      do
326         current_element.child_zero_or_more(get_element(node))
327      end
328
329   child_zero_or_one (node: UNICODE_STRING)
330      require
331         building_element
332      do
333         current_element.child_zero_or_one(get_element(node))
334      end
335
336   child_exactly_one (node: UNICODE_STRING)
337      require
338         building_element
339      do
340         current_element.child_exactly_one(get_element(node))
341      end
342
343feature {}
344   root_element: XML_DTD_ELEMENT
345      do
346         Result := get_element(root_name)
347      end
348
349   get_element (element_name: UNICODE_STRING): XML_DTD_ELEMENT
350      local
351         eltname: UNICODE_STRING
352      do
353         Result := elements.reference_at(element_name)
354         if Result = Void then
355            eltname := element_name.twin
356            if elements_pool.is_empty then
357               create Result.make(eltname)
358            else
359               Result := elements_pool.item
360               Result.make(eltname)
361            end
362            elements.add(Result, eltname)
363         end
364      ensure
365         Result.name.is_equal(element_name)
366      end
367
368   elements_pool: RECYCLING_POOL[XML_DTD_ELEMENT]
369      once
370         create Result.make
371      end
372
373feature {XML_DTD_PARSER} -- <!ATTLIST . . .>
374   attlist_element: XML_DTD_ELEMENT
375
376   building_attlist: BOOLEAN
377      do
378         Result := attlist_element /= Void
379         check
380            Result implies attlist_element.building_attlist
381         end
382      end
383
384   has_attlist (element_name, attribute_name: UNICODE_STRING): BOOLEAN
385      local
386         elt: XML_DTD_ELEMENT
387      do
388         elt := elements.reference_at(element_name)
389         if elt /= Void then
390            Result := elt.has_attlist(attribute_name)
391         end
392      ensure
393         Result implies building_attlist
394      end
395
396   adding_attlist (element_name, attribute_name: UNICODE_STRING): BOOLEAN
397      do
398         Result := attlist_element /= Void and then attlist_element.name.is_equal(element_name) and then attlist_element.adding_attlist(attribute_name)
399      ensure
400         Result implies building_attlist
401      end
402
403   add_attlist (element_name, attribute_name: UNICODE_STRING)
404      require
405         element_built(element_name)
406         not building_element
407         not building_attlist
408      do
409         attlist_element := elements.at(element_name)
410         attlist_element.add_attlist(attribute_name)
411      ensure
412         building_attlist
413      end
414
415   commit_attlist (element_name, attribute_name: UNICODE_STRING)
416      require
417         adding_attlist(element_name, attribute_name)
418      do
419         attlist_element.commit_attlist(attribute_name)
420         attlist_element := Void
421      ensure
422         not building_attlist
423      end
424
425   addlist_list_value (value: UNICODE_STRING)
426      require
427         building_attlist
428      do
429         attlist_element.attlist_list_value(value)
430      end
431
432   attlist_cdata
433      require
434         building_attlist
435      do
436         attlist_element.attlist_cdata
437      end
438
439   attlist_id
440      require
441         building_attlist
442      do
443         attlist_element.attlist_id
444      end
445
446   attlist_idref
447      require
448         building_attlist
449      do
450         attlist_element.attlist_idref
451      end
452
453   attlist_idrefs
454      require
455         building_attlist
456      do
457         attlist_element.attlist_idrefs
458      end
459
460   attlist_nmtoken
461      require
462         building_attlist
463      do
464         attlist_element.attlist_nmtoken
465      end
466
467   attlist_nmtokens
468      require
469         building_attlist
470      do
471         attlist_element.attlist_nmtokens
472      end
473
474   attlist_entity
475      require
476         building_attlist
477      do
478         attlist_element.attlist_entity
479      end
480
481   attlist_entities
482      require
483         building_attlist
484      do
485         attlist_element.attlist_entities
486      end
487
488   attlist_notation
489      require
490         building_attlist
491      do
492         attlist_element.attlist_notation
493      end
494
495   attlist_required
496      require
497         building_attlist
498      do
499         attlist_element.attlist_required
500      end
501
502   attlist_implied
503      require
504         building_attlist
505      do
506         attlist_element.attlist_implied
507      end
508
509   attlist_valid_fixed (value: UNICODE_STRING): BOOLEAN
510      require
511         building_attlist
512      do
513         Result := attlist_element.attlist_valid_fixed(value)
514      end
515
516   attlist_fixed (value: UNICODE_STRING)
517      require
518         building_attlist
519         attlist_valid_fixed(value)
520      do
521         attlist_element.attlist_fixed(value)
522      end
523
524   attlist_default_value (value: UNICODE_STRING)
525      require
526         building_attlist
527      do
528         attlist_element.attlist_default_value(value)
529      end
530
531feature {XML_DTD_PARSER} -- <!ENTITY . . .>
532   entities: HASHED_DICTIONARY[UNICODE_STRING, UNICODE_STRING]
533   entity_urls: HASHED_DICTIONARY[UNICODE_STRING, UNICODE_STRING]
534
535   has_entity (entity_name: UNICODE_STRING): BOOLEAN
536      do
537         Result := entities.has(entity_name)
538      end
539
540   add_entity (entity_name, entity_value, entity__url: UNICODE_STRING)
541      require
542         not has_entity(entity_name)
543      do
544         entities.add(entity_value, entity_name)
545         if entity__url /= Void then
546            entity_urls.add(entity__url, entity_name)
547         end
548      ensure
549         has_entity(entity_name)
550         entity(entity_name, 0, 0) = entity_value
551         entity_url(entity_name, 0, 0) = entity__url
552      end
553
554feature {XML_DTD_ELEMENT}
555   backtrack_valid_data (a_children: FAST_ARRAY[XML_DTD_NODE]; a_node: like backtrack_node; a_data: like backtrack_pcdata): BOOLEAN
556      require
557         a_node /= Void
558      do
559         --| std_output.put_line("backtrack_valid_data: #(1) -> #(2) / #(3)" # a_node.out # a_children.out # (if a_data = Void then "" else a_data.out end))
560         set_backtrack_node(a_node)
561         backtrack_pcdata := a_data
562         backtrack_children := a_children
563         backtrack_index := a_children.lower
564         backtrack_next := backtrack_next_pcdata_marker
565         backtrack_ok := False
566         search_first
567         Result := backtrack_ok
568         if not Result then
569            sedb_breakpoint
570         end
571      end
572
573   backtrack_is_valid (a_children: like backtrack_children; a_node: like backtrack_node; a_next: like backtrack_next): BOOLEAN
574      require
575         a_node /= Void
576         --| `a_next' can be Void, it means we are trying to close the node
577      do
578         --| std_output.put_line("backtrack_is_valid: #(1) -> #(2) / #(3)" # a_node.out # a_children.out # (if a_next = Void then "<none>" else a_next.out end))
579         set_backtrack_node(a_node)
580         backtrack_pcdata := Void
581         backtrack_children := a_children
582         backtrack_index := a_children.lower
583         backtrack_next := a_next
584         backtrack_ok := False
585         search_first
586         Result := backtrack_ok
587         if not Result then
588            sedb_breakpoint
589         end
590      end
591
592feature {XML_DTD_ELEMENT}
593   backtrack_valid_child (elt: XML_DTD_ELEMENT)
594      require
595         elt /= Void
596      do
597         --| std_output.put_line("    backtrack_valid_child: #(1)" # elt.out)
598         if backtrack_next = backtrack_next_pcdata_marker then
599            --| std_output.put_line("        backtrack, expected PCDATA")
600            backtrack
601         elseif backtrack_children.valid_index(backtrack_index) then
602            if elt = backtrack_children.item(backtrack_index).element then
603               backtrack_index := backtrack_index + 1
604               --| std_output.put_line("        continue")
605               continue
606            else
607               --| std_output.put_line("        backtrack, actual #(1) but expected #(2)" # elt.out # backtrack_children.item(backtrack_index).element.out)
608               backtrack
609            end
610         else
611            if backtrack_next = Void then
612               --| std_output.put_line("        backtrack, unexpected next child" # backtrack_next.out)
613               backtrack
614            elseif elt.name.is_equal(backtrack_next) then
615               --| std_output.put_line("        FOUND")
616               backtrack_ok := True
617               stop_search_loop := True
618            else
619               --| std_output.put_line("        backtrack, next child is #(1) not #(2)" # backtrack_next.out # elt.name.out)
620               backtrack
621            end
622         end
623      end
624
625feature {XML_DTD_PCDATA_NODE}
626   backtrack_valid_pcdata
627      do
628         --| std_output.put_line("    backtrack_valid_pcdata")
629         if backtrack_children.valid_index(backtrack_index) then
630            backtrack_index := backtrack_index + 1
631            --| std_output.put_line("        continue")
632            continue
633         elseif backtrack_next = backtrack_next_pcdata_marker or else backtrack_next = Void then
634            --| std_output.put_line("        FOUND")
635            backtrack_ok := True
636            stop_search_loop := True
637         else
638            sedb_breakpoint
639            --| std_output.put_line("        backtrack")
640            backtrack
641         end
642      end
643
644feature {XML_DTD_ANY_NODE}
645   backtrack_valid_any
646      do
647         --| std_output.put_line("    backtrack_valid_any")
648         --| std_output.put_line("        FOUND")
649         backtrack_ok := True
650         stop_search_loop := True
651      end
652
653feature {XML_DTD_EMPTY_NODE}
654   backtrack_valid_empty
655      do
656         --| std_output.put_line("    backtrack_valid_empty")
657         if backtrack_children.is_empty and then (context.is_empty implies backtrack_next = Void) then
658            --| std_output.put_line("        FOUND")
659            backtrack_ok := True
660            stop_search_loop := True
661         else
662            --| std_output.put_line("        backtrack")
663            backtrack
664         end
665      end
666
667feature {XML_DTD_END_NODE}
668   backtrack_valid_end
669      do
670         --| std_output.put_line("    backtrack_valid_end")
671         if not backtrack_children.valid_index(backtrack_index) and then (context.is_empty implies backtrack_next = Void) then
672            --| std_output.put_line("        FOUND")
673            backtrack_ok := True
674            stop_search_loop := True
675         else
676            --| std_output.put_line("        backtrack")
677            backtrack
678         end
679      end
680
681feature {} -- Backtracking internals
682   backtrack_pcdata: UNICODE_STRING
683   backtrack_children: FAST_ARRAY[XML_DTD_NODE]
684   backtrack_index: INTEGER
685   backtrack_ok: BOOLEAN
686   backtrack_next: UNICODE_STRING
687   context: FAST_ARRAY[INTEGER]
688
689   context_clear
690      do
691         context.clear_count
692      end
693
694   context_push
695      do
696         --| std_output.put_line("                context push " + backtrack_index.out)
697         context.add_last(backtrack_index)
698      end
699
700   context_restore
701      do
702         backtrack_index := context.last
703         --| std_output.put_line("                context restore " + backtrack_index.out)
704      end
705
706   context_restore_and_pop
707      do
708         context_restore
709         context_cut
710      end
711
712   context_cut
713      do
714         --| std_output.put_line("                context cut ")
715         context.remove_last
716      end
717
718feature {XML_DTD_MEMORY}
719   root_name: UNICODE_STRING
720
721   make (root_node_name: like root_name)
722      require
723         not root_node_name.is_empty
724      do
725         root_name := root_node_name
726         if attributes = Void then
727            create attributes.make
728            create elements.make
729            create entities.make
730            create entity_urls.make
731            create context.make(0)
732         end
733      ensure
734         root_name = root_node_name
735      end
736
737feature {RECYCLING_POOL}
738   recycle
739      do
740         attributes.clear_count
741         recycle_elements
742         entities.clear_count
743         recycle_entity_urls
744         context.clear_count
745      end
746
747feature {}
748   recycle_elements
749      local
750         i: INTEGER
751      do
752         from
753            i := elements.lower
754         until
755            i > elements.upper
756         loop
757            elements_pool.recycle(elements.item(i))
758            i := i + 1
759         end
760         elements.clear_count
761      ensure
762         elements.is_empty
763      end
764
765   recycle_entity_urls
766      local
767         i: INTEGER
768      do
769         from
770            i := entity_urls.lower
771         until
772            i > entity_urls.upper
773         loop
774            free_string(entity_urls.item(i))
775            i := i + 1
776         end
777         entity_urls.clear_count
778      ensure
779         entity_urls.is_empty
780      end
781
782   backtrack_next_pcdata_marker: UNICODE_STRING once then U"__#PCDATA__" end
783
784invariant
785   not root_name.is_empty
786
787end -- class XML_DTD_VALIDATOR
788--
789-- Copyright (C) 2009-2017: by all the people cited in the AUTHORS file.
790--
791-- Permission is hereby granted, free of charge, to any person obtaining a copy
792-- of this software and associated documentation files (the "Software"), to deal
793-- in the Software without restriction, including without limitation the rights
794-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
795-- copies of the Software, and to permit persons to whom the Software is
796-- furnished to do so, subject to the following conditions:
797--
798-- The above copyright notice and this permission notice shall be included in
799-- all copies or substantial portions of the Software.
800--
801-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
802-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
803-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
804-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
805-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
806-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
807-- THE SOFTWARE.