/**
  * Si no esta salvado:
  *   Para cada hasOne:
  *     ...
  *   save_object()
  *   Para cada hasMany:
  *     ...
  * 
  * @return boolean true si no hubo error, false en caso contrario
  */
 public function save_cascade(PersistentObject $obj, $sessId)
 {
     Logger::getInstance()->pm_log("PM::save_cascade " . get_class($obj) . " SESSIONID: " . $sessId);
     // Para detectar loops en el salvado del modelo
     $obj->setLoopDetectorSessId($sessId);
     // Si el objeto no fue salvado en la operacion actual...
     if (!$obj->isSaved($sessId)) {
         // Nuevo: solo salva si se ha cambiado un atributo o una relacion hasOne (dirty)
         if ($obj->isDirtyOne()) {
             // La relacion con los hasOne, o sea el id, se salva como atributo simple en save_object.
             // Este codigo de abajo solo verifica si hay que salvar en cascada cada objeto hasOne que tenga asociado.
             //asOne no necesita tablas intermedias (salvar la referencia)
             // Retorna los valores no nulos de hasOne
             $sassoc = $obj->getSimpleAssocValues();
             // TODO?: Podria chekear si debe o no salvarse en cascada...
             foreach ($sassoc as $attrName => $assocObj) {
                 // ojo el objeto debe estar cargado (se verifica eso)
                 if ($assocObj !== PersistentObject::NOT_LOADED_ASSOC) {
                     //echo "=== PO loaded: $attrName<br/>";
                     // Si se detecta un loop en el salvado del modelo.
                     // FIXME: deberia salvar solo si el objeto soy owner del relacionado.
                     if ($assocObj->isLoopMarked($sessId)) {
                         //Logger::getInstance()->pm_log("LOOP DETECTADO " . get_class($obj) . " " . get_class($assocObj));
                         // Agrega al objeto un callback para que se llame cuando termine de salvarse, para salvar el objeto hasOne asociado.
                         // Se salva el objeto actual sin el asociado (assocObj viene a ser instancia de A del modelo A -> B -> C -> A, donde obj viene a ser instancia de C).
                         // Esto deja a obj inconsistente, pero se arregla con el callback cuando termina de salvar a A, se actualiza la referencia de C a A.
                         // =============================================================================
                         // Se empezo a salvar desde A, se quiere salvar C que a su vez necesita A.
                         // $assocObj es A.
                         // $obj es C.
                         // 1. Actualizar ids de hasOne. // update_simple_assocs
                         $callb_update = new Callback();
                         $callb_update->set($obj, 'update_simple_assocs', array());
                         // FIXME (posible bug TICKET #4.1): OJO!, este save deberia ser un save simple (no salvar nada en cascada) y hacerce obligatoriamente, sin considerar el id de session...
                         // 2. Salvar el objeto. Llama a save del PO que es el wrapper del PM...
                         $callb_save = new Callback();
                         $callb_save->set($obj, 'single_save', array());
                         // Intento solucion TICKET #4.1
                         // Registro los callbacks en A, para que cuando se salve, se actualice C con su id.
                         $assocObj->registerAfterSaveCallback($callb_update);
                         $assocObj->registerAfterSaveCallback($callb_save);
                         // No se sigue salvando en cascada el objeto asociado xq ya se quiso salvar y se llego
                         // a un loop, se corta el loop y se salvan los objetos con los datos que tienen, y los
                         // datos que no se tienen se salvan en callbacks.
                         // =====================================================================================
                     } else {
                         // El objeto puede estar salvado en otra sesion, por ejemplo se crea y salva, y luego se asocia al obj.
                         // La condicion es: si no esta salvado en esta session o esta sucio, salvo en cascada.
                         if ($obj->isOwnerOf($attrName) && (!$assocObj->isClean() || !$assocObj->isSaved($sessId))) {
                             //Logger::getInstance()->pm_log("PM::save_assoc save_cascade de ". $assocObj->getClass() .__LINE__);
                             // Valido el objeto antes de intentar salvarlo
                             // La excepcion se catchea en el save y hace rollback de todo el save en cascada.
                             if (!$assocObj->validate()) {
                                 throw new Exception("El objeto " . $assocObj->getClass() . " (" . $assocObj->getId() . ") no valida. " . __FILE__ . " " . __LINE__);
                             }
                             // hasOne no necesita tablas intermedias (salvar la referencia)
                             // salva objeto y sus asociaciones.
                             $this->save_cascade($assocObj, $sessId);
                         }
                     }
                 }
                 // Si el hasOne esta cargado. Sino esta cargado, no hago nada (la relacion se actualiza en save_object).
             }
             // Para cada objeto asociado
             // ------------------------------------------------------------------------------------------------------------------
             // VERIFY: Como y donde se setean los atributos de id de las referencias!!
             // (tendria que hacerse en DAL verificando que el atributo corresponde a una asociacion hasOne)
             //
             // Aca tengo los ids de los hasOne y puedo salvar las referencias desde obj a ellos.
             // FIXME!!!!!: TENGO QUE SALVAR ANTES LOS hasOne para tener sus ids y setear los atributos generados "email_id" ...!!!
             $obj->update_simple_assocs();
             // Actualiza los atributos de referencia a objetos de hasOne (como "email_id")
         }
         // si la instancia esta dirty
         //Logger::struct( $obj , "PRE PM.save_object en PM.save_cascade");
         //Logger::getInstance()->pm_log("PM::save_assoc save_object ". $obj->getClass() ." @".__LINE__);
         // salva el objeto simple, verificando restricciones en la instancia $obj
         // FIXME: esta operacion no verifica restricciones, se deberia validar el objeto antes de salvar.
         //        la validacion para el objeto raiz de la estrucura se hace en PO, pero no para el resto
         //        de los objetos asociados que se salvan en cascada
         // FIXME: el problema es que si falla la validacion de un objeto salvado en cascada, deberia
         //        abortar todo el save, o sea ser transaccional, y esto todavia no esta soportado.
         $this->save_object($obj, $sessId);
         // Si se han modificado los hasMany
         if ($obj->isDirtyMany()) {
             $massoc = $obj->getManyAssocValues();
             // Es una lista de listas de objetos.
             foreach ($massoc as $attrName => $objList) {
                 $ord = 0;
                 //Logger::getInstance()->pm_log("save_cascade foreach hasManyAssoc: ". $attrName ." ". __FILE__ ." ". __LINE__ );
                 foreach ($objList as $assocObj) {
                     // Problema con cascada hasMany: a1 -> b1 -> c1 -> a1
                     // cuando c1 quiere salvar a a1 no entra aca, eso esta bien, pero deberia salvarse la relacion c1 -> a1...
                     // No se cual es la condicion para salvar la relacion solo, voy a intentar solo decir que c1 es owner de a1 a ver que pasa...
                     if ($obj->isOwnerOf($attrName)) {
                         //Logger::getInstance()->pm_log("PM::save_assoc ". $obj->getClass()." isOwnerOf $attrName. " .__LINE__);
                         // FIXME ?: por que aca no es igual que en las relaciones hasOne?
                         // FIXME: Es probable que el assocObj no se haya salvado en esta sesion y que este salvado.
                         //        Se pudo haber creado y salvado antes, y luego asociado a hasMany del obj.
                         //        Habria que preguntar si NO esta salvado en esta session o si esta dirty.
                         if (!$assocObj->isClean() || !$assocObj->isSaved($sessId)) {
                             // Valido el objeto antes de intentar salvarlo
                             // La excepcion se catchea en el save y hace rollback de todo el save en cascada.
                             if (!$assocObj->validate()) {
                                 throw new Exception("El objeto " . $assocObj->getClass() . " (" . $assocObj->getId() . ") no valida. " . __FILE__ . " " . __LINE__);
                             }
                             // salva objeto y sus asociaciones.
                             $this->save_cascade($assocObj, $sessId);
                             //Logger::getInstance()->pm_log("PM::save_cascade objeto guardado: ". $assocObj->getClass(). " ". $assocObj->getId(). " " .__LINE__);
                         }
                         //Logger::getInstance()->pm_log("PM::save_assoc save_assoc de ". $obj->getClass(). " ". $assocObj->getClass(). " " .__LINE__);
                         // El objeto puede estar salvado y la relacion no.
                         // Actualiza tabla intermedia.
                         // Necesito tener, si la relacion es bidireccional, el nombre del atributo de assocObj que tiene Many obj, podria haber varios!
                         $this->save_assoc($obj, $assocObj, $attrName, $ord);
                         // Se debe salvar aunque a1 este salvado (problema loop hasmany)
                     }
                     $ord++;
                 }
                 // para cada objeto dentro de una relacion hasMany
             }
             // para cada relacion hasMany
         }
         // si tiene dirtyMany
     }
     // if is_saved obj
     // Termina de guardar el objeto, limpia los bits de dirty.
     $obj->resetDirty();
 }