Mark V
1: <?php
2: namespace MOC\V\Component\Document\Vendor\UniversalXml\Source;
3:
4: /**
5: * Class Parser
6: *
7: * @package MOC\V\Component\Document\Vendor\UniversalXml\Source
8: */
9: class Parser extends Mask
10: {
11:
12: /** @var array $Stack */
13: private $Stack = array();
14: /** @var null|Node $Result */
15: private $Result = null;
16:
17: /**
18: * @param Tokenizer $Tokenizer
19: */
20: function __construct(Tokenizer $Tokenizer)
21: {
22:
23: /** @var Token $Token */
24: foreach ((array)$Tokenizer->getResult() as $Token) {
25: // Convert Token to Node
26: $Node = new Node($Token);
27: // Handle Token by Type
28: if ($Token->isOpenTag()) {
29: $this->processOpen($Node);
30: } elseif ($Token->isCloseTag()) {
31: // Get Parent (OpenTag)
32: /** @var Node $Parent */
33: $Parent = array_pop($this->Stack);
34: // Handle Close by Type
35: switch ($Parent->getType()) {
36: case $Parent::TYPE_CONTENT : {
37: $this->processCloseContent($Parent, $Tokenizer, $Token);
38: break;
39: }
40: case $Parent::TYPE_STRUCTURE : {
41: $this->processCloseStructure($Parent);
42: break;
43: }
44: case $Parent::TYPE_CDATA : {
45: $this->processCloseCDATA($Parent);
46: break;
47: }
48: }
49: } elseif ($Token->isShortTag()) {
50: $this->processShort($Node);
51: } elseif ($Token->isCDATATag()) {
52: $this->processCDATA($Node);
53: } elseif ($Token->isCommentTag()) {
54: $this->processComment($Node);
55: }
56: }
57: // Set parsed Stack as Result
58: $this->Result = array_pop($this->Stack);
59: }
60:
61: /**
62: * @param Node $Node
63: */
64: private function processOpen(Node $Node)
65: {
66:
67: // Set Parent Type to Structure
68: if (!empty( $this->Stack )) {
69: $Parent = array_pop($this->Stack);
70: $Parent->setType($Parent::TYPE_STRUCTURE);
71: array_push($this->Stack, $Parent);
72: }
73: // Add Node to Stack
74: array_push($this->Stack, $Node);
75: }
76:
77: /**
78: * @param Node $Parent
79: * @param Tokenizer $Tokenizer
80: * @param Token $Token
81: */
82: private function processCloseContent(Node $Parent, Tokenizer $Tokenizer, Token $Token)
83: {
84:
85: // Get Content
86: $LengthName = strlen($Parent->getName()) + 1;
87: $LengthAttribute = strlen($Parent->getAttributeString()) + 1;
88: $LengthAttribute = ( $LengthAttribute == 1 ? 0 : $LengthAttribute );
89: $Parent->setContent(
90: substr(
91: $Tokenizer->getContent(),
92:
93: $Parent->getPosition()
94: + $LengthName
95: + $LengthAttribute,
96:
97: ( $Token->getPosition() - $Parent->getPosition() )
98: - ( $LengthName + 1 )
99: - ( $LengthAttribute )
100: )
101: );
102: // Do Parent Close
103: $Ancestor = array_pop($this->Stack);
104: $Ancestor->addChild($Parent);
105: array_push($this->Stack, $Ancestor);
106: }
107:
108: /**
109: * @param Node $Parent
110: */
111: private function processCloseStructure(Node $Parent)
112: {
113:
114: // Set Ancestor <-> Parent Relation
115: /** @var Node $Ancestor */
116: $Ancestor = array_pop($this->Stack);
117: if (is_object($Ancestor)) {
118: // Do Parent Close
119: $Ancestor->addChild($Parent);
120: array_push($this->Stack, $Ancestor);
121: } else {
122: // No Ancestor -> Parent = Root
123: array_push($this->Stack, $Parent);
124: }
125: }
126:
127: /**
128: * @param Node $Parent
129: */
130: private function processCloseCDATA(Node $Parent)
131: {
132:
133: // Set Ancestor <-> Parent Relation
134: /** @var Node $Ancestor */
135: $Ancestor = array_pop($this->Stack);
136: // Do Parent Close
137: $Ancestor->addChild($Parent);
138: array_push($this->Stack, $Ancestor);
139: }
140:
141: /**
142: * @param Node $Node
143: */
144: private function processShort(Node $Node)
145: {
146:
147: // Set Ancestor <-> Node Relation
148: /** @var Node $Parent */
149: $Ancestor = array_pop($this->Stack);
150: $Ancestor->setType($Ancestor::TYPE_STRUCTURE);
151: // Do Node Close
152: $Ancestor->addChild($Node);
153: array_push($this->Stack, $Ancestor);
154: }
155:
156: /**
157: * @param Node $Node
158: */
159: private function processCDATA(Node $Node)
160: {
161:
162: // Set Parent Type/Content
163: /** @var Node $Parent */
164: $Parent = array_pop($this->Stack);
165: $Parent->setType($Parent::TYPE_CDATA);
166: $Parent->setContent($Node->getName());
167: $this->decodePayload($Parent, self::PATTERN_CDATA);
168: // Do Node Close
169: array_push($this->Stack, $Parent);
170: }
171:
172: /**
173: * @param Node $Node
174: */
175: private function processComment(Node $Node)
176: {
177:
178: // Set Parent Type/Content
179: /** @var Node $Parent */
180: $Parent = array_pop($this->Stack);
181: $Node->setType($Node::TYPE_COMMENT);
182: $Node->setContent($Node->getName());
183: $Node->setName('__COMMENT__');
184: $this->decodePayload($Node, self::PATTERN_COMMENT);
185: // Do Node Close
186: $Parent->addChild($Node);
187: array_push($this->Stack, $Parent);
188: }
189:
190: /**
191: * @return Node|null
192: */
193: public function getResult()
194: {
195:
196: return $this->Result;
197: }
198: }
199: