[z3-checkins] r33191 - in z3/deliverance/branches/packaged/deliverance: . test-data test-data/static

ltucker at codespeak.net ltucker at codespeak.net
Wed Oct 11 21:54:39 CEST 2006


Author: ltucker
Date: Wed Oct 11 21:54:32 2006
New Revision: 33191

Modified:
   z3/deliverance/branches/packaged/deliverance/htmlserialize.py
   z3/deliverance/branches/packaged/deliverance/interpreter.py
   z3/deliverance/branches/packaged/deliverance/test-data/static/rules.xml
   z3/deliverance/branches/packaged/deliverance/test-data/static/text-rules.xml
   z3/deliverance/branches/packaged/deliverance/test-data/test_append.xml
   z3/deliverance/branches/packaged/deliverance/test-data/test_appendorreplace.xml
   z3/deliverance/branches/packaged/deliverance/test-data/test_copy.xml
   z3/deliverance/branches/packaged/deliverance/test-data/test_prepend.xml
   z3/deliverance/branches/packaged/deliverance/test-data/test_replace.xml
   z3/deliverance/branches/packaged/deliverance/utils.py
   z3/deliverance/branches/packaged/deliverance/wsgifilter.py
   z3/deliverance/branches/packaged/deliverance/xslt.py
Log:
error handling for missing content, fix for foreign character sets

Modified: z3/deliverance/branches/packaged/deliverance/htmlserialize.py
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/htmlserialize.py	(original)
+++ z3/deliverance/branches/packaged/deliverance/htmlserialize.py	Wed Oct 11 21:54:32 2006
@@ -1,5 +1,5 @@
 from lxml import etree
-
+import re
 
 html_xsl = """
 <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
@@ -37,3 +37,26 @@
         return str(html_transform(doc))
                   
 
+
+
+HTTP_EQUIV_MATCHER_PAT = re.compile(r"\<\s*meta\s+([^\>])*http-equiv\s*=\s*(\'|\")\s*content-type\s*(\'|\")([^\>])*charset\s*=\s*(?P<charset>[\w-]+)([^\>])*\>",re.I|re.M) 
+OTHER_HTTP_EQUIV_MATCHER_PAT = re.compile(r"\<\s*meta\s+([^\>])*charset\s*=\s*(?P<charset>[\w-]+)([^\>])*http-equiv\s*=\s*(\'|\")\s*content-type\s*(\'|\")([^\>])*\>",re.I|re.M) 
+def decodeAndParseHTML(text):
+    """
+    if an html meta tag specifying a charset can be matched, 
+    decode the text to a python unicode string before parsing
+    """
+    m = HTTP_EQUIV_MATCHER_PAT.search(text)
+    if not m:
+        m = OTHER_HTTP_EQUIV_MATCHER_PAT.search(text)
+
+    if m:
+        charset = m.group('charset')
+        text = text.decode(charset)
+
+    return etree.HTML(text)
+    
+
+        
+
+    

Modified: z3/deliverance/branches/packaged/deliverance/interpreter.py
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/interpreter.py	(original)
+++ z3/deliverance/branches/packaged/deliverance/interpreter.py	Wed Oct 11 21:54:32 2006
@@ -62,6 +62,7 @@
         content_els = copy.deepcopy(content.xpath(rule.attrib[self.RULE_CONTENT_KEY]))
 
         if (len(content_els) == 0):
+            self.add_to_body_start(theme, self.format_error("no content matched", rule))
             return 
 
         non_text_els = self.elements_in(content_els)
@@ -96,6 +97,7 @@
         content_els = copy.deepcopy(content.xpath(rule.attrib[self.RULE_CONTENT_KEY]))
 
         if (len(content_els) == 0):
+            self.add_to_body_start(theme, self.format_error("no content matched", rule))
             return 
 
         non_text_els = self.elements_in(content_els)
@@ -141,8 +143,7 @@
         content_els = copy.deepcopy(content.xpath(rule.attrib[self.RULE_CONTENT_KEY]))
 
         if len(content_els) == 0:
-            self.attach_text_to_previous(theme_el,theme_el.tail)
-            theme_el.getparent().remove(theme_el)
+            self.add_to_body_start(theme, self.format_error("no content matched", rule))            
             return       
 
         non_text_els = self.elements_in(content_els)
@@ -195,6 +196,7 @@
         content_els = copy.deepcopy(content.xpath(rule.attrib[self.RULE_CONTENT_KEY]))
 
         if len(content_els) == 0:
+            self.add_to_body_start(theme, self.format_error("no content matched", rule))
             return 
 
         non_text_els = self.elements_in(content_els)
@@ -222,11 +224,16 @@
             self.add_to_body_start(theme,self.format_error("invalid xpath for content", rule=rule))
             return
 
+        content_els = copy.deepcopy(content.xpath(content_xpath))        
+ 
+        if len(content_els) == 0:
+            self.add_to_body_start(theme, self.format_error("no content matched", rule))
+            return 
+
         for el in theme_el:
             if el.tag == remove_tag:
                 theme_el.remove(el)
 
-        content_els = copy.deepcopy(content.xpath(content_xpath))
         self.strip_tails(content_els)
         theme_el.extend(content_els)
 

Modified: z3/deliverance/branches/packaged/deliverance/test-data/static/rules.xml
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/test-data/static/rules.xml	(original)
+++ z3/deliverance/branches/packaged/deliverance/test-data/static/rules.xml	Wed Oct 11 21:54:32 2006
@@ -2,8 +2,8 @@
   <rules xmlns="http://www.plone.org/deliverance">
     <append-or-replace theme="//head" content="//head/title" />
     <append theme="//head" content="//head/link" />
-    <append theme="//head" content="//head/script" />    
-    <append theme="//head" content="//head/style" />    
-    <append theme="//head" content="//head/meta" />
+    <append theme="//head" content="//head/script" onerror="ignore" />    
+    <append theme="//head" content="//head/style" onerror="ignore" />    
+    <append theme="//head" content="//head/meta" onerror="ignore" />
     <copy theme="//div[@id='content']" content="//body/*" />
   </rules>

Modified: z3/deliverance/branches/packaged/deliverance/test-data/static/text-rules.xml
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/test-data/static/text-rules.xml	(original)
+++ z3/deliverance/branches/packaged/deliverance/test-data/static/text-rules.xml	Wed Oct 11 21:54:32 2006
@@ -2,8 +2,8 @@
   <rules xmlns="http://www.plone.org/deliverance">
     <append-or-replace theme="//head" content="//head/title" />
     <append theme="//head" content="//head/link" />
-    <append theme="//head" content="//head/script" />    
-    <append theme="//head" content="//head/style" />    
-    <append theme="//head" content="//head/meta" />
+    <append theme="//head" content="//head/script" onerror="ignore"/>    
+    <append theme="//head" content="//head/style" onerror="ignore"/>    
+    <append theme="//head" content="//head/meta" onerror="ignore"/>
     <copy theme="//div[@id='content']" content="//body/child::node()" />
   </rules>

Modified: z3/deliverance/branches/packaged/deliverance/test-data/test_append.xml
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/test-data/test_append.xml	(original)
+++ z3/deliverance/branches/packaged/deliverance/test-data/test_append.xml	Wed Oct 11 21:54:32 2006
@@ -68,17 +68,51 @@
   </content>
   
   <output>
-    <html>
-      <head>
-	<title>Blah</title>
-      </head>
-      <body>
-	<div class="foo">Dummy Content<div class="deliverance-error">
-	    Deliverance error: multiple elements found in theme<br/>rule: &lt;append theme=".//div[@class='foo']" content=".//div[@id='bar']"/&gt;
-	</div></div>
-	<div class="foo">Dummy Content</div>
-      </body>
-    </html>
+  <html>
+  <head>
+    <title>Blah</title>
+  </head>
+  <body>
+    <div class="deliverance-error">Deliverance error: multiple elements found in theme<br/>rule: &lt;append theme=".//div[@class='foo']" content=".//div[@id='bar']"/&gt;
+  <br/><textarea rows="24" cols="80" readonly="readonly">&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+</textarea></div>
+    <div class="foo">Dummy Content</div>
+    <div class="foo">Dummy Content</div>
+  </body>
+</html>
+
+  </output> 
+</deliverance-test>
+
+
+<!-- tests append command generates an error when no content is found -->
+<deliverance-test>
+  <rules xmlns="http://www.plone.org/deliverance">
+    <append theme=".//div[@id='foo']" content="//div[@id='bar']" />
+  </rules>
+
+  <theme base="http://example.com"> 
+    <html><head><title>Blah</title></head><body><div></div><div id="foo">Dummy Content<p>HI!</p></div>
+    </body></html>
+  </theme>
+
+  <content> 
+    <html><body>leading text <br/> more <p>inside p </p>trailing text</body> tail of body</html>
+  </content>
+  
+  <output>
+<html>
+  <head>
+    <title>Blah</title>
+  </head>
+  <body>
+    <div class="deliverance-error">Deliverance error: no content matched<br/>rule: &lt;append theme=".//div[@id='foo']" content="//div[@id='bar']"/&gt;
+  </div>
+    <div/>
+    <div id="foo">Dummy Content<p>HI!</p></div>
+  </body>
+</html>
   </output> 
 </deliverance-test>
 

Modified: z3/deliverance/branches/packaged/deliverance/test-data/test_appendorreplace.xml
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/test-data/test_appendorreplace.xml	(original)
+++ z3/deliverance/branches/packaged/deliverance/test-data/test_appendorreplace.xml	Wed Oct 11 21:54:32 2006
@@ -91,18 +91,20 @@
   </content>
   
   <output>
-    <html>
-      <head>
-	<title>Blah</title>
-      </head>
-      <body>
-	<div>Dummy Content<div class="deliverance-error">
-	    Deliverance error: multiple elements found in theme<br/>rule: &lt;append-or-replace theme=".//div" content=".//div"/&gt;
-	</div></div>
-	<div>Dummy Content</div>
-      </body>
-    </html>
-  </output> 
+   <html>
+     <head>
+       <title>Blah</title>
+     </head>
+     <body>
+       <div class="deliverance-error">Deliverance error: multiple elements found in theme<br/>rule: &lt;append-or-replace theme=".//div" content=".//div"/&gt;
+	 <br/><textarea rows="24" cols="80" readonly="readonly">&lt;div&gt;Dummy Content&lt;/div&gt;
+&lt;div&gt;Dummy Content&lt;/div&gt;
+       </textarea></div>
+       <div>Dummy Content</div>
+       <div>Dummy Content</div>
+     </body>
+   </html>
+  </output>
 </deliverance-test>
 
 
@@ -167,4 +169,38 @@
   </output>
 </deliverance-test>
 
+
+
+
+<!-- tests append-or-replace command generates an error when no content is found -->
+<deliverance-test>
+  <rules xmlns="http://www.plone.org/deliverance">
+    <append-or-replace theme=".//div[@id='foo']" content="//span[@id='bar']" />
+  </rules>
+
+  <theme base="http://example.com"> 
+    <html><head><title>Blah</title></head><body><div></div><div id="foo"><span>Dummy Content</span><p>HI!</p></div>
+    </body></html>
+  </theme>
+
+  <content> 
+    <html><body>leading text <br/> more <p>inside p </p>trailing text</body> tail of body</html>
+  </content>
+  
+  <output>
+<html>
+  <head>
+    <title>Blah</title>
+  </head>
+  <body>
+    <div class="deliverance-error">Deliverance error: no content matched<br/>rule: &lt;append-or-replace theme=".//div[@id='foo']" content="//span[@id='bar']"/&gt;
+  </div>
+    <div/>
+    <div id="foo"><span>Dummy Content</span><p>HI!</p></div>
+  </body>
+</html>
+  </output> 
+</deliverance-test>
+
 </deliverance-test-suite>
+

Modified: z3/deliverance/branches/packaged/deliverance/test-data/test_copy.xml
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/test-data/test_copy.xml	(original)
+++ z3/deliverance/branches/packaged/deliverance/test-data/test_copy.xml	Wed Oct 11 21:54:32 2006
@@ -69,11 +69,15 @@
     <title>Blah</title>
   </head>
   <body>
-    <div class="foo">Dummy Content<div class="deliverance-error">Deliverance error: multiple elements found in theme<br/>rule: &lt;copy theme=".//div[@class='foo']" content=".//div[@id='bar']"/&gt;
-  </div></div>
+    <div class="deliverance-error">Deliverance error: multiple elements found in theme<br/>rule: &lt;copy theme=".//div[@class='foo']" content=".//div[@id='bar']"/&gt;
+  <br/><textarea rows="24" cols="80" readonly="readonly">&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+</textarea></div>
+    <div class="foo">Dummy Content</div>
     <div class="foo">Dummy Content</div>
   </body>
 </html>
+
   </output> 
 </deliverance-test>
 
@@ -120,4 +124,35 @@
 </deliverance-test>
 
 
+<!-- tests copy command generates an error when no content is found -->
+<deliverance-test>
+  <rules xmlns="http://www.plone.org/deliverance">
+    <copy theme=".//div[@id='foo']" content="//div[@id='bar']" />
+  </rules>
+
+  <theme base="http://example.com"> 
+    <html><head><title>Blah</title></head><body><div></div><div id="foo">Dummy Content<p>HI!</p></div>
+    </body></html>
+  </theme>
+
+  <content> 
+    <html><body>leading text <br/> more <p>inside p </p>trailing text</body> tail of body</html>
+  </content>
+  
+  <output>
+<html>
+  <head>
+    <title>Blah</title>
+  </head>
+  <body>
+    <div class="deliverance-error">Deliverance error: no content matched<br/>rule: &lt;copy theme=".//div[@id='foo']" content="//div[@id='bar']"/&gt;
+  </div>
+    <div/>
+    <div id="foo">Dummy Content<p>HI!</p></div>
+  </body>
+</html>
+  </output> 
+</deliverance-test>
+
+
 </deliverance-test-suite>

Modified: z3/deliverance/branches/packaged/deliverance/test-data/test_prepend.xml
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/test-data/test_prepend.xml	(original)
+++ z3/deliverance/branches/packaged/deliverance/test-data/test_prepend.xml	Wed Oct 11 21:54:32 2006
@@ -68,22 +68,25 @@
   </content>
   
   <output>
-    <html>
-      <head>
-	<title>Blah</title>
-      </head>
-      <body>
-	<div class="foo">Dummy Content<div class="deliverance-error">
-	    Deliverance error: multiple elements found in theme<br/>rule: &lt;prepend theme=".//div[@class='foo']" content=".//div[@id='bar']"/&gt;
-	</div></div>
-	<div class="foo">Dummy Content</div>
-      </body>
-    </html>
+<html>
+  <head>
+    <title>Blah</title>
+  </head>
+  <body>
+    <div class="deliverance-error">Deliverance error: multiple elements found in theme<br/>rule: &lt;prepend theme=".//div[@class='foo']" content=".//div[@id='bar']"/&gt;
+  <br/><textarea rows="24" cols="80" readonly="readonly">&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+</textarea></div>
+    <div class="foo">Dummy Content</div>
+    <div class="foo">Dummy Content</div>
+  </body>
+</html>
+
   </output> 
 </deliverance-test>
 
 
-<!-- tests preppend command successfully prepends the contents of a div tag with all contents of body tag, 
+<!-- tests prepend command successfully prepends the contents of a div tag with all contents of body tag, 
 including unelemented text --> 
 <deliverance-test>
   <rules xmlns="http://www.plone.org/deliverance">
@@ -105,4 +108,35 @@
   </output> 
 </deliverance-test>
 
+
+<!-- tests prepend command generates an error when no content is found -->
+<deliverance-test>
+  <rules xmlns="http://www.plone.org/deliverance">
+    <prepend theme=".//div[@id='foo']" content="//div[@id='bar']" />
+  </rules>
+
+  <theme base="http://example.com"> 
+    <html><head><title>Blah</title></head><body><div></div><div id="foo">Dummy Content<p>HI!</p></div>
+    </body></html>
+  </theme>
+
+  <content> 
+    <html><body>leading text <br/> more <p>inside p </p>trailing text</body> tail of body</html>
+  </content>
+  
+  <output>
+<html>
+  <head>
+    <title>Blah</title>
+  </head>
+  <body>
+    <div class="deliverance-error">Deliverance error: no content matched<br/>rule: &lt;prepend theme=".//div[@id='foo']" content="//div[@id='bar']"/&gt;
+  </div>
+    <div/>
+    <div id="foo">Dummy Content<p>HI!</p></div>
+  </body>
+</html>
+  </output> 
+</deliverance-test>
+
 </deliverance-test-suite>

Modified: z3/deliverance/branches/packaged/deliverance/test-data/test_replace.xml
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/test-data/test_replace.xml	(original)
+++ z3/deliverance/branches/packaged/deliverance/test-data/test_replace.xml	Wed Oct 11 21:54:32 2006
@@ -76,16 +76,20 @@
   </content>
  
   <output>
-    <html>
-      <head>
+<html>
+  <head>
     <title>Blah</title>
   </head>
   <body>
-    <div class="foo">Dummy Content<div class="deliverance-error">Deliverance error: multiple elements found in theme<br/>rule: &lt;replace theme="//div[@class='foo']" content="//div[@id='bar']"/&gt;
-  </div></div>
+    <div class="deliverance-error">Deliverance error: multiple elements found in theme<br/>rule: &lt;replace theme="//div[@class='foo']" content="//div[@id='bar']"/&gt;
+  <br/><textarea rows="24" cols="80" readonly="readonly">&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+&lt;div class="foo"&gt;Dummy Content&lt;/div&gt;
+</textarea></div>
+    <div class="foo">Dummy Content</div>
     <div class="foo">Dummy Content</div>
   </body>
 </html>
+
   </output> 
 </deliverance-test>
 
@@ -148,22 +152,34 @@
   </output> 
 </deliverance-test>
 
-<!-- tests that when no content element is matched, the theme element is simply deleted without error -->
+
+<!-- tests replace command generates an error when no content is found -->
 <deliverance-test>
   <rules xmlns="http://www.plone.org/deliverance">
-    <replace theme="//div[@id='foo']" content="//div[@id='bar']" />
+    <replace theme=".//div[@id='foo']" content="//div[@id='bar']" />
   </rules>
 
   <theme base="http://example.com"> 
-     <html><head><title>Blah</title></head><body>lead<div id="foo">Dummy Content</div>tail</body></html>
+    <html><head><title>Blah</title></head><body><div></div><div id="foo">Dummy Content<p>HI!</p></div>
+    </body></html>
   </theme>
 
   <content> 
-     <html><body><div id="bars">Real Content</div></body></html>
+    <html><body>leading text <br/> more <p>inside p </p>trailing text</body> tail of body</html>
   </content>
- 
+  
   <output>
-     <html><head><title>Blah</title></head><body>leadtail</body></html>
+<html>
+  <head>
+    <title>Blah</title>
+  </head>
+  <body>
+    <div class="deliverance-error">Deliverance error: no content matched<br/>rule: &lt;replace theme=".//div[@id='foo']" content="//div[@id='bar']"/&gt;
+  </div>
+    <div/>
+    <div id="foo">Dummy Content<p>HI!</p></div>
+  </body>
+</html>
   </output> 
 </deliverance-test>
 

Modified: z3/deliverance/branches/packaged/deliverance/utils.py
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/utils.py	(original)
+++ z3/deliverance/branches/packaged/deliverance/utils.py	Wed Oct 11 21:54:32 2006
@@ -1,6 +1,7 @@
 from lxml import etree
 import re
 import urlparse
+import htmlserialize
 
 class RuleSyntaxError(Exception):
     """
@@ -38,8 +39,8 @@
             self.add_to_body_start(theme, e)
             return None
         elif len(theme_els)> 1:
-            e = self.format_error("multiple elements found in theme", rule)
-            theme_els[0].append(e)
+            e = self.format_error("multiple elements found in theme", rule, theme_els)
+            self.add_to_body_start(theme, e)
             return None
         return theme_els[0]
 
@@ -61,7 +62,15 @@
         br.tail = 'rule: %s' % etree.tostring(rule)
         d.append(br)
         if elts:
-            d.extend(elts)
+            d.append(etree.Element('br'))
+            textArea = etree.Element('textarea')
+            textArea.attrib['rows'] = '24'
+            textArea.attrib['cols'] = '80'
+            textArea.attrib['readonly'] = 'readonly'
+            textArea.text = ''
+            for el in elts:
+                textArea.text += htmlserialize.tostring(el)
+            d.append(textArea)
         return d
 
 
@@ -75,6 +84,8 @@
 
 
     def add_to_body_start(self,theme, el):
+        if not el:
+            return
         body = theme.find('body')
         if body is None:
             body = theme[0]

Modified: z3/deliverance/branches/packaged/deliverance/wsgifilter.py
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/wsgifilter.py	(original)
+++ z3/deliverance/branches/packaged/deliverance/wsgifilter.py	Wed Oct 11 21:54:32 2006
@@ -8,6 +8,8 @@
 import urlparse
 import urllib
 from lxml import etree
+#from lxml.etree import HTML as parseHTML 
+from htmlserialize import decodeAndParseHTML as parseHTML
 from paste.wsgilib import intercept_output
 from paste.request import construct_url
 from paste.response import header_value, replace_header
@@ -36,7 +38,6 @@
         try:
             self._lock.acquire()
             if not self._renderer or self.cache_expired():
-                print "rebuild renderer"
                 self._renderer = self.create_renderer(environ)
                 self._cache_time = datetime.datetime.now()
             return self._renderer
@@ -57,7 +58,7 @@
             elif encoding:
                 return text.decode(encoding)
 
-        return Renderer(etree.HTML(theme), full_theme_uri, etree.XML(rule), 
+        return Renderer(parseHTML(theme), full_theme_uri, etree.XML(rule), 
                         reference_resolver=reference_resolver)
 
         
@@ -86,6 +87,7 @@
         if status is None:
             # should_intercept returned False
             return body
+
         body = self.filter_body(environ, body)
         replace_header(headers, 'content-length', str(len(body)))
         replace_header(headers, 'content-type', 'text/html; charset=utf-8')
@@ -97,8 +99,8 @@
         return type.startswith('text/html') or type.startswith('application/xhtml+xml')
 
     def filter_body(self, environ, body):
-            content = self.get_renderer(environ).render(etree.HTML(body))
-            return tostring(content)
+        content = self.get_renderer(environ).render(parseHTML(body))
+        return tostring(content)
 
     def get_resource(self, environ, uri):
         internalBaseURL = environ.get(DELIVERANCE_BASE_URL,None)

Modified: z3/deliverance/branches/packaged/deliverance/xslt.py
==============================================================================
--- z3/deliverance/branches/packaged/deliverance/xslt.py	(original)
+++ z3/deliverance/branches/packaged/deliverance/xslt.py	Wed Oct 11 21:54:32 2006
@@ -42,7 +42,9 @@
         insertion_point = xslt_wrapper.xpath("//xsl:transform/xsl:template[@match='/']",
                                              nsmap)[0]
         insertion_point.append(theme_copy)
+
         self.transform = etree.XSLT(xslt_wrapper)
+    
         
 
     def render(self,content):
@@ -81,15 +83,24 @@
         if theme_el is None:
             return 
 
+        # add an element that produces an error in the theme if 
+        # no content is matched 
+        self.add_conditional_missing_content_error(theme,rule)
+
         copier = etree.SubElement(theme_el,
                                     "{%s}copy-of" % nsmap["xsl"])
         copier.set("select",rule.attrib[self.RULE_CONTENT_KEY])        
 
+
     def apply_prepend(self,rule,theme):
         theme_el = self.get_theme_el(rule,theme)
         if theme_el is None:
             return 
 
+        # add an element that produces an error in the theme if 
+        # no content is matched 
+        self.add_conditional_missing_content_error(theme,rule)
+
         copier = etree.Element("{%s}copy-of" % nsmap["xsl"])
 
         copier.set("select",rule.attrib[self.RULE_CONTENT_KEY])        
@@ -102,11 +113,21 @@
         theme_el = self.get_theme_el(rule,theme)
         if theme_el is None:
             return 
-      
+
+        self.add_conditional_missing_content_error(theme,rule)      
+
         copier = etree.Element("{%s}copy-of" % nsmap["xsl"])
         copier.set("select",rule.attrib[self.RULE_CONTENT_KEY])
 
-        self.replace_element(theme_el,copier)
+
+        # if content is matched, replace the theme element, otherwise, keep the
+        # theme element 
+        choose = self.make_when_otherwise("count(%s)=0" % 
+                                          rule.attrib[self.RULE_CONTENT_KEY], 
+                                          copy.deepcopy(theme_el), 
+                                          copier)
+
+        self.replace_element(theme_el,choose)
 
 
     def apply_copy(self,rule,theme):
@@ -114,11 +135,30 @@
         if theme_el is None:
             return 
 
-        del(theme_el[:])
-        theme_el.text = None
+        # add an element that produces an error in the theme if 
+        # no content is matched 
+        self.add_conditional_missing_content_error(theme,rule)
+
+        # create an element that is like the target theme element 
+        # with its children replaced by an xsl copy element 
+        copy_theme_el = copy.deepcopy(theme_el)
+        del(copy_theme_el[:])
+        copy_theme_el.text = None
         copier = etree.SubElement(theme_el,
                                     "{%s}copy-of" % nsmap["xsl"])
-        copier.set("select",rule.attrib[self.RULE_CONTENT_KEY])        
+        copier.set("select",rule.attrib[self.RULE_CONTENT_KEY])  
+        copy_theme_el.append(copier)
+
+        # create a copy of the current theme element 
+        normal_theme_el = copy.deepcopy(theme_el)
+
+        # create an xsl choose element that picks between them based 
+        # on whether content was matched 
+        choose = self.make_when_otherwise("count(%s)=0" % 
+                                          rule.attrib[self.RULE_CONTENT_KEY], 
+                                          normal_theme_el, 
+                                          copy_theme_el)
+        self.replace_element(theme_el,choose)
         
 
    
@@ -131,12 +171,21 @@
             self.add_to_body_start(theme,self.format_error("invalid xpath for content", rule=rule))
             return 
 
+        # add an element that produces an error in the theme if 
+        # no content is matched 
+        self.add_conditional_missing_content_error(theme,rule)
+
         for el in theme_el:
             if el.tag == remove_tag:
-                theme_el.remove(el)
-        copier = etree.SubElement(theme_el,
-                                    "{%s}copy-of" % nsmap["xsl"])
-        copier.set("select",rule.attrib[self.RULE_CONTENT_KEY])        
+                conditional = etree.Element("{%s}if" % nsmap["xsl"])
+                conditional.set("test","count(%s) = 0" % 
+                                rule.attrib[self.RULE_CONTENT_KEY])
+                conditional.append(copy.deepcopy(el))
+                self.replace_element(el,conditional)
+ 
+        copier = etree.Element("{%s}copy-of" % nsmap["xsl"])
+        copier.set("select",rule.attrib[self.RULE_CONTENT_KEY])
+        theme_el.append(copier)
    
 
     def xsl_escape_comments(self,doc):
@@ -149,3 +198,24 @@
             escaped.text = c.text 
             self.replace_element(c,escaped)
     
+    def add_conditional_missing_content_error(self,theme,rule):
+        """
+        """
+        err = self.format_error("no content matched", rule)
+        if err:
+            conditional = etree.Element("{%s}if" % nsmap["xsl"])
+            conditional.set("test", "count(%s)=0" % rule.attrib[self.RULE_CONTENT_KEY])
+            conditional.append(err)
+            self.add_to_body_start(theme,conditional)
+        
+
+    def make_when_otherwise(self, test, whenbody, otherwisebody):
+        choose = etree.Element("{%s}choose" % nsmap["xsl"])
+        when = etree.Element("{%s}when" % nsmap["xsl"])
+        when.set("test", test)
+        when.append(copy.deepcopy(whenbody))
+        otherwise = etree.Element("{%s}otherwise" % nsmap["xsl"])
+        otherwise.append(otherwisebody)
+        choose.append(when)
+        choose.append(otherwise)
+        return choose


More information about the z3-checkins mailing list