Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
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/>.
17
 
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
 */
26
 
27
/**
28
 * Abstract class representing one nestable element (non final) piece of information
29
 */
30
abstract class base_nested_element extends base_final_element {
31
 
32
    /** @var array final elements of the element (maps to XML final elements of the tag) */
33
    private $final_elements;
34
 
35
    /** @var array children base_elements of this element (describes structure of the XML file) */
36
    private $children;
37
 
38
    /** @var base_optigroup optional group of this element (branches to be processed conditionally) */
39
    private $optigroup;
40
 
41
    /** @var array elements already used by the base_element, to avoid circular references */
42
    private $used;
43
 
44
    /**
45
     * Constructor - instantiates one base_nested_element, specifying its basic info.
46
     *
47
     * @param string $name name of the element
48
     * @param array  $attributes attributes this element will handle (optional, defaults to null)
49
     * @param array  $final_elements this element will handle (optional, defaults to null)
50
     */
51
    public function __construct($name, $attributes = null, $final_elements = null) {
52
        parent::__construct($name, $attributes);
53
        $this->final_elements = array();
54
        if (!empty($final_elements)) {
55
            $this->add_final_elements($final_elements);
56
        }
57
        $this->children = array();
58
        $this->optigroup = null;
59
        $this->used[] = $name;
60
    }
61
 
62
    /**
63
     * Destroy all circular references. It helps PHP 5.2 a lot!
64
     */
65
    public function destroy() {
66
        // Before reseting anything, call destroy recursively
67
        foreach ($this->children as $child) {
68
            $child->destroy();
69
        }
70
        foreach ($this->final_elements as $element) {
71
            $element->destroy();
72
        }
73
        if ($this->optigroup) {
74
            $this->optigroup->destroy();
75
        }
76
        // Everything has been destroyed recursively, now we can reset safely
77
        $this->children = array();
78
        $this->final_elements = array();
79
        $this->optigroup = null;
80
        // Delegate to parent to destroy other bits
81
        parent::destroy();
82
    }
83
 
84
    protected function get_used() {
85
        return $this->used;
86
    }
87
 
88
    protected function set_used($used) {
89
        $this->used = $used;
90
    }
91
 
92
    protected function add_used($element) {
93
        $this->used = array_merge($this->used, $element->get_used());
94
    }
95
 
96
    protected function check_and_set_used($element) {
97
        // First of all, check the element being added doesn't conflict with own final elements
98
        if (array_key_exists($element->get_name(), $this->final_elements)) {
99
            throw new base_element_struct_exception('baseelementchildnameconflict', $element->get_name());
100
        }
101
        $grandparent = $this->get_grandoptigroupelement_or_grandparent();
102
        if ($existing = array_intersect($grandparent->get_used(), $element->get_used())) { // Check the element isn't being used already
103
            throw new base_element_struct_exception('baseelementexisting', implode($existing));
104
        }
105
        $grandparent->add_used($element);
106
        // If the parent is one optigroup, add the element useds to it too
107
        if ($grandparent->get_parent() instanceof base_optigroup) {
108
            $grandparent->get_parent()->add_used($element);
109
        }
110
 
111
    }
112
 
113
/// Public API starts here
114
 
115
    public function get_final_elements() {
116
        return $this->final_elements;
117
    }
118
 
119
    public function get_final_element($name) {
120
        if (array_key_exists($name, $this->final_elements)) {
121
            return $this->final_elements[$name];
122
        } else {
123
            return null;
124
        }
125
    }
126
 
127
    public function get_children() {
128
        return $this->children;
129
    }
130
 
131
    public function get_child($name) {
132
        if (array_key_exists($name, $this->children)) {
133
            return $this->children[$name];
134
        } else {
135
            return null;
136
        }
137
    }
138
 
139
    public function get_optigroup() {
140
        return $this->optigroup;
141
    }
142
 
143
    public function add_final_elements($final_elements) {
144
        if ($final_elements instanceof base_final_element || is_string($final_elements)) { // Accept 1 final_element, object or string
145
            $final_elements = array($final_elements);
146
        }
147
        if (is_array($final_elements)) {
148
            foreach ($final_elements as $final_element) {
149
                if (is_string($final_element)) { // Accept string final_elements
150
                    $final_element = $this->get_new_final_element($final_element);
151
                }
152
                if (!($final_element instanceof base_final_element)) {
153
                    throw new base_element_struct_exception('baseelementnofinalelement', get_class($final_element));
154
                }
155
                if (array_key_exists($final_element->get_name(), $this->final_elements)) {
156
                    throw new base_element_struct_exception('baseelementexists', $final_element->get_name());
157
                }
158
                $this->final_elements[$final_element->get_name()] = $final_element;
159
                $final_element->set_parent($this);
160
            }
161
        } else {
162
            throw new base_element_struct_exception('baseelementincorrect');
163
        }
164
    }
165
 
166
    public function add_child($element) {
167
        if (!is_object($element) || !($element instanceof base_nested_element)) { // parameter must be a base_nested_element
168
            if (!is_object($element) || !($found = get_class($element))) {
169
                $found = 'non object';
170
            }
171
            throw new base_element_struct_exception('nestedelementincorrect', $found);
172
        }
173
        $this->check_and_set_used($element);
174
        $this->children[$element->get_name()] = $element;
175
        $element->set_parent($this);
176
    }
177
 
178
    public function add_optigroup($optigroup) {
179
        if (!($optigroup instanceof base_optigroup)) { // parameter must be a base_optigroup
180
            if (!$found = get_class($optigroup)) {
181
                $found = 'non object';
182
            }
183
            throw new base_element_struct_exception('optigroupincorrect', $found);
184
        }
185
        if ($this->optigroup !== null) {
186
            throw new base_element_struct_exception('optigroupalreadyset', $found);
187
        }
188
        $this->check_and_set_used($optigroup);
189
        $this->optigroup = $optigroup;
190
        $optigroup->set_parent($this);
191
    }
192
 
193
    public function get_value() {
194
        throw new base_element_struct_exception('nestedelementnotvalue');
195
    }
196
 
197
    public function set_value($value) {
198
        throw new base_element_struct_exception('nestedelementnotvalue');
199
    }
200
 
201
    public function clean_value() {
202
        throw new base_element_struct_exception('nestedelementnotvalue');
203
    }
204
 
205
    public function clean_values() {
206
        parent::clean_values();
207
        if (!empty($this->final_elements)) {
208
            foreach ($this->final_elements as $final_element) {
209
                $final_element->clean_values();
210
            }
211
        }
212
        if (!empty($this->children)) {
213
            foreach ($this->children as $child) {
214
                $child->clean_values();
215
            }
216
        }
217
        if (!empty($this->optigroup)) {
218
            $this->optigroup->clean_values();
219
        }
220
    }
221
 
222
    public function to_string($showvalue = false) {
223
        $output = parent::to_string($showvalue);
224
        if (!empty($this->final_elements)) {
225
            foreach ($this->final_elements as $final_element) {
226
                $output .= PHP_EOL . $final_element->to_string($showvalue);
227
            }
228
        }
229
        if (!empty($this->children)) {
230
            foreach ($this->children as $child) {
231
                $output .= PHP_EOL . $child->to_string($showvalue);
232
            }
233
        }
234
        if (!empty($this->optigroup)) {
235
            $output .= PHP_EOL . $this->optigroup->to_string($showvalue);
236
        }
237
        return $output;
238
    }
239
 
240
// Implementable API
241
 
242
    /**
243
     * Returns one instace of the @final_element class to work with
244
     * when final_elements are added simply by name
245
     */
246
    abstract protected function get_new_final_element($name);
247
}
248
 
249
/**
250
 * base_element exception to control all the errors while building the nested tree
251
 *
252
 * This exception will be thrown each time the base_element class detects some
253
 * inconsistency related with the building of the nested tree representing one base part
254
 * (invalid objects, circular references, double parents...)
255
 */
256
class base_element_struct_exception extends base_atom_exception {
257
 
258
    /**
259
     * Constructor - instantiates one base_element_struct_exception
260
     *
261
     * @param string $errorcode key for the corresponding error string
262
     * @param object $a extra words and phrases that might be required in the error string
263
     * @param string $debuginfo optional debugging information
264
     */
265
    public function __construct($errorcode, $a = null, $debuginfo = null) {
266
        parent::__construct($errorcode, $a, $debuginfo);
267
    }
268
}