1  
     2  //!-- UTF8
     3  /*
     4  Oregano Multiuser Server - Version 1.2.0 - January 4th, 2005
     5   
     6  	Web:  www.oregano-server.org
     7  	Mail: info@oregano-server.org
     8   
     9  	Copyright 2003 - 2004 - 2004 Jens Halm / Cologne, Germany
    10   
    11   This library is free software; you can redistribute it and/or
    12   modify it under the terms of the GNU Lesser General Public
    13   License as published by the Free Software Foundation; either
    14   version 2.1 of the License, or (at your option) any later version.
    15   
    16   This library is distributed in the hope that it will be useful,
    17   but WITHOUT ANY WARRANTY; without even the implied warranty of
    18   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    19   Lesser General Public License for more details.
    20   
    21   You should have received a copy of the GNU Lesser General Public
    22   License along with this library; if not, write to the Free Software
    23   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    24   */
    25  
    26  /*
    27  -------------------------------------------
    28  	Classe PropertySet
    29  
    30  	@description :
    31  	Groupe de propriétés persistantes.
    32  
    33  
    34  	@author Jens Halm copyright http://www.spicefactory.org/
    35  	@author erixtekila copyleft http://www.v-i-a.net  
    36  -------------------------------------------
    37  	version history :
    38  	1.2.0 : 17/01/05
    39  			- Portage en actionscript 2 pour le
    40  			compile time type checking
    41  			7/08/05
    42  			- Correction d'une confusion entre propriété et argument (handleLoad)
    43  			Merci à Matthieu Proucelle.
    44  			- Changement de la méthode get par getPropertyByName
    45  -------------------------------------------
    46  */
    47  
    48  import org.omus.util.EventDispatcher;
    49  import org.omus.util.iObservable;
    50  import org.omus.util._Error;
    51  import org.omus.util._Class;
    52  
    53  import org.omus.data.MarshalledProperties;
    54  import org.omus.data.TableProperty;
    55  import org.omus.data.ObjectProperty;
    56  import org.omus.data.Property;
    57  
    58  import org.omus.msg.Message;
    59  import org.omus.msg.Envelope;
    60  import org.omus.msg.EnvelopeFactory;
    61  import org.omus.msg.MessageRouter;
    62  
    63  import org.omus.core.User;
    64  import org.omus.core.Group;
    65  
    66  /**
    67   *	Groupe de propriétés persistantes correspondant à un client ou un groupe.
    68   *	Celles-ci doivent être définies dans le fichiers config.xml
    69   *	Pour obtenir une référence à ces propriétés, utiliser :
    70   *	Properties of the current group : org.omus.core.Group.getInstance().getProperties() 
    71   *	Properties of the currentuser : org.omus.core.User.getInstance().getProperties() 
    72   *	Properties of another user in the current group : org.omus.core.Group.getInstance().getUserProperties("username") 
    73   *	<p>
    74   *	Evénements auxquels s'abonner :
    75   *	<ul>
    76   *	<li>onSynchronise(subset:PropertySet, clientRequest:Boolean)		Généré lors de la synchronisation des propriétés.
    77   *																		_param	Seules les propriétés afféctées.
    78   *																		_param	true si synchronisation demandée de ce client.
    79   *	</li><li>onError(subset:PropertySet, error:_Error)						Généré lors d'une erreur de synchronisation.
    80   *																		_param	Sous groupe contenant la propriétés qui refuse le chargement.
    81   *																		_param	Code de l'erreur.
    82   *	</li><li>onLoad(subset:PropertySet)									Généré lors du chargement de propriétés.
    83   *																		_param	Contient les propriétés concernées.
    84   *	</li></ul></p>
    85   *	Elle est aggrémentée par composition des méthodes des sources d'événements (EventDispatcher).
    86   *	@see org.omus.util.EventDispatcher
    87   *	@see org.omus.util.iObservable
    88   *	@see org.omus.core.User#getProperties
    89   *	@see org.omus.core.Group#getProperties
    90   *	@see org.omus.core.User#getUserProperties
    91   *
    92   *	@author Jens Halm copyright http://www.spicefactory.org/
    93   *	@author erixtekila copyleft http://www.v-i-a.net 
    94   *	@version 1.2.0
    95   */
    96  class org.omus.data.PropertySet implements iObservable
    97  {
    98  	//--------------------
    99  	// PROPRIETES
   100  	//--------------------
   101  	/**
   102  	 *	Conteneur des propriétés.
   103  	 */
   104  	private var props:Object;
   105  	
   106  	/**
   107  	 *	Nombre total de propriétés.
   108  	 */
   109  	private var _size:Number;
   110  	
   111  	/**
   112  	 *	Référence à l'objet parent en cas de synchronisation partielle.
   113  	 *
   114  	 *	@see org.omus.data.PropertySet#onSynchonise
   115  	 *	@see org.omus.data.PropertySet#onLoad
   116  	 */
   117  	private var parent:PropertySet;
   118  	
   119  	/**
   120  	 *	TODO
   121  	 */
   122  	public var marsh:MarshalledProperties;
   123  	
   124  	/**
   125  	 *	Type attaché aux propriétés : group / user
   126  	 */
   127  	private var type:String;
   128  	
   129  	/**
   130  	 *	Posseseur des propriétés.
   131  	 */
   132  	private var owner:Object;
   133  	
   134  	/**
   135  	 *	Identifiant de la clé primaire usrID pour la table userprops et grpID pour groupprops 
   136  	 */
   137  	private var pKey:Number;
   138  	
   139  	/**
   140  	 *	Cache des propriétés.
   141  	 */
   142  	private var cache:Object;
   143  	
   144  	/**
   145  	 *	Référence au système de génération d'événements.
   146  	 */
   147  	private var _eventSource:EventDispatcher;
   148  	
   149  	
   150  	//--------------------
   151  	// CONSTRUCTEUR
   152  	//--------------------
   153  	/**
   154  	 *	L'objet User dispose des méthodes d'EventDispatcher par composition.
   155  	 *
   156  	 *	@param type			"group" ou "user".
   157  	 *	@param owner		Une référence à l'objet propriétaire des propriétés.
   158  	 *	@param pKey			Un identifiant unique au groupe ou utilisateur (clé primaire de la base de données pour grpID et usrID).
   159  	 *	@param par			Parent éventuel du groupe actuel (cf onSynchronise(PropertySet) et onLoad(PropertySet)).
   160  	 *	@see org.omus.util.EventDispatcher
   161  	 *	@see org.omus.util.iObservable
   162  	 */
   163  	public function PropertySet (type:String, owner:Object, pKey:Number, par:PropertySet)
   164  	{
   165  		// Composition avec EventDispatcher
   166  		_eventSource = new EventDispatcher();		
   167  		
   168  		// Propriétés
   169  		props = new Object();
   170  		_size = 0;
   171  		parent = par;
   172  		marsh = (par == null) ? new MarshalledProperties() : par.marsh;
   173  		// TODO : Modifier le nom des propriétés ou arguments pour qu'ils soient différents l'un de l'autre.
   174  		this.type = type;
   175  		this.owner = owner;
   176  		this.pKey = pKey;
   177  		cache = new Object();
   178  		
   179  		// trace(this+ " installé.");
   180  	}
   181  	
   182  	//--------------------
   183  	// METHODES PUBLIQUES
   184  	//--------------------
   185  	/**
   186  	 *	Utilisé dans un contexte littéral
   187  	 *	@return	Une chaine définissant l'objet
   188  	 */
   189  	public function toString ():String
   190  	{
   191  		return format(0);
   192  	}
   193  	
   194  	/**
   195  	 *	Renvoie le parent.
   196  	 *	L'objet PropertySet envoyé lors des événements onSynchronise et onLoad correspond 
   197  	 *	à la partie modifiée. Le parent est l'objet complet.
   198  	 *
   199  	 *	@return		Une référence aux propriétés envoyées partiellement ou null.
   200  	 */
   201  	public function getParent ():PropertySet
   202  	{
   203  		return parent;
   204  	}
   205  	
   206  	/**
   207  	 *	Renvoie un identifiant du possesseur des propriétés.
   208  	 *
   209  	 *	@return		Le nom (string) de l'utilisateur ou une référence à Group.
   210  	 *	@see org.omus.core.Group
   211  	 */
   212  	public function getOwner ():Object
   213  	{
   214  		return owner;
   215  	}
   216  	
   217  	/**
   218  	 *	Renvoie la clé primaire correspondant au possesseur des propriétés.
   219  	 *
   220  	 *	@return		Un identifiant de la base de données.
   221  	 */
   222  	public function getPrimaryKey ():Number
   223  	{
   224  		return pKey;
   225  	}
   226  	
   227  	/**
   228  	 *	Renvoie le nombre de propriétés.
   229  	 *
   230  	 *	@return		Un nombre total.
   231  	 */
   232  	public function size ():Number
   233  	{
   234  		return _size;
   235  	}
   236  	
   237  	/**
   238  	 *	Renvoie une référence aux propiétés encodées.
   239  	 *
   240  	 *	@return		Une référence.
   241  	 */
   242  	public function getMarshalledProperties():MarshalledProperties
   243  	{
   244  		return marsh;
   245  	}
   246  		
   247  	/**
   248  	 *	Renvoie un booléen en fonction de la modification d'une propriété du set, suite à un événément onSynchronise.
   249  	 *
   250  	 *	@return		true si aucune propriété n'a été modifié.
   251  	 */
   252  	public function isSynchronized ():Boolean
   253  	{
   254  		var ps = props;
   255  		for (var each:String in ps)
   256  		{
   257  			if (ps[each].isModified()) return false;
   258  		}
   259  		return true;
   260  	}
   261  
   262  	/**
   263  	 *	Permet de savoir si le set de propriétés appartient à un membre du groupe ou au groupe.
   264  	 *	Une référence à un PropertySet dont le membre aurait quitté le groupe ne sera pas correctement utilisé.
   265  	 *
   266  	 *	@return		true si PropertySet appartient au groupe ou utilisateur actuel.
   267  	 */
   268  	public function isValid ():Boolean
   269  	{
   270  		// TODO : Accès Singleton
   271  		var group = Group.getInstance();
   272  		if (type == "user") 
   273  		{
   274  			// TODO : owner est polymorphe. Autre impélementation ?
   275  			// Ici certitude du type, upcasting.
   276  			return group.containsUser (String (owner));
   277  		} else
   278  		{
   279  			return (group == owner);
   280  		}
   281  	}
   282  	
   283  	/**
   284  	 *	Renvoie la valeur d'une propriété specifiée.
   285  	 *
   286  	 *	@param propName		Le nom d'une propriété.
   287  	 *	@return				Une valeur ou null si la propriété n'a pas été chargée.
   288  	 */
   289  	public function getValue (propName:String):Object
   290  	{
   291  		// TODO : Ancien get().
   292  		var p = this.getPropertyByName (propName);
   293  		return (typeof(p) == "object") ? p.value : null ;
   294  	}
   295  	
   296  	/**
   297  	 *	Renvoie le type d'une d'une propriété spécifiée.
   298  	 *
   299  	 *	@param propName		Le nom d'une propriété.
   300  	 *	@return				type utilisé : …
   301  	 */
   302  	public function getType (propName:String):String
   303  	{
   304  		// TODO : Ancien get().
   305  		var p = this.getPropertyByName (propName);
   306  		return (typeof(p) == "object") ? p.type : null ;
   307  	}
   308  
   309  	/**
   310  	 *	Permet de savoir si une propriété est contenu dans le set.
   311  	 *
   312  	 *	@param propName		Le nom d'une propriété.
   313  	 *	@return				true si propName appartient au PropertySet.
   314  	 */
   315  	public function contains (propName:String):Boolean
   316  	{
   317  		return (props[propName] != undefined);
   318  	}
   319  
   320  	/**
   321  	 *	Permet de savoir si une propriété a été modifiée depuis le dernier événement onSynchronise.
   322  	 *
   323  	 *	@param propName		Le nom de la propriété.
   324  	 *	@return				true s'il ya eu modification.
   325  	 */
   326  	public function isModified (propName:String):Boolean
   327  	{
   328  		// TODO : Ancien get().
   329  		var p = this.getPropertyByName (propName);
   330  		return (typeof(p) == "object") ? p.modified : false ;
   331  	}
   332  	
   333  	/**
   334  	 *	Renseigne si une propriété spécifiée a été chargé dans le poste client.
   335  	 *
   336  	 *	@param propName		Le nom de la propriété.
   337  	 *	@return				true si la valeur d'une propriété est chargée.
   338  	 */
   339  	public function isLoaded (propName:String):Boolean
   340  	{
   341  		// TODO : Ancien get().
   342  		var p = this.getPropertyByName (propName);
   343  		return (typeof(p) == "object") ? (p.value != null) : false ;
   344  	}
   345  	
   346  	/**
   347  	 *	Modifie la valeur d'une propriété sur le poste client 
   348  	 *	dans l'attente de la synchronisation avec le serveur.
   349  	 *	Le type doit correspondre avec celui contenu dans le fichier config.xml 
   350  	 *
   351  	 *	@param propName			Le nom d'une propriété.
   352  	 *	@param newVal			Une valeur.
   353  	 *	@see org.omus.data.PropertySet#synchronise
   354  	 */
   355  	public function setValue (propName:String, newVal:Object):Void
   356  	{
   357  		// TODO : Ancien get().
   358  		var p = this.getPropertyByName (propName);
   359  		if (typeof(p) == "object") p.setValue(newVal);
   360  	}
   361  	
   362  	/**
   363  	 *	Synchronise toutes les propriétés d'un set.
   364  	 *	Le comportement change en fonction de la configuration côté serveur :
   365  	 *	- Ecriture dans la base de donnée : persistance globale
   366  	 *	- Synchronisation avec seulement les membres connectés.
   367  	 */
   368  	public function synchronize ():Void
   369  	{
   370  		if (! isValid())
   371  		{
   372  			// Logs internes
   373  			_global.oregano.iLog.error("clj-020","owner = " + owner);
   374  			return;
   375  		}
   376  		// Message
   377  		var msg = new Message(type);
   378  		var attach = msg.getAttachment();
   379  		var extr = marsh.extract(this.props);
   380  		if (extr.size() == 0) 
   381  		{
   382  			// Logs internes
   383  			_global.oregano.iLog.warn("clj-021","owner = " + owner);
   384  			return;
   385  		}
   386  		attach.props = extr;
   387  		if (type == "user") attach.username = owner;
   388  		// TODO : Accès Singleton
   389  		var envFactory = EnvelopeFactory.getInstance();
   390  		var env = envFactory.getOutgoing(msg, "syncProps");
   391  		// TODO : Accès Singleton
   392  		var msgRouter = MessageRouter.getInstance();
   393  		msgRouter.handleOutgoing(env, this, "synchronize", arguments, {props:extr});
   394  	}
   395  	
   396  	/**
   397  	 *	Charge toutes les propriétés spécifiées dans le client.
   398  	 *
   399  	 *	@param propName		Une liste de chaînes de toutes les propriétés à charger.
   400  	 */
   401  	public function load (propNames:Array):Void
   402  	{
   403  		// TODO : Accès Singleton
   404  		var clazz = _Class.getInstance();
   405  		if (! clazz.checkArguments("org.omus.data.PropertySet.load", [[propNames, Array, true]])) return;
   406  		if (! isValid())
   407  		{
   408  			//!! TODO : Vérifier la réf !!
   409  		 	_global.oregano.iLog.error("clj-022","owner = " + this.owner);
   410  			return;
   411  		}
   412  		//
   413  		var pa = new Array();
   414  		var ps:Object = this.props;
   415  		var long = propNames.length;
   416  		for (var i = 0; i < long ; i++)
   417  		{
   418  			var p = ps[propNames[i]];
   419  			if (p == undefined) 
   420  			{
   421  				// Logs internes
   422  				_global.oregano.iLog.error("clj-023", "property name = " + propNames[i] + " - owner = " + owner);
   423  				return;
   424  			}
   425  			if (p.isLoaded()) 
   426  			{
   427  				// Logs internes
   428  				_global.oregano.iLog.warn("clj-024", "property name = " + propNames[i] + " - owner = " + this.owner);
   429  			} else
   430  			{
   431  				pa.push(p.name);
   432  			}
   433  		}
   434  		loadInternal(pa, "load");
   435  	}
   436  	
   437  	/**
   438  	 *	Charge toutes les propriétés non téléchargés automatiquement.
   439  	 *	Voir config.xml
   440  	 */
   441  	public function loadAll ():Void
   442  	{
   443  		if (! isValid())
   444  		{
   445  			// Logs internes
   446  			_global.oregano.iLog.error("clj-025", "owner = " + owner);
   447  			return;
   448  		}
   449  		var pa = new Array();
   450  		var ps:Object = this.props;
   451  		for (var each:String in ps)
   452  		{
   453  			//!! TODO : Syntaxe !! if (! ps[each].isLoaded()) pa.push(p.name); 
   454  			// TODO : Fixé par Jens Halm
   455  			var p = ps[each];
   456  			if (! p.isLoaded()) pa.push(p.name);
   457  		}
   458  		loadInternal(pa, "loadAll");
   459  	}
   460  	
   461  	/**
   462  	 *	TODO : Renvoie les propriétés ayant été modifiées ??
   463  	 *
   464  	 *	@return		Une référence.
   465  	 */
   466  	public function getChanged ():MarshalledProperties
   467  	{
   468  		return marsh.extract(props);
   469  	}
   470  
   471  	/**
   472  	 *	TODO : Rajoute la propriété comme étant à soumettre au serveur ??
   473  	 *
   474  	 *	@param propName		Le nom de la propriété.
   475  	 *	@param marshVal		Nouvelle valeur de la propriété.
   476  	 */
   477  	public function valueChanged (propName:String, marshVal:Object):Void
   478  	{
   479  		marsh.addProp(propName, marshVal);
   480  	}
   481  	
   482  	/**
   483  	 *	Rajoute les propriétés persistantes en les typant correctement avec leur valeurs.
   484  	 *
   485  	 *	@param propConfig		Tableau associatif des propriétés.
   486  	 *	@param props			Valeurs des propriétés.
   487  	 *	@see org.omus.data.Property
   488  	 *	@see org.omus.data.ObjectProperty
   489  	 *	@see org.omus.data.TableProperty
   490  	 */
   491  	public function fill (propConfig:Object, props:Object):Void
   492  	{
   493  		for (var each:String in propConfig) 
   494  		{
   495  			var typ = propConfig[each].type;
   496  			var cach = propConfig[each].cache;
   497  			var value = props[each];
   498  			// TODO : Accès Singleton
   499  			var clazzInstance = _Class.getInstance();
   500  			var clazz = clazzInstance.propTypeToClass(typ);
   501  			var p:Property;
   502  			// Création des propriétés typées
   503  			if (value != undefined) 
   504  			{
   505  				if (clazz.toString() == "[Object Table]")
   506  				{
   507  					p = new TableProperty(each, typ, clazz, cach, value);
   508  				} else if (typeof(clazz) != "string")
   509  				{
   510  					p = new ObjectProperty(each, typ, clazz, cach, value);
   511  				}else
   512  				{
   513  					p = new Property(each, typ, clazz, cach, value);
   514  				}
   515  			} else 
   516  			{
   517  				p = new Property(each, typ, clazz, cach, null);
   518  			}
   519  			addProperty(p);
   520  		}
   521  	}
   522  	
   523  	//*** Implémentation de iObservable ***\\
   524  	/**
   525  	 *	Notifie les observateurs d'un événement.
   526  	 *
   527  	 *	@param logLevel Une chaine caractérisant le niveau de logging de l'information.
   528  	 *	@param eventName Nom de l'événement.
   529  	 *	@param arg1		[option] Autant de paramêtres de voulu.
   530  	 */
   531  	private function fireEvent (logLevel:String, eventName:String):Void
   532  	{
   533  		_eventSource.fireEvent.apply (_eventSource, arguments);
   534  	}
   535  	
   536  	/**
   537  	 *	Ajoute un nouvel observateur.
   538  	 * 
   539  	 *	@param		listener Référence de l'observateur.
   540  	 *	@return		Un booléen indiquant la réussite de l'opération.
   541  	 */
   542  	public function addListener (listener:Object):Boolean
   543  	{
   544  		return _eventSource.addListener(listener);
   545  	}
   546  	
   547  	/**
   548  	 *	Supprime un observateur.
   549  	 *
   550  	 *	@param		listener Référence de l'observateur.
   551  	 *	@return Un booléen indiquant la réussite de l'opération.
   552  	 */
   553  	public function removeListener (listener:Object):Boolean
   554  	{
   555  		return _eventSource.removeListener(listener);
   556  	}
   557  	
   558  	/**
   559  	 *	Supprime tous les abonnés.
   560  	 */
   561  	public function removeAllListeners ():Void
   562  	{
   563  		_eventSource.removeAllListeners();
   564  	}
   565  	
   566  	/**
   567  	 *	Retourne le nombre d'observateurs.
   568  	 *
   569  	 *	@return Le nombre d'observateurs enregistrés.
   570  	 */
   571  	public function countListeners ():Number
   572  	{
   573  		return _eventSource.countListeners();
   574  	}
   575  	//*** fin Implémentation de iObservable ***\\
   576  	
   577  	//*** incoming messages ***\\
   578  	/**
   579  	 *	Dirige la message d'accusé de réception en fonction du type contenu dans son enveloppe.
   580  	 *
   581  	 *	@param env		Enveloppe du message.
   582  	 */
   583  	public function handleMessage (env:Envelope):Void
   584  	{
   585  		var type = env.getType();
   586  		if (type == "syncProps") 
   587  		{
   588  			handleSynchronize(env);
   589  		}else
   590  		{
   591  			handleLoad(env);
   592  		}
   593  	}
   594  	
   595  	/**
   596  	 *	Gère les accusés de réception de synchronisation
   597  	 *
   598  	 *	@param env		Une enveloppe du message.
   599  	 */
   600  	private function handleSynchronize (env:Envelope):Void
   601  	{
   602  		var attach = env.getMessage().getAttachment();
   603  		var errCode = attach.error;
   604  		var clientRequest = attach.clientRequest;
   605  		// TODO : Accès Singleton
   606  		var msgRouter = MessageRouter.getInstance();
   607  		// Seul PropertySet utilse le dernier argument du MessageRouter.getCache
   608  		var extr = (errCode != "ok" || clientRequest) ? msgRouter.getCache(env.getID(), "info") ["props"].props : null;
   609  		if (errCode == "ok") {
   610  			synchronizeOK(attach.props, clientRequest, extr);
   611  		} else {
   612  			synchronizeFailed(extr, errCode, "synchronize", new Array());
   613  		}
   614  	}
   615  	
   616  	/**
   617  	 *	Gestion de la synchronisation des propriétés suite à une synchronisation serveur.
   618  	 *	Génère un événement onSynchronise aux observateurs.
   619  	 *
   620  	 *	@param props		Un objet des propriétés à jour.
   621  	 *	@param cr			Client request, true si demande émanant du cleint courant.
   622  	 *	@param cache		TODO
   623  	 */
   624  	public function synchronizeOK (props:Object, cr:Boolean, cache:Object):Void
   625  	{
   626  		var par = (this.parent == null) ? this : this.parent;
   627  		// Fils PropertySet
   628  		var ps = new PropertySet(type, owner, pKey, par);
   629  		for (var each:String in props) 
   630  		{
   631  			//!! TODO : Modifier le nom du paramètre, confusion avec la propriété
   632  			// Propriétés à mettre à jour
   633  			var p = this.props[each];
   634  			if (p == undefined)
   635  			{
   636  				// Logs internes
   637  				_global.oregano.iLog.error("clj-026", "property name = " + each + " - owner = " + owner);
   638  				continue;
   639  			}
   640  			// Mis à jour de la Property
   641  			p.update(props[each], cr);
   642  			ps.addProperty(p);
   643  		}
   644  		// gestion du cache
   645  		if (cache != null)
   646  		{
   647  			// TODO : Accès Singleton
   648  			var user = User.getInstance();
   649  			var group = Group.getInstance();
   650  			for (var each:String in cache)
   651  			{
   652  				var p = this.props[each];
   653  				var minCache:Number;
   654  				if (owner == group || owner != user.getName()) 
   655  				{
   656  					minCache = org.omus.data.Property.SYNCHRONIZE_GROUP;
   657  				} else
   658  				{
   659  					minCache = org.omus.data.Property.SYNCHRONIZE_CLIENT;
   660  				}
   661  				if (p.cache < minCache)
   662  				{
   663  					p.update(null, cr);
   664  					ps.addProperty(p);
   665  				}
   666  			}
   667  		}
   668  		// Broadcast
   669  		fireEvent("info", "onSynchronize", ps, cr);
   670  	}
   671  	
   672  	/**
   673  	 *	Gestion de la synchronisation des propriétés suite à une erreur de synchronisation serveur.
   674  	 *	Génère un événement onError aux observateurs.
   675  	 *
   676  	 *	@param cache		Un objet des propriétés à jour.
   677  	 *	@param errorCode	Client request, true si demande émanant du cleint courant.
   678  	 *	@param method		L'accusé de réception d'où émane l'erreur.
   679  	 *	@aparam args		Une liste des propriétés contenues dans le cache.
   680  	 */
   681  	public function synchronizeFailed (cache:Object, errCode:String, method:String, args:Array) {
   682  		var par = (parent == null) ? this : parent;
   683  		// subset PropertySet
   684  		var ps = new PropertySet(type, owner, pKey, par);
   685  		for (var each:String in cache) 
   686  		{
   687  			var p:Property = props[each];
   688  			p.reset();
   689  			ps.addProperty(p);
   690  		}
   691  		fireEvent("error", "onError", ps, new _Error(errCode, method, args));
   692  	}
   693  	
   694  	/**
   695  	 *	Gère les accusés de réception de chargement de propriétés.
   696  	 *	Génère les événements onLoad et onError aux observateurs.
   697  	 *
   698  	 *	@param env		Une enveloppe du message.
   699  	 */
   700  	private function handleLoad (env:Envelope)
   701  	{
   702  		var attach = env.getMessage().getAttachment();
   703  		var errCode = attach.error;
   704  		if (errCode == "ok") 
   705  		{
   706  			var par = (parent == null) ? this : parent;
   707  			var ps = new PropertySet(type, owner, pKey, par);
   708  			//!! TODO : Modifier ce nom de variable, confusion avec variable locale. 
   709  			var props:Property = attach.props;
   710  			for (var each:String in props)
   711  			{
   712  				//!! TODO : Modifier ce nom de variable, confusion avec variable locale. 
   713  				var p = this.props[each];
   714  				if (p == undefined)
   715  				{
   716  					// Logs internes
   717  					_global.oregano.iLog.error("clj-027", "property name = " + each + " - owner = " + owner);
   718  					continue;
   719  				}
   720  				p.loadTemp(props[each]);
   721  				ps.addProperty(p);
   722  			}
   723  			// Broadcast
   724  			fireEvent("info", "onLoad", ps);
   725  			
   726  			// TODO : cf plus ligne 678… 
   727  			for (var each:String in props)
   728  			{
   729  				var p:Property = this.props[each];
   730  				if (p == undefined) continue;
   731  				// annule toutes les propriétés définies (Property)
   732  				p.unload();
   733  			}
   734  		} else 
   735  		{
   736  			// TODO : Accès Singleton
   737  			var msgRouter = MessageRouter.getInstance();
   738  			var cache = msgRouter.getCacheObject(env.getID());
   739  			var method = cache.method;
   740  			var ps:PropertySet;
   741  			if (method == "load")
   742  			{
   743  				//!! TODO : Syntaxe !! ps = filterByName(pn);
   744  				// ps = filterByName(pn);
   745  				// TODO : Fixé par Jens Halm
   746  				var propNames = cache.args[0];
   747  				ps = filterByName(propNames);
   748  			} else 
   749  			{
   750  				// loadAll
   751  				var par = (parent == null) ? this : parent;
   752  				ps = new PropertySet(type, owner, pKey, par);
   753  				var all = this.props;
   754  				for (var each:String in all)
   755  				{
   756  					//!! TODO : Syntaxe !! if (! all[each].isLoaded()) ps.addProperty(p);
   757  					//if (! all[each].isLoaded()) ps.addProperty(p);
   758  					// TODO : Fixé par Jens Halm
   759  					var p = all[each];
   760  					if (! p.isLoaded()) ps.addProperty(p);
   761  				}
   762  			}
   763  			// Broadcast
   764  			fireEvent("error", "onError", ps, new _Error(errCode, method, cache.args));
   765  		}
   766  	}
   767  	
   768  	//--------------------
   769  	// METHODES PRIVEES
   770  	//--------------------
   771  	/**
   772  	 *	Méthode centrale du chargement des propriétés.
   773  	 *
   774  	 *	@param propNames		Une liste de toutes les propriétés à charger.
   775  	 *	@param methodName		Une chaîne identifiant la demande.
   776  	 */
   777  	private function loadInternal (propNames:Array, methodName:String):Void
   778  	{
   779  		if (propNames.length == 0) 
   780  		{
   781  			// Logs internes
   782  			_global.oregano.iLog.warn("clj-064", "owner = " + owner);
   783  			return;
   784  		}
   785  		// Message
   786  		var msg = new Message(type);
   787  		var attach = msg.getAttachment();
   788  		attach.propNames = propNames;
   789  		if (type == "user") attach.username = owner;
   790  		// TODO : Accès Singleton
   791  		var envFactory = EnvelopeFactory.getInstance();
   792  		var env = envFactory.getOutgoing(msg, "loadProps");
   793  		// TODO : Modifié par Jens Halm
   794  		var args = (methodName == "loadAll") ? [] : [propNames] ;
   795  		// TODO : Accès Singleton
   796  		var msgRouter = MessageRouter.getInstance();
   797  		msgRouter.handleOutgoing(env, this, methodName, args, null);
   798  	}
   799  	
   800  	/**
   801  	 *	Renvoie une référence à une propriété.
   802  	 *
   803  	 *	@param propName		Le nom de la propriété.
   804  	 *	@return				Une référence.
   805  	 */
   806  	private function getPropertyByName (propName:String):Object
   807  	{
   808  		var p:Property = props[propName];
   809  		// Logs internes
   810  		if (typeof(p) != "object") _global.oregano.iLog.warn("clj-067", "property name = " + propName + " - owner = " + owner);
   811  		return p;
   812  	}
   813  	
   814  	/**
   815  	 *	Synchronisation partielle avec le serveur.
   816  	 *	TODO : Ne semble pas être utilisée ?
   817  	 *
   818  	 *	@param filterFunc		Une fonction de filtrage devant retourner un booléen.
   819  	 *	@return					Un fils de l'objet PropertySet à synchroniser.
   820  	 */
   821  	private function filter (filterFunc:Function):PropertySet
   822  	{
   823  		// TODO : Accès Singleton
   824  		var clazz = _Class.getInstance();
   825  		// TODO : Type return cannot be Void : if (! _global.org.omus.clazz.checkArguments("org.omus.data.PropertySet.filter", [[filterFunc, "function", true]])) return;
   826  		if (clazz.checkArguments("org.omus.data.PropertySet.filter", [[filterFunc, "function", true]]))
   827  		{
   828  			var par = (parent == null) ? this : parent;
   829  			// Fils PropertySet
   830  			var ps = new PropertySet(type, owner, pKey, par);
   831  			var p = props;
   832  			var long = p.length;
   833  			for (var i = 0; i < long ; i++)
   834  			{
   835  				if (filterFunc(p[i])) ps.addProperty(p[i]);
   836  			}
   837  			return ps;
   838  		}
   839  	}
   840  	
   841  	/**
   842  	 *	TODO
   843  	 *
   844  	 *	@param propNames		Une liste des noms de propriétés.
   845  	 *	@return					Un fils de l'objet PropertySet
   846  	 */
   847  	private function filterByName (propNames:Array):PropertySet
   848  	{
   849  		// TODO : Accès Singleton
   850  		var clazz = _Class.getInstance();
   851  		// TODO : Type return cannot be Void : if (_global.org.omus.clazz.checkArguments("org.omus.data.PropertySet.filterByName", [[propNames, Array, true]])) return;
   852  		if (clazz.checkArguments("org.omus.data.PropertySet.filterByName", [[propNames, Array, true]]))
   853  		{
   854  			var par = (parent == null) ? this : parent;
   855  			// Nouveau PropertySet
   856  			var ps = new PropertySet(type, owner, pKey, par);
   857  			var long = propNames.length;
   858  			for (var i = 0; i < long ; i++) 
   859  			{
   860  				var p:Property = this.props[propNames[i]];
   861  				if (p == undefined)
   862  				{
   863  					_global.oregano.iLog.error("clj-019", "property name = " + propNames[i] + " - owner = " + owner);
   864  				} else {
   865  					ps.addProperty(p[i]);
   866  				}
   867  			}
   868  			return ps;
   869  		}
   870  	}
   871  	
   872  	/**
   873  	 *	Synchronisation partielle avec le serveur.
   874  	 *	TODO : Ne semble pas être utilisée ?
   875  	 *
   876  	 *	@param filterFunc		Une fonction de filtrage devant retourner un booléen.
   877  	 */
   878  	private function filterInternal (filterFunc:Function):Object 
   879  	{
   880  		var props = new Object();
   881  		var p = this.props;
   882  		var long = p.length;
   883  		for (var i = 0; i < long ; i++)
   884  		{
   885  			if (filterFunc(p[i])) props[p[i].name] = p[i].value;
   886  		}
   887  		return props;
   888  	}
   889  	
   890  	/**
   891  	 *	Rajoute une propriété persistante.
   892  	 *
   893  	 *	@param p		Nouvelle propriété.
   894  	 */
   895  	private function addProperty (p:Property):Void
   896  	{
   897  		// Property
   898  		if (parent == null) p.setPropertySet(this);
   899  		_size++;
   900  		// TODO : Accès méthode : props[p.name] = p;
   901  		props[p.getName()] = p;
   902  	}
   903  	
   904  	/**
   905  	 *	Renvoie une chaine de description de la Table.
   906  	 *
   907  	 *	@param indent		Nombre d'espace servant d'indentation.
   908  	 */
   909  	private function format (indent:Number):String
   910  	{
   911  		var s= "org.omus.data.PropertySet:";
   912  		s += "\n";
   913  		for (var idx = 0; idx < indent; idx++) s += "  ";
   914  		s += "type = " + type;
   915  		if (type == "user")
   916  		{
   917  			s += "\n";
   918  			for (var idx = 0; idx < indent; idx++) s += "  ";
   919  			s += "owner = " + owner;
   920  		}
   921  		s += "\n";
   922  		for (var idx = 0; idx < indent; idx++) s += "  ";
   923  		s += "primary key = " + pKey;
   924  		s += "\n";
   925  		for (var idx = 0; idx < indent; idx++) s += "  ";
   926  		s += "number of properties = " + _size;
   927  		
   928  		var pr = props;
   929  			for (var each:String in pr)
   930  			{
   931  				s += "\n";
   932  				for (var idx = 0; idx < indent; idx++) s += "  ";
   933  				s += "===========================";
   934  				s += "\n";
   935  				for (var idx = 0; idx < indent; idx++) s += "  ";
   936  				s += pr[each].format(indent + 1);
   937  			}
   938  		return s;
   939  	}
   940  	
   941  	//--------------------
   942  	// METHODES STATIQUES
   943  	//--------------------
   944  	/**
   945  	 *	Utilisé dans un contexte littéral
   946  	 *	@return	Une chaine définissant l'objet
   947  	 */
   948  	public static function toLog():String
   949  	{
   950  		return "[Object PropertySet]";
   951  	}
   952  	
   953  	/**
   954  	 *	Gestionnaire d'accusé de réception émanant directement de MessageRouter
   955  	 *	et chargé de redistribuer les messages entre les propriétés d'un groupe ou d'un utilisateur.
   956  	 *	TODO : Compileur MMC 2004 n'accepte pas une méthode de classe et d'instance ayant le même nom.
   957  	 *
   958  	 *	@param env		Enveloppe du message.
   959  	 *	@see org.omus.msg.MessageRouter#handleIncoming
   960  	 */
   961  	 public static function handleIncomingMessage (env:Envelope):Void
   962  	{
   963  		var type = env.getType();
   964  		if (type == "syncProps")
   965  		{
   966  			var username = env.getMessage().getAttachment().username;
   967  			var target:PropertySet;
   968  			// TODO : Accès Singleton
   969  			var group = Group.getInstance();
   970  			if (username == undefined){
   971  				target = group.getProperties();
   972  			} else {
   973  				target = group.getUserProperties(username);
   974  			}
   975  			target.handleMessage(env);
   976  			return;
   977  		}
   978  		// TODO : Accès Singleton.
   979  		var msgRouter = MessageRouter.getInstance();
   980  		msgRouter.unknownMsgType(env);
   981  	}
   982  }
   983