/* * 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. */ /* $Id$ */ package org.apache.fop.layoutmgr.inline; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Line layout possibilities. */ public class LineLayoutPossibilities { /** logger instance */ private static final Log LOG = LogFactory.getLog(LineLayoutPossibilities.class); private final class Possibility { private int lineCount; private double demerits; private List<LineLayoutManager.LineBreakPosition> breakPositions; private Possibility(int lc, double dem) { lineCount = lc; demerits = dem; breakPositions = new java.util.ArrayList<LineLayoutManager.LineBreakPosition>(lc); } private int getLineCount() { return lineCount; } private double getDemerits() { return demerits; } private void addBreakPosition(LineLayoutManager.LineBreakPosition pos) { // Positions are always added with index 0 because // they are created backward, from the last one to // the first one breakPositions.add(0, pos); } private LineLayoutManager.LineBreakPosition getBreakPosition(int i) { return breakPositions.get(i); } } private List possibilitiesList; private List savedPossibilities; private int minimumIndex; private int optimumIndex; private int maximumIndex; private int chosenIndex; private int savedOptLineCount; /** default constructor */ public LineLayoutPossibilities() { possibilitiesList = new java.util.ArrayList(); savedPossibilities = new java.util.ArrayList(); optimumIndex = -1; } /** * Add possibility. * @param ln line number * @param dem demerits */ public void addPossibility(int ln, double dem) { possibilitiesList.add(new Possibility(ln, dem)); if (possibilitiesList.size() == 1) { // first Possibility added minimumIndex = 0; optimumIndex = 0; maximumIndex = 0; chosenIndex = 0; } else { if (dem < ((Possibility)possibilitiesList.get(optimumIndex)).getDemerits()) { optimumIndex = possibilitiesList.size() - 1; chosenIndex = optimumIndex; } if (ln < ((Possibility)possibilitiesList.get(minimumIndex)).getLineCount()) { minimumIndex = possibilitiesList.size() - 1; } if (ln > ((Possibility)possibilitiesList.get(maximumIndex)).getLineCount()) { maximumIndex = possibilitiesList.size() - 1; } } } /** * Save in a different array the computed Possibilities, * so possibilitiesList is ready to store different Possibilities. * @param bSaveOptLineCount true if should save optimum line count */ public void savePossibilities(boolean bSaveOptLineCount) { if (bSaveOptLineCount) { savedOptLineCount = getOptLineCount(); } else { savedOptLineCount = 0; } savedPossibilities = possibilitiesList; possibilitiesList = new java.util.ArrayList(); } /** * Replace the Possibilities stored in possibilitiesList with * the ones stored in savedPossibilities and having the same line number. */ public void restorePossibilities() { int index = 0; while (savedPossibilities.size() > 0) { Possibility restoredPossibility = (Possibility) savedPossibilities.remove(0); if (restoredPossibility.getLineCount() < getMinLineCount()) { // if the line number of restoredPossibility is less than the minimum one, // add restoredPossibility at the beginning of the list possibilitiesList.add(0, restoredPossibility); // update minimumIndex minimumIndex = 0; // shift the other indexes; optimumIndex++; maximumIndex++; chosenIndex++; } else if (restoredPossibility.getLineCount() > getMaxLineCount()) { // if the line number of restoredPossibility is greater than the maximum one, // add restoredPossibility at the end of the list possibilitiesList.add(possibilitiesList.size(), restoredPossibility); // update maximumIndex maximumIndex = possibilitiesList.size() - 1; index = maximumIndex; } else { // find the index of the Possibility that will be replaced while (index < maximumIndex && getLineCount(index) < restoredPossibility.getLineCount()) { index++; } if (getLineCount(index) == restoredPossibility.getLineCount()) { possibilitiesList.set(index, restoredPossibility); } else { // this should not happen LOG.error("LineLayoutPossibilities restorePossibilities()," + " min= " + getMinLineCount() + " max= " + getMaxLineCount() + " restored= " + restoredPossibility.getLineCount()); return; } } // update optimumIndex and chosenIndex if (savedOptLineCount == 0 && getDemerits(optimumIndex) > restoredPossibility.getDemerits() || savedOptLineCount != 0 && restoredPossibility.getLineCount() == savedOptLineCount) { optimumIndex = index; chosenIndex = optimumIndex; } } //log.debug(">> minLineCount = " + getMinLineCount() // + " optLineCount = " + getOptLineCount() + " maxLineCount() = " + getMaxLineCount()); } /** * @param pos a position * @param i an index into posibilities list */ public void addBreakPosition(LineLayoutManager.LineBreakPosition pos, int i) { ((Possibility)possibilitiesList.get(i)).addBreakPosition(pos); } /** @return true if can use more lines */ public boolean canUseMoreLines() { return (getOptLineCount() < getMaxLineCount()); } /** @return true if can use fewer lines */ public boolean canUseLessLines() { return (getMinLineCount() < getOptLineCount()); } /** @return the line count of the minimum index */ public int getMinLineCount() { return getLineCount(minimumIndex); } /** @return the line count of the optimum index */ public int getOptLineCount() { return getLineCount(optimumIndex); } /** @return the line count of the maximum index */ public int getMaxLineCount() { return getLineCount(maximumIndex); } /** @return the line count of the chosen index */ public int getChosenLineCount() { return getLineCount(chosenIndex); } /** * @param i the posibilities list index * @return the line count */ public int getLineCount(int i) { return ((Possibility)possibilitiesList.get(i)).getLineCount(); } /** @return the demerits of the chosen index */ public double getChosenDemerits() { return getDemerits(chosenIndex); } /** * @param i the posibilities list index * @return the demerits */ public double getDemerits(int i) { return ((Possibility)possibilitiesList.get(i)).getDemerits(); } /** @return the possibilities count */ public int getPossibilitiesNumber() { return possibilitiesList.size(); } /** * @param i the break position index * @return the chosen position */ public LineLayoutManager.LineBreakPosition getChosenPosition(int i) { return ((Possibility)possibilitiesList.get(chosenIndex)).getBreakPosition(i); } /** * @param adj the adjustment * @return the adjustment or zero */ public int applyLineCountAdjustment(int adj) { if (adj >= (getMinLineCount() - getChosenLineCount()) && adj <= (getMaxLineCount() - getChosenLineCount()) && getLineCount(chosenIndex + adj) == getChosenLineCount() + adj) { chosenIndex += adj; LOG.debug("chosenLineCount= " + (getChosenLineCount() - adj) + " adjustment= " + adj + " => chosenLineCount= " + getLineCount(chosenIndex)); return adj; } else { // this should not happen! LOG.warn("Cannot apply the desired line count adjustment."); return 0; } } /** print all */ public void printAll() { System.out.println("++++++++++"); System.out.println(" " + possibilitiesList.size() + " possibility':"); for (int i = 0; i < possibilitiesList.size(); i++) { System.out.println(" " + ((Possibility)possibilitiesList.get(i)).getLineCount() + (i == optimumIndex ? " *" : "") + (i == minimumIndex ? " -" : "") + (i == maximumIndex ? " +" : "")); } System.out.println("++++++++++"); } }