/*
 * $Id:$
 * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
 * 
 * http://izpack.org/
 * http://izpack.codehaus.org/
 * 
 * Copyright 2006 Klaus Bartz
 * 
 * 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.
 */
package com.izforge.izpack.gui;

import com.izforge.izpack.panels.PathSelectionPanel;
import com.izforge.izpack.util.Log;
import com.izforge.izpack.util.MultiLineLabel;

import javax.swing.*;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.util.ArrayList;

/**
 * This is a special layout manager for IzPanels.
 *
 * @author Klaus Bartz
 */
public class IzPanelLayout implements LayoutManager, LayoutManager2, LayoutConstants
{

    /**
     * holds all the components and layout constraints.
     */
    private ArrayList<ArrayList<IzPanelConstraints>> components = new ArrayList<ArrayList<IzPanelConstraints>>();

    /**
     * Maximum rows to handle symbolic values like NEXT_ROW in constraints.
     */
    private int currentYPos = 0;

    /**
     * Current column to handle symbolic values like NEXT_COLUMN in constraints.
     */
    private int currentXPos = -1;

    /**
     * Dimension object with prefered size. Will be computed new if invalidateLayout will be called.
     */
    private Dimension prefLayoutDim;

    private Dimension oldParentSize;

    private Insets oldParentInsets;

    private int columnFillOutRule;

    private double[] overallYStretch = {-1.0, 0.0};

    protected static int[] DEFAULT_Y_GAPS = {-1, 0, 5, 5, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5, 15, 12,
            9, 6, 3, 0};

    protected static int[] DEFAULT_X_GAPS = {-1, 0, 0, 0, 0, 0, 10, 10, 10, 10, 10, 10, 10, 15,
            12, 9, 6, 3, 0};

    protected static int[] DEFAULT_X_ALIGNMENT = {LEFT, LEFT, LEFT, LEFT};

    protected static int[] DEFAULT_Y_ALIGNMENT = {CENTER, CENTER, CENTER, CENTER};

    /**
     * Array with some default constraints.
     */
    private static IzPanelConstraints DEFAULT_CONSTRAINTS[] = {
            // Default constraints for labels.
            new IzPanelConstraints(DEFAULT_LABEL_ALIGNMENT, DEFAULT_LABEL_ALIGNMENT, NEXT_COLUMN,
                    CURRENT_ROW, 1, 1, AUTOMATIC_GAP, AUTOMATIC_GAP, 0.0, 0.0),
            // Default constraints for text fields.
            new IzPanelConstraints(DEFAULT_TEXT_ALIGNMENT, DEFAULT_TEXT_ALIGNMENT, NEXT_COLUMN,
                    CURRENT_ROW, 1, 1, AUTOMATIC_GAP, AUTOMATIC_GAP, 0.0, 0.0),
            // Default constraints for other controls using two columns if possible.
            new IzPanelConstraints(DEFAULT_CONTROL_ALIGNMENT, DEFAULT_CONTROL_ALIGNMENT,
                    NEXT_COLUMN, CURRENT_ROW, 1, 1, AUTOMATIC_GAP, AUTOMATIC_GAP, 0.0, 0.0),
            // Default constraints for full line controls.
            new IzPanelConstraints(DEFAULT_LABEL_ALIGNMENT, DEFAULT_LABEL_ALIGNMENT, 0, NEXT_ROW,
                    Byte.MAX_VALUE, Byte.MAX_VALUE, AUTOMATIC_GAP, AUTOMATIC_GAP,
                    FULL_LINE_STRETCH, 0.0),
            // Default constraints for constraints for controls/container which are variable in x
            // and y dimension.
            new IzPanelConstraints(DEFAULT_LABEL_ALIGNMENT, DEFAULT_LABEL_ALIGNMENT, 0, NEXT_ROW,
                    Byte.MAX_VALUE, Byte.MAX_VALUE, AUTOMATIC_GAP, AUTOMATIC_GAP,
                    FULL_LINE_STRETCH, FULL_COLUMN_STRETCH),
            // Default constraints for x filler.
            new IzPanelConstraints(DEFAULT_LABEL_ALIGNMENT, DEFAULT_LABEL_ALIGNMENT, NEXT_COLUMN,
                    CURRENT_ROW, 1, 1, 0, 0, 0.0, 0.0),
            // Default constraints for y filler.
            new IzPanelConstraints(DEFAULT_LABEL_ALIGNMENT, DEFAULT_LABEL_ALIGNMENT, 0, NEXT_ROW,
                    1, 1, 0, 0, 0.0, 0.0),
            // Default constraints for other controls using the full line.
            new IzPanelConstraints(DEFAULT_CONTROL_ALIGNMENT, DEFAULT_CONTROL_ALIGNMENT, 0,
                    NEXT_ROW, Byte.MAX_VALUE, Byte.MAX_VALUE, AUTOMATIC_GAP, AUTOMATIC_GAP,
                    FULL_LINE_STRETCH, 0.0),

    };

    /**
     * Anchor to be used for the controls in all panels.
     */
    private static int ANCHOR = CENTER;

    private static int X_STRETCH_TYPE = RELATIVE_STRETCH;

    private static int Y_STRETCH_TYPE = RELATIVE_STRETCH;

    private static double FULL_LINE_STRETCH_DEFAULT = 1.0;

    private static double FULL_COLUMN_STRETCH_DEFAULT = 1.0;

    private static int DEFAULT_TEXTFIELD_LENGTH = 12;

    private static final int[][] GAP_INTERMEDIAER_LOOKUP = {
            {LABEL_GAP, LABEL_TO_TEXT_GAP, LABEL_TO_CONTROL_GAP, LABEL_GAP, LABEL_TO_CONTROL_GAP,
                    LABEL_GAP, LABEL_GAP},
            {TEXT_TO_LABEL_GAP, TEXT_GAP, TEXT_TO_CONTROL_GAP, TEXT_TO_LABEL_GAP,
                    TEXT_TO_CONTROL_GAP, TEXT_GAP, TEXT_GAP},
            {CONTROL_TO_LABEL_GAP, CONTROL_TO_TEXT_GAP, CONTROL_GAP, CONTROL_TO_LABEL_GAP,
                    CONTROL_GAP, CONTROL_GAP, CONTROL_GAP},
            {LABEL_GAP, LABEL_TO_TEXT_GAP, LABEL_TO_CONTROL_GAP, LABEL_GAP, LABEL_TO_CONTROL_GAP,
                    LABEL_GAP, LABEL_GAP},
            {CONTROL_TO_LABEL_GAP, CONTROL_TO_TEXT_GAP, CONTROL_GAP, CONTROL_TO_LABEL_GAP,
                    CONTROL_GAP, CONTROL_GAP, CONTROL_GAP},
            {NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP},
            {NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP},
            {NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP, NO_GAP}};

    /**
     * Default constructor
     */
    public IzPanelLayout()
    {
        this(NO_FILL_OUT_COLUMN);
    }

    /**
     * Creates an layout manager which consider the given column fill out rule. Valid rules are
     * NO_FILL_OUT_COLUMN, FILL_OUT_COLUMN_WIDTH, FILL_OUT_HEIGHT and FILL_OUT_COLUMN_SIZE
     *
     * @param colFillOutRule
     */
    public IzPanelLayout(int colFillOutRule)
    {
        super();
        columnFillOutRule = colFillOutRule;
    }

    /**
     * Returns the y gap for the given constraint dependant on the next y constraint.
     *
     * @param curConst   constraint of the component for which the gap should be returnd
     * @param nextYConst constraint of the component which is the next in y direction
     * @return the y gap
     */
    private static int getYGap(IzPanelConstraints curConst, IzPanelConstraints nextYConst)
    {

        Class nextClass = (nextYConst != null) ? nextYConst.component.getClass()
                : FillerComponent.class;
        int interId = GAP_INTERMEDIAER_LOOKUP[getIntermediarId(curConst.component.getClass(), null)][getIntermediarId(
                nextClass, null)];
        if (interId < 0)
        {
            interId = -interId;
        }
        return (DEFAULT_Y_GAPS[interId]);

    }

    /**
     * Returns the x gap for the given constraint dependant on the next x constraint.
     *
     * @param curConst   constraint of the component for which the gap should be returnd
     * @param nextXConst constraint of the component which is the next in x direction
     * @return the x gap
     */
    private static int getXGap(IzPanelConstraints curConst, IzPanelConstraints nextXConst)
    {

        Class nextClass = (nextXConst != null) ? nextXConst.component.getClass()
                : FillerComponent.class;
        int interId = GAP_INTERMEDIAER_LOOKUP[getIntermediarId(curConst.component.getClass(), null)][getIntermediarId(
                nextClass, null)];
        if (interId < 0)
        {
            interId = -interId;
        }
        return (DEFAULT_X_GAPS[interId]);

    }

    /**
     * Returns an index depending on the class type. Only for internal use.
     *
     * @param clazz class for which the index should be returned
     * @param comp  component for which the index should be returned
     * @return an index depending on the class type
     */
    private static int getIntermediarId(Class clazz, Component comp)
    {

        if (comp != null)
        {
            if (MultiLineLabel.class.isAssignableFrom(clazz)
                    || LabelFactory.FullLineLabel.class.isAssignableFrom(clazz))
            {
                return (FULL_LINE_COMPONENT_CONSTRAINT);
            }
            if (PathSelectionPanel.class.isAssignableFrom(clazz)
                    || JCheckBox.class.isAssignableFrom(clazz)
                    || JRadioButton.class.isAssignableFrom(clazz))
            {
                return (FULL_LINE_CONTROL_CONSTRAINT);
            }
            if (FillerComponent.class.isAssignableFrom(clazz)
                    || javax.swing.Box.Filler.class.isAssignableFrom(clazz))
            {
                Dimension size = comp.getPreferredSize();
                if (size.height >= Short.MAX_VALUE || size.height <= 0)
                {
                    size.height = 0;
                    comp.setSize(size);
                    return (XDUMMY_CONSTRAINT);
                }
                else if (size.width >= Short.MAX_VALUE || size.width <= 0)
                {
                    size.width = 0;
                    comp.setSize(size);
                    return (YDUMMY_CONSTRAINT);
                }
            }
        }
        if (JScrollPane.class.isAssignableFrom(clazz))
        {
            return (XY_VARIABLE_CONSTRAINT);
        }
        if (JLabel.class.isAssignableFrom(clazz))
        {
            return (LABEL_CONSTRAINT);
        }
        if (JTextComponent.class.isAssignableFrom(clazz))
        {
            return (TEXT_CONSTRAINT);
        }
        if (FillerComponent.class.isAssignableFrom(clazz))
        {
            return (XDUMMY_CONSTRAINT);
        }
        if (javax.swing.Box.Filler.class.isAssignableFrom(clazz))
        {
            return (XDUMMY_CONSTRAINT);
        }
        return (CONTROL_CONSTRAINT); // Other controls.
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String, java.awt.Component)
     */
    public void addLayoutComponent(String name, Component comp)
    {
        // Has to be implemented, but not supported in this class.
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
     */
    public void removeLayoutComponent(Component comp)
    {
        // Has to be implemented, but not supported in this class.
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
     */
    public Dimension minimumLayoutSize(Container parent)
    {
        return preferredLayoutSize(parent);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
     */
    public Dimension preferredLayoutSize(Container parent)
    {
        return (determineSize());
    }

    /**
     * Method which determine minimum with and height of this layout. The size will be stored after
     * cumputing in a class member. With a call to invalidateLayout this will be deleted and at the
     * next call to this method the values are computed again.
     *
     * @return current minimum size
     */
    private Dimension determineSize()
    {
        if (prefLayoutDim == null)
        {
            int width = minimumAllColumnsWidth();
            int height = minimumOverallHeight();
            prefLayoutDim = new Dimension(width, height);
        }
        return (Dimension) (prefLayoutDim.clone());
    }

    /**
     * Returns the number of rows that need to be laid out.
     *
     * @return the number of rows that need to be laid out
     */
    private int rows()
    {
        int maxRows = 0;
        for (Object component : components)
        {
            int curRows = ((ArrayList) component).size();
            if (curRows > maxRows)
            {
                maxRows = curRows;
            }

        }
        return (maxRows);
    }

    /**
     * Returns the number of columns that need to be laid out.
     *
     * @return the number of columns that need to be laid out
     */
    private int columns()
    {
        return (components.size());
    }

    /**
     * Minimum height of all rows.
     *
     * @return minimum height of all rows
     */
    private int minimumOverallHeight()
    {
        int height = 0;

        for (int i = 0; i < rows(); i++)
        {
            height += rowHeight(i);
        }

        return (height);
    }

    /**
     * Measures and returns the minimum height required to render the components in the indicated
     * row.
     *
     * @param row the index of the row to measure
     * @return minimum height of a row
     */
    private int rowHeight(int row)
    {
        int height = 0;
        for (int i = 0; i < components.size(); ++i)
        {
            int curHeight = getCellSize(i, row, null).height;
            if (curHeight > height)
            {
                height = curHeight;
            }
        }
        return (height);
    }

    /**
     * Measures and returns the minimum height required to render the components in the indicated
     * row.
     *
     * @param row              the index of the row to measure
     * @param maxOverallHeight
     * @param minOverallHeight
     * @return minimum height of a row
     */
    private int rowHeight(int row, int minOverallHeight, int maxOverallHeight)
    {
        // First determine minimum row height and whether there is a y stretch or not.
        int height = 0;
        double[] yStretch = getOverallYStretch();
        if (yStretch[0] <= 0.0)
        {
            return (rowHeight(row));
        }
        double maxStretch = 0.0;
        double[] stretchParts = new double[components.size()];
        for (int i = 0; i < components.size(); ++i)
        {
            IzPanelConstraints constraints = getConstraints(i, row);
            double stretch = constraints.getYStretch();
            stretchParts[i] = stretch;
            if (stretch > maxStretch)
            {
                maxStretch = stretch;
            }
            int curHeight = getCellSize(i, row, constraints).height;
            if (curHeight > height)
            {
                height = curHeight;
            }
        }
        if (maxOverallHeight <= minOverallHeight)
        {
            return (height);
        }
        // We have a y stretch and place. Let us disperse it.
        int pixels = (int) (maxOverallHeight - yStretch[1] - minOverallHeight);
        int stretchPart = (int) (pixels * maxStretch);
        if (stretchPart > 0)
        {
            for (int i = 0; i < components.size(); ++i)
            {
                if (stretchParts[i] < 0.00000001)
                {
                    continue;
                }
                IzPanelConstraints constraints = getConstraints(i, row);
                Dimension size = constraints.component.getPreferredSize();
                if (size.height + stretchPart * stretchParts[i] < height)
                {
                    size.height = (int) (height + stretchPart * stretchParts[i]);
                }
                else
                {
                    size.height = height + stretchPart;
                }
                if (constraints.component instanceof JScrollPane)
                {   // This is a hack for text areas which uses word wrap. At tests
                    // they have a preferred width of 100 pixel which breaks the layout.

                    if (((JScrollPane) constraints.component).getViewport().getView() instanceof JTextArea)
                    {
                        if (((JTextArea) ((JScrollPane) constraints.component).getViewport()
                                .getView()).getLineWrap())
                        {
                            size.width = 1000;
                        }
                    }
                }
                constraints.component.setPreferredSize(size);

            }
            height += stretchPart;

        }
        return (height);
    }

    /**
     * Returns the sum of the maximum y stretch of each row.
     *
     * @return the sum of the maximum y stretch of each row
     */
    private double[] getOverallYStretch()
    {
        if (overallYStretch[0] >= 0.0)
        {
            return (overallYStretch); // Stretch already computed.
        }
        overallYStretch[0] = 0.0;
        for (int row = 0; row < rows(); ++row)
        {
            double maxStretch = 0.0;
            double maxGap = 0.0;
            for (int i = 0; i < components.size(); ++i)
            {
                IzPanelConstraints constraints = getConstraints(i, row);
                resolveDefaultSettings(i, row);
                if (constraints.getYStretch() == FULL_COLUMN_STRETCH)
                {
                    constraints.setYStretch(IzPanelLayout.getFullColumnStretch());
                }
                double curStretch = constraints.getYStretch();
                if (curStretch > maxStretch)
                {
                    maxStretch = curStretch;
                }
                double curYGap = constraints.getYGap();
                if (curYGap > maxGap)
                {
                    maxGap = curYGap;
                }
            }
            overallYStretch[0] += maxStretch;
            overallYStretch[1] += maxGap;
        }
        // Modify y stretch depending on the current Y-Stretch type.
        if (overallYStretch[0] > 0.0)
        {
            switch (IzPanelLayout.getYStretchType())
            {
                case RELATIVE_STRETCH:
                    break;
                case ABSOLUTE_STRETCH:
                    overallYStretch[0] = 1.0;
                    break;
                case NO_STRETCH:
                default:
                    overallYStretch[0] = 0.0;
                    break;
            }
        }

        return (overallYStretch);
    }

    /**
     * Measures and returns the minimum size required to render the component in the indicated row
     * and column. The constraints can be null.
     *
     * @param row         the index of the row to measure
     * @param column      the column of the component
     * @param constraints constraints of current component
     * @return size of the given cell
     */
    private Dimension getCellSize(int column, int row, IzPanelConstraints constraints)
    {
        Dimension retval = new Dimension();
        Component component;

        try
        {
            if (constraints == null)
            {
                constraints = getConstraints(column, row);
            }
            if (constraints != null)
            {
                component = constraints.component;
                // Some components returns the needed size with getPreferredSize
                // some with getMinimumSize. Therefore following code...
                Dimension dim = component.getPreferredSize();
                Dimension dim2 = component.getMinimumSize();
                retval.height = (dim.height > dim2.height) ? dim.height : dim2.height;
                retval.width = (dim.width > dim2.width) ? dim.width : dim2.width;
                if (component instanceof JScrollPane)
                { // But at a JScrollPane we have to use the minimum size because
                    // preferred size is the size of the view.
                    retval.height = dim2.height;
                    retval.width = dim2.width;
                }
                if (needsReEvaluation(component))
                {
                    retval.width = 0;
                }

            }
        }
        catch (Throwable exception)
        {
            // ----------------------------------------------------
            // we might get an exception if one of the array list is
            // shorter, because we index out of bounds. If there
            // is nothing there then the height is 0, nothing
            // further to worry about!
            // ----------------------------------------------------
        }

        return (retval);
    }

    /**
     * Returns the minimum width of the column requested. This contains not the gaps.
     *
     * @param column the columns to measure
     * @return the minimum width required to fit the components in this column
     */
    private int minimumColumnWidth(int column)
    {
        int maxWidth = 0;
        Dimension[] cs = new Dimension[rows()];
        for (int i = 0; i < rows(); ++i)
        {
            IzPanelConstraints constraints = getConstraints(column, i);
            cs[i] = getCellSize(column, i, constraints);
            if (constraints.getXWeight() <= 1)
            {
                if (maxWidth < cs[i].width)
                {
                    maxWidth = cs[i].width;
                }
            }
        }
        if (maxWidth == 0)
        {
            for (int i = 0; i < rows(); ++i)
            {
                if (maxWidth < cs[i].width)
                {
                    maxWidth = cs[i].width;
                }
            }
        }
        return (maxWidth);
    }

    /**
     * Returns the minimum width needed by all columns.
     *
     * @return the minimum width needed by all columns
     */
    private int minimumAllColumnsWidth()
    {
        int width = 0;
        for (int i = 0; i < this.components.size(); ++i)
        {
            width += minimumColumnWidth(i);
        }
        return (width);
    }

    /**
     * Returns the constraint object of the component at the given place.
     *
     * @param col column of the component
     * @param row row of the component
     * @return the constraint object of the component at the given place
     */
    private IzPanelConstraints getConstraints(int col, int row)
    {
        if (col >= columns() || row >= rows())
        {
            return (null);
        }
        Object obj = components.get(col);
        if (obj != null && obj instanceof ArrayList)
        {
            try
            {
                obj = (components.get(col)).get(row);
            }
            catch (Throwable t)
            {
                obj = null;
            }
            if (obj != null)
            {
                return ((IzPanelConstraints) obj);
            }
            // no constraints is possible if no valid component
            // was added under this component.
            // Put dummy components into the array.

            ArrayList<IzPanelConstraints> colA = components.get(col);
            for (int curRow = colA.size(); row >= curRow; ++curRow)
            {

                IzPanelConstraints currentConst = IzPanelLayout
                        .getDefaultConstraint(XDUMMY_CONSTRAINT);
                currentConst.setXPos(col);
                currentConst.setYPos(curRow);
                currentConst.component = new FillerComponent();
                try
                {
                    (components.get(col)).add(row, currentConst);
                }
                catch (Throwable t)
                {
                    return (null);
                }
            }
            return (getConstraints(col, row));
        }
        return (null);
    }

    private int getAdaptedXPos(int xpos, int curWidth, Dimension curDim,
                               IzPanelConstraints currentConst)
    {
        int adaptedXPos = xpos;// currentConst.getXGap();
        if ((columnFillOutRule & FILL_OUT_COLUMN_WIDTH) > 0)
        {
            return (adaptedXPos);
        }
        switch (currentConst.getXCellAlignment())
        {
            case LEFT:
                break;
            case RIGHT:
                adaptedXPos += curWidth - curDim.width;
                break;
            case CENTER:
            default:
                adaptedXPos += (curWidth - curDim.width) / 2;
                break;

        }
        return (adaptedXPos);
    }

    private int getAdaptedYPos(int ypos, int curHeight, Dimension curDim,
                               IzPanelConstraints currentConst)
    {
        int adaptedYPos = ypos;// + currentConst.getYGap();
        if ((columnFillOutRule & FILL_OUT_COLUMN_HEIGHT) > 0)
        {
            return (adaptedYPos);
        }
        int height = curDim.height;
        switch (currentConst.getYCellAlignment())
        {
            case TOP:
                break;
            case BOTTOM:
                adaptedYPos += curHeight - height;
                break;
            case CENTER:
            default:
                adaptedYPos += (curHeight - height) / 2;
                break;

        }
        if (adaptedYPos < ypos)
        {
            return (ypos);
        }
        return (adaptedYPos);
    }

    private void resolveDefaultSettings(int col, int row)
    {
        IzPanelConstraints currentConst = getConstraints(col, row);
        IzPanelConstraints nextYConst = getConstraints(col, row + 1);
        IzPanelConstraints nextXConst = getConstraints(col + 1, row);
        if (currentConst == null)
        {
            return;
        }
        int gap = currentConst.getYGap();
        if (gap == AUTOMATIC_GAP)
        { // Automatic gap; determine now.
            currentConst.setYGap(getYGap(currentConst, nextYConst));
        }
        else if (gap < 0)
        {
            currentConst.setYGap(DEFAULT_Y_GAPS[-gap]);
        }
        gap = currentConst.getXGap();
        if (gap == AUTOMATIC_GAP)
        { // Automatic gap; determine now.
            currentConst.setXGap(getXGap(currentConst, nextXConst));
        }
        else if (gap < 0)
        {
            currentConst.setXGap(DEFAULT_X_GAPS[-gap]);
        }

        if (currentConst.getXCellAlignment() < 0)
        {
            currentConst.setXCellAlignment(DEFAULT_X_ALIGNMENT[-currentConst.getXCellAlignment()]);
        }
        if (currentConst.getYCellAlignment() < 0)
        {
            currentConst.setYCellAlignment(DEFAULT_Y_ALIGNMENT[-currentConst.getYCellAlignment()]);
        }

    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
     */
    public void layoutContainer(Container parent)
    {
        if (!needNewLayout(parent))
        {
            fastLayoutContainer(parent);
            return;
        }
        prefLayoutDim = null;
        preferredLayoutSize(parent);
        Dimension realSizeDim = parent.getSize();
        Log.getInstance().addDebugMessage("IzPanelLayout.layoutContainer parent size: {0}",
                new String[]{parent.getSize().toString()}, "LayoutTrace", null);
        Insets insets = parent.getInsets();

        int rowHeight = 0;
        int onceAgain = 0;
        int[] generellOffset = new int[]{0, 0};
        // int generellYOffset = 0;
        // int generellXOffset = 0;
        int maxWidth = 0;
        int overallHeight = 0;
        int anchorNeedsReEval = 0;
        Rectangle curRect = new Rectangle();
        int minOverallHeight = minimumOverallHeight();
        int maxOverallHeight = realSizeDim.height - insets.top - insets.bottom;
        while (anchorNeedsReEval < 2)
        {
            int ypos = insets.top;

            int row = 0;
            int minWidth = 0xffff;
            int minHeight = 0xffff;
            maxWidth = 0;
            overallHeight = 0;
            while (row < rows())
            {
                int outerRowHeight = 0;
                int xpos = insets.left;
                // rowHeight = rowHeight(row, minOverallHeight, maxOverallHeight);
                int col = 0;
                IzPanelConstraints[] colConstraints = new IzPanelConstraints[columns()];
                int usedWidth = 0;
                Dimension curDim;
                while (col < columns())
                {
                    if (col == 0)
                    {
                        rowHeight = rowHeight(row, minOverallHeight, maxOverallHeight);
                    }
                    IzPanelConstraints currentConst = getConstraints(col, row);
                    colConstraints[col] = currentConst;
                    Component currentComp = currentConst.component;
                    curDim = currentComp.getPreferredSize();
                    int curWidth = minimumColumnWidth(col);
                    col++;
                    if (currentConst.getXWeight() > 1)
                    {
                        int weight = currentConst.getXWeight();
                        while (weight > 1 && col < columns())
                        {
                            colConstraints[col] = getConstraints(col, row);
                            if (!(colConstraints[col].component instanceof FillerComponent))
                            {
                                break;
                            }
                            curWidth += minimumColumnWidth(col);
                            col++;
                            weight--;
                        }
                    }
                    // width known
                    int adaptedXPos = getAdaptedXPos(xpos, curWidth, curDim, currentConst);
                    int adaptedYPos = getAdaptedYPos(ypos, rowHeight, curDim, currentConst);
                    // currentComp.setBounds(adaptedXPos + generellOffset[0], adaptedYPos
                    // + currentConst.getYGap() + generellOffset[1], curWidth, rowHeight);
                    // + generellOffset[1], curWidth, rowHeight);
                    int useWidth = curDim.width;
                    int useHeight = curDim.height;
                    if ((columnFillOutRule & FILL_OUT_COLUMN_WIDTH) > 0)
                    {
                        useWidth = curWidth;
                    }
                    if ((columnFillOutRule & FILL_OUT_COLUMN_HEIGHT) > 0)
                    {
                        useHeight = rowHeight;
                    }
                    if (curWidth < useWidth)
                    {
                        useWidth = curWidth;
                    }
                    // First setBounds using computed offsets and the size which exist previous.
                    currentComp.setBounds(adaptedXPos + generellOffset[0], adaptedYPos
                            + generellOffset[1], useWidth, useHeight);
                    currentComp.getBounds(curRect);
                    if (curRect.height > 100)
                    {
                        curRect.height = useHeight;
                    }
                    if (!(currentComp instanceof FillerComponent))
                    {
                        if (curRect.x < minWidth)
                        {
                            minWidth = curRect.x;
                        }
                        if (curRect.y < minHeight)
                        {
                            minHeight = curRect.y;
                        }
                    }
                    int curMax = (int) curRect.getMaxX();
                    if (curMax - minWidth > maxWidth)
                    {
                        maxWidth = curMax - minWidth;
                    }
                    curMax = (int) curRect.getMaxY();
                    currentConst.setBounds(curRect);
                    if (curMax - minHeight > overallHeight)
                    {
                        overallHeight = curMax - minHeight;
                    }
                    // xpos += currentComp.getSize().width + currentConst.getXGap();
                    xpos += curWidth + currentConst.getXGap();
                    usedWidth += curWidth;
                    if (outerRowHeight < rowHeight + currentConst.getYGap())
                    {
                        outerRowHeight = rowHeight + currentConst.getYGap();
                    }
                }
                // Now we have made a row, but may be there are place across or/and a component
                // needs a reevaluation.
                double rowStretch = 0.0;
                int i;
                // Determine hole stretch of this row.
                for (i = 0; i < colConstraints.length; ++i)
                {
                    if (colConstraints[i].getXStretch() == FULL_LINE_STRETCH)
                    {
                        colConstraints[i].setXStretch(IzPanelLayout.getFullLineStretch());
                    }
                    rowStretch += colConstraints[i].getXStretch();
                }
                // Modify rowStretch depending on the current X-Stretch type.
                if (rowStretch > 0.0)
                {
                    switch (IzPanelLayout.getXStretchType())
                    {
                        case RELATIVE_STRETCH:
                            break;
                        case ABSOLUTE_STRETCH:
                            rowStretch = 1.0;
                            break;
                        case NO_STRETCH:
                        default:
                            rowStretch = 0.0;
                            break;
                    }
                }
                if (realSizeDim.width - insets.right != xpos && rowStretch > 0.0)
                { // Compute only if there is space to share and at least one control should be
                    // stretched.
                    int pixel = realSizeDim.width - insets.right - xpos; // How many pixel we
                    // can use for stretching.
                    int offset = 0;
                    int oldOnceAgain = onceAgain;
                    for (i = 0; i < colConstraints.length; ++i)
                    {
                        int curPixel = (int) ((colConstraints[i].getXStretch() / rowStretch) * pixel);

                        Rectangle curBounds = colConstraints[i].component.getBounds();
                        // The width of some components differ from time to time. E.g. a JScrollPane
                        // with a JEditorPane as viewport has sometimes the minimum column width and
                        // some times the width of the scroll bar. Therefore we use the minimum
                        // column width.
                        int curWidth = this.minimumColumnWidth(i);
                        if (curBounds.width < curWidth)
                        {
                            curBounds.width = curWidth;
                        }
                        int newWidth = curPixel + curBounds.width;
                        Log
                                .getInstance()
                                .addDebugMessage(
                                        "IzPanelLayout.layoutContainer resize bounds for {2}|{3} old width {0} new width {1}",
                                        new String[]{Integer.toString(curBounds.width),
                                                Integer.toString(newWidth),
                                                Integer.toString(row), Integer.toString(i)},
                                        "LayoutTrace", null);
                        colConstraints[i].component.setBounds(curBounds.x + offset, curBounds.y,
                                newWidth, curBounds.height);
                        colConstraints[i].component.getBounds(curRect);
                        if (curRect.x > 0 && curRect.x < minWidth)
                        {
                            minWidth = curRect.x;
                        }
                        if (curRect.y > 0 && curRect.y < minHeight)
                        {
                            minHeight = curRect.y;
                        }
                        int curMax = (int) curRect.getMaxX();
                        if (curMax - minWidth > maxWidth)
                        {
                            maxWidth = curMax - minWidth;
                        }
                        curMax = (int) curRect.getMaxY();
                        colConstraints[i].setBounds(curRect);

                        if (curMax - minHeight > overallHeight)
                        {
                            overallHeight = curMax - minHeight;
                        }

                        Log
                                .getInstance()
                                .addDebugMessage(
                                        "IzPanelLayout.layoutContainer resize bounds for {2}|{3} ({0}): {1}",
                                        new String[]{
                                                colConstraints[i].component.getClass().getName(),
                                                curRect.toString(), Integer.toString(row),
                                                Integer.toString(i)}, "LayoutTrace", null);

                        Log
                                .getInstance()
                                .addDebugMessage(
                                        "IzPanelLayout.layoutContainer resize bounds for {2}|{3}: maxWidth = {0} maxHeight = {1}",
                                        new String[]{
                                                Integer.toString(maxWidth),
                                                Integer.toString(overallHeight), Integer.toString(row),
                                                Integer.toString(i)}, "LayoutTrace", null);

                        offset += curPixel;
                        if (needsReEvaluation(colConstraints[i].component))
                        {
                            if (oldOnceAgain == onceAgain)
                            {
                                onceAgain++;
                            }
                        }
                    }

                }
                // Seems so that height has changed. Reevaluate only one time else it is possible
                // to go in a endless loop.

                if (onceAgain == 1)
                {
                    continue;
                }
                onceAgain = 0;
                ypos += outerRowHeight;
                row++;
            }
            anchorNeedsReEval += resolveGenerellOffsets(generellOffset, realSizeDim, insets,
                    maxWidth, overallHeight);

        }
    }

    private void fastLayoutContainer(Container parent)
    {
        for (int row = 0; row < rows(); ++row)
        {
            for (int col = 0; col < columns(); ++col)
            {
                IzPanelConstraints currentConst = getConstraints(col, row);
                if (currentConst != null)
                {
                    Log.getInstance().addDebugMessage("IzPanelLayout.fastLayoutContainer bounds: {0}",
                            new String[]{currentConst.getBounds().toString()}, "LayoutTrace", null);
                    currentConst.component.setBounds(currentConst.getBounds());
                }

            }

        }
    }

    private boolean needNewLayout(Container parent)
    {
        Dimension ops = oldParentSize;
        Insets opi = oldParentInsets;
        oldParentSize = parent.getSize();
        oldParentInsets = parent.getInsets();
        if (opi == null)
        {
            return (true);
        }
        if (ops.equals(parent.getSize()) && opi.equals(parent.getInsets()))
        {
            return (false);
        }
        return (true);

    }

    private int resolveGenerellOffsets(int[] generellOffset, Dimension realSizeDim, Insets insets,
                                       int maxWidth, int overallHeight)
    {
        int retval = 1;
        switch (getAnchor())
        {
            case CENTER:
                generellOffset[0] = (realSizeDim.width - insets.left - insets.right - maxWidth) / 2;
                generellOffset[1] = (realSizeDim.height - insets.top - insets.bottom - overallHeight) / 2;
                break;
            case WEST:
                generellOffset[0] = 0;
                generellOffset[1] = (realSizeDim.height - insets.top - insets.bottom - overallHeight) / 2;
                break;
            case EAST:
                generellOffset[0] = realSizeDim.width - insets.left - insets.right - maxWidth;
                generellOffset[1] = (realSizeDim.height - insets.top - insets.bottom - overallHeight) / 2;
                break;
            case NORTH:
                generellOffset[0] = (realSizeDim.width - insets.left - insets.right - maxWidth) / 2;
                generellOffset[1] = 0;
                break;
            case SOUTH:
                generellOffset[0] = (realSizeDim.width - insets.left - insets.right - maxWidth) / 2;
                generellOffset[1] = realSizeDim.height - insets.top - insets.bottom - overallHeight;
                break;
            case NORTH_WEST:
                generellOffset[0] = 0;
                generellOffset[1] = 0;
                retval = 2;
                break;
            case NORTH_EAST:
                generellOffset[0] = realSizeDim.width - insets.left - insets.right - maxWidth;
                generellOffset[1] = 0;
                break;
            case SOUTH_WEST:
                generellOffset[0] = 0;
                generellOffset[1] = realSizeDim.height - insets.top - insets.bottom - overallHeight;
                break;
            case SOUTH_EAST:
                generellOffset[0] = realSizeDim.width - insets.left - insets.right - maxWidth;
                generellOffset[1] = realSizeDim.height - insets.top - insets.bottom - overallHeight;
                break;

        }
        if (generellOffset[0] < 0)
        {
            generellOffset[0] = 0;
        }
        if (generellOffset[1] < 0)
        {
            generellOffset[1] = 0;
        }
        return (retval);
    }

    /**
     * Returns whether the type of component needs potential a reevaluation or not.
     *
     * @param comp component to check
     * @return whether the type of component needs potential a reevaluation or not
     */
    private boolean needsReEvaluation(Component comp)
    {
        if ((comp instanceof com.izforge.izpack.util.MultiLineLabel)
                || (comp instanceof com.izforge.izpack.panels.PathSelectionPanel))
        {
            return (true);
        }
        return (false);
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager2#getLayoutAlignmentX(java.awt.Container)
     */
    public float getLayoutAlignmentX(Container target)
    {
        return 0;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager2#getLayoutAlignmentY(java.awt.Container)
     */
    public float getLayoutAlignmentY(Container target)
    {
        return 0;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager2#invalidateLayout(java.awt.Container)
     */
    public void invalidateLayout(Container target)
    {
        // prefLayoutDim = null;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager2#maximumLayoutSize(java.awt.Container)
     */
    public Dimension maximumLayoutSize(Container target)
    {
        return (minimumLayoutSize(target));
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.awt.LayoutManager2#addLayoutComponent(java.awt.Component, java.lang.Object)
     */
    public void addLayoutComponent(Component comp, Object constraints)
    {
        if (comp == null)
        {
            throw new NullPointerException("component has to be not null");
        }
        IzPanelConstraints cc;
        if (!(constraints instanceof IzPanelConstraints))
        {
            if ((comp instanceof FillerComponent)
                    && ((FillerComponent) comp).getConstraints() != null)
            {
                cc = (IzPanelConstraints) ((FillerComponent) comp).getConstraints().clone();
            }
            else
            {
                cc = (IzPanelConstraints) IzPanelLayout.DEFAULT_CONSTRAINTS[getIntermediarId(comp
                        .getClass(), comp)].clone();
            }
            if (NEXT_LINE.equals(constraints))
            {
                cc.setXPos(0);
                cc.setYPos(NEXT_ROW);
            }
        }
        else
        {
            cc = (IzPanelConstraints) ((IzPanelConstraints) constraints).clone();
        }
        cc.component = comp;
        int i;
        // Modify positions if constraint value is one of the symbolic ints.
        int yPos = cc.getYPos();
        if (yPos == LayoutConstants.NEXT_ROW)
        {
            yPos = currentYPos + 1;
        }
        if (yPos == LayoutConstants.CURRENT_ROW)
        {
            yPos = currentYPos;
        }
        cc.setYPos(yPos);
        int xPos = cc.getXPos();
        if (xPos == LayoutConstants.NEXT_COLUMN)
        {
            xPos = currentXPos + 1;
        }
        if (xPos == LayoutConstants.CURRENT_COLUMN)
        {
            xPos = currentXPos;
        }
        cc.setXPos(xPos);
        // Now we know real x and y position. If needed, expand array or
        // array of array.
        int perfCol = cc.getXWeight() < Byte.MAX_VALUE ? cc.getXWeight() : 1;
        if (components.size() < cc.getXPos() + perfCol)
        {
            for (i = components.size() - 1; i < cc.getXPos() + perfCol - 1; ++i)
            {
                components.add(new ArrayList<IzPanelConstraints>());
            }
        }
        IzPanelConstraints curConst = cc;
        for (int j = 0; j < perfCol; ++j)
        {
            ArrayList<IzPanelConstraints> xComp = components.get(xPos);
            if (xComp.size() < yPos)
            {
                for (i = xComp.size() - 1; i < yPos - 1; ++i)
                {
                    IzPanelConstraints dc = getDefaultConstraint(XDUMMY_CONSTRAINT);
                    dc.component = new FillerComponent();
                    xComp.add(dc);

                }
            }

            xComp.add(yPos, curConst);
            if (j < perfCol - 1)
            {
                curConst = getDefaultConstraint(XDUMMY_CONSTRAINT);
                curConst.component = new FillerComponent();
                xPos++;
            }
        }
        int xcsize = (components.get(xPos)).size() - 1;
        if (currentYPos < xcsize)
        {
            currentYPos = xcsize;
        }
        currentXPos = xPos;

    }

    /**
     * Creates an invisible, component with a defined width. This component will be placed in the
     * given cell of an IzPackLayout. If no constraint will be set (the default) a default
     * constraint with NEXT_COLUMN and CURRENT_ROW will be used. This component has the height 0.
     * The height of the row will be determined by other components in the same row.
     *
     * @param width the width of the invisible component
     * @return the component
     */
    public static Component createHorizontalStrut(int width)
    {
        return (new FillerComponent(new Dimension(width, 0)));
    }

    /**
     * Creates an invisible, component with a defined height. This component will be placed in the
     * given cell of an IzPackLayout. If no constraint will be set (the default) a default
     * constraint with column 0 and NEXT_ROW will be used. If the next component also uses NEXT_ROW,
     * this strut goes over the hole width with the declared height. If more components are in the
     * row, the highest of them determines the height of the row. This component has the width 0.
     * The width of a row will be determined by other rows.
     *
     * @param height the height of the invisible component, in pixels >= 0
     * @return the component
     */
    public static Component createVerticalStrut(int height)
    {
        return (new FillerComponent(new Dimension(0, height)));
    }

    /**
     * Returns a filler component which has self the size 0|0. Additional there is a constraints
     * which has the x position 0,y position NEXT_ROW, x and y weight 10, x-stretch 1.0 and the y
     * gap PARAGRAPH_GAP. Add the returned component to the IzPanel. Use NEXT_LINE (or NEXT_ROW in
     * the constraints) for the next added control, else the layout will be confused.
     *
     * @return a filler component with the height of the defined paragraph gap
     */
    public static Component createParagraphGap()
    {
        return (createGap(PARAGRAPH_GAP, VERTICAL));
    }

    /**
     * Returns a filler component which has self the size 0|0. Additional there is a constraints
     * which has the x position 0,y position NEXT_ROW, x and y weight 10, x-stretch 1.0 and the y
     * gap FILLER&lt;given filler number&gt;_GAP. Add the returned component to the IzPanel. Use
     * NEXT_LINE (or NEXT_ROW in the constraints) for the next added control, else the layout will
     * be confused. Attention! fillerNumber determines not directly the size of filler else the
     * filler type. The size can be determined in the config file as modifier in the info section
     * defining from filler1YGap to filler5YGap.
     *
     * @param fillerNumber number of the filler which should be used
     * @return a filler component with the height of the defined paragraph gap
     */
    public static Component createVerticalFiller(int fillerNumber)
    {
        // The symbolic numbers all are negative. Therefore a higher filler needs
        // a less number.
        return (createGap(FILLER1_GAP + 1 - fillerNumber, VERTICAL));
    }

    /**
     * Returns a filler component which has self the size 0|0. Additional there is a constraints
     * which has the position NEXT_COLUMN ,y position CURRENT_ROW, x and y weight 1, stretch 0.0 and
     * the gap FILLER&lt;given filler number&gt;_GAP. Add the returned component to the IzPanel.
     * Attention! fillerNumber determines not directly the size of filler else the filler type. The
     * size can be determined in the config file as modifier in the info section defining from
     * filler1XGap to filler5XGap.
     *
     * @param fillerNumber number of the filler which should be used
     * @return a filler component with the width of the defined paragraph gap
     */
    public static Component createHorizontalFiller(int fillerNumber)
    {
        // The symbolic numbers all are negative. Therefore a higher filler needs
        // a less number.
        return (createGap(FILLER1_GAP + 1 - fillerNumber, HORIZONTAL));
    }

    /**
     * Returns a filler component which has self the size 0|0. Additional there is a constraints
     * which has the gap defined for the given gap type. If direction is HORIZONTAL, the x position
     * is NEXT_COLUMN ,y position CURRENT_ROW, x and y weight 1, stretch 0.0. If direction is
     * VERTICAL, the x position will by 0,y position NEXT_ROW, x and y weight 10, x-stretch 1.0. Add
     * the returned component to the IzPanel. The result will be that a gap will be inserted into
     * the layout at the current place with the width equal to the defined paragraph gap.
     *
     * @param gapType   type of gap to be used
     * @param direction direction to be used
     * @return a filler component with the width of the defined paragraph gap
     */

    public static Component createGap(int gapType, int direction)
    {
        if (direction == HORIZONTAL)
        {
            return (new FillerComponent(new Dimension(0, 0), new IzPanelConstraints(
                    DEFAULT_CONTROL_ALIGNMENT, DEFAULT_CONTROL_ALIGNMENT, NEXT_COLUMN, CURRENT_ROW,
                    1, 1, gapType, 0, 0.0, 0.0)));
        }
        return (new FillerComponent(new Dimension(0, 0), new IzPanelConstraints(
                DEFAULT_CONTROL_ALIGNMENT, DEFAULT_CONTROL_ALIGNMENT, 0, NEXT_ROW, 10, 10, 0,
                gapType, 1.0, 0.0)));
    }

    /**
     * Returns the constraint for the given type. Valid types are declared in the interface
     * <code>LayoutConstraints</code>. Possible are LABEL_CONSTRAINT, TEXT_CONSTRAINT and
     * CONTROL_CONSTRAINT.
     *
     * @param type for which the constraint should be returned
     * @return a copy of the default constraint for the given type
     */
    public static IzPanelConstraints getDefaultConstraint(int type)
    {
        return ((IzPanelConstraints) DEFAULT_CONSTRAINTS[type].clone());
    }

    /**
     * Component which will be used as placeholder if not extern component will be set or as filler
     * for struts.
     *
     * @author Klaus Bartz
     */
    public static class FillerComponent extends Component
    {

        /**  */
        private static final long serialVersionUID = 7270064864038287900L;

        private Dimension size;

        private IzPanelConstraints constraints;

        /**
         * Default constructor creating an filler with the size 0|0.
         */
        public FillerComponent()
        {
            this(new Dimension(0, 0));
        }

        /**
         * Constructor with giving the filler a size.
         *
         * @param size dimension to be used as size for this filler.
         */
        public FillerComponent(Dimension size)
        {
            this(size, null);
        }

        /**
         * Constructor with giving the filler a size and set the constraints.
         *
         * @param size
         * @param constraints
         */
        public FillerComponent(Dimension size, IzPanelConstraints constraints)
        {
            super();
            this.size = size;
            this.constraints = constraints;
        }

        public Dimension getMinimumSize()
        {
            return (Dimension) (size.clone());
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.awt.Component#getPreferredSize()
         */
        public Dimension getPreferredSize()
        {
            return getMinimumSize();
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.awt.Component#getMaximumSize()
         */
        public Dimension getMaximumSize()
        {
            return getMinimumSize();
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.awt.Component#getBounds()
         */
        public Rectangle getBounds()
        {
            return (getBounds(new Rectangle()));
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.awt.Component#getBounds(java.awt.Rectangle)
         */
        public Rectangle getBounds(Rectangle rect)
        {
            Rectangle rv = (rect != null) ? rect : new Rectangle();
            rv.setBounds(0, 0, size.width, size.height);
            return (rv);
        }

        /**
         * Returns the constraints defined for this component. Often this will be null.
         *
         * @return the constraints defined for this component
         */
        public IzPanelConstraints getConstraints()
        {
            return constraints;
        }

        /**
         * Sets the constraints which should be used by this component.
         *
         * @param constraints constraints to be used
         */
        public void setConstraints(IzPanelConstraints constraints)
        {
            this.constraints = constraints;
        }
    }

    /**
     * Returns the anchor constant.
     *
     * @return the anchor constant
     */
    public static int getAnchor()
    {
        return ANCHOR;
    }

    /**
     * Sets the anchor constant.
     *
     * @param anchor symbolic constant to be used
     */
    public static void setAnchor(int anchor)
    {
        ANCHOR = anchor;
    }

    /**
     * Returns the current used type of stretching for the X-direction. Possible values are NO,
     * RELATIVE and ABSOLUTE.
     *
     * @return the current used type of stretching for the X-direction
     */
    public static int getXStretchType()
    {
        return X_STRETCH_TYPE;
    }

    /**
     * Sets the type of stretching to be used for the X-Direction. Possible values are NO, RELATIVE
     * and ABSOLUTE.
     *
     * @param x_stretch constant to be used for stretch type
     */
    public static void setXStretchType(int x_stretch)
    {
        X_STRETCH_TYPE = x_stretch;
    }

    /**
     * Returns the current used type of stretching for the Y-direction. Possible values are NO,
     * RELATIVE and ABSOLUTE.
     *
     * @return the current used type of stretching for the Y-direction
     */
    public static int getYStretchType()
    {
        return Y_STRETCH_TYPE;
    }

    /**
     * Sets the type of stretching to be used for the Y-Direction. Possible values are NO, RELATIVE
     * and ABSOLUTE.
     *
     * @param y_stretch constant to be used for stretch type
     */
    public static void setYStretchType(int y_stretch)
    {
        Y_STRETCH_TYPE = y_stretch;
    }

    /**
     * Returns the value which should be used stretching to a full line.
     *
     * @return the value which should be used stretching to a full line
     */
    public static double getFullLineStretch()
    {
        return FULL_LINE_STRETCH_DEFAULT;
    }

    /**
     * Sets the value which should be used as default for stretching to a full line.
     *
     * @param fullLineStretch value to be used as full line stretching default
     */
    public static void setFullLineStretch(double fullLineStretch)
    {
        FULL_LINE_STRETCH_DEFAULT = fullLineStretch;

    }

    /**
     * Returns the value which should be used stretching to a full column.
     *
     * @return the value which should be used stretching to a full column
     */
    public static double getFullColumnStretch()
    {
        return FULL_COLUMN_STRETCH_DEFAULT;
    }

    /**
     * Sets the value which should be used as default for stretching to a full column.
     *
     * @param fullStretch value to be used as full column stretching default
     */
    public static void setFullColumnStretch(double fullStretch)
    {
        FULL_COLUMN_STRETCH_DEFAULT = fullStretch;

    }

    /**
     * Verifies whether a gap id is valid or not. If the id is less than zero, the sign will be
     * removed. If the id is out of range, an IndexOutOfBoundsException will be thrown. The return
     * value is the verified unsigned id.
     *
     * @param gapId to be verified
     * @return the verified gap id
     */
    public static int verifyGapId(int gapId)
    {
        if (gapId < 0)
        {
            gapId = -gapId;
        }
        if (gapId >= DEFAULT_X_GAPS.length)
        {
            throw new IndexOutOfBoundsException("gapId is not in the default gap container.");
        }
        return (gapId);
    }

    /**
     * Returns the default x gap for the given gap id.
     *
     * @param gapId for which the default x gap should be returned
     * @return the default x gap for the given gap id
     */
    public static int getDefaultXGap(int gapId)
    {
        gapId = verifyGapId(gapId);
        return DEFAULT_X_GAPS[gapId];
    }

    /**
     * Set the gap for the given gap id for the x default gaps.
     *
     * @param gap   to be used as default
     * @param gapId for which the default should be set
     */
    public static void setDefaultXGap(int gap, int gapId)
    {
        gapId = verifyGapId(gapId);
        DEFAULT_X_GAPS[gapId] = gap;
    }

    /**
     * Returns the default y gap for the given gap id.
     *
     * @param gapId for which the default y gap should be returned
     * @return the default x gap for the given gap id
     */
    public static int getDefaultYGap(int gapId)
    {
        gapId = verifyGapId(gapId);
        return DEFAULT_Y_GAPS[gapId];
    }

    /**
     * Set the gap for the given gap id for the y default gaps.
     *
     * @param gap   to be used as default
     * @param gapId for which the default should be set
     */
    public static void setDefaultYGap(int gap, int gapId)
    {
        gapId = verifyGapId(gapId);
        DEFAULT_Y_GAPS[gapId] = gap;
    }

    /**
     * Returns the default length used by textfields.
     *
     * @return the default length used by textfields
     */
    public static int getDefaultTextfieldLength()
    {
        return DEFAULT_TEXTFIELD_LENGTH;
    }

    /**
     * Sets the value for the default length of textfields.
     *
     * @param val to be set as default length for textfields
     */
    public static void setDefaultTextfieldLength(int val)
    {
        DEFAULT_TEXTFIELD_LENGTH = val;
    }

}
