*/ //////////////////////////////////////////////////////////////////////////////////////////////////// abstract class refnotes_namespace_data_stash { protected $index; /** * Constructor */ public function __construct() { $this->index = array(); } /** * */ abstract public function add($namespace, $data); /** * */ public function getCount() { return count($this->index); } /** * */ public function getIndex() { return array_keys($this->index); } /** * */ public function getAt($index) { return array_key_exists($index, $this->index) ? $this->index[$index] : array(); } /** * */ public function sort() { ksort($this->index); } } //////////////////////////////////////////////////////////////////////////////////////////////////// class refnotes_namespace_data { protected $namespace; protected $data; /** * Constructor */ public function __construct($namespace, $data) { $this->namespace = $namespace; $this->data = $data; } /** * */ public function getNamespace() { return $this->namespace->getName(); } /** * */ public function getData() { return $this->data; } } //////////////////////////////////////////////////////////////////////////////////////////////////// class refnotes_namespace_style_stash extends refnotes_namespace_data_stash { private $page; /** * Constructor */ public function __construct($page) { parent::__construct(); $this->page = $page; } /** * */ public function add($namespace, $data) { $style = new refnotes_namespace_style_info($namespace, $data); $parent = $style->getInheritedNamespace(); if (($parent == '') && ($namespace->getScopesCount() == 1)) { /* Default inheritance for the first scope */ $parent = refnotes_namespace::getParentName($namespace->getName()); } $index = $namespace->getStyleIndex($this->page->findParentNamespace($parent)); $this->index[$index][] = $style; } /** * Sort the style blocks so that the namespaces with inherited style go after * the namespaces they inherit from. */ public function sort() { parent::sort(); $this->sortByDefaultInheritance(); $this->sortByExplicitInheritance(); } /** * */ private function sortByDefaultInheritance() { foreach ($this->index as &$index) { $namespace = array(); foreach ($index as $style) { $namespace[] = $style->getNamespace(); } array_multisort($namespace, SORT_ASC, $index); } } /** * */ private function sortByExplicitInheritance() { foreach ($this->index as &$index) { $derived = array(); $sorted = array(); foreach ($index as $style) { if ($style->isDerived()) { $derived[] = $style; } else { $sorted[] = $style; } } $derivedCount = count($derived); if ($derivedCount > 0) { if ($derivedCount == 1) { $sorted[] = $derived[0]; } else { /* Perform simplified topological sorting */ $target = array(); $source = array(); for ($i = 0; $i < $derivedCount; $i++) { $target[$i] = $derived[$i]->getNamespace(); $source[$i] = $derived[$i]->getInheritedNamespace(); } for ($j = 0; $j < $derivedCount; $j++) { foreach ($source as $i => $s) { if (!in_array($s, $target)) { break; } } $sorted[] = $derived[$i]; unset($target[$i]); unset($source[$i]); } } } $index = $sorted; } } } //////////////////////////////////////////////////////////////////////////////////////////////////// class refnotes_namespace_style_info extends refnotes_namespace_data { /** * */ public function isDerived() { return array_key_exists('inherit', $this->data); } /** * */ public function getInheritedNamespace() { return $this->isDerived() ? $this->data['inherit'] : ''; } } //////////////////////////////////////////////////////////////////////////////////////////////////// class refnotes_namespace_mapping_stash extends refnotes_namespace_data_stash { /** * */ public function add($namespace, $data) { $this->index[$namespace->getMappingIndex()][] = new refnotes_namespace_data($namespace, $data); } } //////////////////////////////////////////////////////////////////////////////////////////////////// class refnotes_namespace { private $name; private $style; private $renderer; private $scope; private $newScope; /** * */ public static function getNamePattern($type) { $result = '(?:(?:' . refnotes_note::getNamePattern('strict') . ')?:)*'; if ($type == 'required') { $result .= '(?::|' . refnotes_note::getNamePattern('strict') . '):*'; } return $result; } /** * Returns canonic name for a namespace */ public static function canonizeName($name) { return preg_replace('/:{2,}/', ':', ':' . $name . ':'); } /** * Returns name of the parent namespace */ public static function getParentName($name) { return preg_replace('/\w*:$/', '', $name); } /** * Splits full note name into namespace and name components */ public static function parseName($name) { $pos = strrpos($name, ':'); if ($pos !== false) { $namespace = self::canonizeName(substr($name, 0, $pos)); $name = substr($name, $pos + 1); } else { $namespace = ':'; } return array($namespace, $name); } /** * Constructor */ public function __construct($name, $parent = NULL) { $this->name = $name; $this->style = array(); $this->renderer = NULL; $this->scope = array(); $this->newScope = true; if ($parent != NULL) { $this->style = $parent->style; } } /** * */ public function getName() { return $this->name; } /** * */ public function getScopesCount() { return count($this->scope); } /** * */ public function inheritStyle($source) { $this->style = $source->style; $this->renderer = NULL; } /** * */ public function setStyle($style) { $this->style = array_merge($this->style, $style); $this->renderer = NULL; } /** * */ public function getStyle($name) { return array_key_exists($name, $this->style) ? $this->style[$name] : ''; } /** * Defer creation of renderer until namespace style is set. */ public function getRenderer() { if ($this->renderer == NULL) { $this->renderer = new refnotes_renderer($this); } return $this->renderer; } /** * */ private function getScope($index) { $index = count($this->scope) + $index; return ($index >= 0) ? $this->scope[$index] : new refnotes_scope_mock(); } /** * */ private function getPreviousScope() { return $this->getScope(-2); } /** * */ private function getCurrentScope() { return $this->getScope(-1); } /** * */ public function getActiveScope() { if ($this->newScope) { $this->scope[] = new refnotes_scope($this, count($this->scope) + 1); $this->newScope = false; } return $this->getCurrentScope(); } /** * */ public function markScopeStart($callIndex) { if (!$this->getCurrentScope()->isOpen()) { $this->scope[] = new refnotes_scope(NULL, 0, $callIndex); } } /** * */ public function markScopeEnd($callIndex) { /* Create an empty scope if there is no open one */ $this->markScopeStart($callIndex - 1); $this->getCurrentScope()->getLimits()->end = $callIndex; } /** * Find last scope end within specified range */ private function findScopeEnd($start, $end) { for ($i = count($this->scope) - 1; $i >= 0; $i--) { $scopeEnd = $this->scope[$i]->getLimits()->end; if (($scopeEnd > $start) && ($scopeEnd < $end)) { return $scopeEnd; } } return -1; } /** * */ public function getStyleIndex($parent) { $previousEnd = $this->getPreviousScope()->getLimits()->end; $currentStart = $this->getCurrentScope()->getLimits()->start; $parentEnd = ($parent != NULL) ? $parent->findScopeEnd($previousEnd, $currentStart) : -1; return max($parentEnd, $previousEnd) + 1; } /** * */ public function getMappingIndex() { return $this->getPreviousScope()->getLimits()->end + 1; } /** * */ public function rewriteReferences($limit = '') { $this->resetScope(); if (count($this->scope) > 0) { $html = $this->getCurrentScope()->rewriteReferences($limit); } } /** * */ public function renderNotes($mode, $limit = '') { $this->resetScope(); $doc = ''; if (count($this->scope) > 0) { $doc = $this->getCurrentScope()->renderNotes($mode, $limit); } return $doc; } /** * */ private function resetScope() { switch ($this->getStyle('scoping')) { case 'single': break; default: $this->newScope = true; break; } } }