|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 00003 // This file is part of Moodle - http://moodle.org/ 00004 // 00005 // Moodle is free software: you can redistribute it and/or modify 00006 // it under the terms of the GNU General Public License as published by 00007 // the Free Software Foundation, either version 3 of the License, or 00008 // (at your option) any later version. 00009 // 00010 // Moodle is distributed in the hope that it will be useful, 00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 // GNU General Public License for more details. 00014 // 00015 // You should have received a copy of the GNU General Public License 00016 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00017 00025 require_once($CFG->dirroot.'/backup/util/xml/parser/processors/progressive_parser_processor.class.php'); 00026 00040 abstract class simplified_parser_processor extends progressive_parser_processor { 00041 protected $paths; // array of paths we are interested on 00042 protected $parentpaths; // array of parent paths of the $paths 00043 protected $parentsinfo; // array of parent attributes to be added as child tags 00044 protected $startendinfo;// array (stack) of startend information 00045 00046 public function __construct(array $paths = array()) { 00047 parent::__construct(); 00048 $this->paths = array(); 00049 $this->parentpaths = array(); 00050 $this->parentsinfo = array(); 00051 $this->startendinfo = array(); 00052 // Add paths and parentpaths. We are looking for attributes there 00053 foreach ($paths as $key => $path) { 00054 $this->add_path($path); 00055 } 00056 } 00057 00058 public function add_path($path) { 00059 $this->paths[] = $path; 00060 $this->parentpaths[] = progressive_parser::dirname($path); 00061 } 00062 00066 abstract protected function dispatch_chunk($data); 00067 00071 abstract protected function notify_path_start($path); 00072 00076 abstract protected function notify_path_end($path); 00077 00083 public function process_chunk($data) { 00084 // Precalculate some vars for readability 00085 $path = $data['path']; 00086 $parentpath = progressive_parser::dirname($path); 00087 $tag = basename($path); 00088 00089 // If the path is a registered parent one, store all its tags 00090 // so, we'll be able to find attributes later when processing 00091 // (child) registered paths (to get attributes if present) 00092 if ($this->path_is_selected_parent($path)) { // if path is parent 00093 if (isset($data['tags'])) { // and has tags, save them 00094 $this->parentsinfo[$path] = $data['tags']; 00095 } 00096 } 00097 00098 // If the path is a registered one, let's process it 00099 if ($this->path_is_selected($path)) { 00100 00101 // Send all the pending notify_path_start/end() notifications 00102 $this->process_pending_startend_notifications($path, 'start'); 00103 00104 // First of all, look for attributes available at parentsinfo 00105 // in order to get them available as normal tags 00106 if (isset($this->parentsinfo[$parentpath][$tag]['attrs'])) { 00107 $data['tags'] = array_merge($this->parentsinfo[$parentpath][$tag]['attrs'], $data['tags']); 00108 unset($this->parentsinfo[$parentpath][$tag]['attrs']); 00109 } 00110 // Now, let's simplify the tags array, ignoring tag attributtes and 00111 // reconverting to simpler name => value array. At the same time, 00112 // check for all the tag values being whitespace-string values, if all them 00113 // are whitespace strings, we aren't going to postprocess/dispatch the chunk 00114 $alltagswhitespace = true; 00115 foreach ($data['tags'] as $key => $value) { 00116 // If the value is already a single value, do nothing 00117 // surely was added above from parentsinfo attributes, 00118 // so we'll process the chunk always 00119 if (!is_array($value)) { 00120 $alltagswhitespace = false; 00121 continue; 00122 } 00123 00124 // If the path including the tag name matches another selected path 00125 // (registered or parent) and is null or begins with linefeed, we know it's part 00126 // of another chunk, delete it, another chunk will contain that info 00127 if ($this->path_is_selected($path . '/' . $key) || 00128 $this->path_is_selected_parent($path . '/' . $key)) { 00129 if (!isset($value['cdata']) || substr($value['cdata'], 0, 1) === "\n") { 00130 unset($data['tags'][$key]); 00131 continue; 00132 } 00133 } 00134 00135 // Convert to simple name => value array 00136 $data['tags'][$key] = isset($value['cdata']) ? $value['cdata'] : null; 00137 00138 // Check $alltagswhitespace continues being true 00139 if ($alltagswhitespace && strlen($data['tags'][$key]) !== 0 && trim($data['tags'][$key]) !== '') { 00140 $alltagswhitespace = false; // Found non-whitespace value 00141 } 00142 } 00143 00144 // Arrived here, if the chunk has tags and not all tags are whitespace, 00145 // send it to postprocess filter that will decide about dispatching. Else 00146 // skip the chunk completely 00147 if (!empty($data['tags']) && !$alltagswhitespace) { 00148 return $this->postprocess_chunk($data); 00149 } else { 00150 $this->chunks--; // Chunk skipped 00151 } 00152 } else { 00153 $this->chunks--; // Chunk skipped 00154 } 00155 00156 return true; 00157 } 00158 00164 public function before_path($path) { 00165 if ($this->path_is_selected($path)) { 00166 $this->startendinfo[] = array('path' => $path, 'action' => 'start'); 00167 } 00168 } 00169 00175 public function after_path($path) { 00176 $toprocess = false; 00177 // If the path being closed matches (same or parent) the first path in the stack 00178 // we process pending startend notifications until one matching end is found 00179 if ($element = reset($this->startendinfo)) { 00180 $elepath = $element['path']; 00181 $eleaction = $element['action']; 00182 if (strpos($elepath, $path) === 0) { 00183 $toprocess = true; 00184 } 00185 00186 // Also, if the stack of startend notifications is empty, we can process current end 00187 // path safely 00188 } else { 00189 $toprocess = true; 00190 } 00191 if ($this->path_is_selected($path)) { 00192 $this->startendinfo[] = array('path' => $path, 'action' => 'end'); 00193 } 00194 // Send all the pending startend notifications if decided to do so 00195 if ($toprocess) { 00196 $this->process_pending_startend_notifications($path, 'end'); 00197 } 00198 } 00199 00200 00201 // Protected API starts here 00202 00212 protected function process_pending_startend_notifications($path, $action) { 00213 00214 // Iterate until one matching path and action is found (or the array is empty) 00215 $elecount = count($this->startendinfo); 00216 $elematch = false; 00217 while ($elecount > 0 && !$elematch) { 00218 $element = array_shift($this->startendinfo); 00219 $elecount--; 00220 $elepath = $element['path']; 00221 $eleaction = $element['action']; 00222 00223 if ($elepath == $path && $eleaction == $action) { 00224 $elematch = true; 00225 } 00226 00227 if ($eleaction == 'start') { 00228 $this->notify_path_start($elepath); 00229 } else { 00230 $this->notify_path_end($elepath); 00231 } 00232 } 00233 } 00234 00235 protected function postprocess_chunk($data) { 00236 $this->dispatch_chunk($data); 00237 } 00238 00239 protected function path_is_selected($path) { 00240 return in_array($path, $this->paths); 00241 } 00242 00243 protected function path_is_selected_parent($path) { 00244 return in_array($path, $this->parentpaths); 00245 } 00246 00250 protected function selected_parent_exists($path) { 00251 $parentpath = progressive_parser::dirname($path); 00252 while ($parentpath != '/') { 00253 if ($this->path_is_selected($parentpath)) { 00254 return $parentpath; 00255 } 00256 $parentpath = progressive_parser::dirname($parentpath); 00257 } 00258 return false; 00259 } 00260 }