### Eclipse Workspace Patch 1.0
#P Temp_WhitespaceManagement
Index: test/java/org/apache/fop/layoutengine/ElementListCheck.java
===================================================================
--- test/java/org/apache/fop/layoutengine/ElementListCheck.java	(revision 1544072)
+++ test/java/org/apache/fop/layoutengine/ElementListCheck.java	(working copy)
@@ -22,6 +22,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import org.apache.fop.layoutmgr.BestFitPenalty;
 import org.apache.fop.layoutmgr.KnuthBox;
 import org.apache.fop.layoutmgr.KnuthElement;
 import org.apache.fop.layoutmgr.KnuthGlue;
@@ -37,10 +38,10 @@
  */
 public class ElementListCheck implements LayoutEngineCheck {
 
-    private String category;
+    private final String category;
     private String id;
     private int index = -1;
-    private Element checkElement;
+    private final Element checkElement;
 
     /**
      * Creates a new instance from a DOM node.
@@ -171,6 +172,24 @@
                                     + " at position " + pos);
                         }
                     }
+                    // A few extended attributes for BestFitPenalty
+                    if ("true".equals(domEl.getAttribute("special"))) {
+                        if (!(pen instanceof BestFitPenalty)) {
+                            fail("Expected a best-fit-penalty"
+                                    + " at position " + pos);
+                        } else {
+                            int variantCount = Integer.parseInt(domEl.getAttribute("variant-count"));
+                            if (variantCount != (((BestFitPenalty) pen).getVariantList().size())) {
+                                fail("Expected a penalty that has " + variantCount + " variants"
+                                        + " at position " + pos);
+                            }
+                        }
+                    } else if ("false".equals(domEl.getAttribute("special"))) {
+                        if ((pen instanceof BestFitPenalty)) {
+                            fail("Expected a normal penalty"
+                                    + " at position " + pos);
+                        }
+                    }
                 } else if ("glue".equals(domEl.getLocalName())) {
                     if (!(knuthEl instanceof KnuthGlue)) {
                         fail("Expected KnuthGlue"
Index: src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java	(revision 1544072)
+++ src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java	(working copy)
@@ -30,6 +30,7 @@
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
+import org.apache.fop.layoutmgr.BestFitPenalty.Variant;
 import org.apache.fop.traits.MinOptMax;
 import org.apache.fop.util.ListUtil;
 
@@ -98,6 +99,11 @@
     private int currentKeepContext = Constants.EN_AUTO;
     private KnuthNode lastBeforeKeepContextSwitch;
 
+    /** Holds the variant that should be assigned to the next node to be created */
+    private Variant variant;
+    /** True if the current variant should be reset to null */
+    private boolean resetCurrentVariant;
+
     /**
      * Construct a page breaking algorithm.
      * @param topLevelLM the top level layout manager
@@ -149,12 +155,14 @@
         /** Index of the last inserted element of the last inserted footnote. */
         public int footnoteElementIndex;
 
+        public Variant variant;
+
         public KnuthPageNode(int position,
                              int line, int fitness,
                              int totalWidth, int totalStretch, int totalShrink,
                              int totalFootnotes, int footnoteListIndex, int footnoteElementIndex,
                              double adjustRatio, int availableShrink, int availableStretch,
-                             int difference, double totalDemerits, KnuthNode previous) {
+                             int difference, double totalDemerits, KnuthNode previous, Variant variant) {
             super(position, line, fitness,
                   totalWidth, totalStretch, totalShrink,
                   adjustRatio, availableShrink, availableStretch,
@@ -162,6 +170,18 @@
             this.totalFootnotes = totalFootnotes;
             this.footnoteListIndex = footnoteListIndex;
             this.footnoteElementIndex = footnoteElementIndex;
+
+            // Propagate the previous node's type to descendant nodes
+            if (previous != null) {
+                this.dynamicNode = previous.dynamicNode;
+            }
+            if (variant != null) {
+                this.variant = variant;
+                this.dynamicNode = true;
+                if (log.isDebugEnabled()) {
+                    log.debug("Creating a KnuthPageNode for a BestFitPenalty.");
+                }
+            }
         }
 
     }
@@ -175,6 +195,7 @@
         private final int[] bestFootnotesLength = new int[4];
         private final int[] bestFootnoteListIndex = new int[4];
         private final int[] bestFootnoteElementIndex = new int[4];
+        private final Variant[] bestVariant = new Variant[4];
 
         @Override
         public void addRecord(double demerits, KnuthNode node, double adjust,
@@ -186,6 +207,7 @@
             bestFootnotesLength[fitness] = insertedFootnotesLength;
             bestFootnoteListIndex[fitness] = footnoteListIndex;
             bestFootnoteElementIndex[fitness] = footnoteElementIndex;
+            bestVariant[fitness] = variant;
         }
 
         public int getFootnotesLength(int fitness) {
@@ -199,6 +221,10 @@
         public int getFootnoteElementIndex(int fitness) {
             return bestFootnoteElementIndex[fitness];
         }
+
+        public Variant getVariant(int fitness) {
+            return bestVariant[fitness];
+        }
     }
 
     /** {@inheritDoc} */
@@ -274,6 +300,13 @@
                 return node2;
             }
         }
+        // If one of the nodes has a dynamic content
+        // return it even if the other node has fewer total demerits
+        if (node1.dynamicNode && !node2.dynamicNode) {
+            return node1;
+        } else if (node2.dynamicNode && !node1.dynamicNode) {
+            return node2;
+        }
 
         /* all other cases: use superclass implementation */
         return super.compareNodes(node1, node2);
@@ -290,7 +323,7 @@
                                  totalWidth, totalStretch, totalShrink,
                                  insertedFootnotesLength, footnoteListIndex, footnoteElementIndex,
                                  adjustRatio, availableShrink, availableStretch,
-                                 difference, totalDemerits, previous);
+                                 difference, totalDemerits, previous, variant);
     }
 
     /** {@inheritDoc} */
@@ -304,7 +337,8 @@
                                  ((BestPageRecords) best).getFootnoteElementIndex(fitness),
                                  best.getAdjust(fitness), best.getAvailableShrink(fitness),
                                  best.getAvailableStretch(fitness), best.getDifference(fitness),
-                                 best.getDemerits(fitness), best.getNode(fitness));
+                                 best.getDemerits(fitness), best.getNode(fitness),
+                                 ((BestPageRecords) best).getVariant(fitness));
     }
 
     /**
@@ -315,6 +349,9 @@
     @Override
     protected void handleBox(KnuthBox box) {
         super.handleBox(box);
+        if (box.getWidth() > 0) {
+            resetCurrentVariant = true;
+        }
         if (box instanceof KnuthBlockBox
             && ((KnuthBlockBox) box).hasAnchors()) {
             handleFootnotes(((KnuthBlockBox) box).getElementLists());
@@ -454,6 +491,9 @@
                 //nop
             }
         }
+        if (resetCurrentVariant) {
+            variant = null;
+        }
         super.considerLegalBreak(element, elementIdx);
         newFootnotes = false;
     }
@@ -500,7 +540,11 @@
         int footnoteSplit;
         boolean canDeferOldFN;
         if (element.isPenalty()) {
-            actualWidth += element.getWidth();
+            if (element instanceof BestFitPenalty) {
+                actualWidth += handleBestFitPenalty(activeNode, (BestFitPenalty) element, elementIndex);
+            } else {
+                actualWidth += element.getWidth();
+            }
         }
         if (footnotesPending) {
             // compute the total length of the footnotes not yet inserted
@@ -560,6 +604,21 @@
         }
     }
 
+    private int handleBestFitPenalty(KnuthNode activeNode, BestFitPenalty penalty,
+                                      int elementIndex) {
+//        variant = null;
+        for (Variant var : penalty.getVariantList()) {
+            int difference = computeDifference(activeNode, var.toPenalty(), elementIndex);
+            double r = computeAdjustmentRatio(activeNode, difference);
+            if (r >= -1.0) {
+                variant = var;
+                resetCurrentVariant = false;
+                return variant.width;
+            }
+        }
+        return 0;
+    }
+
     /**
      * Checks whether footnotes from preceding pages may be deferred to the page after
      * the given element.
@@ -970,6 +1029,14 @@
                             int total) {
         //int difference = (bestActiveNode.line < total)
         //      ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+        // Check if the given node has an attached dynamic content
+        KnuthPageNode pageNode = (KnuthPageNode) bestActiveNode;
+        if (pageNode.variant != null) {
+            BestFitPenalty penalty = findBestFitPenalty(pageNode);
+            if (penalty != null) {
+                penalty.activatePenalty(pageNode.variant);
+            }
+        }
         int difference = bestActiveNode.difference;
         if (difference + bestActiveNode.availableShrink < 0) {
             if (!autoHeight) {
@@ -1032,6 +1099,23 @@
                 ratio, difference));
     }
 
+    private BestFitPenalty findBestFitPenalty(KnuthPageNode node) {
+        int position = node.position;
+        for (int idx = position; idx >= 0; --idx) {
+            ListElement elem = getElement(idx);
+            if (elem instanceof BestFitPenalty) {
+                if (((BestFitPenalty)elem).containsVariant(node.variant)) {
+                    return (BestFitPenalty)elem;
+                }
+            } else if (elem.isBox()) {
+                if (((KnuthBox) elem).getWidth() != 0) {
+                    break;
+                }
+            }
+        }
+        return null;
+    }
+
     /** {@inheritDoc} */
     @Override
     protected int filterActiveNodes() {
Index: src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java	(revision 1544072)
+++ src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java	(working copy)
@@ -264,6 +264,9 @@
         /** next possible node in the same line */
         public KnuthNode next;
 
+        /** Used to denote if this node has an attached dynamic content */
+        boolean dynamicNode;
+
         /**
          * Holds the number of subsequent recovery attempty that are made to get content fit
          * into a line.
Index: test/layoutengine/standard-testcases/multi-switch_best-fit.xml
===================================================================
--- test/layoutengine/standard-testcases/multi-switch_best-fit.xml	(revision 0)
+++ test/layoutengine/standard-testcases/multi-switch_best-fit.xml	(working copy)
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<testcase>
+  <info>
+    <p>
+		A simple layout-engine test of fo:multi-switch using the whitespace management extension.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="small" page-height="10cm" page-width="10cm">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="small">
+        <fo:flow flow-name="xsl-region-body">
+		<!-- Simple testcase -->
+          <fo:multi-switch fox:auto-toggle="best-fit">
+            <!-- First multi-case is too big -->
+			<fo:multi-case>
+              <fo:block line-height="12cm">First variant</fo:block>
+            </fo:multi-case>
+			<fo:multi-case>
+              <fo:block>Second variant</fo:block>
+              <fo:block>Second variant</fo:block>
+            </fo:multi-case>
+          </fo:multi-switch>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="1" xpath="count(/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block)"/>
+    <eval expected="28800" xpath="/areaTree/pageSequence/pageViewport/page[1]/regionViewport/regionBody/mainReference/span/flow/block/@bpd"/>
+	<element-list category="breaker">
+		<!-- Knuth list generated by the multi-switch -->
+		<box w="0"/>
+		<penalty w="0" p="0" flagged="false" aux="false" special="true" variant-count="2"/>
+		<box w="0"/>
+		<!-- Knuth elements added automatically by the breaking algorithm -->
+		<penalty w="0" p="INF" flagged="false" aux="false"/>
+		<glue w="0" z="0" y="10000000"/>
+		<penalty w="0" p="-INF" flagged="false" aux="false"/>
+	</element-list>
+  </checks>
+</testcase>
Index: src/java/org/apache/fop/layoutmgr/BestFitPenalty.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/BestFitPenalty.java	(revision 1544072)
+++ src/java/org/apache/fop/layoutmgr/BestFitPenalty.java	(working copy)
@@ -19,38 +19,56 @@
 
 package org.apache.fop.layoutmgr;
 
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.fop.layoutmgr.BestFitLayoutUtils.BestFitPosition;
 
 /**
- * A penalty class used to specify a set of alternatives for the layout engine
+ * A type of penalty used to specify a set of alternatives for the layout engine
  * to choose from. The chosen alternative must have an occupied size
- * that is less than the available BPD of the current page
- * and it must also be the best match when it is evaluated by {@link FittingStrategy}.
+ * that is less than the available BPD of the current page.
  */
 public class BestFitPenalty extends KnuthPenalty {
 
-    private final List<ListElement> knuthList;
-    public boolean ignorePenalty;
+    public class Variant {
 
-    public BestFitPenalty(int width, List<ListElement> knuthList, Position pos) {
-        super(width, 0, false, pos, false);
-        this.knuthList = knuthList;
+        public Variant(List<ListElement> knuthList, int width) {
+            this.knuthList = knuthList;
+            this.width = width;
+        }
+        public KnuthElement toPenalty() {
+            return new KnuthPenalty(width, 0, false, null, false);
+        }
+
+        public final List<ListElement> knuthList;
+        public final int width;
     }
 
-    public void activateContent() {
+    private final List<Variant> variantList;
+
+    public BestFitPenalty(Position pos) {
+        super(0, 0, false, pos, false);
+        variantList = new ArrayList<Variant>();
+    }
+
+    public void insertVariant(List<ListElement> knuthList, int width) {
+        variantList.add(new Variant(knuthList, width));
+    }
+
+    public void activatePenalty(Variant bestVariant) {
         BestFitPosition pos = getBestFitPosition();
-        pos.setKnuthList(knuthList);
+        pos.setKnuthList(bestVariant.knuthList);
     }
 
-    public int getWidth() {
-        if (ignorePenalty) {
-            return 0;
-        }
-        return super.getWidth();
+    public boolean containsVariant(Variant variant) {
+        return variantList.contains(variant);
     }
 
+    public List<Variant> getVariantList() {
+        return variantList;
+    }
+
     public BestFitPosition getBestFitPosition() {
         Position pos = super.getPosition();
         while (pos != null) {
@@ -66,8 +84,7 @@
     public String toString() {
         String str = super.toString();
         StringBuffer buffer = new StringBuffer(64);
-//        buffer.append(" number of alternatives = " + alternatives.size());
-//        buffer.append(" fitting-strategy = " + strategy.getStrategyName());
+        buffer.append(" number of variants = " + variantList.size());
         return str + buffer.toString();
     }
 
Index: src/java/org/apache/fop/layoutmgr/MultiSwitchLayoutManager.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/MultiSwitchLayoutManager.java	(revision 1544072)
+++ src/java/org/apache/fop/layoutmgr/MultiSwitchLayoutManager.java	(working copy)
@@ -137,7 +137,7 @@
      */
     @Override
     protected void flush() {
-        if (getCurrentArea() != null) {
+        if (curBlockArea != null) {
             parentLayoutManager.addChildArea(getCurrentArea());
         }
     }
@@ -151,6 +151,7 @@
 
         AreaAdditionUtil.addAreas(this, newPosIter, context);
         flush();
+        curBlockArea = null;
     }
 
 }
Index: src/java/org/apache/fop/layoutmgr/BestFitLayoutUtils.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/BestFitLayoutUtils.java	(revision 1544072)
+++ src/java/org/apache/fop/layoutmgr/BestFitLayoutUtils.java	(working copy)
@@ -57,23 +57,43 @@
         }
     }
 
+
+    private static int calculateLength(List<ListElement> childList) {
+        // Add a zero penalty at the start of the list
+        // to force the SpaceResolver to transform
+        // Space elements into Knuth glues.
+        // TODO Vincent: there’s no need to add a dummy penalty at the beginning of the list.
+        // Yes, there are issues with space resolution, but if you add the penalty you will run
+        // into some bugs and if you remove it you will run into other bugs.
+        // The real solution to the problem is to change space resolution to perform
+        // partial resolution on a subset of the Knuth sequence,
+        // and refine it later on. But this is out of the scope of this work
+        // and can be left as is for now
+        // (for that matter, footnotes have been suffering from the same problem for years).
+        childList.add(0, KnuthPenalty.DUMMY_ZERO_PENALTY);
+        SpaceResolver.resolveElementList(childList);
+        return ElementListUtils.calcContentLength(childList);
+    }
+
+
     public static List<ListElement> getKnuthList(LayoutManager lm,
             List<List<ListElement>> childrenLists) {
 
         List<ListElement> knuthList = new LinkedList<ListElement>();
 
         Iterator<List<ListElement>> iter = childrenLists.iterator();
+        BestFitPenalty bestFitPenalty = new BestFitPenalty(new BestFitPosition(lm));
         while (iter.hasNext()) {
 
             List<ListElement> childList = iter.next();
-            SpaceResolver.resolveElementList(childList);
-            int contentLength = ElementListUtils.calcContentLength(childList);
-            BestFitPenalty bestFitPenalty =
-                    new BestFitPenalty(contentLength, childList,
-                    new BestFitPosition(lm));
-            knuthList.add(bestFitPenalty);
+            int contentLength = calculateLength(childList);
+            bestFitPenalty.insertVariant(childList, contentLength);
         }
+        // TODO Adding the two enclosing boxes is definitely a dirty hack.
+        // Let's leave it like that for now, until I find a proper fix.
         knuthList.add(new KnuthBox(0, new Position(lm), false));
+        knuthList.add(bestFitPenalty);
+        knuthList.add(new KnuthBox(0, new Position(lm), false));
         return knuthList;
     }
 
