/**
  * Metodo auxiliar para cargar los objetos asociados en cascada, tanto los hasOne como los hasMany, 
  * verificando previamente si no fueron ya cargados.
  * @param PersistentObject $obj el objeto al que hay que cargarle los objetos asociados.
  */
 private function getCascadeAssocs($obj)
 {
     // Para saber si estaba limpia previamente, para poder limpiar lo que esta operacion ensucia (dirty bits)
     $wasClean = $obj->isClean();
     // TODO: Verificar si para los objetos en hasOne, sus asociaciones son cargadas en cascada.
     // Para cada objeto hasOne, lo trae.
     // Para el objeto hago get para hasOne y getMany para los hasMany.
     $ho_attrs = $obj->getHasOne();
     foreach ($ho_attrs as $attr => $assoc_class) {
         // attr = "email_id" ?
         $ho_instance = new $assoc_class();
         $hasOneAttr = DatabaseNormalization::getSimpleAssocName($attr);
         // email
         $assoc_id = $ho_instance->aGet($attr);
         $assoc_obj = NULL;
         if (ArtifactHolder::getInstance()->existsModel($assoc_class, $assoc_id)) {
             $assoc_obj = ArtifactHolder::getInstance()->getModel($assoc_class, $assoc_id);
         } else {
             $assoc_obj = $this->get($assoc_class, $assoc_id);
             ArtifactHolder::getInstance()->addModel($assoc_obj);
         }
         //$obj->{"set".$attr}( $assoc_obj );
         $obj->aSet($attr, $assoc_obj);
     }
     // Para cada objeto hasMany, lo trae
     // Para el objeto hago get para hasOne y getMany para los hasMany.
     $hm_attrs = $obj->getHasMany();
     foreach ($hm_attrs as $attr => $class) {
         $this->getMany($obj, $attr);
         // Carga los elementos del atributo en la clase.
     }
     // TODO: si carga en cascada, entonces la instancia de obj y sus clases asociadas
     //       se cargan todas en el mismo momento, por lo que no habria nada sucio,
     //       y seria innecesario verificar si estaba sucia antes de la carga.
     // Solo limpia si la clase estaba limpia antes de la operacion
     if ($wasClean) {
         $obj->resetDirty();
     }
     // Apaga las banderas que se prendieron en la carga
 }
 /**
  * Si $resursive es true, se cargan las asociaciones de la clase y tambien se pasan a json.
  */
 public static function toJSON(PersistentObject $po, $recursive = false, $loopDetection = NULL, $currentPath = '')
 {
     if ($loopDetection === NULL) {
         $loopDetection = new ArrayObject();
     }
     // Necesito hacer que cada nodo tenga una path para poder expresar las referencias por loops detectados.
     // La idea es simple: (ver http://goessner.net/articles/JsonPath/)
     // - vacio es la path que referencia al nodo raiz
     // - el nombre del atributo referencia a un objeto hasOne relacionado
     // - el nombre del atributo con notacion de array referencia a un objeto hasMany relacionado
     // p.e. x.store.book[0].title donde x es el objeto raiz, entonces una path valida es: .store.book[0].title
     $loopDetection[$currentPath] = $po->getClass() . '_' . $po->getId();
     // Marca como recorrido: TODO falta la marca de loop cuando se detecta.
     $json = "{";
     $i = 0;
     $n = count($po->getAttributeTypes()) - 1;
     foreach ($po->getAttributeTypes() as $attr => $type) {
         $value = $po->aGet($attr);
         if (is_bool($value)) {
             $value ? $value = 'true' : ($value = 'false');
         }
         // Si no esta esto, aparece 1 para true y nada para false.
         $json .= '"' . $attr . '" : "' . $value . '"';
         // TODO: si es numero, no poner comillas
         if ($i < $n) {
             $json .= ", ";
         }
         $i++;
     }
     // Agrega errores de validacion si los hay
     // http://code.google.com/p/yupp/issues/detail?id=86
     if ($po->getErrors()->hasErrors()) {
         $errors = $po->getErrors();
         $json .= ', "errors": {';
         foreach ($errors as $attr => $theErrors) {
             $json .= '"' . $attr . '": [';
             foreach ($theErrors as $theError) {
                 $json .= '"' . $theError . '", ';
             }
             $json = substr($json, 0, -2);
             // Saco ', ' del final
             $json .= '], ';
         }
         $json = substr($json, 0, -2);
         // Saco ', ' del final
         $json .= '}';
     }
     if ($recursive) {
         foreach ($po->getHasOne() as $attr => $clazz) {
             $relObj = $po->aGet($attr);
             if ($relObj !== NULL) {
                 if (!in_array($relObj->getClass() . '_' . $relObj->getId(), (array) $loopDetection)) {
                     // FIXME: las tags de los atributos hijos de la instancia raiz deberian
                     //        tener su nombre igual al nombre del atributo, no el nombre de
                     //        la clase. Con este codigo es el nombre de la clase.
                     $json .= ', "' . $attr . '": ' . self::toJSON($relObj, $recursive, $loopDetection, $currentPath . '.' . $attr);
                 } else {
                     // Agrego un objeto referencia
                     $keys = array_keys((array) $loopDetection, $relObj->getClass() . '_' . $relObj->getId());
                     $path = $keys[0];
                     $json .= ', "' . $attr . '": "' . $path . '"';
                 }
             }
         }
         foreach ($po->getHasMany() as $attr => $clazz) {
             // TODO: type de la coleccion, en el de XML tengo:
             //$hm_node->setAttribute( 'type', $obj->getHasManyType($attr) ); // list, colection, set
             //$hm_node->setAttribute( 'of', $obj->getAttributeType($attr) ); // clase de las instancias que contiene la coleccion
             $relObjs = $po->aGet($attr);
             if (count($relObjs) > 0) {
                 $json .= ', "' . $attr . '": ';
                 $json .= self::toJSONArray($relObjs, $recursive, $attr, $loopDetection, $currentPath);
                 /*
                 $json .= ', "'. $attr .'": [';
                 
                 echo "attr hasMany $attr<br/>";
                 
                 $idx = 0; // Se usa para la referencia por loop en la JSON path
                 foreach ($relObjs as $relObj)
                 {
                    echo 'relObj: '. $relObj->getClass().'_'.$relObj->getId() .'<br/>';
                  
                    if(!in_array($relObj->getClass().'_'.$relObj->getId(), (array)$loopDetection)) // si no esta marcado como recorrido
                    {
                       $json .= self::toJSON( $relObj, $recursive, $loopDetection, $currentPath.'.'.$attr.'['.$idx.']' ) .', ';
                    }
                    else // referencia por loop
                    {
                       // Agrego un objeto referencia
                       $keys = array_keys((array)$loopDetection, $relObj->getClass().'_'.$relObj->getId());
                       $path = $keys[0];
                       $json .= '"'.$attr.'": "'. $path .'", ';
                    }
                    
                    $idx++;
                 }
                 
                 $json = substr($json, 0, -2); // Saco ', ' del final
                 $json .= ']';
                 */
             }
         }
     }
     $json .= '}';
     return $json;
 }
 private static function display_show(PersistentObject $po)
 {
     $res = '<table>';
     $attrs = $po->getAttributeTypes();
     foreach ($attrs as $attr => $type) {
         $res .= '<tr><td>';
         $res .= $attr;
         // TODO: Habria que ver si esto es i18n, deberia haber algun "display name" asociado al nombre del campo.
         $res .= '</td><td>';
         $res .= self::field_to_html_show($attr, $type, $po->aGet($attr));
         $res .= '</td></tr>';
     }
     // Necesito el nombre de la aplicacion y no deberia ser 'core', lo obtengo de ctx o de los params.
     $ctx = YuppContext::getInstance();
     $m = Model::getInstance();
     // Muestro hasOne: http://code.google.com/p/yupp/issues/detail?id=12
     $hone = $po->getHasOne();
     foreach ($hone as $attr => $clazz) {
         // TODO: Habria que ver si esto es i18n, deberia haber algun "display name" asociado al nombre del campo.
         $res .= "<tr><td>{$attr}</td><td>";
         $relObj = $po->aGet($attr);
         if ($relObj == NULL) {
             continue;
         }
         $ctx = YuppContext::getInstance();
         $app = $m->get('app');
         if (!isset($app)) {
             $app = $ctx->getApp();
         }
         // Link a vista de scaffolding del objeto relacionado con hasOne
         $res .= h('link', array('app' => 'core', 'controller' => 'core', 'action' => 'show', 'class' => $relObj->getClass(), 'id' => $relObj->getId(), 'body' => $relObj->getClass() . ' [' . $relObj->getId() . ']', 'params' => array('app' => $app)));
         $res .= '</td></tr>';
     }
     $res .= '</table>';
     return $res;
 }