Changeset View
Changeset View
Standalone View
Standalone View
src/filesystem/PhutilFileTree.php
- This file was added.
| <?php | |||||
| /** | |||||
| * Data structure for representing filesystem directory trees. | |||||
| */ | |||||
| final class PhutilFileTree extends Phobject { | |||||
| private $name; | |||||
| private $fullPath; | |||||
| private $data; | |||||
| private $depth = 0; | |||||
| private $parentNode; | |||||
| private $children = array(); | |||||
| public function addPath($path, $data) { | |||||
| $parts = $this->splitPath($path); | |||||
| $parts = array_reverse($parts); | |||||
| $this->insertPath($parts, $data); | |||||
| return $this; | |||||
| } | |||||
| public function destroy() { | |||||
| $this->parentNode = null; | |||||
| foreach ($this->children as $child) { | |||||
| $child->destroy(); | |||||
| } | |||||
| $this->children = array(); | |||||
| return $this; | |||||
| } | |||||
| /** | |||||
| * Get the next node, iterating in depth-first order. | |||||
| */ | |||||
| public function getNextNode() { | |||||
| if ($this->children) { | |||||
| return head($this->children); | |||||
| } | |||||
| $cursor = $this; | |||||
| while ($cursor) { | |||||
| if ($cursor->getNextSibling()) { | |||||
| return $cursor->getNextSibling(); | |||||
| } | |||||
| $cursor = $cursor->parentNode; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| public function getName() { | |||||
| return $this->name; | |||||
| } | |||||
| public function getFullPath() { | |||||
| return $this->fullPath; | |||||
| } | |||||
| public function getDepth() { | |||||
| return $this->depth; | |||||
| } | |||||
| public function getData() { | |||||
| return $this->data; | |||||
| } | |||||
| protected function insertPath(array $parts, $data) { | |||||
| $part = array_pop($parts); | |||||
| if ($part === null) { | |||||
| if ($this->data) { | |||||
| $full_path = $this->getFullPath(); | |||||
| throw new Exception( | |||||
| pht("Duplicate insertion for path '%s'.", $full_path)); | |||||
| } | |||||
| $this->data = $data; | |||||
| return; | |||||
| } | |||||
| if (empty($this->children[$part])) { | |||||
| $node = new PhutilFileTree(); | |||||
| $node->parentNode = $this; | |||||
| $node->depth = $this->depth + 1; | |||||
| $node->name = $part; | |||||
| $node->fullPath = $this->parentNode ? ($this->fullPath.'/'.$part) : $part; | |||||
| $this->children[$part] = $node; | |||||
| } | |||||
| $this->children[$part]->insertPath($parts, $data); | |||||
| } | |||||
| protected function splitPath($path) { | |||||
| $path = trim($path, '/'); | |||||
| $parts = preg_split('@/+@', $path); | |||||
| return $parts; | |||||
| } | |||||
| protected function getNextSibling() { | |||||
| if (!$this->parentNode) { | |||||
| return null; | |||||
| } | |||||
| $found = false; | |||||
| foreach ($this->parentNode->children as $node) { | |||||
| if ($found) { | |||||
| return $node; | |||||
| } | |||||
| if ($this->name === $node->name) { | |||||
| $found = true; | |||||
| } | |||||
| } | |||||
| return null; | |||||
| } | |||||
| } | |||||