1: <?php
2: /**
3: * LICENSE (BSD)
4: *
5: * Copyright (c) 2013, Gerd Christian Kunze
6: * All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions are
10: * met:
11: *
12: * * Redistributions of source code must retain the above copyright
13: * notice, this list of conditions and the following disclaimer.
14: *
15: * * Redistributions in binary form must reproduce the above copyright
16: * notice, this list of conditions and the following disclaimer in the
17: * documentation and/or other materials provided with the distribution.
18: *
19: * * Neither the name of Gerd Christian Kunze nor the names of the
20: * contributors may be used to endorse or promote products derived from
21: * this software without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
24: * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
25: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
27: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
28: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
29: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34: *
35: * Parser
36: * 06.01.2013 14:37
37: */
38: namespace MOC\Core\Xml;
39: use MOC\Api;
40: use MOC\Generic\Device\Core;
41:
42: /**
43: *
44: */
45: class Parser implements Core {
46: /**
47: * Get Dependencies
48: *
49: * @static
50: * @return \MOC\Core\Depending
51: */
52: public static function InterfaceDepending() {
53: return Api::Core()->Depending();
54: }
55:
56: /**
57: * Get Singleton/Instance
58: *
59: * @static
60: * @return Parser
61: */
62: public static function InterfaceInstance() {
63: return new Parser();
64: }
65:
66: /**
67: * Get Changelog
68: *
69: * @static
70: * @return \MOC\Core\Changelog
71: */
72: public static function InterfaceChangelog() {
73: return Api::Core()->Changelog()->Create( __CLASS__ );
74: }
75:
76:
77: /** @var Tokenizer $Tokenizer */
78: private $Tokenizer = null;
79: /** @var array $Stack */
80: private $Stack = array();
81: /** @var null|Node $Result */
82: private $Result = null;
83:
84: private $PatternComment = '!(?<=\!--).*?(?=//--)!is';
85: private $PatternCDATA = '!(?<=\!\[CDATA\[).*?(?=\]\])!is';
86:
87: /**
88: * @param Tokenizer $Tokenizer
89: *
90: * @return Parser
91: */
92: public function Setup( Tokenizer $Tokenizer ) {
93: $this->Stack = array();
94: $this->Tokenizer = $Tokenizer;
95: $this->Result = null;
96: $this->Parse();
97: return $this;
98: }
99:
100: private function Parse() {
101: /** @var Token $Token */
102: foreach( (array)$this->Tokenizer->GetResult() as $Token ) {
103: // Convert Token to Node
104: $Node = Node::InterfaceInstance()->Setup( $Token );
105: // Handle Token by Type
106: if( $Token->isOpenTag() ) {
107: // Set Parent Type to Structure
108: if( !empty( $this->Stack ) ) {
109: $Parent = array_pop( $this->Stack );
110: $Parent->SetType( $Parent::TYPE_STRUCTURE );
111: array_push( $this->Stack, $Parent );
112: }
113: // Add Node to Stack
114: array_push( $this->Stack, $Node );
115: } elseif( $Token->isCloseTag() ) {
116: // Get Parent (OpenTag)
117: /** @var Node $Parent */
118: $Parent = array_pop( $this->Stack );
119: // Handle Close by Type
120: switch( $Parent->GetType() ) {
121: case $Parent::TYPE_CONTENT : {
122: // Get Content
123: $LengthName = strlen( $Parent->GetName() ) +1;
124: $LengthAttribute = strlen( $Parent->GetAttributeString() ) +1;
125: $LengthAttribute = ( $LengthAttribute == 1 ? 0 : $LengthAttribute );
126: $Parent->SetContent(
127: substr(
128: $this->Tokenizer->GetContent(),
129:
130: $Parent->GetPosition()
131: + $LengthName
132: + $LengthAttribute,
133:
134: ( $Token->GetPosition() - $Parent->GetPosition() )
135: - ( $LengthName +1 )
136: - ( $LengthAttribute )
137: )
138: );
139: // Do Parent Close
140: $Ancestor = array_pop( $this->Stack );
141: $Ancestor->AddChild( $Parent );
142: array_push( $this->Stack, $Ancestor );
143: break;
144: }
145: case $Parent::TYPE_STRUCTURE : {
146: // Set Ancestor <-> Parent Relation
147: /** @var Node $Ancestor */
148: $Ancestor = array_pop( $this->Stack );
149: if( is_object( $Ancestor ) ) {
150: // Do Parent Close
151: $Ancestor->AddChild( $Parent );
152: array_push( $this->Stack, $Ancestor );
153: } else {
154: // No Ancestor -> Parent = Root
155: array_push( $this->Stack, $Parent );
156: }
157: break;
158: }
159: case $Parent::TYPE_CDATA : {
160: // Set Ancestor <-> Parent Relation
161: /** @var Node $Ancestor */
162: $Ancestor = array_pop( $this->Stack );
163: // Do Parent Close
164: $Ancestor->AddChild( $Parent );
165: array_push( $this->Stack, $Ancestor );
166: break;
167: }
168: }
169: } elseif( $Token->isShortTag() ) {
170: // Set Ancestor <-> Node Relation
171: /** @var Node $Parent */
172: $Ancestor = array_pop( $this->Stack );
173: $Ancestor->SetType( $Ancestor::TYPE_STRUCTURE );
174: // Do Node Close
175: $Ancestor->AddChild( $Node );
176: array_push( $this->Stack, $Ancestor );
177: } elseif( $Token->isCDATATag() ) {
178: // Set Parent Type/Content
179: /** @var Node $Parent */
180: $Parent = array_pop( $this->Stack );
181: $Parent->SetType( $Parent::TYPE_CDATA );
182: $Parent->SetContent( $Node->GetName() );
183: $this->DecodeCDATA( $Parent );
184: // Do Node Close
185: array_push( $this->Stack, $Parent );
186: } elseif( $Token->isCommentTag() ) {
187: // Set Parent Type/Content
188: /** @var Node $Parent */
189: $Parent = array_pop( $this->Stack );
190: $Node->SetType( $Node::TYPE_COMMENT );
191: $Node->SetContent( $Node->GetName() );
192: $Node->SetName( '__COMMENT__' );
193: $this->DecodeComment( $Node );
194: // Do Node Close
195: $Parent->AddChild( $Node );
196: array_push( $this->Stack, $Parent );
197: }
198: }
199: // Set parsed Stack as Result
200: $this->Result = array_pop( $this->Stack );
201: }
202:
203: /**
204: * @return Node|null
205: */
206: public function GetResult() {
207: return $this->Result;
208: }
209:
210: /**
211: * @param Node $Node
212: */
213: private function DecodeCDATA( Node &$Node ) {
214: $Match = array();
215: preg_match( $this->PatternCDATA, $Node->GetContent(), $Match );
216: $Node->SetContent( $this->DecodeBase64( $Match[0] ) );
217: }
218:
219: /**
220: * @param Node $Node
221: */
222: private function DecodeComment( Node &$Node ) {
223: $Match = array();
224: preg_match( $this->PatternComment, $Node->GetContent(), $Match );
225: $Node->SetContent( trim( $this->DecodeBase64( $Match[0] ) ) );
226: }
227:
228: /**
229: * @param $Content
230: *
231: * @return string
232: */
233: private function DecodeBase64( $Content ) {
234: return base64_decode( $Content );
235: }
236: }
237: