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