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