07 April 2004

Apédice B: Algoritmos de Espacios de Nombres

Editores:
Arnaud Le Hors, IBM
Elena Litani, IBM

Tabla de Contenido

Este apédice contiene varios algoritmos de espacio de nombre, como el algoritmo de normalización de espacio de nombre que fija la información de espacio de nombre en el Modelo de Objetos del Documento para producir un documento de espacio de nombre bien formado. Si [XML 1.0] se usa (ver Document.xmlVersion) los algoritmos se conforman para [Espacios de Nombres de XML], sino si [XML 1.1] se usa, los algoritmos conforman para [Espacios de Nombre de XML 1.1].

B.1 Normalización de Espacio de Nombre

Las declaraciones de los atributos de Espacio de Nombre y prefijos son normalizadas como parte del método normalizeDocument de la interfaz Document como si el método descrito en el siguiente pseudo código fue llamado sobre el elemento documento.

void Element.normalizeNamespaces()
{

  // Recoje las declaraciones locales de espacio de nombre
  // 
  for ( todas las declarciones de atributos de espacio de nombre local de Element válidas en DOM Level 2 ) 
  {    
      if (la declaración del espacio de nombre no es válida) 
      {
          // Nota: El prefijo xmlns es usado solamente para declarar vínculos y
          // por definición está enlazado al nombre del espacio de nombre http://www.w3.org/2000/xmlns/.
          // Esto no debe ser declarado. Ningún otro prefijo puede estar vinculado a este nombre de espacio
          // de nombre.         
               
          ==> Reporta un error.

      } 
      else 
      {
          ==>  Registra la declaración del espacio de nombre
      }
  }


  // Recoge el espacio de nombre del elemento
  //
  if ( namespaceURI del Element != null )
  {
    if ( el par prefijo/espacio de nombre de Element (o el espacio de nombre predeterminado si no hay prefijo)
        están dentro del alcance de un vínculo )
    {
      ==> No hace nada, la declaración al alcance es heredada

      Vea la sección "B.1.1: Alcance de un vínculo" para un ejemplo

    }
    else
    {
      ==> Crea una declaración attr de espacio de nombre local para este espacio de nombre,
          con el prefijo actual de Element (o un espacio de nombre predeterminado, si no hay prefijo). Si hay 
          una declaración local contrapuesta a la ya puesta, se cambia su valor para usar este espacio 
          de nombre.

          Vea la sección "B.1.2: Declaración de espacio de nombre contradictorios" para un ejemplo

          // Oberve que esto puede romper otros nodos en este subárbol de Element,
          // si estos están ya usando estos prefijos.
          // Estos serán reparados cuando sean alcanzados.
    }
  }
  else
  {
    // Element no tiene namespaceURI:
    if ( Nombre local de Element es null )
    {
       // Nodo de DOM Nivel 1
       ==> si un proceso de validación contra un esquema de espacio de nombre conocido (es decir
           Esquema XML) repora un error grave: el procesador no puede the processor recuperarse de esta
           situación.
           De otro modo, reporta un error: ningún espacio de nombre recuperado será representado sobre
           este nodo.
    }
    else
    {
      // Element no tiene pseudo-prefijo
      if ( hay un especio de nombre local predefinido contradictorio con el ya presente )
      {
        ==> cambia su valor para usar este espacio de nombre vacío.

      }
      // Oberve que esto puede romper otros nodos en este subárbol de Element,
      // si estos están ya usando estos prefijos.
      // Estos serán reparados cuando sean alcanzados.
    }
  }


  // Examina y pule los atributos
  //
  for ( todos los Attrs sin espacio de nombre de Element )
  {
     if ( Attr[i] tiene un namespaceURI )
     {
        if ( atributo no tiene prefijo (espacio de nombre por defecto declarado no se puede aplicar a 
             los atributos) 
             O
             el prefijo del atributo no es declarado
             O
             conflicto: el atributo tiene un prefijo en conflicto con un vículo ya activo al alcance )
        {              
           if ( namespaceURI emparejado con una declaración de alcance de uno o más prefijos) 
           {
               // escoje el vículo más local disponible; 
               // si hay más de uno escoje uno aleatoriamente

               ==> cambia el prefijo del atributo.
           }
           else 
           {
               if (el prefijo actual no es null y no tiene declaración de alcance) 
               {
                   ==> declara este prefijo
               } 
               else 
               {
                   // encuentra el prefijo siguiendo el modelo "NS" +índice (empezando por 1)
                   // asegurando que este prefijo no es declarado en el alcance actual.
                   // crea un atributo de declaració de espacio de nombre local

                   ==> cambia el prefijo del atributo.
               }
           }           
        }
     }    
     else
     {
        // Attr[i] no tiene namespaceURI
            
        if ( Attr[i] no tiene Nombre local )
        {
           // Nodo de DOM Nivel 1
           ==> si el proceso de validación contra un esquema de espacio de nombre conocido (es decir
               Esquema XML) reporta un error grave: el procesador no puede recuperarse de esta situación.
               De otro modo, reporta un error: ningún espacio de nombre recuperado será representado
               sobre este nodo.
        }
        else
        { 
           // attr no tiene ningún namespaceURI ni prefijo
           // no se requiere ninguna acción, attrs no usa el predeterminado
           ==> no hace nada
        }
     }
  } // fin para todos los Attrs

  // hace esto recursivamente
  for ( todos los hijos elementos de Element )
  {
    childElement.normalizeNamespaces()
  }
} // fin de Element.normalizeNamespaces
      

B.1.1 Alcance de una Vinculación

Nota: Esta sección es informativa.

Un par prefijo/namespaceURI de un elemento se dice que está en el alcance de una vinculación si su prefijo de espacio de nombre está limitado al mismo namespaceURI en la definición [in-scope namespaces] en el [Conjunto de Información en XML].

Como ejemplo, el siguiente documento es cargado en un árbol DOM:

<root>
  <parent xmlns:ns="http://www.example.org/ns1"
          xmlns:bar="http://www.example.org/ns2">
    <ns:child1 xmlns:ns="http://www.example.org/ns2"/>
  </parent>
</root>
      

En el caso del elemento child1 element, el prefijo de espacio de nombre y namespaceURI están dentro del alcance de la declaración de espacio de nombre apropiada dado que el prefijo de espacio de nombre ns de child1 limita con http://www.example.org/ns2.

Usando el método Node.appendChild, un elemento child2 es añadido como un hermano de child1 con el mismo prefijo de espacio de nombre y namespaceURI, es decir, "ns" y "http://www.example.org/ns2" respectivamente. A diferencia child1 que contiene la declaración de espacio de nombre apropiada en su atributo, el par prefijo/namespaceURI de child2 está dentro del alcance de la declaración de espacio de nombre de su padre, y el prefijo de espacio de nombre "ns" está limitado por "http://www.example.org/ns1". El par prefijo/namespaceURI de child2 por lo tanto no está al alcance de una vinculación. En orden a poner estos en un alcance de una vinculación, el algoritmo de normalización de espacio de nombre creará un valor de atributo de declaración de espacio de nombre para unir el prefijo de espacio de nombre "ns" al namespaceURI "http://www.example.org/ns2" y atará a child2. La representación XML del documento despues de terminar el algoritmo de normalización de espacio de nombre será:

<root>
  <parent xmlns:ns="http://www.example.org/ns1"
          xmlns:bar="http://www.example.org/ns2">
    <ns:child1 xmlns:ns="http://www.example.org/ns2"/>
    <ns:child2 xmlns:ns="http://www.example.org/ns2"/>
  </parent>
</root>
      

Para determinar si un elementos está dentro del alcance de una vinculación, uno pude invocar Node.lookupNamespaceURI, usando su prefijo de espacio de nombre como parámetro, y compara el namespaceURI resultante con el URI deseado, o uno puede invocar Node.isDefaultNamespaceURI usando su namespaceURI si el elemento no tiene prefijo de espacio de nombre.

B.1.2 Declaración de Espacio de Nombre Contradictorios

Nota: Esta sección es informativa.

Un conflicto de declaraci&ocute;n de espacio de nombre podría ocurrir sobre un elemento si un nodo Element y un atributo de declaración de espacio de nombre usan el mismo prefijo pero trazan un mapa a dos URIs de espacio de nombre diferentes.

Como ejempo, el documento siguiente es cargado en un árbol DOM:

<root>
  <ns:child1 xmlns:ns="http://www.example.org/ns1">
    <ns:child2/> 
  </ns:child1>   
</root>
      

Usando el método Node.renameNode, el namespaceURI del elemento child1 es renombrado desde "http://www.example.org/ns1" a "http://www.example.org/ns2". El prefijo de espacio de nombre "ns" es ahora mapeado a dos URIs de espacio de nombre diferentes al nivel del elemento child1 y así la declaración del espacio de nombre es declarada contradictoriamente. El algoritmo de normalización de espacio de nombre resolverá el conflicto del prefijo de espacio de nombre modificando el valor de la declaración de espacio de nombre desde "http://www.example.org/ns1" a "http://www.example.org/ns2". El algoritmo entonces continuará y considerará al elemento child2, no encontrarán un mapa de declaración de espacio de nombre del prefijo de espacio de nombre "ns" a "http://www.example.org/ns1" en el alcance del elemento, y creará uno nuevo. La representación XML del documento despues de terminar el algoritmo de normalización de espacio de nombre será:

<root>
  <ns:child1 xmlns:ns="http://www.example.org/ns2">
    <ns:child2  xmlns:ns="http://www.example.org/ns1"/> 
  </ns:child1>   
</root>
      

B.2 Consulta del Prefijo de Espacio de Nombre

Lo siguiente describe en pseudo código el algoritmo usado en el método lookupPrefix de la interfaz Node. Antes de devolver el prefijo encontrado el algoritmo necesita asegurarse de que el prefijo no está referido a un elemento del que comenzó la consulta. Estos métodos ignoran los nodos DOM Nivel 1.

Nota: Este método ignora todas las declaraciones de espacio de nombre predeterminadas. Para encontrar el espacio de nombre predeterminado usa el método isDefaultNamespace.

DOMString lookupPrefix(en DOMString de namespaceURI)
{
  if (namespaceURI no tiene valor, es decir, el namespaceURI es null o cadena vacía) {
     return null;
  }
  short type = this.getNodeType(); 
  switch (type) { 
        case Node.ELEMENT_NODE: 
        { 
             return lookupNamespacePrefix(namespaceURI, this); 
        } 
        case Node.DOCUMENT_NODE:
        { 
             return getDocumentElement().lookupNamespacePrefix(namespaceURI); 
        } 
        case Node.ENTITY_NODE : 
        case Node.NOTATION_NODE: 
        case Node.DOCUMENT_FRAGMENT_NODE: 
        case Node.DOCUMENT_TYPE_NODE: 
            return null;  // type is unknown  
        case Node.ATTRIBUTE_NODE:
        {
             if ( Attr tiene un Element propio ) 
             { 
                 return ownerElement.lookupNamespacePrefix(namespaceURI); 
             } 
             return null; 
        } 
        default:
        { 
           if (Node tiene un ascendente Element )
           // EntityReferences deberín ser saltadas para conseguirlo
           { 
                    return ancestor.lookupNamespacePrefix(namespaceURI); 
           } 
            return null; 
        } 
     } 
 } 


DOMString lookupNamespacePrefix(DOMString namespaceURI, Element originalElement){ 
        if ( Element tiene un espacio de nombre y un espacio de nombre de Element == namespaceURI 
             y Element tiene un prefijo y
             originalElement.lookupNamespaceURI(prefijo de Element) == namespaceURI) 
        { 
             return (el prefijo de Element); 
        } 
        if ( Element tiene atributos)
        { 
            for ( todos los atributos de declaraci&ocute;n de espacio de nombre local válidos en 
                  DOM Nivel 2 de Element )
            {
                if (prefijos de Attr == "xmlns" y valor de Attr == namespaceURI y
                   originalElement.lookupNamespaceURI(nombre local de Attr) == namespaceURI) 
                   { 
                      return (nombre local de Attr);
                   } 
            }
        } 

        if (Node tiene un ascendente Element ) 
           // EntityReferences deberín ser saltadas para conseguirlo 
        { 
            return ancestor.lookupNamespacePrefix(namespaceURI, originalElement); 
        } 
        return null; 
    } 

B.3 Consulta de Espacio de Nombre Predeterminado

Lo siguiente describe en pseudo código el algoritmo usado en el método isDefaultNamespace de la interfaz Node. Este método ignora los nodos de DOM Nivel 1.

boolean isDefaultNamespace(in DOMString namespaceURI)
{
  switch (nodeType) {
  case ELEMENT_NODE:  
     if ( Element no tiene prefijo )
     {
          return (espacio de nombre de Element == namespaceURI);
     }
     if ( Element tiene atributos y hay una declaración de espacio de nombre predeterminada válida 
          en DOM Nivel 2, es decir, el Nombre local de Attr == "xmlns" )
     {
	  return (valor de Attr == namespaceURI);
     }

     if ( Element tiene un ascendente Element )
         // EntityReferences deberín ser saltadas para conseguirlo
     {
          return ancestorElement.isDefaultNamespace(namespaceURI);
     }
     else {
          return unknown (false);
     }    
  case DOCUMENT_NODE:
     return documentElement.isDefaultNamespace(namespaceURI);
  case ENTITY_NODE:
  case NOTATION_NODE:
  case DOCUMENT_TYPE_NODE:
  case DOCUMENT_FRAGMENT_NODE:
     return unknown (false);
  case ATTRIBUTE_NODE:
     if ( Attr tiene un Element propio )
     {          
          return ownerElement.isDefaultNamespace(namespaceURI);
     }
     else {
          return unknown (false);
     }    
  default:
     if ( Node has an ancestor Element )
         // EntityReferences deberín ser saltadas para conseguirlo
     {          
          return ancestorElement.isDefaultNamespace(namespaceURI);
     }
     else {
          return unknown (false);
     }    
  }
}
      

B.4 Consulta de Namespace URI

Lo siguiente describe en pseudo código el algoritmo usado en el método lookupNamespaceURI de la interfaz Node. Estos métodos ignoran los nodos del DOM Nivel 1.

DOMString lookupNamespaceURI(in DOMString prefix) 
{ 
  switch (nodeType) { 
     case ELEMENT_NODE: 
     { 
         if ( espacio de nombre de Element != null y prefijo de Element == prefix ) 
         { 
               // Nota: el prefijo podría ser "null" en este caso se buscaría el espacio de nombre
                  predeterminado
               return (espacio de nombre de Element);
         } 
         if ( Element tiene atributos)
         { 
            for ( todos los atributos de declaración de espacio de nombre local válidos en
                  DOM Nivel 2 de Element )
            {
                 if (prefijo de Attr == "xmlns" y localName de Attr == prefix ) 
                       // no espacio de nombre predeterminado
                 { 
                        if (valor de Attr es no vacío) 
                        {
                          return (valor de Attr);
                        }         
                        return unknown (null);                   
                 } 
                 else if (nombre local de Attr == "xmlns" y prefijo == null)
                       // espacio de nombre predeterminado
                 { 
                        if (valor de Attr no es vacío) 
                        {
                          return (valor de Attr);
                        }         
                        return unknown (null); 
                 } 
           }
         } 
         if ( Element tiene un ascendente Element ) 
            // EntityReferences  deberín ser saltadas para conseguirlo
         { 
                   return ancestorElement.lookupNamespaceURI(prefix); 
         } 
         return null; 
     } 
     case DOCUMENT_NODE: 
          return documentElement.lookupNamespaceURI(prefix) 

     case ENTITY_NODE: 
     case NOTATION_NODE: 
     case DOCUMENT_TYPE_NODE: 
     case DOCUMENT_FRAGMENT_NODE: 
           return unknown (null); 

     case ATTRIBUTE_NODE: 
         if (Attr tiene un Element propio) 
         { 
             return ownerElement.lookupNamespaceURI(prefix); 
         } 
         else 
         { 
             return unknown (null); 
         } 
     default: 
         if (Node has an ancestor Element) 
          // EntityReferences deberín ser saltadas para conseguirlo
         { 
             return ancestorElement.lookupNamespaceURI(prefix); 
         } 
         else { 
             return unknown (null); 
         } 
  } 
}