1*be906b56SAndreas Gohr<?php 2*be906b56SAndreas Gohr 3*be906b56SAndreas Gohrnamespace dokuwiki\Parsing\Handler; 4*be906b56SAndreas Gohr 5*be906b56SAndreas Gohrclass Lists implements ReWriterInterface 6*be906b56SAndreas Gohr{ 7*be906b56SAndreas Gohr 8*be906b56SAndreas Gohr /** @var CallWriterInterface original call writer */ 9*be906b56SAndreas Gohr protected $callWriter; 10*be906b56SAndreas Gohr 11*be906b56SAndreas Gohr protected $calls = array(); 12*be906b56SAndreas Gohr protected $listCalls = array(); 13*be906b56SAndreas Gohr protected $listStack = array(); 14*be906b56SAndreas Gohr 15*be906b56SAndreas Gohr protected $initialDepth = 0; 16*be906b56SAndreas Gohr 17*be906b56SAndreas Gohr const NODE = 1; 18*be906b56SAndreas Gohr 19*be906b56SAndreas Gohr 20*be906b56SAndreas Gohr /** @inheritdoc */ 21*be906b56SAndreas Gohr public function __construct(CallWriterInterface $CallWriter) 22*be906b56SAndreas Gohr { 23*be906b56SAndreas Gohr $this->callWriter = $CallWriter; 24*be906b56SAndreas Gohr } 25*be906b56SAndreas Gohr 26*be906b56SAndreas Gohr /** @inheritdoc */ 27*be906b56SAndreas Gohr public function writeCall($call) 28*be906b56SAndreas Gohr { 29*be906b56SAndreas Gohr $this->calls[] = $call; 30*be906b56SAndreas Gohr } 31*be906b56SAndreas Gohr 32*be906b56SAndreas Gohr /** 33*be906b56SAndreas Gohr * @inheritdoc 34*be906b56SAndreas Gohr * Probably not needed but just in case... 35*be906b56SAndreas Gohr */ 36*be906b56SAndreas Gohr public function writeCalls($calls) 37*be906b56SAndreas Gohr { 38*be906b56SAndreas Gohr $this->calls = array_merge($this->calls, $calls); 39*be906b56SAndreas Gohr } 40*be906b56SAndreas Gohr 41*be906b56SAndreas Gohr /** @inheritdoc */ 42*be906b56SAndreas Gohr public function finalise() 43*be906b56SAndreas Gohr { 44*be906b56SAndreas Gohr $last_call = end($this->calls); 45*be906b56SAndreas Gohr $this->writeCall(array('list_close',array(), $last_call[2])); 46*be906b56SAndreas Gohr 47*be906b56SAndreas Gohr $this->process(); 48*be906b56SAndreas Gohr $this->callWriter->finalise(); 49*be906b56SAndreas Gohr unset($this->callWriter); 50*be906b56SAndreas Gohr } 51*be906b56SAndreas Gohr 52*be906b56SAndreas Gohr /** @inheritdoc */ 53*be906b56SAndreas Gohr public function process() 54*be906b56SAndreas Gohr { 55*be906b56SAndreas Gohr 56*be906b56SAndreas Gohr foreach ($this->calls as $call) { 57*be906b56SAndreas Gohr switch ($call[0]) { 58*be906b56SAndreas Gohr case 'list_item': 59*be906b56SAndreas Gohr $this->listOpen($call); 60*be906b56SAndreas Gohr break; 61*be906b56SAndreas Gohr case 'list_open': 62*be906b56SAndreas Gohr $this->listStart($call); 63*be906b56SAndreas Gohr break; 64*be906b56SAndreas Gohr case 'list_close': 65*be906b56SAndreas Gohr $this->listEnd($call); 66*be906b56SAndreas Gohr break; 67*be906b56SAndreas Gohr default: 68*be906b56SAndreas Gohr $this->listContent($call); 69*be906b56SAndreas Gohr break; 70*be906b56SAndreas Gohr } 71*be906b56SAndreas Gohr } 72*be906b56SAndreas Gohr 73*be906b56SAndreas Gohr $this->callWriter->writeCalls($this->listCalls); 74*be906b56SAndreas Gohr return $this->callWriter; 75*be906b56SAndreas Gohr } 76*be906b56SAndreas Gohr 77*be906b56SAndreas Gohr protected function listStart($call) 78*be906b56SAndreas Gohr { 79*be906b56SAndreas Gohr $depth = $this->interpretSyntax($call[1][0], $listType); 80*be906b56SAndreas Gohr 81*be906b56SAndreas Gohr $this->initialDepth = $depth; 82*be906b56SAndreas Gohr // array(list type, current depth, index of current listitem_open) 83*be906b56SAndreas Gohr $this->listStack[] = array($listType, $depth, 1); 84*be906b56SAndreas Gohr 85*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$listType.'_open',array(),$call[2]); 86*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_open',array(1),$call[2]); 87*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_open',array(),$call[2]); 88*be906b56SAndreas Gohr } 89*be906b56SAndreas Gohr 90*be906b56SAndreas Gohr 91*be906b56SAndreas Gohr protected function listEnd($call) 92*be906b56SAndreas Gohr { 93*be906b56SAndreas Gohr $closeContent = true; 94*be906b56SAndreas Gohr 95*be906b56SAndreas Gohr while ($list = array_pop($this->listStack)) { 96*be906b56SAndreas Gohr if ($closeContent) { 97*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_close',array(),$call[2]); 98*be906b56SAndreas Gohr $closeContent = false; 99*be906b56SAndreas Gohr } 100*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_close',array(),$call[2]); 101*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$list[0].'_close', array(), $call[2]); 102*be906b56SAndreas Gohr } 103*be906b56SAndreas Gohr } 104*be906b56SAndreas Gohr 105*be906b56SAndreas Gohr protected function listOpen($call) 106*be906b56SAndreas Gohr { 107*be906b56SAndreas Gohr $depth = $this->interpretSyntax($call[1][0], $listType); 108*be906b56SAndreas Gohr $end = end($this->listStack); 109*be906b56SAndreas Gohr $key = key($this->listStack); 110*be906b56SAndreas Gohr 111*be906b56SAndreas Gohr // Not allowed to be shallower than initialDepth 112*be906b56SAndreas Gohr if ($depth < $this->initialDepth) { 113*be906b56SAndreas Gohr $depth = $this->initialDepth; 114*be906b56SAndreas Gohr } 115*be906b56SAndreas Gohr 116*be906b56SAndreas Gohr if ($depth == $end[1]) { 117*be906b56SAndreas Gohr // Just another item in the list... 118*be906b56SAndreas Gohr if ($listType == $end[0]) { 119*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_close',array(),$call[2]); 120*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_close',array(),$call[2]); 121*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]); 122*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_open',array(),$call[2]); 123*be906b56SAndreas Gohr 124*be906b56SAndreas Gohr // new list item, update list stack's index into current listitem_open 125*be906b56SAndreas Gohr $this->listStack[$key][2] = count($this->listCalls) - 2; 126*be906b56SAndreas Gohr 127*be906b56SAndreas Gohr // Switched list type... 128*be906b56SAndreas Gohr } else { 129*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_close',array(),$call[2]); 130*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_close',array(),$call[2]); 131*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]); 132*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]); 133*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]); 134*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_open',array(),$call[2]); 135*be906b56SAndreas Gohr 136*be906b56SAndreas Gohr array_pop($this->listStack); 137*be906b56SAndreas Gohr $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2); 138*be906b56SAndreas Gohr } 139*be906b56SAndreas Gohr } elseif ($depth > $end[1]) { // Getting deeper... 140*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_close',array(),$call[2]); 141*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]); 142*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]); 143*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_open',array(),$call[2]); 144*be906b56SAndreas Gohr 145*be906b56SAndreas Gohr // set the node/leaf state of this item's parent listitem_open to NODE 146*be906b56SAndreas Gohr $this->listCalls[$this->listStack[$key][2]][1][1] = self::NODE; 147*be906b56SAndreas Gohr 148*be906b56SAndreas Gohr $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2); 149*be906b56SAndreas Gohr } else { // Getting shallower ( $depth < $end[1] ) 150*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_close',array(),$call[2]); 151*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_close',array(),$call[2]); 152*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]); 153*be906b56SAndreas Gohr 154*be906b56SAndreas Gohr // Throw away the end - done 155*be906b56SAndreas Gohr array_pop($this->listStack); 156*be906b56SAndreas Gohr 157*be906b56SAndreas Gohr while (1) { 158*be906b56SAndreas Gohr $end = end($this->listStack); 159*be906b56SAndreas Gohr $key = key($this->listStack); 160*be906b56SAndreas Gohr 161*be906b56SAndreas Gohr if ($end[1] <= $depth) { 162*be906b56SAndreas Gohr // Normalize depths 163*be906b56SAndreas Gohr $depth = $end[1]; 164*be906b56SAndreas Gohr 165*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_close',array(),$call[2]); 166*be906b56SAndreas Gohr 167*be906b56SAndreas Gohr if ($end[0] == $listType) { 168*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_open',array($depth-1),$call[2]); 169*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_open',array(),$call[2]); 170*be906b56SAndreas Gohr 171*be906b56SAndreas Gohr // new list item, update list stack's index into current listitem_open 172*be906b56SAndreas Gohr $this->listStack[$key][2] = count($this->listCalls) - 2; 173*be906b56SAndreas Gohr } else { 174*be906b56SAndreas Gohr // Switching list type... 175*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$end[0].'_close', array(), $call[2]); 176*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$listType.'_open', array(), $call[2]); 177*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_open', array($depth-1), $call[2]); 178*be906b56SAndreas Gohr $this->listCalls[] = array('listcontent_open',array(),$call[2]); 179*be906b56SAndreas Gohr 180*be906b56SAndreas Gohr array_pop($this->listStack); 181*be906b56SAndreas Gohr $this->listStack[] = array($listType, $depth, count($this->listCalls) - 2); 182*be906b56SAndreas Gohr } 183*be906b56SAndreas Gohr 184*be906b56SAndreas Gohr break; 185*be906b56SAndreas Gohr 186*be906b56SAndreas Gohr // Haven't dropped down far enough yet.... ( $end[1] > $depth ) 187*be906b56SAndreas Gohr } else { 188*be906b56SAndreas Gohr $this->listCalls[] = array('listitem_close',array(),$call[2]); 189*be906b56SAndreas Gohr $this->listCalls[] = array('list'.$end[0].'_close',array(),$call[2]); 190*be906b56SAndreas Gohr 191*be906b56SAndreas Gohr array_pop($this->listStack); 192*be906b56SAndreas Gohr } 193*be906b56SAndreas Gohr } 194*be906b56SAndreas Gohr } 195*be906b56SAndreas Gohr } 196*be906b56SAndreas Gohr 197*be906b56SAndreas Gohr protected function listContent($call) 198*be906b56SAndreas Gohr { 199*be906b56SAndreas Gohr $this->listCalls[] = $call; 200*be906b56SAndreas Gohr } 201*be906b56SAndreas Gohr 202*be906b56SAndreas Gohr protected function interpretSyntax($match, & $type) 203*be906b56SAndreas Gohr { 204*be906b56SAndreas Gohr if (substr($match, -1) == '*') { 205*be906b56SAndreas Gohr $type = 'u'; 206*be906b56SAndreas Gohr } else { 207*be906b56SAndreas Gohr $type = 'o'; 208*be906b56SAndreas Gohr } 209*be906b56SAndreas Gohr // Is the +1 needed? It used to be count(explode(...)) 210*be906b56SAndreas Gohr // but I don't think the number is seen outside this handler 211*be906b56SAndreas Gohr return substr_count(str_replace("\t", ' ', $match), ' ') + 1; 212*be906b56SAndreas Gohr } 213*be906b56SAndreas Gohr} 214