Index: src/java/org/apache/fop/fo/FOEventHandler.java
===================================================================
--- src/java/org/apache/fop/fo/FOEventHandler.java	(revision 348793)
+++ src/java/org/apache/fop/fo/FOEventHandler.java	(working copy)
@@ -83,6 +83,11 @@
     protected PropertyListMaker propertyListMaker;
 
     /**
+     * The XMLWhitespaceHandler for this tree
+     */
+    protected XMLWhitespaceHandler whitespaceHandler = new XMLWhitespaceHandler();
+    
+    /**
      * Main constructor
      * @param foUserAgent the apps.FOUserAgent instance for this process
      */
Index: src/java/org/apache/fop/fo/XMLWhitespaceHandler.java
===================================================================
--- src/java/org/apache/fop/fo/XMLWhitespaceHandler.java	(revision 0)
+++ src/java/org/apache/fop/fo/XMLWhitespaceHandler.java	(revision 0)
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fo;
+
+import org.apache.fop.fo.flow.Block;
+import org.apache.fop.util.CharUtilities;
+
+public class XMLWhitespaceHandler {
+    
+    // True if we are in a run of white space
+    private boolean inWhitespace = false;
+    // True if the last char was a linefeed
+    private boolean afterLinefeed = true;
+    
+    private Block currentBlock;
+    private FONode lastNestedNode;
+    private int linefeedTreatment;
+    private int whitespaceTreatment;
+    private int whitespaceCollapse;
+    private RecursiveCharIterator charIter;
+    
+    public void handleWhitespace(FObjMixed fo, FONode firstTextNode) {
+        if (fo.getNameId() == Constants.FO_BLOCK) {
+            this.currentBlock = (Block) fo;
+            this.linefeedTreatment = currentBlock.getLinefeedTreatment();
+            this.whitespaceCollapse = currentBlock.getWhitespaceCollapse();
+            this.whitespaceTreatment = currentBlock.getWhitespaceTreatment();
+        }
+        if (firstTextNode == null) {
+            //nothing to do but initialize related properties
+            return;
+        }
+        charIter = new RecursiveCharIterator(fo, firstTextNode);
+        inWhitespace = false;
+        if (fo.getNameId() == Constants.FO_BLOCK) {
+            int textNodeIndex = -1;
+            if (fo.childNodes != null) {
+                textNodeIndex = fo.childNodes.indexOf(firstTextNode);
+            }
+            afterLinefeed = (textNodeIndex == 0
+                    || ((FONode) fo.childNodes.get(textNodeIndex - 1))
+                            .getNameId() == Constants.FO_BLOCK);
+        }
+        handleWhitespace();
+        lastNestedNode = fo;
+    }
+    
+    private void handleWhitespace() {
+        
+        EOLchecker lfCheck = new EOLchecker(charIter);
+
+        while (charIter.hasNext()) {
+            char currentChar = charIter.nextChar();
+            int currentCharClass = CharUtilities.classOf(currentChar);
+            if (currentCharClass == CharUtilities.LINEFEED
+                && linefeedTreatment == Constants.EN_TREAT_AS_SPACE) {
+                // if we have a linefeed and it is supposed to be treated
+                // like a space, that's what we do and continue
+                currentChar = '\u0020';
+                charIter.replaceChar('\u0020');
+                currentCharClass = CharUtilities.classOf(currentChar);
+            }
+            switch (CharUtilities.classOf(currentChar)) {
+                case CharUtilities.XMLWHITESPACE:
+                    // Some kind of whitespace character, except linefeed.
+                    if (inWhitespace && whitespaceCollapse == Constants.EN_TRUE) {
+                        // We are in a run of whitespace and should collapse
+                        // Just delete the char
+                        charIter.remove();
+                    } else {
+                        // Do the white space treatment here
+                        boolean bIgnore = false;
+
+                        switch (whitespaceTreatment) {
+                            case Constants.EN_IGNORE:
+                                bIgnore = true;
+                                break;
+                            case Constants.EN_IGNORE_IF_BEFORE_LINEFEED:
+                                bIgnore = linefeedTreatment == Constants.EN_PRESERVE
+                                            && lfCheck.beforeLinefeed();
+                                break;
+                            case Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED:
+                                bIgnore = (afterLinefeed
+                                           || (linefeedTreatment == Constants.EN_PRESERVE
+                                               && lfCheck.beforeLinefeed()));
+                                break;
+                            case Constants.EN_IGNORE_IF_AFTER_LINEFEED:
+                                bIgnore = afterLinefeed;
+                                break;
+                            case Constants.EN_PRESERVE:
+                                // nothing to do now, replacement takes place later
+                                break;
+                        }
+                        // Handle ignore and replacement
+                        if (bIgnore) {
+                            charIter.remove();
+                        } else {
+                            // this is to retain a single space between words
+                            inWhitespace = true;
+                            if (currentChar != '\u0020') {
+                                charIter.replaceChar('\u0020');
+                            }
+                        }
+                    }
+                    break;
+
+                case CharUtilities.LINEFEED:
+                    // A linefeed
+                    switch (linefeedTreatment) {
+                        case Constants.EN_IGNORE:
+                            charIter.remove();
+                            break;
+                        case Constants.EN_TREAT_AS_ZERO_WIDTH_SPACE:
+                            charIter.replaceChar(CharUtilities.ZERO_WIDTH_SPACE);
+                            inWhitespace = false;
+                            break;
+                        case Constants.EN_PRESERVE:
+                            lfCheck.reset();
+                            inWhitespace = false;
+                            afterLinefeed = true; // for following whitespace
+                            break;
+                    }
+                    break;
+
+                case CharUtilities.EOT:
+                    // A "boundary" objects such as non-character inline
+                    // or nested block object was encountered.
+                    // If any whitespace run in progress, finish it.
+                    // FALL THROUGH
+
+                default:
+                    // Any other character
+                    inWhitespace = afterLinefeed = false;
+                    lfCheck.reset();
+                    break;
+            }
+        }
+    }
+    
+    private static class EOLchecker {
+        private boolean nextIsEOL = false;
+        private RecursiveCharIterator charIter;
+
+        EOLchecker(RecursiveCharIterator charIter) {
+            this.charIter = charIter;
+        }
+
+        boolean beforeLinefeed() {
+            if (nextIsEOL == false) {
+                CharIterator lfIter = charIter.mark();
+                while (lfIter.hasNext()) {
+                    int charClass = CharUtilities.classOf(lfIter.nextChar());
+                    if (charClass == CharUtilities.LINEFEED) {
+                        nextIsEOL = true;
+                        return nextIsEOL;
+                    } else if (charClass != CharUtilities.XMLWHITESPACE) {
+                        return nextIsEOL;
+                    }
+                }
+                // No more characters == end of block == end of line
+                nextIsEOL = true;
+                return nextIsEOL;
+            }
+            return nextIsEOL;
+        }
+
+        void reset() {
+            nextIsEOL = false;
+        }
+    }
+}

Property changes on: src/java/org/apache/fop/fo/XMLWhitespaceHandler.java
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Index: src/java/org/apache/fop/fo/FObjMixed.java
===================================================================
--- src/java/org/apache/fop/fo/FObjMixed.java	(revision 348793)
+++ src/java/org/apache/fop/fo/FObjMixed.java	(working copy)
@@ -32,6 +32,9 @@
     /** Represents accumulated, pending FO text. See flushText(). */
     protected FOText ft = null;
     
+    /** Used for white-space handling; start CharIterator at node ... */
+    protected FONode currentTextNode;
+    
     /**
      * @param parent FONode that is the parent of this object
      */
@@ -54,6 +57,8 @@
     /** @see org.apache.fop.fo.FONode#endOfNode() */
     protected void endOfNode() throws FOPException {
         flushText();
+        getFOEventHandler().whitespaceHandler
+            .handleWhitespace(this, currentTextNode);
         super.endOfNode();
     }
 
@@ -74,9 +79,19 @@
 
     protected void addChildNode(FONode child) throws FOPException {
         flushText();
+        if (child instanceof FOText || child.getNameId() == FO_CHARACTER) {
+            if (currentTextNode == null) {
+                currentTextNode = child;
+            }
+        } else {
+            // handle white-space for all text up to here
+            getFOEventHandler().whitespaceHandler
+                .handleWhitespace(this, currentTextNode);
+            currentTextNode = null;
+        }
         super.addChildNode(child);
     }
-
+    
     /**
      * @return iterator for this object
      */
Index: src/java/org/apache/fop/fo/flow/Block.java
===================================================================
--- src/java/org/apache/fop/fo/flow/Block.java	(revision 348793)
+++ src/java/org/apache/fop/fo/flow/Block.java	(working copy)
@@ -25,14 +25,10 @@
 import org.apache.fop.datatypes.Length;
 import org.apache.fop.datatypes.Numeric;
 import org.apache.fop.fo.CharIterator;
-import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.FOText;
 import org.apache.fop.fo.FObjMixed;
 import org.apache.fop.fo.NullCharIterator;
 import org.apache.fop.fo.PropertyList;
-import org.apache.fop.fo.PropertySets;
-import org.apache.fop.fo.RecursiveCharIterator;
 import org.apache.fop.fo.ValidationException;
 import org.apache.fop.fo.properties.CommonAccessibility;
 import org.apache.fop.fo.properties.CommonAural;
@@ -43,7 +39,6 @@
 import org.apache.fop.fo.properties.CommonRelativePosition;
 import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.fo.properties.SpaceProperty;
-import org.apache.fop.util.CharUtilities;
 
 /*
   Modified by Mark Lillywhite mark-fop@inomial.com. The changes
@@ -94,13 +89,13 @@
     private int lineHeightShiftAdjustment;
     private int lineStackingStrategy;
     private Numeric orphans;
-    private int whiteSpaceTreatment;
+    private int whitespaceTreatment;
     private int span;
     private int textAlign;
     private int textAlignLast;
     private Length textIndent;
     private int visibility;
-    private int whiteSpaceCollapse;
+    private int whitespaceCollapse;
     private Numeric widows;
     private int wrapOption;
     // End of property values
@@ -109,12 +104,6 @@
     private boolean anythingLaidOut = false;
 
     /**
-     * Index of first inline-type FO seen in a sequence.
-     * Used during FO tree building to do white-space handling.
-     */
-    private FONode firstInlineChild = null;
-
-    /**
      * @param parent FONode that is the parent of this object
      *
      */
@@ -152,13 +141,13 @@
         lineHeightShiftAdjustment = pList.get(PR_LINE_HEIGHT_SHIFT_ADJUSTMENT).getEnum();
         lineStackingStrategy = pList.get(PR_LINE_STACKING_STRATEGY).getEnum();
         orphans = pList.get(PR_ORPHANS).getNumeric();
-        whiteSpaceTreatment = pList.get(PR_WHITE_SPACE_TREATMENT).getEnum();
+        whitespaceTreatment = pList.get(PR_WHITE_SPACE_TREATMENT).getEnum();
         span = pList.get(PR_SPAN).getEnum();
         textAlign = pList.get(PR_TEXT_ALIGN).getEnum();
         textAlignLast = pList.get(PR_TEXT_ALIGN_LAST).getEnum();
         textIndent = pList.get(PR_TEXT_INDENT).getLength();
         visibility = pList.get(PR_VISIBILITY).getEnum();
-        whiteSpaceCollapse = pList.get(PR_WHITE_SPACE_COLLAPSE).getEnum();
+        whitespaceCollapse = pList.get(PR_WHITE_SPACE_COLLAPSE).getEnum();
         widows = pList.get(PR_WIDOWS).getNumeric();
         wrapOption = pList.get(PR_WRAP_OPTION).getEnum();
     }
@@ -176,7 +165,6 @@
      */
     protected void endOfNode() throws FOPException {
         super.endOfNode();
-        handleWhiteSpace();
         getFOEventHandler().endBlock(this);
     }
 
@@ -348,173 +336,32 @@
     }
 
     /**
-     * @see org.apache.fop.fo.FONode#addChildNode(FONode)
+     * Accessor for the linefeed-treatment property
+     * 
+     * @return the enum value of linefeed-treatment
      */
-    public void addChildNode(FONode child) throws FOPException {
-        flushText();
-        // Handle whitespace based on values of properties
-        // Handle a sequence of inline-producing child nodes in
-        // one pass
-        if (child instanceof FOText
-            || PropertySets.generatesInlineAreas(child.getNameId())) {
-                if (firstInlineChild == null) {
-                    firstInlineChild = child;
-                }
-                // lastInlineChild = childNodes.size();
-        } else {
-            // Handle whitespace in preceeding inline areas if any
-            handleWhiteSpace();
-        }
-        super.addChildNode(child);
+    public int getLinefeedTreatment() {
+        return linefeedTreatment;
     }
     
     /**
-     * @see org.apache.fop.fo.FObj#notifyChildRemoval(org.apache.fop.fo.FONode)
+     * Accessor for the white-space-treatment property
+     * 
+     * @return the enum value of white-space-treatment
      */
-    protected void notifyChildRemoval(FONode node) {
-        if (node != null && node == firstInlineChild) {
-            firstInlineChild = null;
-        }
+    public int getWhitespaceTreatment() {
+        return whitespaceTreatment;
     }
-
-    private void handleWhiteSpace() {
-        //getLogger().debug("fo:block: handleWhiteSpace");
-        if (firstInlineChild == null) {
-            return; // Nothing to do
-        }
-        
-        boolean inWS = false; // True if we are in a run of white space
-        /*
-         * True if the last non white space char seen was a linefeed.
-         * We start from the beginning of a line so it defaults to True.
-         */
-        boolean prevWasLF = true; 
-
-        RecursiveCharIterator charIter =
-          new RecursiveCharIterator(this, firstInlineChild);
-        EOLchecker lfCheck = new EOLchecker(charIter);
-
-        while (charIter.hasNext()) {
-            char currentChar = charIter.nextChar();
-            int currentCharClass = CharUtilities.classOf(currentChar);
-            if (currentCharClass == CharUtilities.LINEFEED
-                && linefeedTreatment == EN_TREAT_AS_SPACE) {
-                // if we have a linefeed and it is suppose to be treated
-                // like a space, that's what we do and continue
-                currentChar = ' ';
-                charIter.replaceChar(' ');
-                currentCharClass = CharUtilities.classOf(currentChar);
-            }
-            switch (CharUtilities.classOf(currentChar)) {
-                case CharUtilities.XMLWHITESPACE:
-                    /* Some kind of whitespace character, except linefeed. */
-                    if (inWS && whiteSpaceCollapse == EN_TRUE) {
-                        // We are in a run of whitespace and should collapse
-                        // Just delete the char
-                        charIter.remove();
-                    } else {
-                        // Do the white space treatment here
-                        boolean bIgnore = false;
-
-                        switch (whiteSpaceTreatment) {
-                            case Constants.EN_IGNORE:
-                                bIgnore = true;
-                                break;
-                            case Constants.EN_IGNORE_IF_BEFORE_LINEFEED:
-                                bIgnore = linefeedTreatment == Constants.EN_PRESERVE
-                                            && lfCheck.nextIsLF();
-                                break;
-                            case Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED:
-                                bIgnore = (prevWasLF
-                                           || (linefeedTreatment == Constants.EN_PRESERVE
-                                               && lfCheck.nextIsLF()));
-                                break;
-                            case Constants.EN_IGNORE_IF_AFTER_LINEFEED:
-                                bIgnore = prevWasLF;
-                                break;
-                            case Constants.EN_PRESERVE:
-                                // nothing to do now, replacement takes place later
-                                break;
-                        }
-                        // Handle ignore and replacement
-                        if (bIgnore) {
-                            charIter.remove();
-                        } else {
-                            // this is to retain a single space between words
-                            inWS = true;
-                            if (currentChar != '\u0020') {
-                                charIter.replaceChar('\u0020');
-                            }
-                        }
-                    }
-                    break;
-
-                case CharUtilities.LINEFEED:
-                    /* A linefeed */
-                    switch (linefeedTreatment) {
-                        case Constants.EN_IGNORE:
-                            charIter.remove();
-                            break;
-                        case Constants.EN_TREAT_AS_ZERO_WIDTH_SPACE:
-                            charIter.replaceChar(CharUtilities.ZERO_WIDTH_SPACE);
-                            inWS = false;
-                            break;
-                        case Constants.EN_PRESERVE:
-                            lfCheck.reset();
-                            inWS = false;
-                            prevWasLF = true; // for following whitespace
-                            break;
-                    }
-                    break;
-
-                case CharUtilities.EOT:
-                    // A "boundary" objects such as non-character inline
-                    // or nested block object was encountered.
-                    // If any whitespace run in progress, finish it.
-                    // FALL THROUGH
-
-                default:
-                    /* Any other character */
-                    inWS = prevWasLF = false;
-                    lfCheck.reset();
-                    break;
-            }
-        }
-        firstInlineChild = null;
+    
+    /**
+     * Accessor for the white-space-collapse property
+     * 
+     * @return the enum value of white-space-collapse
+     */
+    public int getWhitespaceCollapse() {
+        return whitespaceCollapse;
     }
-
-    private static class EOLchecker {
-        private boolean nextIsEOL = false;
-        private RecursiveCharIterator charIter;
-
-        EOLchecker(RecursiveCharIterator charIter) {
-            this.charIter = charIter;
-        }
-
-        boolean nextIsLF() {
-            if (nextIsEOL == false) {
-                CharIterator lfIter = charIter.mark();
-                while (lfIter.hasNext()) {
-                    int charClass = CharUtilities.classOf(lfIter.nextChar());
-                    if (charClass == CharUtilities.LINEFEED) {
-                        nextIsEOL = true;
-                        return nextIsEOL;
-                    } else if (charClass != CharUtilities.XMLWHITESPACE) {
-                        return nextIsEOL;
-                    }
-                }
-                // No more characters == end of block == end of line
-                nextIsEOL = true;
-                return nextIsEOL;
-            }
-            return nextIsEOL;
-        }
-
-        void reset() {
-            nextIsEOL = false;
-        }
-    }
-     
+    
     /** @see org.apache.fop.fo.FONode#charIterator() */
     public CharIterator charIterator() {
         return NullCharIterator.getInstance();
Index: src/java/org/apache/fop/fo/flow/Inline.java
===================================================================
--- src/java/org/apache/fop/fo/flow/Inline.java	(revision 348793)
+++ src/java/org/apache/fop/fo/flow/Inline.java	(working copy)
@@ -24,7 +24,7 @@
 import org.apache.fop.datatypes.Length;
 import org.apache.fop.fo.CharIterator;
 import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.InlineCharIterator;
+import org.apache.fop.fo.OneCharIterator;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.ValidationException;
 import org.apache.fop.fo.properties.CommonRelativePosition;
@@ -108,6 +108,7 @@
        }
 
         checkId(id);
+        
         getFOEventHandler().startInline(this);
     }
 
@@ -186,7 +187,7 @@
      * @see org.apache.fop.fo.FObjMixed#charIterator
      */
     public CharIterator charIterator() {
-        return new InlineCharIterator(this, commonBorderPaddingBackground);
+        return new OneCharIterator('|');
     }
 
     /** @see org.apache.fop.fo.FONode#getLocalName() */
