WordPress 4.3
[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 __construct( $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          * PHP4 constructor.
48          */
49         public function Text_Diff_Renderer( $params = array() ) {
50                 self::__construct( $params );
51         }
52
53     /**
54      * Get any renderer parameters.
55      *
56      * @return array  All parameters of this renderer object.
57      */
58     function getParams()
59     {
60         $params = array();
61         foreach (get_object_vars($this) as $k => $v) {
62             if ($k[0] == '_') {
63                 $params[substr($k, 1)] = $v;
64             }
65         }
66
67         return $params;
68     }
69
70     /**
71      * Renders a diff.
72      *
73      * @param Text_Diff $diff  A Text_Diff object.
74      *
75      * @return string  The formatted output.
76      */
77     function render($diff)
78     {
79         $xi = $yi = 1;
80         $block = false;
81         $context = array();
82
83         $nlead = $this->_leading_context_lines;
84         $ntrail = $this->_trailing_context_lines;
85
86         $output = $this->_startDiff();
87
88         $diffs = $diff->getDiff();
89         foreach ($diffs as $i => $edit) {
90             /* If these are unchanged (copied) lines, and we want to keep
91              * leading or trailing context lines, extract them from the copy
92              * block. */
93             if (is_a($edit, 'Text_Diff_Op_copy')) {
94                 /* Do we have any diff blocks yet? */
95                 if (is_array($block)) {
96                     /* How many lines to keep as context from the copy
97                      * block. */
98                     $keep = $i == count($diffs) - 1 ? $ntrail : $nlead + $ntrail;
99                     if (count($edit->orig) <= $keep) {
100                         /* We have less lines in the block than we want for
101                          * context => keep the whole block. */
102                         $block[] = $edit;
103                     } else {
104                         if ($ntrail) {
105                             /* Create a new block with as many lines as we need
106                              * for the trailing context. */
107                             $context = array_slice($edit->orig, 0, $ntrail);
108                             $block[] = new Text_Diff_Op_copy($context);
109                         }
110                         /* @todo */
111                         $output .= $this->_block($x0, $ntrail + $xi - $x0,
112                                                  $y0, $ntrail + $yi - $y0,
113                                                  $block);
114                         $block = false;
115                     }
116                 }
117                 /* Keep the copy block as the context for the next block. */
118                 $context = $edit->orig;
119             } else {
120                 /* Don't we have any diff blocks yet? */
121                 if (!is_array($block)) {
122                     /* Extract context lines from the preceding copy block. */
123                     $context = array_slice($context, count($context) - $nlead);
124                     $x0 = $xi - count($context);
125                     $y0 = $yi - count($context);
126                     $block = array();
127                     if ($context) {
128                         $block[] = new Text_Diff_Op_copy($context);
129                     }
130                 }
131                 $block[] = $edit;
132             }
133
134             if ($edit->orig) {
135                 $xi += count($edit->orig);
136             }
137             if ($edit->final) {
138                 $yi += count($edit->final);
139             }
140         }
141
142         if (is_array($block)) {
143             $output .= $this->_block($x0, $xi - $x0,
144                                      $y0, $yi - $y0,
145                                      $block);
146         }
147
148         return $output . $this->_endDiff();
149     }
150
151     function _block($xbeg, $xlen, $ybeg, $ylen, &$edits)
152     {
153         $output = $this->_startBlock($this->_blockHeader($xbeg, $xlen, $ybeg, $ylen));
154
155         foreach ($edits as $edit) {
156             switch (strtolower(get_class($edit))) {
157             case 'text_diff_op_copy':
158                 $output .= $this->_context($edit->orig);
159                 break;
160
161             case 'text_diff_op_add':
162                 $output .= $this->_added($edit->final);
163                 break;
164
165             case 'text_diff_op_delete':
166                 $output .= $this->_deleted($edit->orig);
167                 break;
168
169             case 'text_diff_op_change':
170                 $output .= $this->_changed($edit->orig, $edit->final);
171                 break;
172             }
173         }
174
175         return $output . $this->_endBlock();
176     }
177
178     function _startDiff()
179     {
180         return '';
181     }
182
183     function _endDiff()
184     {
185         return '';
186     }
187
188     function _blockHeader($xbeg, $xlen, $ybeg, $ylen)
189     {
190         if ($xlen > 1) {
191             $xbeg .= ',' . ($xbeg + $xlen - 1);
192         }
193         if ($ylen > 1) {
194             $ybeg .= ',' . ($ybeg + $ylen - 1);
195         }
196
197         // this matches the GNU Diff behaviour
198         if ($xlen && !$ylen) {
199             $ybeg--;
200         } elseif (!$xlen) {
201             $xbeg--;
202         }
203
204         return $xbeg . ($xlen ? ($ylen ? 'c' : 'd') : 'a') . $ybeg;
205     }
206
207     function _startBlock($header)
208     {
209         return $header . "\n";
210     }
211
212     function _endBlock()
213     {
214         return '';
215     }
216
217     function _lines($lines, $prefix = ' ')
218     {
219         return $prefix . implode("\n$prefix", $lines) . "\n";
220     }
221
222     function _context($lines)
223     {
224         return $this->_lines($lines, '  ');
225     }
226
227     function _added($lines)
228     {
229         return $this->_lines($lines, '> ');
230     }
231
232     function _deleted($lines)
233     {
234         return $this->_lines($lines, '< ');
235     }
236
237     function _changed($orig, $final)
238     {
239         return $this->_deleted($orig) . "---\n" . $this->_added($final);
240     }
241
242 }