1 //!-- UTF8 2 /* 3 Oregano Multiuser Server - Version 1.2.0 - January 4th, 2005 4 5 Web: www.oregano-server.org 6 Mail: info@oregano-server.org 7 8 Copyright 2003 - 2004 - 2004 Jens Halm / Cologne, Germany 9 10 This library is free software; you can redistribute it and/or 11 modify it under the terms of the GNU Lesser General Public 12 License as published by the Free Software Foundation; either 13 version 2.1 of the License, or (at your option) any later version. 14 15 This library is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Lesser General Public License for more details. 19 20 You should have received a copy of the GNU Lesser General Public 21 License along with this library; if not, write to the Free Software 22 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23 */ 24 25 /* 26 ------------------------------------------- 27 Classe Message 28 29 @description : 30 Donne une représentaion du schéma de l'objet Table. 31 32 @author Jens Halm copyright http://www.spicefactory.org/ 33 @author erixtekila copyleft http://www.v-i-a.net 34 ------------------------------------------- 35 version history : 36 1.2.0 : 03/02/05 37 - Portage en actionscript 2 pour le 38 compile time type checking 39 - Suppression des membres statiques column et asc. 40 ------------------------------------------- 41 */ 42 43 import org.omus.util.EventDispatcher; 44 import org.omus.util._Class; 45 46 import org.omus.data.TableDefinition; 47 import org.omus.data.UpdateSequence; 48 import org.omus.data.Property; 49 50 /** 51 * Type de données natif à Oregano. 52 * Permet la conservation structurée de large partie de données. 53 * De plus, les données sont synchronisées avec le serveur uniquement en cas de changement, 54 * d'où une certaine forme de préservation de la bande passante. 55 * Par contre, si la synchronisation des données est réalisée totalement avec object et array… 56 * <p> 57 * Evénements auxquels s'abonner : 58 * Ces événements sont générés si la synchronisation avec le serveur a fonctionné. 59 * <ul> 60 * <li>onAddRow(table:Table, row:Object, clientRequest:Boolean) Généréré lors de l'ajout d'un enregistrement. 61 * _param table Table mise à jour 62 * _param row Enregistrement. 63 * _param clientRequest true si demande émanant du client actuel, false demande émanant d'un autre utilisateur. 64 * </li><li>onRemoveAllRows(table:Table, row:Object, clientRequest:Boolean) Généré lorsque tout les enregistrements d'une Table sont supprimés. 65 * _param table Table mise à jour 66 * _param row Enregistrement. 67 * _param clientRequest true si demande émanant du client actuel, false demande émanant d'un autre utilisateur. 68 * </li><li>onRemoveRow(table:Table, row:Object, clientRequest:Boolean) Généré lorsqu'un enregistrement est ôté de la Table. 69 * _param table Table mise à jour 70 * _param row Enregistrement. 71 * _param clientRequest true si demande émanant du client actuel, false demande émanant d'un autre utilisateur. 72 * </li><li>onUpdateRow Généré lorsqu'un enrtegistrement a été mis à jour. 73 * _param table Table mise à jour 74 * _param row Enregistrement. 75 * _param clientRequest true si demande émanant du client actuel, false demande émanant d'un autre utilisateur. 76 * </li></ul></p> 77 * 78 * Elle est aggrémentée par composition des méthodes des sources d'événements (EventDispatcher). 79 * @see org.omus.util.EventDispatcher 80 * @see org.omus.util.iObservable 81 * 82 * @author Jens Halm copyright http://www.spicefactory.org/ 83 * @author erixtekila copyleft http://www.v-i-a.net 84 * @version 1.2.0 85 */ 86 class org.omus.data.Table 87 { 88 //-------------------- 89 // PROPRIETES 90 //-------------------- 91 /** 92 * Liste des enregistrements. 93 */ 94 public var rowList:Array; 95 96 /** 97 * Indexation des enregistrements. 98 */ 99 private var rowMap:Object; 100 101 /** 102 * Indexation des identifiants associés aux enregistrements. 103 */ 104 public var rowIDMap:Object; 105 106 /** 107 * Curseur de recherche. 108 */ 109 public var nextRowID:Number; 110 111 /** 112 * Source d'événements. 113 */ 114 private var _eventSource:EventDispatcher; 115 116 /** 117 * Référence à la TableDefinition. 118 */ 119 private var def:TableDefinition; 120 121 /** 122 * Une référence à UpdateSeaquence. 123 */ 124 private var us:UpdateSequence; 125 126 /** 127 * 128 */ 129 private var record:Boolean; 130 131 /** 132 * Utilisé uniquement avec les Tables étant des propriétés. 133 */ 134 private var container:Property; 135 136 //-------------------- 137 // CONSTRUCTEUR 138 //-------------------- 139 /** 140 * L'objet User dispose des méthodes d'EventDispatcher 141 * Il contient par composition une référence à TableDefinition et UpdateSequence. 142 * Attention, une fois construit, l'object TableDefinition ne peut (doit) être modifié. 143 * 144 * @param regInfo Informations du compte utilisateur enregistré dans la base de données. 145 * @param loginInfo Préférences utilisateur : Mail, Buddy list… 146 * @param props Valeurs des propriétés persistantes définies. 147 * @param propConfig Propriétés persistantes. 148 * @see org.omus.util.EventDispatcher 149 * @see org.omus.util.iObservable 150 * @see org.omus.data.TableDefinition 151 * @see org.omus.data.UpdateSequence 152 */ 153 public function Table (td:TableDefinition) 154 { 155 // Composition avec EventDispatcher 156 _eventSource = new EventDispatcher(); 157 158 // Propriétés 159 def = td; 160 us = new UpdateSequence(td); 161 record = false; 162 container = null; // only set for tables that are Properties 163 164 init(); 165 166 // trace(this+ " installé."); 167 } 168 169 //-------------------- 170 // METHODES PUBLIQUES 171 //-------------------- 172 /** 173 * Utilisé dans un contexte littéral 174 * @return Une chaine définissant l'objet 175 */ 176 public function toString():String 177 { 178 return format(0); 179 } 180 181 /** 182 * Permet l'enregistrement 183 * 184 * @param bool true si enregistrable. 185 */ 186 public function enableRecording (bool:Boolean):Void 187 { 188 record = bool; 189 } 190 191 /** 192 * Renvoie une référence à TableDefinition. 193 * 194 * @return Le schéma des coloànnes de la Table. 195 */ 196 public function getDefinition ():TableDefinition 197 { 198 return def; 199 } 200 201 /** 202 * Renvoie la nombre d'enregistrements dans la Table 203 * 204 * @return Un nombre de lignes. 205 */ 206 public function size ():Number 207 { 208 return rowList.length; 209 } 210 211 /** 212 * Renvoie le contenu d'un enregsitrement. 213 * 214 * @param index Position de l'enregistrement. 215 * @return Renvoie un objet conteneur d'un enregistrement. 216 */ 217 public function getRow (index:Number):Object 218 { 219 if (typeof(index) != "number" || index < 0 || index >= rowList.length) return null; 220 return rowList[index]; 221 } 222 223 /** 224 * Classe la table grâce à une fonction de comparaison. 225 * Fonctionne excatement comme Array.sort() 226 * Modifie la table elle-même. 227 * 228 * @param func Une fonction de comparaison. 229 */ 230 public function sort (func:Function):Void 231 { 232 // TODO : Accès Singleton 233 var clazz = _Class.getInstance(); 234 if (! clazz.checkArguments("org.omus.data.Table.sort", [[func, "function", true]])) return; 235 rowList.sort(func); 236 rowIDMap = null; 237 } 238 239 /* 240 * Classe la table en fonction du nom d'une colonne. 241 * Modifie la table elle-même. 242 * 243 * @param column Nom de la colonne. 244 * @param ascending Ordre de classement, true équiavut à alphabétique. 245 */ 246 public function sortBy (column:String, ascending:Boolean):Void 247 { 248 // org.omus.data.Table.column = column; 249 // org.omus.data.Table.asc = ascending; // Flash 6 could just use the local variables 250 // TODO : Suppression des membres statiques au profit de l'utilisation de l'activation object. 251 var func = function (row1, row2) 252 { 253 var p1 = row1[column]; 254 var p2 = row2[column]; 255 if (p1 == p2) return 0; 256 var bool = (p1 < p2); 257 if ((bool && ascending) || (! bool && ! ascending)) return -1; 258 return 1; 259 }; 260 rowList.sort(func); 261 } 262 263 /** 264 * Renvoie la liste des identifiants. 265 * 266 * @return Un liste ordonnée de tous les identifiants des enregistrements de la table. 267 */ 268 public function getRowIDList ():Array 269 { 270 var arr = new Array(); 271 var rl = rowList; 272 var long = rl.length; 273 for (var i = 0; i < long ; i++) 274 { 275 arr.push(rl[i].__rowID); 276 } 277 return arr; 278 } 279 280 281 /** 282 * Définit le conteneur des propriétés persistantes. 283 */ 284 private function setContainer (prop:Property) { 285 container = prop; 286 } 287 288 /** 289 * Rajoute un enregistrement à la Table. 290 * Ne fonctionne que si l'objet correpond au schéma de la TableDefinition. 291 * 292 * @param row Nouvel enregistrement. 293 * @see org.omus.data.TableDefinition#matches 294 */ 295 public function addRow (row:Object):Void 296 { 297 // TODO : Accès Singleton 298 var clazz = _Class.getInstance(); 299 if (! clazz.checkArguments("org.omus.data.Table.addRow", [[row, Object, true]])) return; 300 // Vérifie TableDefinition 301 if (! def.matches(row)) 302 { 303 // Logs internes 304 _global.oregano.iLog.error("clj-028","error = " + def.getError() + "\n\n" + def + "\n\naffected row = " + org.omus.util.Log.formatObject(row, 1)); 305 return; 306 } 307 308 // UpdateSequence ou this 309 if (record) 310 { 311 us.addRow(row); 312 }else 313 { 314 addRowInternal(nextRowID++, row); 315 } 316 // PropertySet 317 if (container != null) container.setModified(); 318 } 319 320 /** 321 * Prise ne compte d'un nouvel enregistrement à metter à jour(AddRow.execute). 322 * Génère un événement onAddRow aux observateurs. 323 * 324 * @param rowID Identifiant de la ligne. 325 * @param row Ligne de l'nregistrement. 326 * @param cr Client request, true si demande émanant de ce client, false demande émanant d'un autre utilisateur. 327 * @see org.omus.data.AddRow#execute 328 */ 329 public function addEvent (rowID:Number, row:Object, cr:Boolean):Void 330 { 331 // TODO : Accès Singleton 332 var clazz = _Class.getInstance(); 333 var argCheck = [ 334 [rowID, "number", true], 335 [row, Object, true], 336 [cr, "boolean", true] 337 ]; 338 339 if (! clazz.checkArguments("org.omus.data.Table.addEvent", argCheck)) return; 340 341 var newRow = addRowInternal(rowID, row); 342 // Broadcast 343 fireEvent("info", "onAddRow", this, newRow, cr); 344 } 345 346 /** 347 * Rajoute un enregistrement sans mettre à jour UpdateSequence. 348 * Ne fonctionne que si l'objet correpond au schéma de la TableDefinition. 349 * 350 * @param rowID Identifiant de la ligne. 351 * @oaral row Ligne de l'enregistrement. 352 * @see org.omus.data.TableDefinition#matches 353 */ 354 public function addExistingRow (rowID:Number, row:Object):Void 355 { 356 // TODO : Accès Singleton 357 var clazz = _Class.getInstance(); 358 if (! clazz.checkArguments("org.omus.data.Table.addExistingRow", [[rowID, "number", true], [row, Object, true]])) return; 359 // Vérifie TableDefinition 360 if (! def.matches(row)) 361 { 362 // Logs internes 363 _global.oregano.iLog.error("clj-029","error = " + def.getError() + "\n\n" + def + "\n\naffected row = " + org.omus.util.Log.formatObject(row, 1)); 364 return; 365 } 366 addRowInternal(rowID, row); 367 } 368 369 /** 370 * Ajoute tous les enregistrements d'une autre Table. 371 * 372 * @param table Une référence à Table. 373 */ 374 public function addAllRows (table:Table):Void 375 { 376 // TODO : Accès Singleton 377 var clazz = _Class.getInstance(); 378 if (! clazz.checkArguments("org.omus.data.Table.addAllRows", [[table, org.omus.data.Table, true]])) return; 379 var cnt = table.size(); 380 for (var i = 0; i < cnt; i++) 381 { 382 addRow(table.rowList[i]); 383 } 384 } 385 386 /** 387 * Met à jour un enregistrement existant. 388 * Ne fonctionne que si l'objet correpond au schéma de la TableDefinition. 389 * 390 * @param index Identifiant de l'enregistrement. 391 * @param row Nouvel enregistrement venant en remplacement. 392 * @see org.omus.data.TableDefinition#matches 393 */ 394 public function updateRow (index:Number, row:Object):Void 395 { 396 // TODO : Accès Singleton 397 var clazz = _Class.getInstance(); 398 if (! clazz.checkArguments("org.omus.data.Table.updateRow", [[index, "number", true], [row, Object, true]])) return; 399 // TableDefinition 400 if (! def.matches(row)) 401 { 402 // Logs internes 403 _global.oregano.iLog.error("clj-030","error = " + def.getError() + "\n\n" + def + "\n\naffected row = " + org.omus.util.Log.formatObject(row, 1)); 404 return; 405 } 406 // UpdateSequence ou this 407 if (record) 408 { 409 us.updateRow(rowList[index].__rowID, row); 410 } else 411 { 412 updateRowInternal(index, row); 413 } 414 // PropertySet 415 if (container != null) container.setModified(); 416 } 417 418 /** 419 * Prise ne compte d'une nouvellle mise à jour d'un enregistrement (UpdateRow.execute). 420 * Génère un événement onInfo aux observateurs. 421 * 422 * @param rowID Identifiant de la ligne. 423 * @param row Ligne de l'enregistrement. 424 * @param cr Client request, true si demande émanant de ce client, false demande émanant d'un autre utilisateur. 425 * @see org.omus.data.UpdateRow 426 */ 427 public function updateEvent (rowID:Number, row:Object, cr:Boolean):Void 428 { 429 // TODO : Accès Singleton 430 var clazz = _Class.getInstance(); 431 if (! clazz.checkArguments("org.omus.data.Table.updateEvent", [[rowID, "number", true], [row, Object, true], [cr, "boolean", true]])) return; 432 var index = rowIDtoIndex(rowID); 433 if (index == undefined) 434 { 435 // Logs internes 436 _global.oregano.iLog.error("clj-031","rowID = " + rowID); 437 return; 438 } 439 var newRow = updateRowInternal(index, row); 440 fireEvent("info", "onUpdateRow", this, newRow, cr); 441 } 442 443 /** 444 * Prise ne compte d'une nouvellle suppression d'un enregistrement (RemoveRow.execute). 445 * Génère un événement onRemoveRow aux observateurs. 446 * 447 * @param rowID Identifiant de la ligne 448 * @param cr Client request, true si demande émanant de ce client, false demande émanant d'un autre utilisateur. 449 * @see org.omus.data.RemoveRow 450 */ 451 public function removeEvent (rowID:Number, cr:Boolean):Void 452 { 453 // TODO : Accès Singleton 454 var clazz = _Class.getInstance(); 455 if (! clazz.checkArguments("org.omus.data.Table.removeEvent", [[rowID, "number", true], [cr, "boolean", true]])) return; 456 var index = rowIDtoIndex(rowID); 457 var oldRow = removeRowInternal(index, rowID); 458 // Broadcast 459 fireEvent("info", "onRemoveRow", this, oldRow, cr); 460 } 461 462 /** 463 * Supprime un enregistrement de la Table et renvoie l'enregistrement concerné. 464 * 465 * @param index Identifiant de l'enregistrement. 466 * @return Une référence vers l'enregistrement supprimé. 467 */ 468 public function removeRow (index:Number):Object 469 { 470 if (typeof(index) != "number" || index < 0 || index >= rowList.length) return null; 471 var rowID = rowList[index].__rowID; 472 // UpdateSequence ou this 473 if (record) 474 { 475 us.removeRow(rowID); 476 }else 477 { 478 removeRowInternal(index, rowID); 479 } 480 // PropertySet 481 if (container != null) container.setModified(); 482 483 return rowList[index]; 484 } 485 486 /** 487 * TODO 488 * Génère un événement onRemoveAllRows aux observateurs. 489 * 490 * @param cr Client request, true si demande émanant de ce client, false demande émanant d'un autre utilisateur. 491 */ 492 public function removeAllEvent (cr:Boolean):Void 493 { 494 // TODO : Accès Singleton 495 var clazz = _Class.getInstance(); 496 if (! clazz.checkArguments("org.omus.data.Table.removeAllEvent", [[cr, "boolean", true]])) return; 497 init(); 498 fireEvent("info", "onRemoveAllRows", this, cr); 499 } 500 501 /** 502 * Supprime tout les enregistrement de la Table. 503 */ 504 public function removeAllRows ():Void 505 { 506 // UpdateSequence ou this 507 if (this.record) 508 { 509 us.clearTable(); 510 }else 511 { 512 init(); 513 } 514 // PropertySet 515 if (container != null) container.setModified(); 516 } 517 518 //*** Implémentation de iObservable ***\ 519 /** 520 * Notifie les observateurs d'un événement. 521 * 522 * @param logLevel Une chaine caractérisant le niveau de logging de l'information. 523 * @param eventName Nom de l'événement. 524 * @param arg1 [option] Autant de paramètres de voulu. 525 */ 526 private function fireEvent (logLevel:String, eventName:String):Void 527 { 528 _eventSource.fireEvent.apply (_eventSource, arguments); 529 } 530 531 /** 532 * Ajoute un nouvel observateur. 533 * 534 * @param listener Référence de l'observateur. 535 * @return Un booléen indiquant la réussite de l'opération. 536 */ 537 public function addListener (listener:Object):Boolean 538 { 539 return _eventSource.addListener(listener); 540 } 541 542 /** 543 * Supprime un observateur. 544 * 545 * @param listener Référence de l'observateur. 546 * @return Un booléen indiquant la réussite de l'opération. 547 */ 548 public function removeListener (listener:Object):Boolean 549 { 550 return _eventSource.removeListener(listener); 551 } 552 553 /** 554 * Supprime tous les abonnés. 555 */ 556 public function removeAllListeners ():Void 557 { 558 _eventSource.removeAllListeners(); 559 } 560 561 /** 562 * Retourne le nombre d'observateurs. 563 * 564 * @return Le nombre d'observateurs enregistrés. 565 */ 566 public function countListeners ():Number 567 { 568 return _eventSource.countListeners(); 569 } 570 571 //-------------------- 572 // METHODES PRIVEES 573 //-------------------- 574 /** 575 * Initialisation d'une Table. 576 */ 577 private function init ():Void 578 { 579 rowList = new Array(); 580 rowMap = new Object(); 581 rowIDMap = new Object(); 582 nextRowID = 1; 583 } 584 585 /** 586 * Crée une référence et un identifiant à un nouvel enregistrement. 587 * 588 * @param rowID Identifiant de la nouvelle ligne. 589 * @param row Ligne à dupliquer. 590 * @return Un nouvel enregistrement à insérer. 591 */ 592 private function copyRow (rowID:Number, row:Object):Object 593 { 594 var newRow = new Object(); 595 for (var each:String in row) { 596 newRow[each] = row[each]; 597 } 598 newRow.__rowID = rowID; 599 _global.ASSetPropFlags(newRow, ["__rowID"], 7); 600 return newRow; 601 } 602 603 /** 604 * Rajoute un identifiant à l'indexation des enregistrements. 605 * 606 * @param rowID Identifiant de la ligne. 607 * @return L'index numérique "mappé" dans l'indexation. 608 */ 609 private function rowIDtoIndex (rowID:Number):Number 610 { 611 if (rowIDMap == null) { 612 var m = new Object(); 613 var rl = rowList; 614 var long = rl.length; 615 for (var i = 0; i < long ; i++) 616 { 617 m["r" + rl[i].__rowID] = i; 618 } 619 rowIDMap = m; 620 } 621 return rowIDMap["r" + rowID]; 622 } 623 624 /** 625 * Méthode centrale des rajouts d'enregistrements dans la Table. 626 * 627 * @param rowID Identifiant de la ligne. 628 * @param row Référence à l'enregistrement. 629 */ 630 private function addRowInternal (rowID:Number, row:Object):Object 631 { 632 var newRow = copyRow(rowID, row); 633 rowList.push(newRow); 634 rowMap["r" + rowID] = newRow; 635 // Identifiant mappé. 636 if (rowIDMap != null) rowIDMap["r" + rowID] = rowList.length - 1; 637 return newRow; 638 } 639 640 /** 641 * Méthode centrale des mise à jour d'enregistrements. 642 * 643 * @param index Identifiant de la ligne. 644 * @param row Enregistrement de la ligne. 645 */ 646 private function updateRowInternal (index:Number, row:Object):Object 647 { 648 var oldRow = rowList[index]; 649 if (oldRow != row) 650 { 651 var rowID = oldRow.__rowID; 652 var newRow = copyRow(rowID, row); 653 rowList[index] = newRow; 654 rowMap["r" + rowID] = newRow; 655 return newRow; 656 } else 657 { 658 return row; 659 } 660 } 661 662 /** 663 * Méthode centrale de suppresion d'enregistrements. 664 * 665 * @param index Identifiant de l'enregistrement. 666 * @param rowID Enregistrement. 667 */ 668 private function removeRowInternal (index:Number, rowID:Number):Object 669 { 670 var oldRow = rowList[index]; 671 rowList.splice(index, 1); 672 delete rowMap["r" + rowID]; 673 rowIDMap = null; 674 return oldRow; 675 } 676 677 /** 678 * Renvoie une chaine de description de la Table. 679 * 680 * @param indent Nombre d'espace servant d'indentation. 681 */ 682 private function format (indent:Number):String 683 { 684 var s= "org.omus.data.Table:"; 685 var arr = rowList; 686 var len = arr.length; 687 for (var i = 0; i < len; i++) 688 { 689 s += "\n"; 690 for (var idx = 0; idx < indent; idx++) s += " "; 691 s += "row " + i + " = " + org.omus.util.Log.formatObject(arr[i], indent+1); 692 } 693 return s; 694 } 695 696 //-------------------- 697 // METHODES STATIQUES 698 //-------------------- 699 /** 700 * Utilisé dans un contexte littéral 701 * @return Une chaine définissant l'objet 702 */ 703 public static function toLog():String 704 { 705 return "[Object Table]"; 706 } 707 } 708