]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Finn's refactoring of Knuth's breaking code, with small changes
authorLuca Furini <lfurini@apache.org>
Mon, 2 May 2005 16:23:37 +0000 (16:23 +0000)
committerLuca Furini <lfurini@apache.org>
Mon, 2 May 2005 16:23:37 +0000 (16:23 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_KnuthStylePageBreaking@198601 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/KnuthSequence.java
src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java

index d224217791119bd5c27cbe053b8b1e3b3d4b6e91..c0d87a7728babcc259bf0edc696a4391cca188e5 100644 (file)
 
 package org.apache.fop.layoutmgr;
 
-import java.util.LinkedList;
-import java.util.ListIterator;
-
+import java.util.ArrayList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * The set of nodes is sorted into lines indexed into activeLines.
+ * The nodes in each line are linked together in a single linked list by the 
+ * KnuthNode.next field. The activeLines array contains a link to the head of
+ * the linked list in index 'line*2' and a link to the tail at index 'line*2+1'.
+ * <p>
+ * The set of active nodes can be traversed by 
+ * <pre>
+ * for (int line = startLine; line < endLine; line++) {
+ *     for (KnuthNode node = getNode(line); node != null; node = node.next) {
+ *         // Do something with 'node'
+ *     }
+ * }
+ * </pre> 
+ */
 public abstract class BreakingAlgorithm {
+    // parameters of Knuth's algorithm:
+    // penalty value for flagged penalties
+    private int flaggedPenalty = 50;
+    // demerit for consecutive lines ending at flagged penalties
+    private int repeatedFlaggedDemerit = 50;
+    // demerit for consecutive lines belonging to incompatible fitness classes 
+    private int incompatibleFitnessDemerit = 50;
+    // suggested modification to the "optimum" number of lines
+    private int looseness = 0;
+
+    /**
+     * The threshold for considering breaks to be acceptable.
+     */
+    private double threshold;
+
+    /**
+     * The paragraph of KnuthElements.
+     */
+    private KnuthSequence par;
+    
+    /**
+     * The width of a line.
+     */
+    private int lineWidth = 0;
+    private boolean force =  false;
+
+    protected KnuthNode lastDeactivatedNode = null;
+    private KnuthNode lastTooLong;
+    private KnuthNode lastTooShort;
+    private KnuthNode lastDeactivated;
+
+    protected int alignment;
+    protected int alignmentLast;
+    protected boolean bFirst;
+
+    /**
+     * The set of active nodes.
+     */
+    private KnuthNode[] activeLines;
+    
+    /**
+     * The number of active nodes.
+     */
+    protected int activeNodeCount;
+    
+    /**
+     * The lowest available line in the set of active nodes.
+     */
+    protected int startLine = 0;
+
+    /**
+     * The highest + 1 available line in the set of active nodes.
+     */
+    protected int endLine = 0;
+
+    /**
+     * The total width of all elements handled so far.
+     */
+    private int totalWidth;
+
+    /**
+     * The total stretch of all elements handled so far.
+     */
+    private int totalStretch = 0;
+
+    /**
+     * The total shrink of all elements handled so far.
+     */
+    private int totalShrink = 0;
+
+    private BestRecords best;
+    private KnuthNode[] positions;
+
+    private static final int INFINITE_RATIO = 1000;
+
+    protected static Log log = LogFactory.getLog(KnuthParagraph.class);
+
+    public BreakingAlgorithm(int align, int alignLast,
+                             boolean first) {
+        alignment = align;
+        alignmentLast = alignLast;
+        bFirst = first;
+        this.best = new BestRecords();
+    }
 
-    private final int KNUTH_ALGORITHM = 0;
-    private final int FIRST_FIT_ALGORITHM = 1;
 
     // this class represent a feasible breaking point
     public class KnuthNode {
@@ -49,8 +150,11 @@ public abstract class BreakingAlgorithm {
         // adjustment ratio if the line ends at this breakpoint
         public double adjustRatio;
 
-/*LF*/  public int availableShrink;
-/*LF*/  public int availableStretch;
+        // available stretch of the line ending at this breakpoint
+        public int availableShrink;
+
+        // available shrink of the line ending at this breakpoint
+        public int availableStretch;
 
         // difference between target and actual line width
         public int difference;
@@ -61,6 +165,10 @@ public abstract class BreakingAlgorithm {
         // best node for the preceding breakpoint
         public KnuthNode previous;
 
+        // next possible node in the same line 
+        public KnuthNode next;
+
+
         public KnuthNode(int position, int line, int fitness,
                          int totalWidth, int totalStretch, int totalShrink,
                          double adjustRatio, int availableShrink, int availableStretch, int difference,
@@ -72,44 +180,52 @@ public abstract class BreakingAlgorithm {
             this.totalStretch = totalStretch;
             this.totalShrink = totalShrink;
             this.adjustRatio = adjustRatio;
-/*LF*/      this.availableShrink = availableShrink;
-/*LF*/      this.availableStretch = availableStretch;
+            this.availableShrink = availableShrink;
+            this.availableStretch = availableStretch;
             this.difference = difference;
             this.totalDemerits = totalDemerits;
             this.previous = previous;
         }
+
+        public String toString() {
+            return "<KnuthNode at " + position + " " +
+            totalWidth + "+" + totalStretch + "-" + totalShrink +
+            " line:" + line +
+            " prev:" + (previous != null ? previous.position : -1) +
+            " dem:" + totalDemerits +
+            ">"; 
+        }
     }
 
     // this class stores information about how the nodes
     // which could start a line
     // ending at the current element
     private class BestRecords {
-        private static final double INFINITE_DEMERITS = 1E11;
-
-        private double bestDemerits[] = {
-            INFINITE_DEMERITS, INFINITE_DEMERITS,
-            INFINITE_DEMERITS, INFINITE_DEMERITS
-        };
-        private KnuthNode bestNode[] = {null, null, null, null};
-        private double bestAdjust[] = {0.0, 0.0, 0.0, 0.0};
-        private int bestDifference[] = {0, 0, 0, 0};
-/*LF*/  private int bestAvailableShrink[] = {0, 0, 0, 0};
-/*LF*/  private int bestAvailableStretch[] = {0, 0, 0, 0};
+        private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
+        //private static final double INFINITE_DEMERITS = 1E11;
+
+        private double bestDemerits[] = new double[4];
+        private KnuthNode bestNode[] = new KnuthNode[4];
+        private double bestAdjust[] = new double[4];
+        private int bestDifference[] = new int[4];
+        private int bestAvailableShrink[] = new int[4];
+        private int bestAvailableStretch[] = new int[4];
         private int bestIndex = -1;
 
         public BestRecords() {
+            reset();
         }
 
         public void addRecord(double demerits, KnuthNode node, double adjust,
                               int availableShrink, int availableStretch, int difference, int fitness) {
             if (demerits > bestDemerits[fitness]) {
-                //log.error("New demerits value greter than the old one");
+                log.error("New demerits value greter than the old one");
             }
             bestDemerits[fitness] = demerits;
             bestNode[fitness] = node;
             bestAdjust[fitness] = adjust;
-/*LF*/      bestAvailableShrink[fitness] = availableShrink;
-/*LF*/      bestAvailableStretch[fitness] = availableStretch;
+            bestAvailableShrink[fitness] = availableShrink;
+            bestAvailableStretch[fitness] = availableStretch;
             bestDifference[fitness] = difference;
             if (bestIndex == -1 || demerits < bestDemerits[bestIndex]) {
                 bestIndex = fitness;
@@ -136,13 +252,13 @@ public abstract class BreakingAlgorithm {
             return bestAdjust[fitness];
         }
 
-/*LF*/  public int getAvailableShrink(int fitness) {
-/*LF*/      return bestAvailableShrink[fitness];
-/*LF*/  }
+        public int getAvailableShrink(int fitness) {
+            return bestAvailableShrink[fitness];
+        }
 
-/*LF*/  public int getAvailableStretch(int fitness) {
-/*LF*/      return bestAvailableStretch[fitness];
-/*LF*/  }
+        public int getAvailableStretch(int fitness) {
+            return bestAvailableStretch[fitness];
+        }
 
        public int getDifference(int fitness) {
             return bestDifference[fitness];
@@ -156,40 +272,21 @@ public abstract class BreakingAlgorithm {
                 return INFINITE_DEMERITS;
             }
         }
-    }
-
-    //     parameters of Knuth's algorithm:
-    // penalty value for flagged penalties
-    private int flaggedPenalty = 50;
-    // demerit for consecutive lines ending at flagged penalties
-    private int repeatedFlaggedDemerit = 50;
-    // demerit for consecutive lines belonging to incompatible fitness classes 
-    private int incompatibleFitnessDemerit = 50;
-    // suggested modification to the "optimum" number of lines
-    private int looseness = 0;
-
-    private static final int INFINITE_RATIO = 1000;
-    private static int MAX_DEMERITS_INCREASE = 1000;
 
-    protected LinkedList activeList = null;
-    protected LinkedList auxActiveList = null;
-//    private LinkedList inactiveList = null;
-    private LinkedList auxInactiveList = null;
-    protected KnuthNode lastDeactivatedNode = null;
-    private KnuthNode lastTooLong;
-    private KnuthNode lastTooShort;
-    private KnuthNode lastDeactivated;
+        public void reset() {
+            for (int i = 0; i < 4; i ++) {
+                bestDemerits[i] = INFINITE_DEMERITS;
+                bestNode[i] = null;
+                bestAdjust[i] = 0.0;
+                bestDifference[i] = 0;
+                bestAvailableShrink[i] = 0;
+                bestAvailableStretch[i] = 0;
+            }
+            bestIndex = -1;
+        }
+    }
 
-    protected int alignment;
-    protected int alignmentLast;
-    protected boolean bFirst;
 
-    public BreakingAlgorithm(int align, int alignLast,
-                             boolean first) {
-        alignment = align;
-        alignmentLast = alignLast;
-        bFirst = first;
-    }
 
     public abstract void updateData1(int total, double demerits) ;
 
@@ -200,135 +297,46 @@ public abstract class BreakingAlgorithm {
     public int findBreakingPoints(KnuthSequence par, int lineWidth,
                                   double threshold, boolean force,
                                   boolean hyphenationAllowed) {
+        this.par = par;
+        this.threshold = threshold;
+        this.force = force;
+        this.lineWidth = lineWidth;
+        this.totalWidth = 0;
+        this.totalStretch = 0;
+        this.totalShrink = 0;
 
-        findBreakingPoints(KNUTH_ALGORITHM, par, 0, lineWidth, threshold, force, hyphenationAllowed);
-
-        boolean bForced = false;
-        int optLineNumber;
-        if (activeList.size() > 0) {
-            // there is at least one set of breaking points
-            // select one or more active nodes, removing the others from the list
-            optLineNumber = filterActiveList();
-/* a causa delle modifiche di Finn Bock, non c'e' piu' bisogno di usare first fit */
-//        } else if (force) {
-//            // no set of breaking points, but must find one
-//            // use a different algorithm
-///*LF*/      System.out.println("si ricorre all'algoritmo first fit dalla riga " + lastDeactivatedNode.line + " elemento " + (lastDeactivatedNode.position == 0 ? 0 :lastDeactivatedNode.position + 1));
-//            findBreakingPoints(FIRST_FIT_ALGORITHM, par, lastDeactivatedNode.position == 0 ? 0 :lastDeactivatedNode.position + 1, lineWidth, threshold, force, hyphenationAllowed);
-//            activeList.add(auxActiveList.getLast());
-//            optLineNumber = ((KnuthNode) auxActiveList.getLast()).line;
-//            bForced = true;
-        } else {
-            // no set of breaking points, do nothing
-            return 0;
-        }
-
-        // now, activeList is not empty:
-        // if bForced == true, there is ONLY one node in activeList
-        // if bForced == false, there is AT LEAST one node in activeList
-/*LF*/  //System.out.println("BA> ora activeList.size() = " + activeList.size());
-        ListIterator activeListIterator = activeList.listIterator();
-        while (activeListIterator.hasNext()) {
-            KnuthNode activeNode = (KnuthNode)activeListIterator.next();
-            int totalLines;
-            if (bForced) {
-                updateData1(optLineNumber, 0);
-                totalLines = optLineNumber;
-                auxActiveList.clear();
-                auxInactiveList.clear();
-            } else {
-                updateData1(activeNode.line, activeNode.totalDemerits);
-                totalLines = activeNode.line;
-            }
-            calculateBreakPoints(activeNode, par, totalLines);
-        }
-
-        activeList.clear();
-//        inactiveList.clear();
-        return optLineNumber;
-    }
-
-/* ---------------------------- */
-    public int firstFit(KnuthSequence par, int lineWidth,
-                        double threshold, boolean force) {
-        lastDeactivatedNode = new KnuthNode(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null);
-
-        findBreakingPoints(FIRST_FIT_ALGORITHM, par, 0, lineWidth, threshold, force, true);
-
-/*LF*/  activeList = new LinkedList();
-///*LF*/  inactiveList = new LinkedList();
-        activeList.add(auxActiveList.getLast());
-        int optLineNumber = ((KnuthNode) auxActiveList.getLast()).line;
-
-        // now, activeList is not empty:
-        // if bForced == true, there is ONLY one node in activeList
-        // if bForced == false, there is AT LEAST one node in activeList
-/*LF*/  //System.out.println("BA> ora activeList.size() = " + activeList.size());
-        ListIterator activeListIterator = activeList.listIterator();
-        while (activeListIterator.hasNext()) {
-            KnuthNode activeNode = (KnuthNode)activeListIterator.next();
-            int totalLines;
-            updateData1(optLineNumber, 0);
-            totalLines = optLineNumber;
-            auxActiveList.clear();
-            auxInactiveList.clear();
-            calculateBreakPoints(activeNode, par, totalLines);
-        }
-
-        activeList.clear();
-//        inactiveList.clear();
-        return optLineNumber;
-    }
-/* ---------------------------- */
-
-    private void findBreakingPoints(int algorithm,
-                                    KnuthSequence par, int firstIndex,
-                                    int lineWidth,
-                                    double threshold, boolean force,
-                                    boolean hyphenationAllowed) {
-        int totalWidth = 0;
-        int totalStretch = 0;
-        int totalShrink = 0;
+        activeLines = new KnuthNode[20];
 
         // reset lastTooShort and lastTooLong, as they could be not null
         // because of previous calls to findBreakingPoints
         lastTooShort = lastTooLong = null; 
-
+        // reset startLine and endLine
+        startLine = endLine = 0;
         // current element in the paragraph
         KnuthElement thisElement = null;
-        // previous element in the paragraph is a KnuthBox
+        // previous element in the paragraph is a KnuthBox?
         boolean previousIsBox = false;
 
-/*LF*/  // index of the first KnuthBox in the sequence
-/*LF*/  int firstBoxIndex = firstIndex;
-/*LF*/  while (alignment != org.apache.fop.fo.Constants.EN_CENTER
-/*LF*/         && ! ((KnuthElement) par.get(firstBoxIndex)).isBox()) {
-/*LF*/      firstBoxIndex++;
-/*LF*/  }
-/*LF*/
+        // index of the first KnuthBox in the sequence
+        int firstBoxIndex = 0;
+        while (alignment != org.apache.fop.fo.Constants.EN_CENTER
+               && ! ((KnuthElement) par.get(firstBoxIndex)).isBox()) {
+            firstBoxIndex++;
+        }
+
         // create an active node representing the starting point
-        if (algorithm == KNUTH_ALGORITHM) {
-            activeList = new LinkedList();
-/*LF*/      activeList.add(new KnuthNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
-//            inactiveList = new LinkedList();
-        } else {
-            auxActiveList = new LinkedList();
-/*LF*/      auxActiveList.add(new KnuthNode(lastDeactivatedNode.position, lastDeactivatedNode.line, 1,
-                                            0, 0, 0,
-                                            lastDeactivatedNode.adjustRatio,
-                                            0, 0, lastDeactivatedNode.difference,
-                                            0, lastDeactivatedNode.previous));
-            auxInactiveList = new LinkedList();
+        activeLines = new KnuthNode[20];
+        addNode(0, new KnuthNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
+
+        if (log.isTraceEnabled()) {
+            log.trace("Looping over " + par.size() + " box objects");
         }
+        
+        KnuthNode lastForced = getNode(0);
 
-        KnuthNode lastForced = (KnuthNode) activeList.getFirst();
         // main loop
-/*LF*/  ListIterator paragraphIterator = par.listIterator(firstBoxIndex);
-/*LF*/  //System.out.println("");
-/*LF*/  //System.out.println("inizio main loop");
-        while (paragraphIterator.hasNext()) {
-            thisElement = (KnuthElement) paragraphIterator.next();
-/*LF*/      //System.out.println("main loop, elemento " + paragraphIterator.previousIndex());
+        for (int i = 0; i < par.size(); i++) {
+            thisElement = getElement(i);
             if (thisElement.isBox()) {
                 // a KnuthBox object is not a legal line break
                 totalWidth += thisElement.getW();
@@ -337,19 +345,11 @@ public abstract class BreakingAlgorithm {
                 // a KnuthGlue object is a legal line break
                 // only if the previous object is a KnuthBox
                 if (previousIsBox) {
-                    if (algorithm == KNUTH_ALGORITHM) {
-                        considerLegalBreak(par, lineWidth, thisElement,
-                                           totalWidth, totalStretch, totalShrink,
-                                           threshold);
-                    } else {
-                        considerLegalBreak2(par, lineWidth, thisElement,
-                                            totalWidth, totalStretch, totalShrink,
-                                            threshold);
-                    }
+                    considerLegalBreak(thisElement, i);
                 }
                 totalWidth += thisElement.getW();
-                totalStretch += ((KnuthGlue) thisElement).getY();
-                totalShrink += ((KnuthGlue) thisElement).getZ();
+                totalStretch += thisElement.getY();
+                totalShrink += thisElement.getZ();
                 previousIsBox = false;
             } else {
                 // a KnuthPenalty is a legal line break
@@ -358,382 +358,418 @@ public abstract class BreakingAlgorithm {
                 if (((KnuthPenalty) thisElement).getP()
                     < KnuthElement.INFINITE
                     && (hyphenationAllowed || !((KnuthPenalty) thisElement).isFlagged())) {
-                    if (algorithm == KNUTH_ALGORITHM) {
-                        considerLegalBreak(par, lineWidth, thisElement,
-                                           totalWidth, totalStretch, totalShrink,
-                                           threshold);
-                    } else {
-                        considerLegalBreak2(par, lineWidth, thisElement,
-                                            totalWidth, totalStretch, totalShrink,
-                                            threshold);
-                    }
+                    considerLegalBreak(thisElement, i);
                 }
                 previousIsBox = false;
             }
-            /* *** inizio modifiche proposte da Finn Bock *** */
-            if (activeList.size() == 0) {
+            if (activeNodeCount == 0) {
                 if (!force) {
-                    return;
+                    log.debug("Could not find a set of breaking points " + threshold);
+                    return 0;
                 }
-/*LF*/          //System.out.println(" ");
-/*LF*/          //System.out.println("  lastTooShort.position= " + (lastTooShort == null ? -1 : lastTooShort.position) + " lastTooLong.position= " + (lastTooLong == null ? -1 : lastTooLong.position));
                 if (lastTooShort == null || lastForced.position == lastTooShort.position) {
                     lastForced = lastTooLong;
                 } else {
                     lastForced = lastTooShort;
                 }
 
-/*LF*/          //System.out.println("  Restarting: position= " + lastForced.position);
-/*LF*/          //System.out.println(" ");
+                log.debug("Restarting at node " + lastForced);
                 lastForced.totalDemerits = 0;
-                activeList.add(lastForced);
+                addNode(lastForced.line, lastForced);
+                i = lastForced.position;
+                startLine = lastForced.line;
+                endLine = startLine + 1;
                 totalWidth = lastForced.totalWidth;
                 totalStretch = lastForced.totalStretch;
                 totalShrink = lastForced.totalShrink;
                 lastTooShort = lastTooLong = null;
-                while (paragraphIterator.nextIndex() > (lastForced.position + 1)) {
-                    paragraphIterator.previous();
-                }
             }
-            /* *** fine modifiche proposte da Finn Bock  *** */
         }
-    }
-
-    private void considerLegalBreak2(LinkedList par, int lineWidth,
-                                     KnuthElement element,
-                                     int totalWidth, int totalStretch,
-                                     int totalShrink, double threshold) {
-        //System.out.println("(" + par.indexOf(element) + ")");
-        KnuthNode startNode = (KnuthNode) auxActiveList.getFirst();
-        KnuthNode endNode = auxActiveList.size() > 1 ? (KnuthNode) auxActiveList.getLast() : null;
-
-        // these are the new values that must be computed
-        // in order to define a new active node
-        int newWidth;
-        int newStretch;
-        int newShrink;
-        int newDifference;
-        double newRatio;
-
-        // compute width, stretch and shrink of the new node
-        newWidth = totalWidth;
-        newStretch = totalStretch;
-        newShrink = totalShrink;
-        ListIterator tempIterator = par.listIterator(par.indexOf(element));
-        while (tempIterator.hasNext()) {
-            KnuthElement tempElement = (KnuthElement)tempIterator.next();
-            if (tempElement.isBox()) {
-                break;
-            } else if (tempElement.isGlue()) {
-                newWidth += ((KnuthGlue) tempElement).getW();
-                newStretch += ((KnuthGlue) tempElement).getY();
-                newShrink += ((KnuthGlue) tempElement).getZ();
-            } else if (((KnuthPenalty) tempElement).getP() 
-                       == -KnuthElement.INFINITE
-                       && tempElement != element) {
-                break;
-            }
+        if (log.isTraceEnabled()) {
+            log.trace("Main loop completed " + activeNodeCount);
+            log.trace("Active nodes=" + toString(""));
         }
 
-        if (endNode == null
-            || totalWidth + (element.isPenalty() ? element.getW() : 0) - startNode.totalWidth <= lineWidth
-            || alignment == org.apache.fop.fo.Constants.EN_JUSTIFY
-               && totalWidth  + (element.isPenalty() ? element.getW() : 0)- startNode.totalWidth - (totalShrink - startNode.totalShrink) <= lineWidth) {
-            // add content to the same line
-
-            // compute difference and ratio
-            int actualWidth = totalWidth - startNode.totalWidth;
-            if (element.isPenalty()) {
-                actualWidth += element.getW();
-            }
-            newDifference = lineWidth - actualWidth;
-            int available = newDifference >= 0 ? totalStretch - startNode.totalStretch
-                                               : totalShrink - startNode.totalShrink;
-            newRatio = available != 0 ? (float) newDifference / available
-                                      : 0;
-
-            if (endNode != null) {
-                auxActiveList.removeLast();
-            }
-            auxActiveList.add(new KnuthNode(par.indexOf(element), startNode.line + 1, 0,
-                                            newWidth, newStretch, newShrink,
-                                            newRatio, 0, 0, newDifference, 0.0,
-                                            startNode));
-            //System.out.println("da " + startNode.position + " a " + par.indexOf(element));
-            //System.out.println("difference = " + newDifference + " available = " + available + " ratio = " + newRatio);
-            //System.out.println(" ");
-        } else {
-            // start a new line
+        // there is at least one set of breaking points
+        // select one or more active nodes, removing the others from the list
+        int line = filterActiveNodes();
 
-            // compute difference and ratio
-            int actualWidth = totalWidth - endNode.totalWidth;
-            if (element.isPenalty()) {
-                actualWidth += element.getW();
+        // for each active node, create a set of breaking points
+        for (int i = startLine; i < endLine; i++) {
+            for (KnuthNode node = getNode(line); node != null; node = node.next) {
+                updateData1(node.line, node.totalDemerits);
+                calculateBreakPoints(node, par, node.line);
             }
-            newDifference = lineWidth - actualWidth;
-            int available = newDifference >= 0 ? totalStretch - endNode.totalStretch
-                                               : totalShrink - endNode.totalShrink;
-            newRatio = available != 0 ? (float) newDifference / available
-                                      : 0;
-
-            auxInactiveList.add(auxActiveList.removeFirst());
-            auxActiveList.add(new KnuthNode(par.indexOf(element), endNode.line + 1, 0,
-                                            newWidth, newStretch, newShrink,
-                                            newRatio, 0, 0, newDifference, 0.0,
-                                            endNode));
-            //System.out.println("da " + endNode.position + " a " + par.indexOf(element));
-            //System.out.println("difference = " + newDifference + " available = " + available + " ratio = " + newRatio);
-            //System.out.println(" ");
         }
-    }
 
+        activeLines = null;
+        return line;
+    }
 
-    private void considerLegalBreak(LinkedList par, int lineWidth,
-                                    KnuthElement element,
-                                    int totalWidth, int totalStretch,
-                                    int totalShrink, double threshold) {
-        KnuthNode activeNode = null;
+    private void considerLegalBreak(KnuthElement element, int elementIdx) {
 
-        ListIterator activeListIterator = activeList.listIterator();
-        if (activeListIterator.hasNext()) {
-            activeNode = (KnuthNode)activeListIterator.next();
-        } else {
-            activeNode = null;
+        if (log.isTraceEnabled()) {
+            log.trace("Feasible breakpoint at " + par.indexOf(element) + " " + totalWidth + "+" + totalStretch + "-" + totalShrink);
+            log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t"));
         }
 
         lastDeactivated = null;
         lastTooLong = null;
-
-        while (activeNode != null) {
-            BestRecords best = new BestRecords();
-
-            // these are the new values that must be computed
-            // in order to define a new active node
-            int newLine = 0;
-            int newFitnessClass = 0;
-            int newWidth = 0;
-            int newStretch = 0;
-            int newShrink = 0;
-            double newIPDAdjust = 0;
-            double newDemerits = 0;
-
-            while (activeNode != null) {
-                // compute the line number
-                newLine = activeNode.line + 1;
-
-                // compute the adjustment ratio
-                int actualWidth = totalWidth - activeNode.totalWidth;
-                if (element.isPenalty()) {
-                    actualWidth += element.getW();
+        for (int line = startLine; line < endLine; line++) {
+            for (KnuthNode node = getNode(line); node != null; node = node.next) {
+                if (node.position == elementIdx) {
+                    continue;
                 }
-                int neededAdjustment = lineWidth - actualWidth;
-                int maxAdjustment = 0;
-/*LF*/          int availableShrink = totalShrink - activeNode.totalShrink;
-/*LF*/          int availableStretch = totalStretch - activeNode.totalStretch;
-                if (neededAdjustment > 0) {
-                    maxAdjustment = availableStretch;
-                    if (maxAdjustment > 0) {
-                        newIPDAdjust
-                            = (double) neededAdjustment / maxAdjustment;
-                    } else {
-                        newIPDAdjust = INFINITE_RATIO;
-                    }
-                } else if (neededAdjustment < 0) {
-                    maxAdjustment = availableShrink;
-                    if (maxAdjustment > 0) {
-                        newIPDAdjust
-                            = (double) neededAdjustment / maxAdjustment;
-                    } else {
-                        newIPDAdjust = -INFINITE_RATIO;
-                    }
-/*LF*/          } else {
-/*LF*/              // neededAdjustment == 0
-/*LF*/              newIPDAdjust = 0;
+                int difference = computeDifference(node, element);
+                double r = computeAdjustmentRatio(node, difference);
+                int availableShrink = totalShrink - node.totalShrink;
+                int availableStretch = totalStretch - node.totalStretch;
+                if (log.isTraceEnabled()) {
+                    log.trace("\tr=" + r);
+                    log.trace("\tline=" + line);
                 }
 
-                /* calcola demeriti e fitness class in ogni caso, poi,
-                 * a seconda che il coefficiente sia o meno nel range permesso,
-                 * aggiorna i record o i migliori nodi disattivati
-                 */
-                // compute demerits and fitness class
-                if (element.isPenalty()
-                    && ((KnuthPenalty) element).getP() >= 0) {
-                    newDemerits
-                        = Math.pow((1
-                                    + 100 * Math.pow(Math.abs(newIPDAdjust), 3)
-                                    + ((KnuthPenalty) element).getP()), 2);
-                } else if (element.isPenalty()
-                           && ((KnuthPenalty)element).getP()
-                                > - KnuthElement.INFINITE) {
-                                /*> -INFINITE_RATIO) { ??? */
-                    newDemerits
-                        = Math.pow((1
-                                    + 100 * Math.pow(Math.abs(newIPDAdjust), 3)), 2)
-                        - Math.pow(((KnuthPenalty) element).getP(), 2);
-                } else {
-                    newDemerits
-                        = Math.pow((1
-                                    + 100 * Math.pow(Math.abs(newIPDAdjust), 3)), 2);
-                }
-                if (element.isPenalty()
-                    && ((KnuthPenalty) element).isFlagged()
-                    && ((KnuthElement) par.get(activeNode.position)).isPenalty()
-                    && ((KnuthPenalty) par.get(activeNode.position)).isFlagged()) {
-                    // add demerit for consecutive breaks at flagged penalties
-                    newDemerits += repeatedFlaggedDemerit;
-                }
-                if (newIPDAdjust < -0.5) {
-                    newFitnessClass = 0;
-                } else if (newIPDAdjust <= 0.5) {
-                    newFitnessClass = 1;
-                } else if (newIPDAdjust <= 1) {
-                    newFitnessClass = 2;
-                } else {
-                    newFitnessClass = 3;
-                }
-                if (Math.abs(newFitnessClass - activeNode.fitness) > 1) {
-                    // add demerit for consecutive breaks
-                    // with very different fitness classes
-                    newDemerits += incompatibleFitnessDemerit;
+                // The line would be too long.
+                if (r < -1 || element.isForcedBreak()) {
+                    // Deactivate node.
+                    if (log.isTraceEnabled()) {
+                        log.trace("Removing " + node);
+                    }
+                    removeNode(line, node);
+                    lastDeactivated = compareNodes(lastDeactivated, node);
                 }
-                newDemerits += activeNode.totalDemerits;
-
-/*LF*/          //System.out.println(" possibile soluzione, da " + activeNode.position + " a " + par.indexOf(element));
-                if ((-1 <= newIPDAdjust) && (newIPDAdjust <= threshold)) {
-/*LF*/              //System.out.println("  possibile soluzione buona");
-                    if (newDemerits < best.getDemerits(newFitnessClass)) {
+    
+                // The line is within the available shrink and the threshold.
+                if (r >= -1 && r <= threshold) {
+                    int fitnessClass = computeFitness(r);
+                    double demerits = computeDemerits(node, element, fitnessClass, r);
+    
+                    if (log.isTraceEnabled()) {
+                        log.trace("\tDemerits=" + demerits);
+                        log.trace("\tFitness class=" + fitnessClass);
+                    }
+    
+                    if (demerits < best.getDemerits(fitnessClass)) {
                         // updates best demerits data
-                        best.addRecord(newDemerits, activeNode, newIPDAdjust,
-                                       availableShrink, availableStretch, neededAdjustment, newFitnessClass);
-/*LF*/                  lastTooShort = null;
+                        best.addRecord(demerits, node, r, availableShrink, availableStretch,
+                                       difference, fitnessClass);
+                        lastTooShort = null;
                     }
-                } else {
-                     // la linea e' troppo piena o troppo vuota
-                    if (newIPDAdjust <= -1) {
-/*LF*/                  //System.out.println("  possibile soluzione lunga");
-                        if (lastTooLong == null || newDemerits < lastTooLong.totalDemerits) {
-/*LF*/                      //System.out.println("  aggiornato lastTooLong, da " + activeNode.position + " a " + par.indexOf(element));
-                            lastTooLong = new KnuthNode(par.indexOf(element), newLine, newFitnessClass,
+                }
+                
+                // The line is way too short, but we are in forcing mode, so a node is
+                // calculated and stored in lastValidNode.
+                if (force && (r <= -1 || r > threshold)) {
+                    int fitnessClass = computeFitness(r);
+                    double demerits = computeDemerits(node, element, fitnessClass, r);
+                    if (r <= -1) {
+                        if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+                            lastTooLong = new KnuthNode(elementIdx, line + 1, fitnessClass,
                                     totalWidth, totalStretch, totalShrink,
-                                    newIPDAdjust, availableShrink, availableStretch, neededAdjustment, newDemerits, activeNode);
+                                    r, availableShrink, availableStretch,
+                                    difference, demerits, node);
+                            if (log.isTraceEnabled()) {
+                                log.trace("Picking tooLong " + lastTooLong);
+                            }
                         }
                     } else {
-/*LF*/                  //System.out.println("  possibile soluzione corta");
-/*LF*/                  //if (lastTooShort != null) {
-/*LF*/                  //    System.out.println("  vecchia soluzione corta da " + lastTooShort.previous.position + " a " + lastTooShort.position);
-/*LF*/                  //}
-                        if (lastTooShort == null || newDemerits <= lastTooShort.totalDemerits) {
-/*LF*/                      //System.out.println("  aggiornato lastTooShort, da " + activeNode.position + " a " + par.indexOf(element));
-                            lastTooShort = new KnuthNode(par.indexOf(element), newLine, newFitnessClass,
+                        if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+                            lastTooShort = new KnuthNode(elementIdx, line + 1, fitnessClass,
                                     totalWidth, totalStretch, totalShrink,
-                                    newIPDAdjust, availableShrink, availableStretch, neededAdjustment, newDemerits, activeNode);
+                                    r, availableShrink, availableStretch,
+                                    difference, demerits, node);
+                            if (log.isTraceEnabled()) {
+                                log.trace("Picking tooShort " + lastTooShort);
+                            }
                         }
                     }
                 }
-                /* disattivazione di un nodo */
-                if (newIPDAdjust < -1
-                    || (element.isPenalty() 
-                        && ((KnuthPenalty)element).getP()
-                             == -KnuthElement.INFINITE)
-/*LF*/                  && !(activeNode.position
-                             == par.indexOf(element))) {
-                    // deactivate activeNode
-                    KnuthNode tempNode
-                        = (KnuthNode)activeListIterator.previous();
-                    int iCallNext = 0;
-                    while (tempNode != activeNode) {
-                        // this is not the node we meant to remove!
-                        tempNode = (KnuthNode)activeListIterator.previous();
-                        iCallNext ++;
-                    }
-                    activeListIterator.remove();
-                    for (int i = 0; i < iCallNext; i++) {
-                        activeListIterator.next();
-                    }
-                }
+            }
+            addBreaks(line, elementIdx);
+        }
+    }
 
-                if (activeListIterator.hasNext()) {
-                    activeNode = (KnuthNode) activeListIterator.next();
-                } else {
-                    activeNode = null;
-                    break;
-                }
-                if (activeNode.line >= newLine) {
-                    break;
-                }
-            } // end of the inner while
-
-            if (best.hasRecords()) {
-                // compute width, stratchability and shrinkability
-                newWidth = totalWidth;
-                newStretch = totalStretch;
-                newShrink = totalShrink;
-                ListIterator tempIterator
-                    = par.listIterator(par.indexOf(element));
-                while (tempIterator.hasNext()) {
-                    KnuthElement tempElement
-                        = (KnuthElement)tempIterator.next();
-                    if (tempElement.isBox()) {
-                        break;
-                    } else if (tempElement.isGlue()) {
-                        newWidth += ((KnuthGlue) tempElement).getW();
-                        newStretch += ((KnuthGlue) tempElement).getY();
-                        newShrink += ((KnuthGlue) tempElement).getZ();
-                    } else if (((KnuthPenalty) tempElement).getP() 
-                               == -KnuthElement.INFINITE
-                               && tempElement != element) {
-                        break;
-                    }
-                }
+    private void addBreaks(int line, int elementIdx) {
+        if (!best.hasRecords()) {
+            return;
+        }
 
-                // add nodes to the active nodes list
-                for (int i = 0; i <= 3; i++) {
-                    if (best.notInfiniteDemerits(i)
-                        && best.getDemerits(i)
-                           <= (best.getMinDemerits()
-                               + incompatibleFitnessDemerit)) {
-                        // the nodes in activeList must be ordered
-                        // by line number and position;
-                        // so:
-                        // 1) advance in the list until the end,
-                        // or a node with a higher line number, is reached
-/*LF*/                  int iStepsForward = 0;
-/*LF*/                  KnuthNode tempNode;
-/*LF*/                  while (activeListIterator.hasNext()) {
-/*LF*/                      iStepsForward ++;
-/*LF*/                      tempNode = (KnuthNode) activeListIterator.next();
-/*LF*/                      if (tempNode.line > (best.getNode(i).line + 1)) {
-/*LF*/                          activeListIterator.previous();
-/*LF*/                          iStepsForward --;
-/*LF*/                          break;
-/*LF*/                      }
-/*LF*/                  }
-                        // 2) add the new node
-                        activeListIterator.add
-                            (new KnuthNode(par.indexOf(element),
-                                           best.getNode(i).line + 1, i,
-                                           newWidth, newStretch, newShrink,
-                                           best.getAdjust(i),
-                                           best.getAvailableShrink(i), best.getAvailableStretch(i),
-                                           best.getDifference(i),
-                                           best.getDemerits(i), 
-                                           best.getNode(i)));
-                        // 3) go back
-/*LF*/                  for (int j = 0;
-/*LF*/                       j <= iStepsForward;
-/*LF*/                       j ++) {
-/*LF*/                      activeListIterator.previous();
-/*LF*/                  }
-                    }
+        int newWidth = totalWidth;
+        int newStretch = totalStretch;
+        int newShrink = totalShrink;
+
+        for (int i = elementIdx; i < par.size(); i++) {
+            KnuthElement tempElement = getElement(i);
+            if (tempElement.isBox()) {
+                break;
+            } else if (tempElement.isGlue()) {
+                newWidth += tempElement.getW();
+                newStretch += tempElement.getY();
+                newShrink += tempElement.getZ();
+            } else if (tempElement.isForcedBreak() && i != elementIdx) {
+                break;
+            }
+        }
+
+        // add nodes to the active nodes list
+        double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit;
+        for (int i = 0; i <= 3; i++) {
+            if (best.notInfiniteDemerits(i) && best.getDemerits(i) <= minimumDemerits) {
+                // the nodes in activeList must be ordered
+                // by line number and position;
+                if (log.isTraceEnabled()) {
+                    log.trace("\tInsert new break in list of " + activeNodeCount);
                 }
+                KnuthNode newNode = new KnuthNode(elementIdx, line + 1, i,
+                                   newWidth, newStretch, newShrink,
+                                   best.getAdjust(i),
+                                   best.getAvailableShrink(i),
+                                   best.getAvailableStretch(i),
+                                   best.getDifference(i),
+                                   best.getDemerits(i),
+                                   best.getNode(i));
+                addNode(line + 1, newNode);
             }
-            if (activeNode == null) {
-                break;
+        }
+        best.reset();
+    }
+
+    /**
+     * Return the difference between the line width and the width of the break that
+     * ends in 'element'.
+     * @param activeNode
+     * @param element
+     * @return The difference in width. Positive numbers mean extra space in the line,
+     * negative number that the line overflows. 
+     */
+    private int computeDifference(KnuthNode activeNode, KnuthElement element) {
+        // compute the adjustment ratio
+        int actualWidth = totalWidth - activeNode.totalWidth;
+        if (element.isPenalty()) {
+            actualWidth += element.getW();
+        }
+        return lineWidth - actualWidth;
+    }
+
+    /**
+     * Return the adjust ration needed to make up for the difference. A ration of 
+     * <ul>
+     *    <li>0 means that the break has the exact right width</li>
+     *    <li>&gt;= -1 && &lt; 0  means that the break is to wider than the line, 
+     *        but within the minimim values of the glues.</li> 
+     *    <li>&gt;0 && &lt 1 means that the break is smaller than the line width, 
+     *        but within the maximum values of the glues.</li>
+     *    <li>&gt 1 means that the break is too small to make up for the glues.</li> 
+     * </ul>
+     * @param activeNode
+     * @param difference
+     * @return The ration.
+     */
+    private double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
+        // compute the adjustment ratio
+        if (difference > 0) {
+            int maxAdjustment = totalStretch - activeNode.totalStretch;
+            if (maxAdjustment > 0) {
+                return (double) difference / maxAdjustment;
+            } else {
+                return INFINITE_RATIO;
+            }
+        } else if (difference < 0) {
+            int maxAdjustment = totalShrink - activeNode.totalShrink;
+            if (maxAdjustment > 0) {
+                return (double) difference / maxAdjustment;
+            } else {
+                return -INFINITE_RATIO;
             }
-        } // end of the outer while
+        } else {
+            return 0;
+        }
+    }
+    
+    /**
+     * Figure out the fitness class of this line (tight, loose,
+     * very tight or very loose).
+     * @param r
+     * @return
+     */
+    private int computeFitness(double r) {
+        int newFitnessClass;
+        if (r < -0.5) {
+            return 0;
+        } else if (r <= 0.5) {
+            return 1;
+        } else if (r <= 1) {
+            return 2;
+        } else {
+            return 3;
+        }
+    }
+
+    private double computeDemerits(KnuthNode activeNode, KnuthElement element, 
+                                  int fitnessClass, double r) {
+        double demerits = 0;
+        // compute demerits
+        double f = Math.abs(r);
+        f = 1 + 100 * f * f * f;
+        if (element.isPenalty() && element.getP() >= 0) {
+            f += element.getP();
+            demerits = f * f;
+        } else if (element.isPenalty() && !element.isForcedBreak()) {
+            double penalty = element.getP();
+            demerits = f * f - penalty * penalty;
+        } else {
+            demerits = f * f;
+        }
+    
+        if (element.isPenalty() && ((KnuthPenalty) element).isFlagged()
+            && getElement(activeNode.position).isPenalty()
+            && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
+            // add demerit for consecutive breaks at flagged penalties
+            demerits += repeatedFlaggedDemerit;
+        }
+        if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
+            // add demerit for consecutive breaks
+            // with very different fitness classes
+            demerits += incompatibleFitnessDemerit;
+        }
+        demerits += activeNode.totalDemerits;
+        return demerits;
+    }
+
+    /**
+     * Return the element at index idx in the paragraph.
+     * @param idx index of the element.
+     * @return
+     */
+    private KnuthElement getElement(int idx) {
+        return (KnuthElement) par.get(idx);
+    }
+
+    /**
+     * Compare two KnuthNodes and return the node with the least demerit. 
+     * @param node1 The first knuth node.
+     * @param node2 The other knuth node.
+     * @return
+     */
+    protected KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) {
+        if (node1 == null || node2.position > node1.position) {
+            return node2;
+        }
+        if (node2.position == node1.position) {
+            if (node2.totalDemerits < node1.totalDemerits) {
+                return node2;
+            }
+        }
+        return node1;
+    }
+
+    /**
+     * Add a KnuthNode at the end of line 'line'. 
+     * If this is the first node in the line, adjust endLine accordingly.
+     * @param line
+     * @param node
+     */
+    private void addNode(int line, KnuthNode node) {
+        int headIdx = line * 2;
+        if (headIdx >= activeLines.length) {
+            KnuthNode[] oldList = activeLines;
+            activeLines = new KnuthNode[headIdx + headIdx];
+            System.arraycopy(oldList, 0, activeLines, 0, oldList.length);
+        }
+        node.next = null;
+        if (activeLines[headIdx + 1] != null) {
+            activeLines[headIdx + 1].next = node;
+        } else {
+            activeLines[headIdx] = node;
+            endLine = line+1;
+        }
+        activeLines[headIdx + 1] = node;
+        activeNodeCount++;
+    }
+
+    /**
+     * Remove the first node in line 'line'. If the line then becomes empty, adjust the
+     * startLine accordingly.
+     * @param line
+     * @param node
+     */
+    protected void removeNode(int line, KnuthNode node) {
+        KnuthNode n = getNode(line);
+        if (n != node) {
+            log.error("Should be first");
+        } else {
+            activeLines[line*2] = node.next;
+            if (node.next == null) {
+                activeLines[line*2+1] = null;
+            }
+            while (startLine < endLine && getNode(startLine) == null) {
+                startLine++;
+            }
+        }
+        activeNodeCount--;
+    }
+
+    protected KnuthNode getNode(int line) {
+        return activeLines[line * 2];
+    }
+
+    /**
+     * Return true if the position 'idx' is a legal breakpoint.
+     * @param idx
+     * @return
+     */
+    private boolean isLegalBreakpoint(int idx) {
+        KnuthElement elm = getElement(idx);
+        if (elm.isPenalty() && elm.getP() != KnuthElement.INFINITE) {
+            return true;
+        } else if (idx > 0 && elm.isGlue() && getElement(idx-1).isBox()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    public int getDifference(int line) {
+        return positions[line].difference;
+    }
+
+    public double getAdjustRatio(int line) {
+        return positions[line].adjustRatio;
+    }
+
+    public int getStart(int line) {
+        KnuthNode previous = positions[line].previous;
+        return line == 0 ? 0 : previous.position + 1; 
+    }
+
+    public int getEnd(int line) {
+        return positions[line].position;
+    }
+
+    /**
+     * Return a string representation of a MinOptMax in the form of a 
+     * "width+stretch-shrink". Useful only for debugging.
+     * @param mom
+     * @return 
+     */
+    private static String width(MinOptMax mom) {
+        return mom.opt + "+" + (mom.max - mom.opt) + "-" + (mom.opt - mom.min); 
+
+    }
+
+    public String toString(String prepend) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("[\n");
+        for (int i = startLine; i < endLine; i++) {
+            for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                sb.append(prepend + "\t" + node + ",\n");
+            }
+        }
+        sb.append(prepend + "]");
+        return sb.toString();
     }
 
-    protected abstract int filterActiveList() ;
+    protected abstract int filterActiveNodes() ;
 
     private void calculateBreakPoints(KnuthNode node, KnuthSequence par,
                                       int total) {
index fe52c6b87659f0250a4b27b3aa364838e746f3c8..55465f83dfa8631555e5b145c58dd5cd39dc293f 100644 (file)
 
 package org.apache.fop.layoutmgr;
 
-import java.util.LinkedList;
+import java.util.ArrayList;
 
 /**
  * Represents a list of Knuth elements.
  */
-public class KnuthSequence extends LinkedList {
+public class KnuthSequence extends ArrayList {
     /** Number of elements to ignore at the beginning of the list. */ 
     public int ignoreAtStart = 0;
     /** Number of elements to ignore at the end of the list. */
@@ -64,4 +64,20 @@ public class KnuthSequence extends LinkedList {
             return null;
         }
     }
+
+    public KnuthElement getLast() {
+        int idx = size();
+        if (idx == 0) {
+            return null; 
+        }
+        return (KnuthElement) get(idx - 1);
+    }
+
+    public KnuthElement removeLast() {
+        int idx = size();
+        if (idx == 0) {
+            return null; 
+        }
+        return (KnuthElement) remove(idx - 1);
+    }
 }
index b0c59ddb7458abef34d11f0058f3406ceb574e1a..d611446b80e7559c827843958e3d9d00722afe31 100644 (file)
@@ -415,55 +415,45 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             return super.findBreakingPoints(par, lineWidth, threshold, force, hyphenationAllowed);
         }
 
-        protected int filterActiveList() {
+        protected int filterActiveNodes() {
             KnuthNode bestActiveNode = null;
 
             if (pageAlignment == EN_JUSTIFY) {
-                // leave all active nodes in the activeList
-                // and find the optimum line number
-                ListIterator activeListIterator = activeList.listIterator();
-                KnuthNode tempNode = null;
-                //System.out.println("LBA.filterActiveList> " + activeList.size() + " layouts");
-                while (activeListIterator.hasNext()) {
-                    tempNode = (KnuthNode)activeListIterator.next();
-                    //System.out.println("                      + lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
-                    if (bestActiveNode == null
-                        || tempNode.totalDemerits < bestActiveNode.totalDemerits) {
-                        bestActiveNode = tempNode;
+                // leave all active nodes and find the optimum line number
+                //System.out.println("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
+                for (int i = startLine; i < endLine; i++) {
+                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                        //System.out.println("                       + lines = " + node.line + " demerits = " + node.totalDemerits);
+                        bestActiveNode = compareNodes(bestActiveNode, node);
                     }
                 }
 
-                // scan activeList once again and remove some nodes
-                activeListIterator = activeList.listIterator();
-                tempNode = null;
+                // scan the node set once again and remove some nodes
                 //System.out.println("LBA.filterActiveList> layout selection");
-                while (activeListIterator.hasNext()) {
-                    tempNode = (KnuthNode)activeListIterator.next();
-                    //if (Math.abs(tempNode.line - bestActiveNode.line) > maxDiff) {
-                    //if (false) {
-                    if (tempNode.line != bestActiveNode.line
-                        && tempNode.totalDemerits > MAX_DEMERITS) {
-                        //System.out.println("                    XXX lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
-                        activeListIterator.remove();
-                    } else {
-                        //System.out.println("                     ok lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
+                for (int i = startLine; i < endLine; i++) {
+                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                        //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
+                        //if (false) {
+                        if (node.line != bestActiveNode.line
+                            && node.totalDemerits > MAX_DEMERITS) {
+                            //System.out.println("                     XXX lines = " + node.line + " demerits = " + node.totalDemerits);
+                            removeNode(i, node);
+                        } else {
+                            //System.out.println("                      ok lines = " + node.line + " demerits = " + node.totalDemerits);
+                        }
                     }
                 }
             } else {
-                // leave only bestActiveNode in the activeList
-                KnuthNode tempNode = null;
-                //System.out.println("LBA.filterActiveList> " + activeList.size() + " layouts");
-                while (activeList.size() > 0) {
-                    tempNode = (KnuthNode)activeList.removeFirst();
-                    //System.out.println("                      + lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
-                    if (bestActiveNode == null
-                        || tempNode.totalDemerits < bestActiveNode.totalDemerits) {
-                        bestActiveNode = tempNode;
+                // leave only the active node with fewest total demerits
+                for (int i = startLine; i < endLine; i++) {
+                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                        bestActiveNode = compareNodes(bestActiveNode, node);
+                        if (node != bestActiveNode) {
+                            removeNode(i, node);
+                        }
                     }
                 }
-                activeList.add(bestActiveNode);
             }
-            //System.out.println("                      best " + bestActiveNode.line);
             return bestActiveNode.line;
         }
     }
index 463e0d0e4ad78607d4ea5001f97ccf7a9bbfcf52..766ca99c802d7069f8808fcaa5a0fd0e475d43df 100644 (file)
@@ -64,21 +64,17 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                 bestActiveNode.position, ratio, difference));
     }
 
-    protected int filterActiveList() {
-        // leave only bestActiveNode in the activeList
-        KnuthNode tempNode = null;
+    protected int filterActiveNodes() {
+        // leave only the active node with fewest total demerits
         KnuthNode bestActiveNode = null;
-        System.out.println("PBA.filterActiveList> " + activeList.size() + " possibilita'");
-        while (activeList.size() > 0) {
-            tempNode = (KnuthNode)activeList.removeFirst();
-            System.out.println("                      + linee= " + tempNode.line + " demeriti= " + tempNode.totalDemerits);
-            if (bestActiveNode == null
-                || tempNode.totalDemerits < bestActiveNode.totalDemerits) {
-                bestActiveNode = tempNode;
+        for (int i = startLine; i < endLine; i++) {
+            for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                bestActiveNode = compareNodes(bestActiveNode, node);
+                if (node != bestActiveNode) {
+                    removeNode(i, node);
+                }
             }
         }
-        activeList.add(bestActiveNode);
-        System.out.println("                      migliore " + bestActiveNode.line);
         return bestActiveNode.line;
     }