]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/diff3.php
var --> public
[SourceForge/phpwiki.git] / lib / diff3.php
1 <?php
2
3 // diff3.php
4 //
5 // A class for computing three way diffs
6 //
7 // Copyright (C) 2001 Geoffrey T. Dairiki <dairiki@dairiki.org>
8 // You may copy this code freely under the conditions of the GPL.
9 //
10
11 require_once 'lib/difflib.php';
12
13 class _Diff3_Block
14 {
15     public $type = 'diff3';
16
17     function _Diff3_Block($orig = false, $final1 = false, $final2 = false)
18     {
19         $this->orig = $orig ? $orig : array();
20         $this->final1 = $final1 ? $final1 : array();
21         $this->final2 = $final2 ? $final2 : array();
22     }
23
24     function merged()
25     {
26         if (!isset($this->_merged)) {
27             if ($this->final1 === $this->final2)
28                 $this->_merged = &$this->final1;
29             elseif ($this->final1 === $this->orig)
30                 $this->_merged = &$this->final2; elseif ($this->final2 === $this->orig)
31                 $this->_merged = &$this->final1; else
32                 $this->_merged = false;
33         }
34         return $this->_merged;
35     }
36
37     function is_conflict()
38     {
39         return $this->merged() === false;
40     }
41 }
42
43 class _Diff3_CopyBlock extends _Diff3_Block
44 {
45     public $type = 'copy';
46
47     function _Diff3_CopyBlock($lines = false)
48     {
49         $this->orig = $lines ? $lines : array();
50         $this->final1 = &$this->orig;
51         $this->final2 = &$this->orig;
52     }
53
54     function merged()
55     {
56         return $this->orig;
57     }
58
59     function is_conflict()
60     {
61         return false;
62     }
63 }
64
65 class _Diff3_BlockBuilder
66 {
67     function _Diff3_BlockBuilder()
68     {
69         $this->_init();
70     }
71
72     function _init()
73     {
74         $this->orig = $this->final1 = $this->final2 = array();
75     }
76
77     function _append(&$array, $lines)
78     {
79         array_splice($array, sizeof($array), 0, $lines);
80     }
81
82     function input($lines)
83     {
84         if ($lines)
85             $this->_append($this->orig, $lines);
86     }
87
88     function out1($lines)
89     {
90         if ($lines)
91             $this->_append($this->final1, $lines);
92     }
93
94     function out2($lines)
95     {
96         if ($lines)
97             $this->_append($this->final2, $lines);
98     }
99
100     function is_empty()
101     {
102         return !$this->orig && !$this->final1 && !$this->final2;
103     }
104
105     function finish()
106     {
107         if ($this->is_empty())
108             return false;
109         else {
110             $block = new _Diff3_Block($this->orig, $this->final1, $this->final2);
111             $this->_init();
112             return $block;
113         }
114     }
115 }
116
117 class Diff3
118 {
119     function Diff3($orig, $final1, $final2)
120     {
121         $eng = new _DiffEngine;
122         $this->ConflictingBlocks = 0; //Conflict counter
123         $this->blocks = $this->__diff3($eng->diff($orig, $final1),
124             $eng->diff($orig, $final2));
125     }
126
127     function __diff3($edits1, $edits2)
128     {
129         $blocks = array();
130         $bb = new _Diff3_BlockBuilder;
131
132         $e1 = current($edits1);
133         $e2 = current($edits2);
134         while ($e1 || $e2) {
135 //          echo "====\n";
136 //          print_r($e1);
137 //          print_r($e2);
138 //          echo "====\n";
139
140             if ($e1 && $e2 && $e1->type == 'copy' && $e2->type == 'copy') {
141                 // We have copy blocks from both diffs.  This is the (only)
142                 // time we want to emit a diff3 copy block.
143                 // Flush current diff3 diff block, if any
144                 if ($block = $bb->finish())
145                     $blocks[] = $block;
146
147                 $ncopy = min($e1->norig(), $e2->norig());
148                 assert($ncopy > 0);
149                 $blocks[] = new _Diff3_CopyBlock(array_slice($e1->orig, 0, $ncopy));
150
151                 if ($e1->norig() > $ncopy) {
152                     array_splice($e1->orig, 0, $ncopy);
153                     array_splice($e1->final, 0, $ncopy);
154                 } else
155                     $e1 = next($edits1);
156
157                 if ($e2->norig() > $ncopy) {
158                     array_splice($e2->orig, 0, $ncopy);
159                     array_splice($e2->final, 0, $ncopy);
160                 } else
161                     $e2 = next($edits2);
162             } else {
163                 if ($e1 && $e2) {
164                     if ($e1->orig && $e2->orig) {
165                         $norig = min($e1->norig(), $e2->norig());
166                         $orig = array_splice($e1->orig, 0, $norig);
167                         array_splice($e2->orig, 0, $norig);
168                         $bb->input($orig);
169                     }
170
171                     if ($e1->type == 'copy')
172                         $bb->out1(array_splice($e1->final, 0, $norig));
173
174                     if ($e2->type == 'copy')
175                         $bb->out2(array_splice($e2->final, 0, $norig));
176                 }
177                 if ($e1 && !$e1->orig) {
178                     $bb->out1($e1->final);
179                     $e1 = next($edits1);
180                 }
181                 if ($e2 && !$e2->orig) {
182                     $bb->out2($e2->final);
183                     $e2 = next($edits2);
184                 }
185             }
186         }
187
188         if ($block = $bb->finish())
189             $blocks[] = $block;
190
191         return $blocks;
192     }
193
194     function merged_output($label1 = false, $label2 = false)
195     {
196         $lines = array();
197         foreach ($this->blocks as $block) {
198             if ($block->is_conflict()) {
199                 // FIXME: this should probably be moved somewhere else...
200                 $lines = array_merge($lines,
201                     array("<<<<<<<" . ($label1 ? " $label1" : '')),
202                     $block->final1,
203                     array("======="),
204                     $block->final2,
205                     array(">>>>>>>" . ($label2 ? " $label2" : '')));
206                 $this->ConflictingBlocks++;
207             } else {
208                 $lines = array_merge($lines, $block->merged());
209             }
210         }
211         return $lines;
212     }
213 }
214
215 // Local Variables:
216 // mode: php
217 // tab-width: 8
218 // c-basic-offset: 4
219 // c-hanging-comment-ender-p: nil
220 // indent-tabs-mode: nil
221 // End: