Back to index

d-push  2.0
diffstate.php
Go to the documentation of this file.
00001 <?php
00002 /***********************************************
00003 * File      :   diffstate.php
00004 * Project   :   Z-Push
00005 * Descr     :   This is the differential engine.
00006 *               We do a standard differential
00007 *               change detection by sorting both
00008 *               lists of items by their unique id,
00009 *               and then traversing both arrays
00010 *               of items at once. Changes can be
00011 *               detected by comparing items at
00012 *               the same position in both arrays.
00013 *
00014 * Created   :   02.01.2012
00015 *
00016 * Copyright 2007 - 2012 Zarafa Deutschland GmbH
00017 *
00018 * This program is free software: you can redistribute it and/or modify
00019 * it under the terms of the GNU Affero General Public License, version 3,
00020 * as published by the Free Software Foundation with the following additional
00021 * term according to sec. 7:
00022 *
00023 * According to sec. 7 of the GNU Affero General Public License, version 3,
00024 * the terms of the AGPL are supplemented with the following terms:
00025 *
00026 * "Zarafa" is a registered trademark of Zarafa B.V.
00027 * "Z-Push" is a registered trademark of Zarafa Deutschland GmbH
00028 * The licensing of the Program under the AGPL does not imply a trademark license.
00029 * Therefore any rights, title and interest in our trademarks remain entirely with us.
00030 *
00031 * However, if you propagate an unmodified version of the Program you are
00032 * allowed to use the term "Z-Push" to indicate that you distribute the Program.
00033 * Furthermore you may use our trademarks where it is necessary to indicate
00034 * the intended purpose of a product or service provided you use it in accordance
00035 * with honest practices in industrial or commercial matters.
00036 * If you want to propagate modified versions of the Program under the name "Z-Push",
00037 * you may only do so if you have a written permission by Zarafa Deutschland GmbH
00038 * (to acquire a permission please contact Zarafa at trademark@zarafa.com).
00039 *
00040 * This program is distributed in the hope that it will be useful,
00041 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00042 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00043 * GNU Affero General Public License for more details.
00044 *
00045 * You should have received a copy of the GNU Affero General Public License
00046 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00047 *
00048 * Consult LICENSE file for details
00049 ************************************************/
00050 
00051 class DiffState implements IChanges {
00052     protected $syncstate;
00053     protected $backend;
00054     protected $flags;
00055 
00066     public function Config($state, $flags = 0) {
00067         if ($state == "")
00068             $state = array();
00069 
00070         if (!is_array($state))
00071             throw new StatusException("Invalid state", SYNC_FSSTATUS_CODEUNKNOWN);
00072 
00073         $this->syncstate = $state;
00074         $this->flags = $flags;
00075         return true;
00076     }
00077 
00085     public function GetState() {
00086         if (!isset($this->syncstate) || !is_array($this->syncstate))
00087             throw new StatusException("DiffState->GetState(): Error, state not available", SYNC_FSSTATUS_CODEUNKNOWN, null, LOGLEVEL_WARN);
00088 
00089         return $this->syncstate;
00090     }
00091 
00092 
00106     static public function RowCmp($a, $b) {
00107         // TODO implement different comparing functions
00108         return $a["id"] < $b["id"] ? 1 : -1;
00109     }
00110 
00120     protected function getDiffTo($new) {
00121         $changes = array();
00122 
00123         // Sort both arrays in the same way by ID
00124         usort($this->syncstate, array("DiffState", "RowCmp"));
00125         usort($new, array("DiffState", "RowCmp"));
00126 
00127         $inew = 0;
00128         $iold = 0;
00129 
00130         // Get changes by comparing our list of messages with
00131         // our previous state
00132         while(1) {
00133             $change = array();
00134 
00135             if($iold >= count($this->syncstate) || $inew >= count($new))
00136                 break;
00137 
00138             if($this->syncstate[$iold]["id"] == $new[$inew]["id"]) {
00139                 // Both messages are still available, compare flags and mod
00140                 if(isset($this->syncstate[$iold]["flags"]) && isset($new[$inew]["flags"]) && $this->syncstate[$iold]["flags"] != $new[$inew]["flags"]) {
00141                     // Flags changed
00142                     $change["type"] = "flags";
00143                     $change["id"] = $new[$inew]["id"];
00144                     $change["flags"] = $new[$inew]["flags"];
00145                     $changes[] = $change;
00146                 }
00147 
00148                 if($this->syncstate[$iold]["mod"] != $new[$inew]["mod"]) {
00149                     $change["type"] = "change";
00150                     $change["id"] = $new[$inew]["id"];
00151                     $changes[] = $change;
00152                 }
00153 
00154                 $inew++;
00155                 $iold++;
00156             } else {
00157                 if($this->syncstate[$iold]["id"] > $new[$inew]["id"]) {
00158                     // Message in state seems to have disappeared (delete)
00159                     $change["type"] = "delete";
00160                     $change["id"] = $this->syncstate[$iold]["id"];
00161                     $changes[] = $change;
00162                     $iold++;
00163                 } else {
00164                     // Message in new seems to be new (add)
00165                     $change["type"] = "change";
00166                     $change["flags"] = SYNC_NEWMESSAGE;
00167                     $change["id"] = $new[$inew]["id"];
00168                     $changes[] = $change;
00169                     $inew++;
00170                 }
00171             }
00172         }
00173 
00174         while($iold < count($this->syncstate)) {
00175             // All data left in 'syncstate' have been deleted
00176             $change["type"] = "delete";
00177             $change["id"] = $this->syncstate[$iold]["id"];
00178             $changes[] = $change;
00179             $iold++;
00180         }
00181 
00182         while($inew < count($new)) {
00183             // All data left in new have been added
00184             $change["type"] = "change";
00185             $change["flags"] = SYNC_NEWMESSAGE;
00186             $change["id"] = $new[$inew]["id"];
00187             $changes[] = $change;
00188             $inew++;
00189         }
00190 
00191         return $changes;
00192     }
00193 
00204     protected function updateState($type, $change) {
00205         // Change can be a change or an add
00206         if($type == "change") {
00207             for($i=0; $i < count($this->syncstate); $i++) {
00208                 if($this->syncstate[$i]["id"] == $change["id"]) {
00209                     $this->syncstate[$i] = $change;
00210                     return;
00211                 }
00212             }
00213             // Not found, add as new
00214             $this->syncstate[] = $change;
00215         } else {
00216             for($i=0; $i < count($this->syncstate); $i++) {
00217                 // Search for the entry for this item
00218                 if($this->syncstate[$i]["id"] == $change["id"]) {
00219                     if($type == "flags") {
00220                         // Update flags
00221                         $this->syncstate[$i]["flags"] = $change["flags"];
00222                     } else if($type == "delete") {
00223                         // Delete item
00224                         array_splice($this->syncstate, $i, 1);
00225                     }
00226                     return;
00227                 }
00228             }
00229         }
00230     }
00231 
00246     protected function isConflict($type, $folderid, $id) {
00247         $stat = $this->backend->StatMessage($folderid, $id);
00248 
00249         if(!$stat) {
00250             // Message is gone
00251             if($type == "change")
00252                 return true; // deleted here, but changed there
00253             else
00254                 return false; // all other remote changes still result in a delete (no conflict)
00255         }
00256 
00257         foreach($this->syncstate as $state) {
00258             if($state["id"] == $id) {
00259                 $oldstat = $state;
00260                 break;
00261             }
00262         }
00263 
00264         if(!isset($oldstat)) {
00265             // New message, can never conflict
00266             return false;
00267         }
00268 
00269         if($stat["mod"] != $oldstat["mod"]) {
00270             // Changed here
00271             if($type == "delete" || $type == "change")
00272                 return true; // changed here, but deleted there -> conflict, or changed here and changed there -> conflict
00273             else
00274                 return false; // changed here, and other remote changes (move or flags)
00275         }
00276     }
00277 
00278 }
00279 
00280 ?>