source: trunk/zpush/lib/default/diffbackend/diffstate.php @ 7589

Revision 7589, 9.4 KB checked in by douglas, 11 years ago (diff)

Ticket #3209 - Integrar módulo de sincronização Z-push ao Expresso

Line 
1<?php
2/***********************************************
3* File      :   diffstate.php
4* Project   :   Z-Push
5* Descr     :   This is the differential engine.
6*               We do a standard differential
7*               change detection by sorting both
8*               lists of items by their unique id,
9*               and then traversing both arrays
10*               of items at once. Changes can be
11*               detected by comparing items at
12*               the same position in both arrays.
13*
14* Created   :   02.01.2012
15*
16* Copyright 2007 - 2012 Zarafa Deutschland GmbH
17*
18* This program is free software: you can redistribute it and/or modify
19* it under the terms of the GNU Affero General Public License, version 3,
20* as published by the Free Software Foundation with the following additional
21* term according to sec. 7:
22*
23* According to sec. 7 of the GNU Affero General Public License, version 3,
24* the terms of the AGPL are supplemented with the following terms:
25*
26* "Zarafa" is a registered trademark of Zarafa B.V.
27* "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
28* The licensing of the Program under the AGPL does not imply a trademark license.
29* Therefore any rights, title and interest in our trademarks remain entirely with us.
30*
31* However, if you propagate an unmodified version of the Program you are
32* allowed to use the term "Z-Push" to indicate that you distribute the Program.
33* Furthermore you may use our trademarks where it is necessary to indicate
34* the intended purpose of a product or service provided you use it in accordance
35* with honest practices in industrial or commercial matters.
36* If you want to propagate modified versions of the Program under the name "Z-Push",
37* you may only do so if you have a written permission by Zarafa Deutschland GmbH
38* (to acquire a permission please contact Zarafa at trademark@zarafa.com).
39*
40* This program is distributed in the hope that it will be useful,
41* but WITHOUT ANY WARRANTY; without even the implied warranty of
42* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
43* GNU Affero General Public License for more details.
44*
45* You should have received a copy of the GNU Affero General Public License
46* along with this program.  If not, see <http://www.gnu.org/licenses/>.
47*
48* Consult LICENSE file for details
49************************************************/
50
51class DiffState implements IChanges {
52    protected $syncstate;
53    protected $backend;
54    protected $flags;
55
56    /**
57     * Initializes the state
58     *
59     * @param string        $state
60     * @param int           $flags
61     *
62     * @access public
63     * @return boolean status flag
64     * @throws StatusException
65     */
66    public function Config($state, $flags = 0) {
67        if ($state == "")
68            $state = array();
69
70        if (!is_array($state))
71            throw new StatusException("Invalid state", SYNC_FSSTATUS_CODEUNKNOWN);
72
73        $this->syncstate = $state;
74        $this->flags = $flags;
75        return true;
76    }
77
78    /**
79     * Returns state
80     *
81     * @access public
82     * @return string
83     * @throws StatusException
84     */
85    public function GetState() {
86        if (!isset($this->syncstate) || !is_array($this->syncstate))
87            throw new StatusException("DiffState->GetState(): Error, state not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN);
88
89        return $this->syncstate;
90    }
91
92
93    /**----------------------------------------------------------------------------------------------------------
94     * DiffState specific stuff
95     */
96
97    /**
98     * Comparing function used for sorting of the differential engine
99     *
100     * @param array        $a
101     * @param array        $b
102     *
103     * @access public
104     * @return boolean
105     */
106    static public function RowCmp($a, $b) {
107        // TODO implement different comparing functions
108        return $a["id"] < $b["id"] ? 1 : -1;
109    }
110
111    /**
112     * Differential mechanism
113     * Compares the current syncstate to the sent $new
114     *
115     * @param array        $new
116     *
117     * @access protected
118     * @return array
119     */
120    protected function getDiffTo($new) {
121        $changes = array();
122
123        // Sort both arrays in the same way by ID
124        usort($this->syncstate, array("DiffState", "RowCmp"));
125        usort($new, array("DiffState", "RowCmp"));
126
127        $inew = 0;
128        $iold = 0;
129
130        // Get changes by comparing our list of messages with
131        // our previous state
132        while(1) {
133            $change = array();
134
135            if($iold >= count($this->syncstate) || $inew >= count($new))
136                break;
137
138            if($this->syncstate[$iold]["id"] == $new[$inew]["id"]) {
139                // Both messages are still available, compare flags and mod
140                if(isset($this->syncstate[$iold]["flags"]) && isset($new[$inew]["flags"]) && $this->syncstate[$iold]["flags"] != $new[$inew]["flags"]) {
141                    // Flags changed
142                    $change["type"] = "flags";
143                    $change["id"] = $new[$inew]["id"];
144                    $change["flags"] = $new[$inew]["flags"];
145                    $changes[] = $change;
146                }
147
148                if($this->syncstate[$iold]["mod"] != $new[$inew]["mod"]) {
149                    $change["type"] = "change";
150                    $change["id"] = $new[$inew]["id"];
151                    $changes[] = $change;
152                }
153
154                $inew++;
155                $iold++;
156            } else {
157                if($this->syncstate[$iold]["id"] > $new[$inew]["id"]) {
158                    // Message in state seems to have disappeared (delete)
159                    $change["type"] = "delete";
160                    $change["id"] = $this->syncstate[$iold]["id"];
161                    $changes[] = $change;
162                    $iold++;
163                } else {
164                    // Message in new seems to be new (add)
165                    $change["type"] = "change";
166                    $change["flags"] = SYNC_NEWMESSAGE;
167                    $change["id"] = $new[$inew]["id"];
168                    $changes[] = $change;
169                    $inew++;
170                }
171            }
172        }
173
174        while($iold < count($this->syncstate)) {
175            // All data left in 'syncstate' have been deleted
176            $change["type"] = "delete";
177            $change["id"] = $this->syncstate[$iold]["id"];
178            $changes[] = $change;
179            $iold++;
180        }
181
182        while($inew < count($new)) {
183            // All data left in new have been added
184            $change["type"] = "change";
185            $change["flags"] = SYNC_NEWMESSAGE;
186            $change["id"] = $new[$inew]["id"];
187            $changes[] = $change;
188            $inew++;
189        }
190
191        return $changes;
192    }
193
194    /**
195     * Update the state to reflect changes
196     *
197     * @param string        $type of change
198     * @param array         $change
199     *
200     *
201     * @access protected
202     * @return
203     */
204    protected function updateState($type, $change) {
205        // Change can be a change or an add
206        if($type == "change") {
207            for($i=0; $i < count($this->syncstate); $i++) {
208                if($this->syncstate[$i]["id"] == $change["id"]) {
209                    $this->syncstate[$i] = $change;
210                    return;
211                }
212            }
213            // Not found, add as new
214            $this->syncstate[] = $change;
215        } else {
216            for($i=0; $i < count($this->syncstate); $i++) {
217                // Search for the entry for this item
218                if($this->syncstate[$i]["id"] == $change["id"]) {
219                    if($type == "flags") {
220                        // Update flags
221                        $this->syncstate[$i]["flags"] = $change["flags"];
222                    } else if($type == "delete") {
223                        // Delete item
224                        array_splice($this->syncstate, $i, 1);
225                    }
226                    return;
227                }
228            }
229        }
230    }
231
232    /**
233     * Returns TRUE if the given ID conflicts with the given operation. This is only true in the following situations:
234     *   - Changed here and changed there
235     *   - Changed here and deleted there
236     *   - Deleted here and changed there
237     * Any other combination of operations can be done (e.g. change flags & move or move & delete)
238     *
239     * @param string        $type of change
240     * @param string        $folderid
241     * @param string        $id
242     *
243     * @access protected
244     * @return
245     */
246    protected function isConflict($type, $folderid, $id) {
247        $stat = $this->backend->StatMessage($folderid, $id);
248
249        if(!$stat) {
250            // Message is gone
251            if($type == "change")
252                return true; // deleted here, but changed there
253            else
254                return false; // all other remote changes still result in a delete (no conflict)
255        }
256
257        foreach($this->syncstate as $state) {
258            if($state["id"] == $id) {
259                $oldstat = $state;
260                break;
261            }
262        }
263
264        if(!isset($oldstat)) {
265            // New message, can never conflict
266            return false;
267        }
268
269        if($stat["mod"] != $oldstat["mod"]) {
270            // Changed here
271            if($type == "delete" || $type == "change")
272                return true; // changed here, but deleted there -> conflict, or changed here and changed there -> conflict
273            else
274                return false; // changed here, and other remote changes (move or flags)
275        }
276    }
277
278}
279
280?>
Note: See TracBrowser for help on using the repository browser.