Empleo eficaz de las plantillas

Para mostrar esto más claramente sustituiremos la segunda plantilla de nuestro ejemplo:

<pattern>
  <rule context="arc:animal[@carnivoro='sí']">
    <report test="parent::*/arc:animal[@carnivoro='no']">
    Hay animales carnívoros y herbívoros en un mismo compartimento. 
    ¡Los animales no pueden ser utilizados como fuente de comida!
    </report>
    <report test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 2)">
    Noé, este animal carnívoro es muy fuerte para su compañero. 
    Podría usarlo como fuente de comida.
    </report>
  </rule>
</pattern>
<pattern>
  <rule context="arc:animal">
    <report test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 10)">
    Noé, este animal es demasiado grande para su compañero. 
    Lo podría aplastar.
    </report>
  </rule>
</pattern>

La segunda plantilla contiene una regla que comprueba para todos los animales si estos son demasiado pesados para sus compañeros de compartimento. Para ello se verificará, si el peso del animal seleccionado es diez veces mayor que el de su compañero.
Como puede comprobarse, este test es parecido al segundo de la ya conocida regla. Ahora la consulta es más estricta para los carnívoros: Estos pueden pesar como máximo el doble que sus compañeros, mientras que la restricción general para todos los animales es 10 veces el peso del compañero.
Esto significa que un animal que supere el test en la regla para los carnívoros, lo superará también en la segunda regla. Así, la comprobación para los carnívoros en la segunda regla es redundante. En una nueva regla se podría modificar la segunda plantilla, ya que ésta sólo es necesaria para los animales no carnívoros:

<pattern>
  <rule context="arc:animal[@carnivoro='sí']">
    <report test="parent::*/arc:animal[@carnivoro='no']">
    Hay animales carnívoros y herbívoros en un mismo compartimento. 
    ¡Los animales no pueden ser utilizados como fuente de comida!
    </report>
    <report test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 2)">
    Noé, este animal carnívoro es muy fuerte para su compañero. 
    Podría usarlo como fuente de comida.
    </report>
  </rule>
</pattern>
<pattern>
  <rule context=" arc:animal[@carnivoro='no']">
    <report test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 10)">
    Noé, este animal es demasiado grande para su compañero. 
    Lo podría aplastar.
    </report>
  </rule>
</pattern>

Con esta versión existe el riesgo de que en la instancia el atributo carnivoro reciba un valor adicional. En caso de que se incluya el valor vegano en el atributo, éste no sería seleccionado por ninguna de las reglas. Este error se puede evitar mediante la condición [not(@carnivoro='sí')] en lugar de [@carnivoro='no']. Sin embargo, un método más elegante y eficaz es anidar ambas reglas en una plantilla:

<pattern>
  <rule context="arc:animal[@carnivoro='sí']">
    <report test="parent::*/arc:animal[@carnivoro='no']">
    Hay animales carnívoros y herbívoros en un mismo compartimento. 
    ¡Los animales no pueden ser utilizados como fuente de comida!
    </report>
    <report test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 2)">
    Noé, este animal carnívoro es muy fuerte para su compañero. 
    Podría usarlo como fuente de comida.
    </report>
  </rule>
  <rule context="arc:animal">
    <report test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 10)">
    Noé, este animal es demasiado grande para su compañero. 
    Lo podría aplastar.
    </report>
  </rule>
</pattern>

Con las dos reglas en una plantilla es posible aprovechar el orden de las reglas: Cada elemento <animal> que sea seleccionado por la primera regla no será verificado por la segunda. El resto de los animales, es decir, los animales que no sean carnívoros, serán seleccionados por la segunda regla. Por ello podemos prescindir de condiciones para el elemento <animal> a seleccionar.
Debido a que el analizador recorre el árbol de nodos completo para cada plantilla y comprueba si cada uno de los nodos se puede aplicar a una de las reglas, es mucho mejor de cara al rendimiento anidar dos reglas en una misma plantilla. Volveremos al tema de la disposición de las plantillas cuando nos ocupemos del elemento <phase>.

Nota para expertos en XSLT:
Para los expertos en XSLT echaremos una ojeada detrás de las cortinas. Una implememtación XSLT transforma el esquema Schematron con dos plantillas de la manera siguiente:

<xsl:template match="/">
  <xsl:apply-templates mode="P1"/>                         (1)
  <xsl:apply-templates mode="P2"/>                         (1)
</xsl:template>
<xsl:template match="arc:animal[@carnivoro='sí']" mode="P1">           (2)
  <xsl:if test="parent::*/arc:animal[@carnivoro='no']">
    <!-- Implementación del aviso de error -->
  </xsl:if>
  <xsl:if test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 2)">
    <!-- Implementación del aviso de error -->
  </xsl:if>
</xsl:template>
<xsl:template match="arc:animal[@carnivoro='no']" mode="P2">         (2)
  <xsl:if test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 10)">
    <!-- Implementación del aviso de error -->
  </xsl:if>
</xsl:template>
(1) Para cada plantilla se ejecuta un comando <xsl:apply-templates> en un modo específico.
(2) Para cada regla se creará en cada uno de los modos una plantilla <xsl:template> por separado.

A modo de comparación presentamos a continuación como se implementa una versión con una sola plantilla:

<xsl:template match="/">
  <xsl:apply-templates mode="P1"/>                         (1)
</xsl:template>
<xsl:template match="arc:animal[@carnivoro='sí']" priority="100" mode="P1">   (2)
  <xsl:if test="parent::*/arc:animal[@carnivoro='no']">
    <!-- Implementación del aviso de error -->
  </xsl:if>
  <xsl:if test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 2)">
    <!-- Implementación del aviso de error -->
  </xsl:if>
</xsl:template>
<xsl:template match="arc:animal" priority="99" mode="P1">                          (2)
  <xsl:if test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 10)">
    <!-- Implementación del aviso de error -->
  </xsl:if>
</xsl:template>
(1) Gracias a que se omite una plantilla puede prescindirse del segundo comando <xsl:apply-templates>.
(2) Puesto que ambas reglas se encuentran en la misma plantilla, los "templates" se ejecutan igualmente en el mismo modo. El conflicto que surge cuando un nodo es aplicable a varias reglas se soluciona mediante el atributo priority, que se genera con independencia del orden de las reglas. Aquí se muestra por qué los nodos aplicables a una regla no se verifican a través de otra regla de la misma plantilla.

 

Puesto que ya hemos profundizado en nuestros conocimientos sobre la implementación "Skeleton", en la siguiente tabla ofrecemos un breve resumen de la transformación de contrucciones Schematron en elementos XSLT:

Schematron XSLT
<assert test="XPath ">Aviso de error
</assert>
<xsl:choose>
<xsl:when test="XPath "/>
<xsl:otherwise>Aviso de error
</xsl:otherwise>
</xsl:choose>
<report test="XPath ">Aviso de error
</report>
<xsl:if test="XPath">Aviso de error
</xsl:if>
<rule context="XPath "> <xsl:template match="XPath"
priority="xxx" mode="Pattern-ID">
<pattern> <xsl:apply-templates select ="/"
mode="Pattern-ID"/>
<< anterior siguiente >>

 

 

 


Derechos de autor © dpunkt.verlag GmbH 2011
El usuario podrá imprimir la versión online. La copia será exclusivamente para uso personal. Por lo demás el presente capítulo del libro publicado en lengua alemana "Schematron - Effiziente Business Rules für XML-Dokumente" está sometido a los mismos términos y condiciones que la versión impresa. La presente obra está protegida en su totalidad por la ley de propiedad intelectual. Reservados todos los derechos, incluyendo los derechos de reproducción, traducción, microfilmación, así como el almacenamiento y procesamiento en sistemas electrónicos.

dpunkt.verlag GmbH, Ringstraße 19B, 69115 Heidelberg, teléfono + 49 (0) 6221 14830, fax + 49 (0) 6221 148399, hallo(at)dpunkt.de