org.eclipse.wb.internal.swing.model.layout.gbl.DimensionOperations.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wb.internal.swing.model.layout.gbl.DimensionOperations.java

Source

/*******************************************************************************
 * Copyright (c) 2011 Google, Inc.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Google, Inc. - initial API and implementation
 *******************************************************************************/
package org.eclipse.wb.internal.swing.model.layout.gbl;

import org.eclipse.wb.internal.core.model.clipboard.ClipboardCommand;
import org.eclipse.wb.internal.core.model.clipboard.ComponentClipboardCommand;
import org.eclipse.wb.internal.core.model.property.converter.DoubleConverter;
import org.eclipse.wb.internal.core.model.property.converter.IntegerConverter;
import org.eclipse.wb.internal.core.utils.ast.AstEditor;
import org.eclipse.wb.internal.core.utils.ast.DomGenerics;
import org.eclipse.wb.internal.swing.model.component.ComponentInfo;
import org.eclipse.wb.internal.swing.model.component.ContainerInfo;

import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Expression;

import org.apache.commons.lang.StringUtils;

import java.awt.GridBagLayout;
import java.text.MessageFormat;
import java.util.LinkedList;
import java.util.List;

/**
 * Entry point for operations with {@link DimensionInfo}'s.
 * 
 * @author scheglov_ke
 * @coverage swing.model.layout
 */
public abstract class DimensionOperations<T extends DimensionInfo> {
    protected final AbstractGridBagLayoutInfo m_layout;
    protected final AstEditor m_editor;
    private final String m_sizeField;
    private final String m_weightField;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Constructor
    //
    ////////////////////////////////////////////////////////////////////////////
    public DimensionOperations(AbstractGridBagLayoutInfo layout, String sizeField, String weightField) {
        m_layout = layout;
        m_editor = layout.getEditor();
        m_sizeField = sizeField;
        m_weightField = weightField;
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Access
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return <code>true</code> if this {@link DimensionInfo} has not components in it.
     */
    public abstract boolean isEmpty(int index);

    /**
     * @return the {@link ClipboardCommand} for copying dimensions information.
     */
    public ClipboardCommand getClipboardCommand() {
        LinkedList<T> dimensions = getDimensions();
        final boolean forColumns = this instanceof DimensionOperationsColumn;
        final int count = dimensions.size();
        final int[] sizeArray = new int[count];
        final double[] weightArray = new double[count];
        for (int i = 0; i < count; i++) {
            T dimension = dimensions.get(i);
            sizeArray[i] = dimension.getSize();
            weightArray[i] = dimension.getWeight();
        }
        // create command
        return new ComponentClipboardCommand<ContainerInfo>() {
            private static final long serialVersionUID = 0L;

            @Override
            @SuppressWarnings("unchecked")
            protected void execute(ContainerInfo container) throws Exception {
                AbstractGridBagLayoutInfo layout = (AbstractGridBagLayoutInfo) container.getLayout();
                DimensionOperations<T> operations = (DimensionOperations<T>) (forColumns
                        ? layout.getColumnOperations()
                        : layout.getRowOperations());
                operations.prepare(count - 1, false);
                LinkedList<T> dimensions = operations.getDimensions();
                for (int i = 0; i < count; i++) {
                    T dimension = dimensions.get(i);
                    dimension.setSize(sizeArray[i]);
                    dimension.setWeight(weightArray[i]);
                }
            }
        };
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Operations
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Inserts new empty {@link DimensionInfo} before given index.
     */
    public abstract T insert(int index) throws Exception;

    /**
     * Deletes {@link DimensionInfo} with given index and all components located in it.
     */
    public final void delete(final int index) throws Exception {
        // delete components, update constraints
        m_layout.visitComponents(new IComponentVisitor() {
            public void visit(ComponentInfo component, AbstractGridBagConstraintsInfo constraints)
                    throws Exception {
                int location = getLocation(constraints);
                int size = getSize(constraints);
                if (location == index) {
                    component.delete();
                } else if (location > index) {
                    setLocation(constraints, location - 1);
                } else if (location + size > index) {
                    setSize(constraints, size - 1);
                }
            }
        });
        // remove dimension
        {
            getDimensions().remove(index);
            removeFieldArrayElement(m_sizeField, index);
            removeFieldArrayElement(m_weightField, index);
        }
        // fix gaps
        m_layout.ensureGapInsets();
    }

    /**
     * Deletes {@link ComponentInfo}'s located in {@link DimensionInfo} with given index, but does not
     * delete {@link DimensionInfo} itself.
     */
    public final void clear(final int index) throws Exception {
        m_layout.visitComponents(new IComponentVisitor() {
            public void visit(ComponentInfo component, AbstractGridBagConstraintsInfo constraints)
                    throws Exception {
                if (getLocation(constraints) == index) {
                    component.delete();
                }
            }
        });
    }

    /**
     * Splits {@link DimensionInfo} with given index, i.e. adds new {@link DimensionInfo} with same
     * size/width and spans all components to both {@link DimensionInfo}.
     */
    public final void split(final int index) throws Exception {
        // insert dimension
        insert(index + 1);
        // copy size/weight
        {
            List<T> dimensions = getDimensions();
            T dimension = dimensions.get(index);
            T newDimension = dimensions.get(index + 1);
            newDimension.setSize(dimension.getSize());
            newDimension.setWeight(dimension.getWeight());
        }
        // span components
        m_layout.visitComponents(new IComponentVisitor() {
            public void visit(ComponentInfo component, AbstractGridBagConstraintsInfo constraints)
                    throws Exception {
                int location = getLocation(constraints);
                int size = getSize(constraints);
                if (location + size - 1 == index) {
                    setSize(constraints, size + 1);
                }
            }
        });
    }

    /**
     * Moves {@link DimensionInfo} with given index into target index.
     */
    public void move(int index, final int targetIndex) throws Exception {
        insert(targetIndex);
        final int sourceIndex = targetIndex < index ? index + 1 : index;
        // transfer size/weight into newly inserted dimension
        exchangeFieldArrayElement(m_sizeField, sourceIndex, targetIndex);
        exchangeFieldArrayElement(m_weightField, sourceIndex, targetIndex);
        // exchange dimensions
        {
            List<T> dimensions = getDimensions();
            T sourceDimension = dimensions.get(sourceIndex);
            T targetDimension = dimensions.get(targetIndex);
            dimensions.set(sourceIndex, targetDimension);
            dimensions.set(targetIndex, sourceDimension);
        }
        // move components
        m_layout.visitComponents(new IComponentVisitor() {
            public void visit(ComponentInfo component, AbstractGridBagConstraintsInfo constraints)
                    throws Exception {
                if (getLocation(constraints) == sourceIndex) {
                    moveComponent(component, constraints, targetIndex);
                    setSize(constraints, 1);
                }
            }
        });
        // delete old dimension
        delete(sourceIndex);
        m_layout.ensureGapInsets();
    }

    /**
     * If there are components that span multiple dimensions, and no other "real" components in these
     * dimensions, then removes these excess dimensions.
     */
    public final void normalizeSpanning() throws Exception {
        LinkedList<T> dimensions = getDimensions();
        // prepare filled dimensions
        final boolean[] filledDimensions = new boolean[dimensions.size()];
        m_layout.visitComponents(new IComponentVisitor() {
            public void visit(ComponentInfo component, AbstractGridBagConstraintsInfo constraints)
                    throws Exception {
                int location = getLocation(constraints);
                filledDimensions[location] = true;
            }
        });
        // remove empty dimensions
        for (int index = dimensions.size() - 1; index >= 0; index--) {
            if (!filledDimensions[index]) {
                delete(index);
            }
        }
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Internal operations
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * Cuts dimensions to the size rendered by {@link GridBagLayout}.<br>
     * We don't analyze that there are empty columns/rows ourselves. Instead we just allow
     * {@link GridBagLayout} to this for us and obey it - remove {@link DimensionInfo}'s and cut
     * "weight" arrays.
     * 
     * @return <code>true</code> if any dimension was removed.
     */
    boolean cutToGrid(int renderedSize) throws Exception {
        LinkedList<T> dimensions = getDimensions();
        // cut dimensions
        boolean removedAnyDimension = false;
        while (dimensions.size() > renderedSize) {
            dimensions.removeLast();
            removedAnyDimension = true;
        }
        // cut "weight"
        {
            ArrayInitializer initializer = getFieldArrayInitializer(m_weightField);
            if (initializer != null) {
                while (initializer.expressions().size() > renderedSize) {
                    m_editor.removeArrayElement(initializer, initializer.expressions().size() - 1);
                }
            }
        }
        //
        return removedAnyDimension;
    }

    /**
     * Ensures that {@link DimensionInfo} with given index exists in {@link AbstractGridBagLayoutInfo}
     * , may be inserts/appends new {@link DimensionInfo}'s.
     */
    void prepare(final int index, boolean insert) throws Exception {
        // prepare dimensions
        {
            List<T> dimensions = getDimensions();
            if (insert) {
                addNewDimension(dimensions, index);
            } else {
                while (index >= dimensions.size()) {
                    addNewDimension(dimensions, dimensions.size());
                }
            }
        }
        // move components
        if (insert) {
            m_layout.visitComponents(new IComponentVisitor() {
                public void visit(ComponentInfo component, AbstractGridBagConstraintsInfo constraints)
                        throws Exception {
                    int location = getLocation(constraints);
                    int size = getSize(constraints);
                    if (location >= index) {
                        setLocation(constraints, location + 1);
                    } else if (location + size > index) {
                        setSize(constraints, size + 1);
                    }
                }
            });
        }
    }

    /**
     * Adds new {@link DimensionInfo} into collection, may be updates also size/weight arrays.
     */
    private void addNewDimension(List<T> dimensions, int index) throws Exception {
        T dimension = newDimension();
        dimensions.add(index, dimension);
        // update size/weight arrays
        insertFieldArrayElement(m_sizeField, index, "0");
        insertFieldArrayElement(m_weightField, index, "0.0");
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Internal dimensions
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the all {@link DimensionInfo}'s.
     */
    protected abstract LinkedList<T> getDimensions();

    /**
     * @return the new {@link DimensionInfo} instance.
     */
    protected abstract T newDimension();

    /**
     * Moves {@link ComponentInfo} into given location.
     */
    protected abstract void moveComponent(ComponentInfo component, AbstractGridBagConstraintsInfo constraints,
            int location) throws Exception;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Internal GridBagConstraintsInfo operations
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the x/y location.
     */
    protected abstract int getLocation(AbstractGridBagConstraintsInfo constraints);

    /**
     * @return the width/height size.
     */
    protected abstract int getSize(AbstractGridBagConstraintsInfo constraints);

    /**
     * Sets the x/y location.
     */
    protected abstract void setLocation(AbstractGridBagConstraintsInfo constraints, int location) throws Exception;

    /**
     * Sets the width/height size.
     */
    protected abstract void setSize(AbstractGridBagConstraintsInfo constraints, int size) throws Exception;

    ////////////////////////////////////////////////////////////////////////////
    //
    // Size/weight arrays operations
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the (optional, may be <code>null</code>) element of minimum size field.
     */
    Expression getSizeFieldElement(int index) {
        return getFieldArrayElement(index, m_sizeField);
    }

    /**
     * @return the (optional, may be <code>null</code>) element of weight field.
     */
    Expression getWeightFieldElement(int index) {
        return getFieldArrayElement(index, m_weightField);
    }

    /**
     * Sets the minimum size of {@link DimensionInfo}.
     */
    void setSizeFieldElement(int index, int value) throws Exception {
        String source = IntegerConverter.INSTANCE.toJavaSource(m_layout, value);
        setFieldArrayElement(m_sizeField, "int", "0", index, source);
    }

    /**
     * Sets the weight of {@link DimensionInfo}.
     */
    void setWeightFieldElement(int index, double value) throws Exception {
        String source = DoubleConverter.INSTANCE.toJavaSource(m_layout, value);
        setFieldArrayElement(m_weightField, "double", "0.0", index, source);
    }

    /**
     * Sets the value for element of {@link ArrayInitializer} assigned to field.<br>
     * If there are no assignment to this field, adds assignment.
     */
    private void setFieldArrayElement(String fieldName, String typeName, String emptyElementSource, int index,
            String elementSource) throws Exception {
        ArrayInitializer initializer = getFieldArrayInitializer(fieldName);
        // ensure assignment
        if (initializer == null) {
            String initializerSource = StringUtils.repeat(", " + emptyElementSource, getDimensions().size())
                    .substring(2);
            String fieldSource = MessageFormat.format("new {0}[]'{'{1}'}'", typeName, initializerSource);
            m_layout.addFieldAssignment(fieldName, fieldSource);
            initializer = getFieldArrayInitializer(fieldName);
        }
        // replace element
        m_editor.replaceExpression(DomGenerics.expressions(initializer).get(index), elementSource);
    }

    ////////////////////////////////////////////////////////////////////////////
    //
    // Field arrays operations
    //
    ////////////////////////////////////////////////////////////////////////////
    /**
     * @return the {@link ArrayInitializer} of (optional) field assignment, such as
     *         {@link GridBagLayout#columnWidths}, or <code>null</code>, if no assignments to this
     *         field exists.
     */
    private ArrayInitializer getFieldArrayInitializer(String fieldName) {
        Assignment assignment = m_layout.getFieldAssignment(fieldName);
        if (assignment != null && assignment.getRightHandSide() instanceof ArrayCreation) {
            ArrayCreation arrayCreation = (ArrayCreation) assignment.getRightHandSide();
            return arrayCreation.getInitializer();
        }
        return null;
    }

    /**
     * @return the element of {@link ArrayInitializer} of (optional) field assignment, such as
     *         {@link GridBagLayout#columnWidths}, or <code>null</code>, if no assignments to this
     *         field exists, or given index is greater than size of {@link ArrayInitializer}.
     */
    private Expression getFieldArrayElement(int index, String fieldName) {
        ArrayInitializer initializer = getFieldArrayInitializer(fieldName);
        if (initializer != null && initializer.expressions().size() > index) {
            return DomGenerics.expressions(initializer).get(index);
        }
        return null;
    }

    /**
     * Inserts new element into (optional) field assignment, such as
     * {@link GridBagLayout#columnWidths}. Does nothing, if no assignments to this field exists.
     */
    private void insertFieldArrayElement(String fieldName, int index, String source) throws Exception {
        ArrayInitializer arrayInitializer = getFieldArrayInitializer(fieldName);
        if (arrayInitializer != null) {
            while (arrayInitializer.expressions().size() < index) {
                int i = arrayInitializer.expressions().size();
                m_editor.addArrayElement(arrayInitializer, i, source);
            }
            m_editor.addArrayElement(arrayInitializer, index, source);
        }
    }

    /**
     * Removes element from field assignment, such as {@link GridBagLayout#columnWidths}. Does
     * nothing, if no assignments to this field exists.
     */
    private void removeFieldArrayElement(String fieldName, int index) throws Exception {
        ArrayInitializer arrayInitializer = getFieldArrayInitializer(fieldName);
        if (arrayInitializer != null) {
            m_editor.removeArrayElement(arrayInitializer, index);
        }
    }

    /**
     * Exchanges two elements of field assignment, such as {@link GridBagLayout#columnWidths}. Does
     * nothing, if no assignments to this field exists.
     */
    private void exchangeFieldArrayElement(String fieldName, int index_1, int index_2) throws Exception {
        ArrayInitializer arrayInitializer = getFieldArrayInitializer(fieldName);
        if (arrayInitializer != null) {
            m_editor.exchangeArrayElements(arrayInitializer, index_1, index_2);
        }
    }
}