/**
  * Nombre de la tabla que almacena informacion sobre la relacion entre 2 instancias de PO.
  * El nombre de la tabla de la relacion es el nombre derivado de la instancia1 concatenado
  * al nombre del atributo que apunta a la instancia 2, concatenado al nombre derivador de
  * la instancia 2.
  * @param PersistentObject $ins1 Instancia duenia de la asociacion.
  * @param String Atributo de $ins1 que apunta a $ins2.
  * @param PersistentObject $ins2 Instancia hija en la asociacion.
  */
 public static function relTableName(PersistentObject $ins1, $inst1Attr, PersistentObject $ins2)
 {
     // Problema: si el atributo pertenece a C1, y $ins1 es instancia de G1,
     //           la tabla que se genera para hasMany a UnaClase es "gs_unaclase_"
     //           y deberia ser "cs_unaclase_", esto es un problema porque si cargo
     //           una instancia de C1 no tiene acceso a sus hasMany "UnaClase".
     // TODO: esta es una solucion rapida al problema, hay que mejorarla.
     // Esta solucion intenta buscar cual es la clase en la que se declara el atributo hasMany
     // para el que se quiere generar la tabla intermedia de referencias, si no la encuentra,
     // es que el atributo hasMany se declaro en $ins1.
     // Tambien hay un problema cuando hay composite>
     // Si ins1->hasMany[ins1Attr] es a una superclase de ins2, genera mal el nombre de la tabla de join.
     // El nombre se tiene que generar a la clase para la que se declara le hasMany,
     // no para el nombre de tabla de ins2 (porque ins2 puede guardarse en otra tabla
     // que no sea la que se guarda su superclase a la cual fue declarado el hasMany
     // ins1->hasMany[inst1Attr]).
     // Solucion: ver si la clase a la que se declara el hasMany no es la clase de ins2,
     //           y verificar si ins2 se guarda en otra tabla que la clase a la que se
     //           declara el hasMany en ins1. Si es distinta, el nombre debe apuntar al
     //           de la clase declarada en el hasMany. (aunque en ambos casos es a esto,
     //           asi que no es necesario verificar).
     $classes = ModelUtils::getAllAncestorsOf($ins1->getClass());
     //Logger::struct( $classes, "Superclases de " . $ins1->getClass() );
     $instConElAtributoHasMany = $ins1;
     // En ppio pienso que la instancia es la que tiene el atributo masMany.
     foreach ($classes as $aclass) {
         $ins = new $aclass(NULL, true);
         //if ( $ins->hasManyOfThis( $ins2->getClass() ) ) // la clase no es la que tenga el atributo, debe ser en la que se declara el atributo
         if ($ins->attributeDeclaredOnThisClass($inst1Attr)) {
             //Logger::getInstance()->log("TIENE MANY DE " . $ins2->getClass());
             $instConElAtributoHasMany = $ins;
             break;
         }
         //Logger::struct( $ins, "Instancia de $aclass" );
     }
     $tableName1 = self::tableName($instConElAtributoHasMany);
     //echo "=== "  .  $ins1->getAttributeType( $inst1Attr ) . " ==== <br/>";
     // La tabla de join considera la tabla en la que se guardan las instancias del tipo
     // declarado en el hasMany, NO A LOS DE SUS SUBCLASES!!! (como podia ser ins2)
     $tableName2 = self::tableName($ins1->getAttributeType($inst1Attr));
     // $tableName2 = self::tableName( $ins2 );
     // TODO: Normalizar $inst1Attr ?
     //      echo "Nombre tabla relTableName: ". $tableName1 . "_" . $inst1Attr . "_" . $tableName2 ."<br/>";
     return $tableName1 . "_" . $inst1Attr . "_" . $tableName2;
     // owner_child
 }
 /**
  * Devuelve HTML para edicion de un objeto como una tabla con 2 columnas, la primera de nombres de campos la segunda con campos con valores para modificar.
  */
 private static function display_edit(PersistentObject $po)
 {
     $res = '<table>';
     $attrs = $po->getAttributeTypes();
     foreach ($attrs as $attr => $type) {
         // Los atributos inyectados no se deberian poder editar!
         $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>';
         if ($po->isInyectedAttribute($attr)) {
             //$res .= $po->aGet($attr);
             $res .= self::field_to_html_show($attr, $type, $po->aGet($attr));
         } else {
             if (DatabaseNormalization::isSimpleAssocName($attr)) {
                 // Si es un fk a un id de un hasOne, quiero mostrar una lista de los posibles ids
                 // de la clase de la relacion HO que le puedo asignar, y como esto es create o edit,
                 // si tiene un valor actual, deberia quedar seleccionado en el select.
                 $currentValue = $po->aGet($attr);
                 // Puede ser NULL
                 $role = DatabaseNormalization::getSimpleAssocName($attr);
                 // email_id -> email
                 $relClass = $po->getAttributeType($role);
                 // Clase de la relacion HO
                 // Objetos que puedo tener relacionadoss
                 // Se puede en PHP 5.3.0...
                 //$list = $relClass::listAll(new ArrayObject()); // Objetos que podria tener asociados
                 // ... pero por las dudas ...
                 $list = call_user_func_array(array($relClass, 'listAll'), array(new ArrayObject()));
                 $select = '<select name="' . $attr . '"><option value=""></option>';
                 foreach ($list as $relObj) {
                     $sel = $currentValue == $relObj->getId() ? ' selected="true"' : '';
                     $select .= '<option value="' . $relObj->getId() . '"' . $sel . '>' . $relClass . '[' . $relObj->getId() . ']</option>';
                     // TODO: Si se tuviera un toString en la clase se mostraria mejor
                 }
                 $select .= '</select>';
                 $res .= $select;
             } else {
                 $maxStringLength = NULL;
                 if ($type === Datatypes::TEXT) {
                     $maxLengthConstraint = $po->getConstraintOfClass($attr, 'MaxLengthConstraint');
                     if ($maxLengthConstraint !== NULL) {
                         $maxStringLength = $maxLengthConstraint->getValue();
                     }
                 }
                 $res .= self::field_to_html_edit($attr, $type, $po->aGet($attr), $maxStringLength);
                 // Si el campo tiene errores, los muestro
                 if ($po->getErrors()->hasFieldErrors($attr)) {
                     $res .= '<div class="errors">';
                     $res .= self::fieldErrors($po, $attr);
                     $res .= '</div>';
                 }
             }
         }
         $res .= '</td></tr>';
     }
     $res .= '</table>';
     return $res;
 }