cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Julien Oster <frod...@frodoid.org>
Subject CForms patch: allow multiple characters() SAX events within one element
Date Mon, 21 Feb 2005 14:52:59 GMT
Hello,

well, it really, really took me a while to track this bug down. Let me 
tell you the whole story, because it really freaked me out. That issue 
came up a few hours before the go-live deadline :)

Our application makes quite extensive and sophisticated use of 
Generators and Stylesheets, fed into CForms in different manners. That 
means, the form which is generated is completely dynamic. Dynamic fields 
with dynamic form definitions, prefilled with dynamic values resulting 
in a dynamic XML pipeline for binding and thus also needing a dynamic 
template.

So, the three basics of XML-bound forms -- Definition, Binding and 
Template -- are coming from three different pipelines, having three 
different stylesheets but only one generator feeding them.

This approach, by the way, really works like a charm. It's fast, it's 
flexible, it's dynamic.

The XML-binding however had some problem. Sometimes, the prefilled 
values in the fields would be missing some digits. So, instead of the 
correct "1106" like it was to be found in the database, the field was 
just showing "106". You couldn't make out any pattern for which fields 
this applied under which circumstances.

Changing completely unrelated things (like the used Oracle driver, 
which, as I now know, really is completely unrelated) restored some 
fields to correct behaviour but broke others. It was a real mess, and I 
was fearing a strange JVM bug or a major flaw in the dynamic form XML 
which we were using, and which we were relying on.

Luckily I finally found what was wrong: the way the XMLAdapter class for 
the CForms XML Binding handled SAX characters() events.

Inside an element, so, visually spoken, between an opening and a closing 
tag (<foo>...</foo>), the data is passed to SAX listeners as 
characters() events. All data can come as one event or as multiple events.

The XMLAdapter characters() method handles one event well, but when 
anything between the original SAX event source and the XMLAdapter 
decides to split up the events, any subsequent characters() event was 
just overwriting the former parts of the whole value.

This is simple enough to find once you really know what's going on, but 
given the fact that pipelines can be quite complicated with a 
complicated generator, complicated transformers and several XSLTs in 
between you eventually start to drown in confusion.

This very small patch against the cocoon-2.1.6 release fixes this issue 
and should be comprehensible without any further comments.

Regards,
Julien

--- XMLAdapter.java.orig     2004-11-19 09:53:39.000000000 +0100
+++ XMLAdapter.java     2005-02-21 14:29:56.968946331 +0100
@@ -80,7 +80,8 @@
      private Locale locale;
      /** Is a <code>MultiValueField</code> handled? */
      private boolean isMultiValueItem = false;
-
+    /** The current input string where XML characters get appended to, 
or nullif there's no input inside the current element yet **/
+    private String currentinput;

      /**
       * Wrap a <code>Form</code> with an <code>XMLAdapter</code>
@@ -89,6 +90,8 @@
      public XMLAdapter(Widget widget) {
          this.widget = widget;
          this.locale = Locale.US;
+
+        this.currentinput = null;
      }

      /**
@@ -175,6 +178,8 @@
       */
      public void endElement(String uri, String loc, String raw)
      throws SAXException {
+        currentinput = null;
+
          if (this.currentWidget == null)
              throw new SAXException("Wrong state");

@@ -208,7 +213,15 @@
       */
      public void characters(char ch[], int start, int len)
      throws SAXException {
-        String input = new String(ch, start, len);
+        String input;
+
+        if (currentinput != null) {
+            input = currentinput + new String(ch, start, len);
+        } else {
+            input = new String(ch, start, len);
+            currentinput = input;
+        }
+
          if ("".equals(input.trim()))
              return;


Mime
View raw message