35f4fbf32cadb0a0e5cf0cb4affce707013d82fa
[moodle.git] / backup / util / structure / base_final_element.class.php
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * @package    moodlecore
20  * @subpackage backup-structure
21  * @copyright  2010 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  *
24  * TODO: Finish phpdocs
25  */
27 /**
28  * Abstract class representing one final element atom (name/value/parent) piece of information
29  */
30 abstract class base_final_element extends base_atom {
32     /** @var array base_attributes of the element (maps to XML attributes of the tag) */
33         private $attributes;
35     /** @var base_nested_element parent of this element (describes structure of the XML file) */
36         private $parent;
38     /**
39      * Constructor - instantiates one base_final_element, specifying its basic info.
40      *
41      * @param string $name name of the element
42      * @param array  $attributes attributes this element will handle (optional, defaults to null)
43      */
44     public function __construct($name, $attributes = null) {
45         parent::__construct($name);
46         $this->attributes = array();
47         if (!empty($attributes)) {
48             $this->add_attributes($attributes);
49         }
50         $this->parent = null;
51     }
53     protected function set_parent($element) {
54         if ($this->parent) {
55             $info = new stdClass();
56             $info->currparent= $this->parent->get_name();
57             $info->newparent = $element->get_name();
58             $info->element   = $this->get_name();
59             throw new base_element_parent_exception('baseelementhasparent', $info);
60         }
61         $this->parent = $element;
62     }
64     protected function get_grandparent() {
65         $parent = $this->parent;
66         if ($parent instanceof base_nested_element) {
67             return $parent->get_grandparent();
68         } else {
69             return $this;
70         }
71     }
73     protected function get_grandoptigroupelement_or_grandparent() {
74         $parent = $this->parent;
75         if ($parent instanceof base_optigroup) {
76             return $this; // Have found one parent optigroup, so I (first child of optigroup) am
77         } else if ($parent instanceof base_nested_element) {
78             return $parent->get_grandoptigroupelement_or_grandparent(); // Continue searching
79         } else {
80             return $this;
81         }
82     }
84     protected function find_element_by_path($path) {
85         $patharr = explode('/', trim($path, '/')); // Split the path trimming slashes
86         if (substr($path, 0, 1) == '/') { // Absolute path, go to grandparent and process
87             if (!$this->get_grandparent() instanceof base_nested_element) {
88                 throw new base_element_struct_exception('baseelementincorrectgrandparent', $patharr[0]);
89             } else if ($this->get_grandparent()->get_name() !== $patharr[0]) {
90                 throw new base_element_struct_exception('baseelementincorrectgrandparent', $patharr[0]);
91             } else {
92                 $newpath = implode('/', array_slice($patharr, 1)); // Take out 1st element
93                 return $this->get_grandparent()->find_element_by_path($newpath); // Process as relative in grandparent
94             }
95         } else {
96             if ($patharr[0] == '..') { // Go to parent
97                 if (!$this->get_parent() instanceof base_nested_element) {
98                     throw new base_element_struct_exception('baseelementincorrectparent', $patharr[0]);
99                 } else {
100                     $newpath = implode('/', array_slice($patharr, 1)); // Take out 1st element
101                     return $this->get_parent()->find_element_by_path($newpath); // Process as relative in parent
102                 }
103             } else if (count($patharr) > 1) { // Go to next child
104                 if (!$this->get_child($patharr[0]) instanceof base_nested_element) {
105                     throw new base_element_struct_exception('baseelementincorrectchild', $patharr[0]);
106                 } else {
107                     $newpath = implode('/', array_slice($patharr, 1)); // Take out 1st element
108                     return $this->get_child($patharr[0])->find_element_by_path($newpath); // Process as relative in parent
109                 }
110             } else { // Return final element or attribute
111                 if ($this->get_final_element($patharr[0]) instanceof base_final_element) {
112                     return $this->get_final_element($patharr[0]);
113                 } else if ($this->get_attribute($patharr[0]) instanceof base_attribute) {
114                     return $this->get_attribute($patharr[0]);
115                 } else {
116                     throw new base_element_struct_exception('baseelementincorrectfinalorattribute', $patharr[0]);
117                 }
118             }
119         }
120     }
122     protected function find_first_parent_by_name($name) {
123         if ($parent = $this->get_parent()) { // If element has parent
124             $element   = $parent->get_final_element($name); // Look for name into parent finals
125             $attribute = $parent->get_attribute($name);     // Look for name into parent attrs
126             if ($element instanceof base_final_element) {
127                 return $element;
129             } else if ($attribute instanceof base_attribute) {
130                 return $attribute;
132             } else { // Not found, go up 1 level and continue searching
133                 return $parent->find_first_parent_by_name($name);
134             }
135         } else { // No more parents available, return the original backup::VAR_PARENTID, exception
136             throw new base_element_struct_exception('cannotfindparentidforelement', $name);
137         }
138     }
141 /// Public API starts here
143     public function get_attributes() {
144         return $this->attributes;
145     }
147     public function get_attribute($name) {
148         if (array_key_exists($name, $this->attributes)) {
149             return $this->attributes[$name];
150         } else {
151             return null;
152         }
153     }
155     public function get_parent() {
156         return $this->parent;
157     }
159     public function get_level() {
160         return $this->parent == null ? 1 : $this->parent->get_level() + 1;
161     }
163     public function add_attributes($attributes) {
164         if ($attributes instanceof base_attribute || is_string($attributes)) { // Accept 1 attribute, object or string
165             $attributes = array($attributes);
166         }
167         if (is_array($attributes)) {
168             foreach ($attributes as $attribute) {
169                 if (is_string($attribute)) { // Accept string attributes
170                     $attribute = $this->get_new_attribute($attribute);
171                 }
172                 if (!($attribute instanceof base_attribute)) {
173                     throw new base_element_attribute_exception('baseelementnoattribute', get_class($attribute));
174                 }
175                 if (array_key_exists($attribute->get_name(), $this->attributes)) {
176                     throw new base_element_attribute_exception('baseelementattributeexists', $attribute->get_name());
177                 }
178                 $this->attributes[$attribute->get_name()] = $attribute;
179             }
180         } else {
181             throw new base_element_attribute_exception('baseelementattributeincorrect');
182         }
183     }
185     public function clean_values() {
186         parent::clean_value();
187         if (!empty($this->attributes)) {
188             foreach ($this->attributes as $attribute) {
189                 $attribute->clean_value();
190             }
191         }
192     }
194     public function to_string($showvalue = false) {
195         // Decide the correct prefix
196         $prefix = '#'; // default
197         if ($this->parent instanceof base_optigroup) {
198             $prefix = '?';
199         } else if ($this instanceof base_nested_element) {
200             $prefix = '';
201         }
202         $indent = str_repeat('    ', $this->get_level()); // Indent output based in level (4cc)
203         $output = $indent . $prefix . $this->get_name() . ' (level: ' . $this->get_level() . ')';
204         if ($showvalue) {
205             $value = $this->is_set() ? $this->get_value() : 'not set';
206             $output .= ' => ' . $value;
207         }
208         if (!empty($this->attributes)) {
209             foreach ($this->attributes as $attribute) {
210                 $output .= PHP_EOL . $indent . '    ' . $attribute->to_string($showvalue);
211             }
212         }
213         return $output;
214     }
216 // Implementable API
218     /**
219      * Returns one instace of the @base_attribute class to work with
220      * when attributes are added simply by name
221      */
222     abstract protected function get_new_attribute($name);
225 /**
226  * base_element exception to control all the errors related with parents handling
227  */
228 class base_element_parent_exception extends base_atom_exception {
230     /**
231      * Constructor - instantiates one base_element_parent_exception
232      *
233      * @param string $errorcode key for the corresponding error string
234      * @param object $a extra words and phrases that might be required in the error string
235      * @param string $debuginfo optional debugging information
236      */
237     public function __construct($errorcode, $a = null, $debuginfo = null) {
238         parent::__construct($errorcode, $a, $debuginfo);
239     }
242 /**
243  * base_element exception to control all the errors related with attributes handling
244  */
245 class base_element_attribute_exception extends base_atom_exception {
247     /**
248      * Constructor - instantiates one base_element_attribute_exception
249      *
250      * @param string $errorcode key for the corresponding error string
251      * @param object $a extra words and phrases that might be required in the error string
252      * @param string $debuginfo optional debugging information
253      */
254     public function __construct($errorcode, $a = null, $debuginfo = null) {
255         parent::__construct($errorcode, $a, $debuginfo);
256     }