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 324 /** 325 * Renderer core is used by both references and notes syntax plugins during the rendering 326 * stage. The instance has to be shared between the plugins, and since there should be no 327 * more than one rendering pass during a DW page request, a single instance of the syntax 328 * core should be enough. 329 */ 330 public static function getInstance() { 331 if (self::$instance == NULL) { 332 self::$instance = new refnotes_renderer_core(); 333 } 334 335 return self::$instance; 336 } 337 338 /** 339 * 340 */ 341 public function addReference($attributes, $data) { 342 $note = $this->getNote($attributes['ns'], $attributes['name']); 343 $reference = new refnotes_renderer_reference($note, $attributes, $data); 344 345 $note->addReference($reference); 346 347 return $reference; 348 } 349 350 /** 351 * 352 */ 353 public function renderNotes($mode, $namespaceName, $limit) { 354 $this->clearNamespaceMapping($namespaceName); 355 356 $html = ''; 357 358 if ($namespaceName == '*') { 359 foreach ($this->namespace as $namespace) { 360 $html .= $namespace->renderNotes($mode); 361 } 362 } 363 else { 364 $namespace = $this->findNamespace($namespaceName); 365 if ($namespace != NULL) { 366 $html = $namespace->renderNotes($mode, $limit); 367 } 368 } 369 370 return $html; 371 } 372 373 /** 374 * 375 */ 376 protected function createNote($scope, $namespaceName, $noteName) { 377 return new refnotes_renderer_note($scope, $namespaceName, $noteName); 378 } 379} 380 381//////////////////////////////////////////////////////////////////////////////////////////////////// 382class refnotes_action_core extends refnotes_core { 383 384 private $styleStash; 385 private $mappingStash; 386 387 /** 388 * Constructor 389 */ 390 public function __construct() { 391 parent::__construct(); 392 393 $this->styleStash = new refnotes_namespace_style_stash($this); 394 $this->mappingStash = new refnotes_namespace_mapping_stash(); 395 } 396 397 /** 398 * 399 */ 400 public function markScopeStart($namespaceName, $callIndex) { 401 $this->getNamespace($namespaceName)->markScopeStart($callIndex); 402 } 403 404 /** 405 * 406 */ 407 public function markScopeEnd($namespaceName, $callIndex) { 408 $this->getNamespace($namespaceName)->markScopeEnd($callIndex); 409 } 410 411 /** 412 * Collect styling information from the page 413 */ 414 public function addStyle($namespaceName, $style) { 415 $this->styleStash->add($this->getNamespace($namespaceName), $style); 416 } 417 418 /** 419 * 420 */ 421 public function getStyles() { 422 return $this->styleStash; 423 } 424 425 /** 426 * Collect mapping information from the page 427 */ 428 public function addMapping($namespaceName, $map) { 429 $this->mappingStash->add($this->getNamespace($namespaceName), $map); 430 } 431 432 /** 433 * 434 */ 435 public function getMappings() { 436 return $this->mappingStash; 437 } 438 439 /** 440 * 441 */ 442 public function reset() { 443 $this->namespace = array(); 444 } 445 446 /** 447 * 448 */ 449 public function addReference($attributes, $data, $call) { 450 $note = $this->getNote($attributes['ns'], $attributes['name']); 451 $reference = new refnotes_action_reference($note, $attributes, $data, $call); 452 453 $note->addReference($reference); 454 455 return $reference; 456 } 457 458 /** 459 * 460 */ 461 public function rewriteReferences($namespaceName, $limit) { 462 $this->clearNamespaceMapping($namespaceName); 463 464 if ($namespaceName == '*') { 465 foreach ($this->namespace as $namespace) { 466 $namespace->rewriteReferences(); 467 } 468 } 469 else { 470 $namespace = $this->findNamespace($namespaceName); 471 if ($namespace != NULL) { 472 $namespace->rewriteReferences($limit); 473 } 474 } 475 } 476 477 /** 478 * 479 */ 480 protected function createNote($scope, $namespaceName, $noteName) { 481 return new refnotes_action_note($scope, $namespaceName, $noteName); 482 } 483} 484