Wordpress 2.6.2
[autoinstalls/wordpress.git] / wp-includes / Text / Diff / Renderer.php
1 <?php
2 /**
3  * A class to render Diffs in different formats.
4  *
5  * This class renders the diff in classic diff format. It is intended that
6  * this class be customized via inheritance, to obtain fancier outputs.
7  *
8  * $Horde: framework/Text_Diff/Diff/Renderer.php,v 1.21 2008/01/04 10:07:50 jan Exp $
9  *
10  * Copyright 2004-2008 The Horde Project (http://www.horde.org/)
11  *
12  * See the enclosed file COPYING for license information (LGPL). If you did
13  * not receive this file, see http://opensource.org/licenses/lgpl-license.php.
14  *
15  * @package Text_Diff
16  */
17 class Text_Diff_Renderer {
18
19     /**
20      * Number of leading context "lines" to preserve.
21      *
22      * This should be left at zero for this class, but subclasses may want to
23      * set this to other values.
24      */
25     var $_leading_context_lines = 0;
26
27     /**
28      * Number of trailing context "lines" to preserve.
29      *
30      * This should be left at zero for this class, but subclasses may want to
31      * set this to other values.
32      */
33     var $_trailing_context_lines = 0;
34
35     /**
36      * Constructor.
37      */
38     function Text_Diff_Renderer($params = array())
39     {
40         foreach ($params as $param => $value) {
41             $v = '_' . $param;
42             if (isset($this->$v)) {
43                 $this->$v = $value;
44             }
45         }
46     }
47
48     /**
49      * Get any renderer parameters.
50      *
51      * @return array  All parameters of this renderer object.
52      */
53     function getParams()
54     {
55         $params = array();
56         foreach (get_object_vars($this) as $k => $v) {
57             if ($k[0] == '_') {
58                 $params[substr($k, 1)] = $v;
59             }
60         }
61
62         return $params;
63     }
64
65     /**
66      * Renders a diff.
67      *
68      * @param Text_Diff $diff  A Text_Diff object.
69      *
70      * @return string  The formatted output.
71      */
72     function render($diff)
73     {
74         $xi = $yi = 1;
75         $block = false;
76         $context = array();
77
78         $nlead = $this->_leading_context_lines;
79         $ntrail = $this->_trailing_context_lines;
80
81         $output = $this->_startDiff();
82
83         $diffs = $diff->getDiff();
84         foreach ($diffs as $i => $edit) {
85             /* If these are unchanged (copied) lines, and we want to keep
86              * leading or trailing context lines, extract them from the copy
87              * block. */
88             if (is_a($edit, 'Text_Diff_Op_copy')) {
89                 /* Do we have any diff blocks yet? */
90                 if (is_array($block)) {
91                     /* How many lines to keep as context from the copy
92                      * block. */
93                     $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
94                     if (count($edit->orig) <= $keep) {
95                         /* We have less lines in the block than we want for
96                          * context => keep the whole block. */
97                         $block[] = $edit;
98                     } else {
99                         if ($ntrail) {
100                             /* Create a new block with as many lines as we need
101                              * for the trailing context. */
102                             $context = array_slice($edit->orig, 0, $ntrail);
103                             $block[] = &new Text_Diff_Op_copy($context);
104                         }
105                         /* @todo */
106                         $output .= $this->_block($x0, $ntrail + $xi - $x0,
107                                                  $y0, $ntrail + $yi - $y0,
108                                                  $block);
109                         $block = false;
110                     }
111                 }
112                 /* Keep the copy block as the context for the next block. */
113                 $context = $edit->orig;
114             } else {
115                 /* Don't we have any diff blocks yet? */
116                 if (!is_array($block)) {
117                     /* Extract context lines from the preceding copy block. */
118                     $context = array_slice($context, count($context) - $nlead);
119                     $x0 = $xi - count($context);
120                     $y0 = $yi - count($context);
121                     $block = array();
122                     if ($context) {
123                         $block[] = &new Text_Diff_Op_copy($context);
124                     }
125                 }
126                 $block[] = $edit;
127             }
128
129             if ($edit->orig) {
130                 $xi += count($edit->orig);
131             }
132             if ($edit->final) {
133                 $yi += count($edit->final);
134             }
135         }
136
137         if (is_array($block)) {
138             $output .= $this->_block($x0, $xi - $x0,
139                                      $y0, $yi - $y0,
140                                      $block);
141         }
142
143         return $output . $this->_endDiff();
144     }
145
146     function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
147     {
148         $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
149
150         foreach ($edits as $edit) {
151             switch (strtolower(get_class($edit))) {
152             case 'text_diff_op_copy':
153                 $output .= $this->_context($edit->orig);
154                 break;
155
156             case 'text_diff_op_add':
157                 $output .= $this->_added($edit->final);
158                 break;
159
160             case 'text_diff_op_delete':
161                 $output .= $this->_deleted($edit->orig);
162                 break;
163
164             case 'text_diff_op_change':
165                 $output .= $this->_changed($edit->orig, $edit->final);
166                 break;
167             }
168         }
169
170         return $output . $this->_endBlock();
171     }
172
173     function _startDiff()
174     {
175         return '';
176     }
177
178     function _endDiff()
179     {
180         return '';
181     }
182
183     function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
184     {
185         if ($xlen > 1) {
186             $xbeg .= ',' . ($xbeg + $xlen - 1);
187         }
188         if ($ylen > 1) {
189             $ybeg .= ',' . ($ybeg + $ylen - 1);
190         }
191
192         // this matches the GNU Diff behaviour
193         if ($xlen && !$ylen) {
194             $ybeg--;
195         } elseif (!$xlen) {
196             $xbeg--;
197         }
198
199         return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
200     }
201
202     function _startBlock($header)
203     {
204         return $header . "\n";
205     }
206
207     function _endBlock()
208     {
209         return '';
210     }
211
212     function _lines($lines, $prefix = ' ')
213     {
214         return $prefix . implode("\n$prefix", $lines) . "\n";
215     }
216
217     function _context($lines)
218     {
219         return $this->_lines($lines, '  ');
220     }
221
222     function _added($lines)
223     {
224         return $this->_lines($lines, '> ');
225     }
226
227     function _deleted($lines)
228     {
229         return $this->_lines($lines, '< ');
230     }
231
232     function _changed($orig, $final)
233     {
234         return $this->_deleted($orig) . "---\n" . $this->_added($final);
235     }
236
237 }