diff options
Diffstat (limited to 'platform/www/lib/plugins/refnotes/namespace.php')
-rw-r--r-- | platform/www/lib/plugins/refnotes/namespace.php | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/platform/www/lib/plugins/refnotes/namespace.php b/platform/www/lib/plugins/refnotes/namespace.php new file mode 100644 index 0000000..c3fa4b5 --- /dev/null +++ b/platform/www/lib/plugins/refnotes/namespace.php @@ -0,0 +1,463 @@ +<?php + +/** + * Plugin RefNotes: Namespace heplers + * + * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) + * @author Mykola Ostrovskyy <dwpforge@gmail.com> + */ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +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; + } + } +} |