/[cvs]/nfo/php/libs/org.netfrag.glib/Data/Lift/hash/topic/EasyTree.php
ViewVC logotype

Annotation of /nfo/php/libs/org.netfrag.glib/Data/Lift/hash/topic/EasyTree.php

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations)
Tue May 13 15:25:44 2003 UTC (21 years, 4 months ago) by joko
Branch: MAIN
CVS Tags: HEAD
Changes since 1.1: +498 -101 lines
revamped completely

1 joko 1.1 <?
2     /**
3 joko 1.2 * This file contains a Data::Lift actor component:
4     * Data::Lift::hash::topic::EasyTree
5     * Helper class to negotiate tree node browsing against a remote service.
6 joko 1.1 *
7     * @author Andreas Motl <andreas.motl@ilo.de>
8     * @package org.netfrag.glib
9 joko 1.2 * @name Data::Lift::hash::topic::EasyTree
10 joko 1.1 *
11     */
12    
13     /**
14     * Cvs-Log:
15     *
16 joko 1.2 * $Id: EasyTree.php,v 1.1 2003/04/18 15:49:52 joko Exp $
17 joko 1.1 *
18 joko 1.2 * $Log: EasyTree.php,v $
19     * Revision 1.1 2003/04/18 15:49:52 joko
20     * initial commit
21 joko 1.1 *
22     */
23    
24     /**
25 joko 1.2 * Data::Lift::hash::topic::EasyTree
26     *
27     * Helper class to establish tree node browsing
28     * negotiation against an api of a remote service
29     * responding with linked lists in XML::Parser::EasyTree
30     * style. (available on CPAN)
31     *
32     *
33     * [1.] This actor traverses a nested data structure in style of
34     * CPAN's XML::Parser::EasyTree and translates it into
35     * a representation which is possible to feed to the
36     * PEAR::Tree lift actor component.
37     * "NodeAnchoring" (see below) also occours at this stage.
38     *
39     * 2. The PEAR::Tree lift will translate the tree
40     * to yet another representation which is fed to the
41     * HTML_TreeMenu Class (also available from PEAR).
42     *
43     * 3. This step - you may guess it - is also performed by
44     * another lift actor:
45     * It's Data::Lift::object::tree::common::PEAR::HTML::TreeMenu
46     * and it's worker components TreeMenu::DHTML and TreeMenu::Listbox.
47     *
48     *
49     * By this - and as you may see above - the translation
50     * process is spawned over three differents steps.
51     * This may be memory hungry and/or a performance bottleneck.
52     * Please be aware of this. The implementation gears
53     * towards clean seperation of all steps required to
54     * recieve a nested data representation of an xml document
55     * beeing able to "anchor" each single node.
56     *
57     * These "anchors" are made up of XPath filter queries
58     * which in turn will handle the "DiveIntoNode"-negotiation
59     * with the backend transparently for your convenience.
60     *
61     * The creation/maintenance of the proper "anchor" to access
62     * while tree traversal is also the job of this class and/or
63     * its descendants. So be aware of this when mungling
64     * anything or inheriting from us overwriting methods.
65     * Most probably it will break behavior at some
66     * translation step. (If the tree doesn't even break completely,
67     * but this is what we try to avoid through breaking up the
68     * translation into some steps)
69     * TODO: This "Anchoring" should *also* be split up into
70     * a seperate component. Maybe we could reuse it for
71     * other data widgets besides trees?
72     * However - this would be the last stuff required to
73     * split up the functionality of *this* class into its
74     * topic parts and should increase stability as well
75     * when reusing and/or extending this class.
76     *
77     *
78     * If you are interested in "Anchoring" and want to change behavior:
79     *
80     * Anchoring is relatively simple. If we arrive at known parts of the
81     * nested data structure, we just push the appropriate "elements" onto
82     * the anchor stack. If we "dive out" of this part, we pop the element
83     * off it.
84     * The "elements" of the anchor stack are already in XPath syntax
85     * describing "the node we mean" relatively to the current level.
86     *
87     * What's that about?
88     * It's difficult to map "nodes" orthogonally from the xml document to
89     * its tree representation. The translator has to decide about how to
90     * wrap a) the attributes and b) possible childnodes inside the content
91     * area of each xml tag to a memory data structure representing this
92     * node. The easiest way to achieve this is by doing oo programming
93     * and making up (e.g.) a class "Node" having appropriate attributes
94     * and code to implement its behavior as an arbitrary tree node.
95     * Multiple instances of a nodes' class make up the tree at runtime.
96     *
97     * The problem with this approach is if it comes to the point you
98     * want to transport your tree via some RPC mechanism, where
99     * the data structure again has to be serialized somehow.
100     * How to do that with your object structure? It would require a
101     * convenient traverse-in, traverse-out api to solve this having
102     * KISS on every layer. Since the layercount is always increasing
103     * when trying to achieve some abstract/generic components
104     * working together in a *flexible* way (regardless of performance
105     * payoff), the tree should be kept in "simple" linked list style
106     * troughout the whole translation- and transportation stages.
107     *
108     * Otherwise we would mix oo and non-oo stuff at this middleware
109     * layer and this should be reserved to the very backend side (e.g.
110     * orm stuff) and the very frontend side (e.g. taglib stuff).
111     * The middleware just handles non-oo data structures which should
112     * be easy to rewrite or forward since ...
113     * It's always just a hash (blessed or not), isn't it?
114     *
115     * Assuming this (and there are lists, of course ...) it should be
116     * possible to translate or echo them in a generic way *without*
117     * requiring knowledge about each detail of their internal representation.
118     *
119     * More detailed layout of the translation: (Perl lingo)
120     * The tree nodes are automagically vivified at the xml-2-tree
121     * transformation stage at the remote side through the algorithm
122     * implemented in {@link XML::Parser::EasyTree}. It follows these rules
123     * to represent a xml document as a linked list:
124     * 1. A xml tag (node) is mapped to a hash having
125     * the keys qw( attrib content name type ).
126     * 2. Attributes of this tag are wrapped as hashref into 'attrib'.
127     * 3. Child nodes inside the tag's content area are wrapped as arrayref
128     * into 'content'.
129     * 4. 'name' and 'type' are just plain scalars. The former contains the
130     * tag's name and the latter a type identifier of the tag (one of 'e|.').
131     *
132     * {@link XML::Parser::EasyTree} is written by Eric Bohlman <ebohlman@omsdev.com>
133     * and acts as a "built-in" style for
134     * {@link http://search.cpan.org/author/COOPERCL/XML-Parser-2.31/Expat/Expat.pm XML::Parser::Expat}
135     * similar to XML::Parser::Tree.
136     * XML::Parser currently has version 2.31 and is
137     * Copyright (c) 1998-2000 Larry Wall and Clark Cooper.
138 joko 1.1 *
139     *
140     * @author Andreas Motl <andreas.motl@ilo.de>
141     * @copyright (c) 2003 - All Rights reserved.
142     * @license GNU LGPL (GNU Lesser General Public License)
143     *
144     * @link http://www.netfrag.org/~joko/
145     * @link http://www.gnu.org/licenses/lgpl.txt
146     *
147     * @package org.netfrag.glib
148     * @subpackage DataLift
149 joko 1.2 * @name Data::Lift::hash::topic::EasyTree
150 joko 1.1 *
151     * @link http://cvs.netfrag.org/php/libs/org.netfrag.glib
152     *
153     */
154     class Data_Lift_hash_topic_EasyTree {
155    
156 joko 1.2 var $_limit_deepness;
157    
158     function perform(&$data) {
159    
160     $this->_limit_deepness = 2;
161     //$this->_limit_listcount = 10;
162    
163     //print Dumper($data);
164    
165     //array_multisort($data);
166     //asort($data);
167    
168     // TODO: could this initial perform code be revamped into the recursion logic?
169    
170     $caption = 'root';
171     if ($data[name]) {
172     $caption = $data[name];
173     }
174    
175     // push root node to anchor array
176     //$this->push_anchor('/' . $data[name]);
177    
178     // V1
179     $data = $this->transform_xml_node($data);
180     $data = $this->mkContainer( $data, $this->make_tree_node($caption) );
181    
182     //print Dumper($data);
183    
184     // V2
185     //$data = $this->mkNode('root', $data);
186    
187     return $data;
188    
189     }
190    
191 joko 1.1
192 joko 1.2 function transform_xml_node($xml_node, $atnode = null) {
193 joko 1.1
194     //print Dumper($source);
195 joko 1.2 //$this->push_anchor('/' . $xml_node[name]);
196    
197     static $level;
198     $level++;
199    
200     if ($level > $this->_limit_deepness) { $level--; return; }
201    
202     //print "level: $level" . "<br/>";
203 joko 1.1
204     $buffer = array();
205    
206 joko 1.2 foreach ($xml_node as $key => $val) {
207 joko 1.1
208 joko 1.2 //$this->push_anchor('/' . $key);
209    
210     // default node - acts as fallback if not furthermore modified somehow in this loop
211     //$node = $this->make_tree_node($key, $val);
212     $node = $this->make_tree_node($key);
213    
214     // 1. encapsulate attribute hash into a node container
215     if ($key && ($key == 'attrib')) {
216    
217     // aggregate element keys to build xpq query pointing to the node
218     $buf = array();
219     foreach ($val as $attr_key => $attr_val) {
220    
221     // push attribute anchor
222     $this->push_anchor('[@' . $attr_key . '="' . $attr_val . '"]');
223 joko 1.1
224 joko 1.2 $attr_node = $this->make_tree_node($attr_key, $attr_val);
225     array_push($buf, $attr_node);
226    
227     $this->pop_anchor();
228     }
229     $node = $this->mkContainer( $buf, $node );
230 joko 1.1
231 joko 1.2 // 2. encapsulate content node
232     } elseif ($key && ($key == 'content') ) {
233    
234     //print Dumper($val);
235     //$node_name = $val[$key][name];
236    
237     //$this->pop_anchor();
238     //$this->push_anchor('/' . $xml_node[name]);
239    
240     // transform list of nodes
241     $nodelist = $this->transform_xml_nodelist($val);
242    
243     // propagate this list
244     $buf = array();
245     foreach ($nodelist as $idx => $entry) {
246    
247     //print Dumper($entry);
248    
249     // build and push anchor
250     // dive down one level and use the name from there
251     //$sub_name = '/' . $subnode[name];
252     $parent_name = '/' . $val[$idx][name];
253     // increase index - XPath counts like humans do. (base=1)
254     $sub_index = '[' . ($idx + 1) . ']';
255     $this->push_anchor($parent_name . $sub_index);
256    
257     // numeric indexed array(?) ...
258     $subnode = $this->make_tree_node($idx);
259    
260     // ... encapsulate!
261     $subnode = $this->mkContainer( $entry, $subnode );
262    
263     //print Dumper($subnode);
264    
265     // ... aggregate!
266     array_push($buf, $subnode);
267    
268     // release anchor
269     $this->pop_anchor();
270     }
271     $node = $this->mkContainer( $buf, $node );
272    
273     //$this->pop_anchor();
274    
275    
276     // 3. make plain node from key <-> value pair
277     // These are not the xml-attributes! These are the node-tag names!
278     } else {
279     //$this->push_anchor('/' . $key);
280     $node = $this->make_tree_node($key, $val);
281     //$this->pop_anchor();
282 joko 1.1 }
283 joko 1.2
284     //$this->pop_anchor();
285    
286     // aggregate all nodes into a flat list at this level
287     array_push($buffer, $node);
288 joko 1.1
289     }
290    
291 joko 1.2 //$this->pop_anchor();
292    
293     $level--;
294    
295 joko 1.1 return $buffer;
296    
297     }
298    
299 joko 1.2 function transform_xml_nodelist($xml_nodes) {
300     $sub_buffer = array();
301     foreach ($xml_nodes as $xml_node) {
302     $this->push_anchor('/' . $xml_node[name]);
303     array_push($sub_buffer, $this->transform_xml_node($xml_node));
304     $this->pop_anchor();
305     }
306     return $sub_buffer;
307     }
308    
309    
310     function make_tree_node($name, $val = '') {
311    
312     // The label of the node, shown to the user.
313     $caption = $name;
314     //$filter = $name;
315     if ($val) {
316     $caption .= ': ' . $val;
317     //$filter = $name . '_' . $val;
318     }
319    
320     // The name of the xml-file in this case.
321     // This is *not* an arbitrary filename - so you *can't* access any file
322     // on the host which should be considered bad.
323     // In fact, the names are keys in some subnode table sitting on top of
324     // a storage handle bundling together some files to a FileSet.
325     // FIXME: HACK !!!
326     // Do we already have a Nirvana-API for such things?
327     // Propagating this through the lift seems impossible...
328     $identifier = rawurlencode($_GET[ecdid]);
329    
330     // A filter expression (XPath lingo) anchoring each node.
331     //print Dumper($this->_anchors);
332     //print "anchor: " . join('', $this->_anchors) . '<br/>';
333     $anchor = join('', $this->_anchors);
334     //$filter = $anchor;
335     $filter = rawurlencode($anchor);
336    
337     $parent_filter = rawurlencode(stripslashes($_GET[ecdf]));
338     //$parent_filter = $_GET[ecdf];
339    
340     // Build proper link arguments to refer to a node as item to let it become view-/editable.
341     // The identfier (by now the filename) transitions to a meta-argument here
342     // to make room for the sub-nodename becoming the new identifier. ($name!)
343     //$link_args = array( ecat => 'item', ecdm => $parent_identifier, ecdf => $filter );
344     //$link_args = array( ecat => 'tree', ecdm => $parent_identifier, ecdf => $filter );
345     //$link_args = array( ecat => 'tree', ecdm => $parent_identifier, ecdf => $filter, ecdid => $parent_identifier );
346     $link_args = array( ecat => 'tree', ecdid => $identifier, ecdf => $filter, ecdm => $parent_filter );
347    
348     $url = url::viewdatanode($name, $link_args);
349    
350     // vivify single node
351     // V1 [2002]
352     //$node = array( name => $name, attributes => array( url => $url ) );
353     // NEW [2003-04-20]: behavior of underlying lift changed
354     $node = array( name => $caption, link => $url );
355    
356     // TEST: default childnode injection
357     //$node[children] = array( array( name => '123' ) );
358    
359     return $node;
360     }
361    
362     function mkContainer($data, $parent) {
363    
364     /*
365     print "<hr/>";
366     print Dumper($data);
367     print "<br/>";
368     print Dumper($parent);
369     */
370    
371     // autocreate parent if undef
372     //if (!$parent) { $parent = $this->mkNode('dummy'); }
373    
374     // sort keys of children alphabetically
375     // TODO: add behavior to this
376     //asort($data);
377    
378     $p_children = $parent[children];
379     $c_children = $data;
380    
381     if ($p_children && $c_children) {
382     //$children = php::array_join_merge($p_children, $c_children);
383     $children = array_merge($p_children, $c_children);
384     } else {
385     $children = $c_children;
386     }
387    
388     // vivify list of children
389     /*
390     $node = array(
391     'children' => $data,
392     );
393     */
394     $parent[children] = $children;
395    
396     //print "<hr/>";
397     //print "parent: " . Dumper($parent) . "<br/>";
398     //print "node: " . Dumper($node) . "<br/>";
399    
400     // merge them together and return result
401     //$result = php::array_join_merge($parent, $node);
402     //$result = php::array_join_merge($node, $parent);
403    
404     //print "result: " . Dumper($result);
405     //print "<hr/>";
406    
407     //return $result;
408     return $parent;
409     }
410    
411     function pop_anchor() {
412     array_pop($this->_anchors);
413     }
414    
415     function push_anchor($anchor) {
416     if (!is_array($this->_anchors)) {
417     $this->_anchors = array();
418     }
419     array_push($this->_anchors, $anchor);
420     }
421    
422    
423    
424    
425    
426    
427    
428     function mkNode_old($name, $payload = null) {
429 joko 1.1
430     static $level;
431     static $anchor;
432    
433     if (!is_array($anchor)) { $anchor = array(); }
434     //if (!$caption) { $caption = $name; }
435    
436     // TODO: propagate to this place inside some argument container
437     // (make adjustable from View)
438 joko 1.2 $level_max = 10;
439 joko 1.1 //print "level: $level<br/>";
440    
441     // build url to single node
442     //$url = linkargs::topic($name);
443    
444    
445     /*
446     $main_buf = array();
447     //for ($i = 0; $i < $level - 1; $i++) {
448     for ($i = 0; $i <= 2; $i++) {
449     if (!$anchor[$i]) { continue; }
450     array_push($main_buf, $anchor[$i]);
451     }
452     $main = join('.', $main_buf);
453    
454     //$filter = join('.', $anchor);
455     // FIXME: this is limited to three levels only
456     //$appendix = $anchor[2] ? (':' . join('.', array( $anchor[2], $anchor[3] ))) : '';
457     $appendix = '';
458     $appendix_buf = array();
459     for ($i = 2; $i <= sizeof($anchor); $i++) {
460     //for ($i = $level - 1; $i <= sizeof($anchor); $i++) {
461     if (!$anchor[$i]) { continue; }
462     array_push($appendix_buf, $anchor[$i]);
463     }
464     if ($appendix_buf) {
465     $appendix = ':' . join('.', $appendix_buf);
466     }
467    
468     //$filter = join('.', array( $anchor[0], $anchor[1] )) . $appendix;
469     $filter = $main . $appendix;
470     */
471    
472     $buf = array();
473     for ($i = 0; $i <= $level; $i = $i + 3) {
474     $part = array();
475     for ($j = $i; $j <= $i + 3; $j++) {
476     if (!$anchor[$j]) { continue; }
477     array_push($part, $anchor[$j]);
478     }
479     array_push($buf, join('.', $part));
480     }
481     $filter = join(':', $buf);
482    
483    
484 joko 1.2 if (is_array($payload) && sizeof($payload)) {
485    
486     //print "payload: " . Dumper($payload) . "<br/>";
487    
488 joko 1.1 // count the tree-level
489     $level++;
490     array_push($anchor, $name);
491     if ($level <= $level_max) {
492 joko 1.2
493     //print "payload: " . Dumper($payload) . "<hr/>";
494    
495     // check xml node
496     $is_xml_node =
497     php::is_hash($payload) &&
498     isset($payload[attrib]) &&
499     isset($payload[content]) &&
500     isset($payload[type]) &&
501     isset($payload[name]);
502    
503     if ($is_xml_node) {
504     $childnode = $this->transform($payload);
505     $node = $this->mkContainer($childnode, $node);
506    
507     } elseif (is_array($payload) && !php::is_hash($payload)) {
508     //$childnode = $this->transform_xml_node($payload);
509     //$node = $this->mkContainer($childnode, $node);
510    
511     // make up a virtual xml node
512     } elseif (php::is_hash($payload)) {
513    
514     print "===================== HASH ============<br/>";
515    
516     /*
517     $data = array(
518     attrib => array(),
519     content => $payload,
520     type => '',
521     name => 'test2',
522     );
523     //$data = $payload;
524     */
525    
526     //$childnode = $this->transform($payload);
527    
528     /*
529     $data = array(
530     name => $node[name],
531     attributes => array( url => $url ),
532     //children => array( $payload ),
533     //children => $this->mkNode('sub', $childnode),
534     children => array( $this->mkNode('teyt'), $this->mkNode('huhu') ),
535     );
536     */
537    
538     /*
539     //$childnode = $this->transform(array($payload));
540     $childnode = $this->transform($data);
541     print Dumper($childnode);
542    
543     //$node[children] = $childnode;
544     $node = $this->mkContainer($childnode, $node);
545     */
546    
547     //$node = $this->transform($data);
548     //$node = $data;
549    
550     //$node = $this->transform($data);
551     //$node = $this->mkContainer($childnode, $node);
552    
553    
554    
555    
556     //$childnode = $this->transform($data);
557     //$node2 = array( name => $name, attributes => array( url => $url ) );
558    
559     //$childnode = array( $data );
560     //$childnode = $data;
561    
562     //$node = $this->mkContainer($data, $node);
563     //$node = $this->mkContainer($childnode, $node);
564    
565     /*
566     //$node = $this->mkContainer($data, $this->mkNode('node'));
567     $node[name] = 'hello';
568     $node[type] = '';
569     $node[attrib] = array();
570     $node[content] = array();
571     //$node[children] = array( array( name => 'hello' ) );
572     $node[children] = array( $data );
573     */
574    
575     //$new = $this->mkNode('acme');
576     //$node = $this->mkContainer($new);
577    
578     $attribs = array();
579     foreach ($payload as $key => $val) {
580     array_push($attribs, "$key=$val");
581     }
582     $attribs_serialized = join(', ', $attribs);
583    
584     //$node[name] = "$name: " . $attribs_serialized;
585    
586     //return $childnode;
587     //$node = $this->mkNode('test', array($payload));
588    
589     //$childnode = $this->mkNode('attributes', $data);
590     //$subnodes = $this->transform($payload);
591     //$subnodes = $this->mkNode('attributes', $payload);
592     //$subnodes = array($payload);
593     //$childnode2 = $this->mkNode('attributes', $subnodes);
594     //$childnode = $this->mkContainer($subnodes, $node);
595     //print "payload: " . Dumper($payload) . "<hr/>";
596     //$childnode = $payload;
597     }
598    
599     //$childnode->abc = 'def';
600     //print "childnode: " . Dumper($childnode) . "<hr/>";
601     /*
602     if (is_array($childnode)) {
603     $payload = $this->mkContainer($childnode, $node);
604     $childnode = $this->transform($payload);
605     }
606     */
607 joko 1.1 //array_push($buffer, $cnode);
608     //array_push($buffer, $this->mkContainer(array()));
609     }
610     $level--;
611     array_pop($anchor);
612    
613     } else {
614     // TODO: !!
615     //$node[attributes][alt] = $name;
616     //print "no array: " . Dumper($payload) . "<br/>";
617     $node[name] = "$name: $payload";
618     }
619    
620     return $node;
621     }
622    
623    
624     }
625    
626     ?>

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed