1<?php 2 3/** 4 * Plugin RefNotes: Core functionality 5 * 6 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 7 * @author Mykola Ostrovskyy <dwpforge@gmail.com> 8 */ 9 10require_once(DOKU_PLUGIN . 'refnotes/locale.php'); 11require_once(DOKU_PLUGIN . 'refnotes/config.php'); 12require_once(DOKU_PLUGIN . 'refnotes/refnote.php'); 13require_once(DOKU_PLUGIN . 'refnotes/reference.php'); 14require_once(DOKU_PLUGIN . 'refnotes/note.php'); 15require_once(DOKU_PLUGIN . 'refnotes/namespace.php'); 16require_once(DOKU_PLUGIN . 'refnotes/scope.php'); 17require_once(DOKU_PLUGIN . 'refnotes/rendering.php'); 18require_once(DOKU_PLUGIN . 'refnotes/database.php'); 19 20//////////////////////////////////////////////////////////////////////////////////////////////////// 21class refnotes_parser_core { 22 23 private static $instance = NULL; 24 25 private $context; 26 private $lexer; 27 private $handler; 28 29 /** 30 * 31 */ 32 public static function getInstance() { 33 if (self::$instance == NULL) { 34 self::$instance = new refnotes_parser_core(); 35 } 36 37 return self::$instance; 38 } 39 40 /** 41 * Constructor 42 */ 43 public function __construct() { 44 /* Default context. Should never be used, but just in case... */ 45 $this->context = array(new refnotes_parsing_context()); 46 $this->lexer = NULL; 47 $this->handler = NULL; 48 } 49 50 /** 51 * 52 */ 53 public function registerLexer($lexer) { 54 $this->lexer = $lexer; 55 } 56 57 /** 58 * 59 */ 60 public function enterParsingContext() { 61 $this->context[] = new refnotes_parsing_context(); 62 } 63 64 /** 65 * 66 */ 67 public function exitParsingContext($handler) { 68 $this->handler = $handler; 69 70 unset($this->context[count($this->context) - 1]); 71 } 72 73 /** 74 * 75 */ 76 public function getInstructions($text) { 77 $this->callWriter = new refnotes_nested_call_writer($this->handler->getCallWriter(), $this->handler); 78 79 $this->callWriter->connect(); 80 $this->lexer->parse($text); 81 $this->callWriter->disconnect(); 82 83 return $this->callWriter->calls; 84 } 85 86 /** 87 * 88 */ 89 private function getCurrentContext() { 90 return end($this->context); 91 } 92 93 /** 94 * 95 */ 96 public function canHandle($state) { 97 return $this->getCurrentContext()->canHandle($state); 98 } 99 100 /** 101 * 102 */ 103 public function enterReference($name, $data) { 104 $this->getCurrentContext()->enterReference($name, $data); 105 } 106 107 /** 108 * 109 */ 110 public function exitReference() { 111 return $this->getCurrentContext()->exitReference(); 112 } 113} 114 115//////////////////////////////////////////////////////////////////////////////////////////////////// 116class refnotes_parsing_context { 117 118 private $handling; 119 private $reference; 120 121 /** 122 * Constructor 123 */ 124 public function __construct() { 125 $this->reset(); 126 } 127 128 /** 129 * 130 */ 131 private function reset() { 132 $this->handling = false; 133 $this->reference = NULL; 134 } 135 136 /** 137 * 138 */ 139 public function canHandle($state) { 140 switch ($state) { 141 case DOKU_LEXER_ENTER: 142 $result = !$this->handling; 143 break; 144 145 case DOKU_LEXER_EXIT: 146 $result = $this->handling; 147 break; 148 149 default: 150 $result = false; 151 break; 152 } 153 154 return $result; 155 } 156 157 /** 158 * 159 */ 160 public function enterReference($name, $data) { 161 $this->handling = true; 162 $this->reference = new refnotes_parser_reference($name, $data); 163 } 164 165 /** 166 * 167 */ 168 public function exitReference() { 169 $reference = $this->reference; 170 171 $this->reset(); 172 173 return $reference; 174 } 175} 176 177//////////////////////////////////////////////////////////////////////////////////////////////////// 178abstract class refnotes_core { 179 180 protected $presetStyle; 181 protected $namespace; 182 protected $mapping; 183 184 /** 185 * Constructor 186 */ 187 public function __construct() { 188 $this->presetStyle = refnotes_configuration::load('namespaces'); 189 $this->namespace = array(); 190 $this->mapping = array(); 191 } 192 193 /** 194 * 195 */ 196 public function getNamespaceCount() { 197 return count($this->namespace); 198 } 199 200 /** 201 * Returns a namespace given it's name. The namespace is created if it doesn't exist yet. 202 */ 203 public function getNamespace($name) { 204 $result = $this->findNamespace($name); 205 206 if ($result == NULL) { 207 $result = $this->createNamespace($name); 208 } 209 210 return $result; 211 } 212 213 /** 214 * Finds a namespace given it's name 215 */ 216 protected function findNamespace($name) { 217 $result = NULL; 218 219 if (array_key_exists($name, $this->namespace)) { 220 $result = $this->namespace[$name]; 221 } 222 223 return $result; 224 } 225 226 /** 227 * Finds a namespace or it's parent 228 */ 229 public function findParentNamespace($name) { 230 while (($name != '') && !array_key_exists($name, $this->namespace)) { 231 $name = refnotes_namespace::getParentName($name); 232 } 233 234 return ($name != '') ? $this->namespace[$name] : NULL; 235 } 236 237 /** 238 * 239 */ 240 public function styleNamespace($namespaceName, $style) { 241 $namespace = $this->getNamespace($namespaceName); 242 243 if (array_key_exists('inherit', $style)) { 244 $source = $this->getNamespace($style['inherit']); 245 $namespace->inheritStyle($source); 246 } 247 248 $namespace->setStyle($style); 249 } 250 251 /** 252 * 253 */ 254 public function setNamespaceMapping($namespaceName, $map) { 255 foreach ($map as $ns) { 256 $this->mapping[$ns] = $namespaceName; 257 } 258 } 259 260 /** 261 * 262 */ 263 protected function clearNamespaceMapping($namespaceName) { 264 $this->mapping = array_diff($this->mapping, array($namespaceName)); 265 } 266 267 /** 268 * 269 */ 270 protected function createNamespace($name) { 271 if ($name != ':') { 272 $parentName = refnotes_namespace::getParentName($name); 273 $parent = $this->getNamespace($parentName); 274 $this->namespace[$name] = new refnotes_namespace($name, $parent); 275 } 276 else { 277 $this->namespace[$name] = new refnotes_namespace($name); 278 } 279 280 if (array_key_exists($name, $this->presetStyle)) { 281 $this->namespace[$name]->setStyle($this->presetStyle[$name]); 282 } 283 284 return $this->namespace[$name]; 285 } 286 287 /** 288 * 289 */ 290 protected function getNote($namespaceName, $noteName) { 291 $scope = $this->getNamespace($namespaceName)->getActiveScope(); 292 $note = $scope->findNote($namespaceName, $noteName); 293 294 if (($note == NULL) && array_key_exists($namespaceName, $this->mapping)) { 295 $scope = $this->getNamespace($this->mapping[$namespaceName])->getActiveScope(); 296 $note = $scope->findNote($namespaceName, $noteName); 297 } 298 299 if ($note == NULL) { 300 if (!is_int($noteName)) { 301 $note = $this->createNote($scope, $namespaceName, $noteName); 302 303 $scope->addNote($note); 304 } 305 else { 306 $note = new refnotes_note_mock(); 307 } 308 } 309 310 return $note; 311 } 312 313 /** 314 * 315 */ 316 abstract protected function createNote($scope, $namespaceName, $noteName); 317} 318 319//////////////////////////////////////////////////////////////////////////////////////////////////// 320class refnotes_renderer_core extends refnotes_core { 321 322 private static $instance = NULL; 323 private $leftoversRenderingBlocks = 0; 324 325 /** 326 * Renderer core is used by both references and notes syntax plugins during the rendering 327 * stage. The instance has to be shared between the plugins, and since there should be no 328 * more than one rendering pass during a DW page request, a single instance of the syntax 329 * core should be enough. 330 */ 331 public static function getInstance() { 332 if (self::$instance == NULL) { 333 self::$instance = new refnotes_renderer_core(); 334 } 335 336 return self::$instance; 337 } 338 339 /** 340 * 341 */ 342 public function addReference($attributes, $data) { 343 $note = $this->getNote($attributes['ns'], $attributes['name']); 344 $reference = new refnotes_renderer_reference($note, $attributes, $data); 345 346 $note->addReference($reference); 347 348 return $reference; 349 } 350 351 /** 352 * 353 */ 354 public function renderNotes($mode, $namespaceName, $limit) { 355 $html = ''; 356 357 if ($namespaceName == '*') { 358 if ($this->leftoversRenderingBlocks == 0) { 359 foreach ($this->namespace as $namespace) { 360 $html .= $namespace->renderNotes($mode); 361 } 362 } 363 } 364 else { 365 $this->clearNamespaceMapping($namespaceName); 366 367 $namespace = $this->findNamespace($namespaceName); 368 if ($namespace != NULL) { 369 $html = $namespace->renderNotes($mode, $limit); 370 } 371 } 372 373 return $html; 374 } 375 376 /** 377 * Keep track of leftover note rendering blocking on included pages 378 */ 379 public function updateRenderingBlocks($block) { 380 switch ($block) { 381 case 'enter': 382 ++$this->leftoversRenderingBlocks; 383 break; 384 385 case 'exit': 386 if ($this->leftoversRenderingBlocks > 0) { 387 --$this->leftoversRenderingBlocks; 388 } 389 break; 390 } 391 } 392 393 /** 394 * 395 */ 396 protected function createNote($scope, $namespaceName, $noteName) { 397 return new refnotes_renderer_note($scope, $namespaceName, $noteName); 398 } 399} 400 401//////////////////////////////////////////////////////////////////////////////////////////////////// 402class refnotes_action_core extends refnotes_core { 403 404 private $styleStash; 405 private $mappingStash; 406 407 /** 408 * Constructor 409 */ 410 public function __construct() { 411 parent::__construct(); 412 413 $this->styleStash = new refnotes_namespace_style_stash($this); 414 $this->mappingStash = new refnotes_namespace_mapping_stash(); 415 } 416 417 /** 418 * 419 */ 420 public function markScopeStart($namespaceName, $callIndex) { 421 $this->getNamespace($namespaceName)->markScopeStart($callIndex); 422 } 423 424 /** 425 * 426 */ 427 public function markScopeEnd($namespaceName, $callIndex) { 428 $this->getNamespace($namespaceName)->markScopeEnd($callIndex); 429 } 430 431 /** 432 * Collect styling information from the page 433 */ 434 public function addStyle($namespaceName, $style) { 435 $this->styleStash->add($this->getNamespace($namespaceName), $style); 436 } 437 438 /** 439 * 440 */ 441 public function getStyles() { 442 return $this->styleStash; 443 } 444 445 /** 446 * Collect mapping information from the page 447 */ 448 public function addMapping($namespaceName, $map) { 449 $this->mappingStash->add($this->getNamespace($namespaceName), $map); 450 } 451 452 /** 453 * 454 */ 455 public function getMappings() { 456 return $this->mappingStash; 457 } 458 459 /** 460 * 461 */ 462 public function reset() { 463 $this->namespace = array(); 464 } 465 466 /** 467 * 468 */ 469 public function addReference($attributes, $data, $call) { 470 $note = $this->getNote($attributes['ns'], $attributes['name']); 471 $reference = new refnotes_action_reference($note, $attributes, $data, $call); 472 473 $note->addReference($reference); 474 475 return $reference; 476 } 477 478 /** 479 * 480 */ 481 public function rewriteReferences($namespaceName, $limit) { 482 $this->clearNamespaceMapping($namespaceName); 483 484 if ($namespaceName == '*') { 485 foreach ($this->namespace as $namespace) { 486 $namespace->rewriteReferences(); 487 } 488 } 489 else { 490 $namespace = $this->findNamespace($namespaceName); 491 if ($namespace != NULL) { 492 $namespace->rewriteReferences($limit); 493 } 494 } 495 } 496 497 /** 498 * 499 */ 500 protected function createNote($scope, $namespaceName, $noteName) { 501 return new refnotes_action_note($scope, $namespaceName, $noteName); 502 } 503} 504