alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.form.layoutsupport;

import java.awt.*;
import java.beans.*;
import java.util.*;
import java.lang.reflect.Method;

import org.openide.ErrorManager;
import org.openide.nodes.Node;
import org.openide.util.Utilities;

import org.netbeans.modules.form.*;
import org.netbeans.modules.form.codestructure.*;

/**
 * Default implementation of LayoutSupportDelegate interface. This class
 * implements most of general methods of LayoutSupportDelegate and introduces
 * other more specific methods which can be easily customized in subclasses.
 *
 * This class provides basic support for layouts following these rules:
 * (1) the supported layout manager is a JavaBean (it means that it has an
 *     empty public constructor, the parameters are set as properties,
 *     BeanInfo is used to obtain the properties),
 * (2) Container.setLayout(LayoutManager) method is used on the container to
 *     set the layout,
 * (3) Container.add(Component) and Container.add(Component, Object) methods
 *     are used on the container to add components (the second method for
 *     adding with layout constraints).
 *
 * To create basic support for such a layout manager, it's enough to implement
 * getSupportedClass method only.
 *
 * Note that the subclass should have public constructor without parameters,
 * otherwise it should override cloneSupportInstance method.
 *
 * Note that the default implementation does not (and even cannot) provide any
 * working support for layout constraints of components in general - this must
 * be implemented in the sublasses individually. See BorderLayoutSupport for an
 * example of using simple value constraints, AbsoluteLayoutSupport for complex
 * object constraints (created by constructor), GridBagLayoutSupport for complex
 * constraints with special initialization code.
 * 
 * @author Tomas Pavek
 */

public abstract class AbstractLayoutSupport implements LayoutSupportDelegate
{
    /** Default icon URL. */
    private static String iconURL =
        "org/netbeans/modules/form/layoutsupport/resources/AbstractLayout.gif"; // NOI18N
    /** Default icon URL. */
    private static String icon32URL =
        "org/netbeans/modules/form/layoutsupport/resources/AbstractLayout32.gif"; // NOI18N

    private static Method simpleAddMethod = null;
    private static Method addWithConstraintsMethod = null;
    private static Method setLayoutMethod = null;

    // ------

    private LayoutSupportContext layoutContext;

    private java.util.List componentCodeExpressions;
    private java.util.List componentCodeGroups;
    private java.util.List componentConstraints;

    private BeanCodeManager layoutBeanCode;
    private CodeGroup setLayoutCode;

    private MetaLayout metaLayout;
    private FormProperty[] allProperties;

    // ------------------
    // LayoutSupportDelegate interface implementation

    /** Initialization of the layout delegate before the first use.
     * There are three types of initialization supported:
     * (1) default initialization for an empty (newly created) layout
     *    (lmInstance == null, fromCode == false),
     * (2) initialization from an already existing instance of LayoutManager
     *    (lmInstance != null, fromCode == false),
     * (3) initialization from persistent code structure,
     *    (lmInstance == null, fromCode == true).
     * @param layoutContext provides a necessary context information for the
     *                      layout delegate
     * @param lmInstance LayoutManager instance for initialization (may be null)
     * @param fromCode indicates whether to initialize from code structure
     * @exception any Exception occurred during initialization
     */
    public void initialize(LayoutSupportContext layoutContext,
                           LayoutManager lmInstance,
                           boolean fromCode)
        throws Exception
    {
        if (this.layoutContext == layoutContext) {
            // already initialized - just reuse the delegate
            if (setLayoutCode != null)
                setLayoutCode.removeAll();
            else setLayoutCode =
                     layoutContext.getCodeStructure().createCodeGroup();

            readLayoutCode(setLayoutCode); //  reinstate layout code (for sure)
            return;
        }

        this.layoutContext = layoutContext;
        clean();

        Class cls = getSupportedClass();
        if (cls != null && LayoutManager.class.isAssignableFrom(cls)) {
            // create MetaLayout to manage layout manager as a bean
            boolean defaultInstance = lmInstance == null;
            if (lmInstance == null || !lmInstance.getClass().equals(cls)) {
                // no valid layout manager instance - create a default one
                lmInstance = createDefaultLayoutInstance();
                defaultInstance = true;
            }

            if (lmInstance != null)
                metaLayout = new MetaLayout(this, lmInstance, defaultInstance);
        }
        else metaLayout = null;

        // read layout code (if there's any)
        readLayoutCode(setLayoutCode);

        if (fromCode) {
            // read components from code
            CodeGroup componentCode = null;
            Iterator it = CodeStructure.getDefinedStatementsIterator(
                                          getActiveContainerCodeExpression());
            while (it.hasNext()) {
                if (componentCode == null)
                    componentCode =
                        layoutContext.getCodeStructure().createCodeGroup();

                CodeStatement statement = (CodeStatement) it.next();
                CodeExpression compExp = readComponentCode(statement,
                                                            componentCode);
                if (compExp != null) {
                    componentCodeExpressions.add(compExp);
                    componentCodeGroups.add(componentCode);
                    componentCode = null;

                    if (componentConstraints.size()
                            < componentCodeExpressions.size())
                        componentConstraints.add(null);
                }
            }
        }
    }

    /** States whether this support class is dedicted to some special container.
     * @return true if only certain container is supported,
     *         false if a layout manager for use in any container is supported
     */
    public boolean isDedicated() {
        Class cls = getSupportedClass();
        return cls != null && !LayoutManager.class.isAssignableFrom(cls);
    }

    /** Indicates whether the layout should be presented as a node in Component
     * Inspector (for setting properties). The node is provided for layout
     * managers typically (except null layou), and not for dedicated containers
     * support.
     * @return whether a node should be created for the layout
     */
    public boolean shouldHaveNode() {
        Class cls = getSupportedClass();
        return cls != null && LayoutManager.class.isAssignableFrom(cls);
    }

    /** Provides a display name for the layout node - derived from the name
     * of supported class here.
     * @return display name of supported layout
     */
    public String getDisplayName() {
        String name = getSupportedClass().getName();
        int lastdot = name.lastIndexOf('.');
        if (lastdot > 0)
            name = name.substring(lastdot + 1);
        return name;
    }

    /** Provides an icon to be used for the layout node in Component
     * Inspector. Only 16x16 color icon is required. The default implementation
     * tries to obtain the icon from BeanInfo of the layout manager.
     * @param type is one of BeanInfo constants: ICON_COLOR_16x16,
     *        ICON_COLOR_32x32, ICON_MONO_16x16, ICON_MONO_32x32
     * @return icon to be used for layout node
     */
    public Image getIcon(int type) {
        if (metaLayout != null) {
            Image icon = metaLayout.getBeanInfo().getIcon(type);
            if (icon != null)
                return icon;
        }

        switch (type) {
            case BeanInfo.ICON_COLOR_16x16:
            case BeanInfo.ICON_MONO_16x16:
                return Utilities.loadImage(iconURL);
            default:
                return Utilities.loadImage(icon32URL);
        }
    }

    /** This method provides properties of the supported layout - if it is
     * a JavaBean class implementing LayoutManager. The properties are obtained
     * from the BeanInfo of the layout manager. Note these are not properties
     * of individual components constraints.
     * @return properties of supported layout
     */
    public Node.PropertySet[] getPropertySets() {
        Node.PropertySet[] propertySets;

        FormProperty[] properties = getProperties();
        if (properties == null) {
            propertySets = metaLayout != null ?
                               metaLayout.getProperties() : null;
        }
        else { // a subclass provides special properties
            propertySets = new Node.PropertySet[1];
            propertySets[0] = new Node.PropertySet(
                "properties", // NOI18N
                FormUtils.getBundleString("CTL_PropertiesTab"), // NOI18N
                FormUtils.getBundleString("CTL_PropertiesTabHint")) // NOI18N
            {
                public Node.Property[] getProperties() {
                    return AbstractLayoutSupport.this.getProperties();
                }
            };
        }

        if (propertySets != null) {
            ArrayList allPropsList = new ArrayList();
            for (int i=0; i < propertySets.length; i++) {
                Node.Property[] props = propertySets[i].getProperties();
                for (int j=0; j < props.length; j++)
                    if (props[j] instanceof FormProperty)
                        allPropsList.add(props[j]);
            }
            allProperties = new FormProperty[allPropsList.size()];
            allPropsList.toArray(allProperties);
        }
        else {
            allProperties = new FormProperty[0];
            propertySets = new Node.PropertySet[0];
        }

        return propertySets;
    }

    /** Returns a class of a customizer for the layout manager being used as
     * a JavaBean. The class should be a java.awt.Component and
     * java.beans.Customizer. The default implementation tries to get the
     * customizer class from layout manager's BeanInfo.
     * @return layout bean customizer class, null if no customizer is provided
     */
    public Class getCustomizerClass() {
        return metaLayout == null ? null :
            metaLayout.getBeanInfo().getBeanDescriptor().getCustomizerClass();
    }

    /** Returns an instance of a special customizer provided by the layout
     * delegate. This customizer need not implement java.beans.Customizer,
     * because its creation is under full control of the layout delegate - and
     * vice versa, the customizer can have full control over the layout
     * delegate (unlike the bean customizer which operates only with layout
     * manager bean instance). The default implementation returns null.
     * @return instance of layout support customizer
     */
    public Component getSupportCustomizer() {
        return null;
    }

    /** Gets the complete code for setting up the layout (including adding
     * components).
     * @return whole container's layout code
     */
    public CodeGroup getLayoutCode() {
        return setLayoutCode;
    }

    /** Gets code for setting up one component's constraints and adding the
     * component to the layout (container).
     * @return one component's layout code
     */
    public CodeGroup getComponentCode(int index) {
        return (CodeGroup) componentCodeGroups.get(index);
    }

    /** Gets CodeExpression object representing one component.
     * @param index index of the component in the layout
     * @return CodeExpression for a component
     */
    public CodeExpression getComponentCodeExpression(int index) {
        return (CodeExpression) componentCodeExpressions.get(index);
    }

    /** Gets number of components in the layout.
     * @return number of components in the layout
     */
    public int getComponentCount() {
        return componentCodeExpressions != null ?
                 componentCodeExpressions.size() : 0;
    }

    /** This method is called to accept new components before they are added
     * to the layout (by calling addComponents method). It may adjust the
     * constraints, or refuse the components by throwing a RuntimeException
     * (e.g. IllegalArgumentException). It's up to the delagate to display an
     * error or warning message, the exception is not reported outside.
     * The default implementation accepts any components - simply does nothing.
     * @param compExpressions array of CodeExpression objects representing the
     *        components to be accepted
     * @param constraints array of layout constraints of the components, may
     *        contain nulls
     * @param index position at which the components are to be added (inserted);
     *        -1 means that the components will be added at the end
     */
    public void acceptNewComponents(CodeExpression[] compExpressions,
                                    LayoutConstraints[] constraints,
                                    int index)
    {
    }

    /** This method is called after a property of the layout is changed by
     * the user. Subclasses may check whether the layout is valid after the
     * change and throw PropertyVetoException if the change should be reverted.
     * It's up to the delagate to display an error or warning message, the
     * exception is not reported outside. The default implementation accepts
     * any change.
     * @param ev PropertyChangeEvent object describing the change
     */
    public void acceptContainerLayoutChange(PropertyChangeEvent ev)
        throws PropertyVetoException
    {
        // as this method is called for each change, we update the layout
        // bean code here too
        if (layoutBeanCode != null)
            layoutBeanCode.updateCode();
    }

    /** This method is called after a constraint property of some component
     * is changed by the user. Subclasses may check if the layout is valid
     * after the change and throw PropertyVetoException if the change should
     * be reverted. It's up to the delagate to display an error or warning
     * message, the exception is not reported outside. The default
     * implementation accepts any change.
     * @param index index of the component in the layout
     * @param ev PropertyChangeEvent object describing the change
     */
    public void acceptComponentLayoutChange(int index, PropertyChangeEvent ev)
        throws PropertyVetoException
    {
    }

    /** Adds new components to the layout. This is done just at the metadata
     * level, no real components but their CodeExpression representations
     * are added.
     * The code structures describing the layout is updated immediately.
     * @param compExpressions array of CodeExpression objects representing the
     *        components to be accepted
     * @param constraints array of layout constraints of the components, may
     *        contain nulls
     * @param index position at which the components should be added (inserted);
     *        if -1, the components should be added at the end
     */
    public void addComponents(CodeExpression[] newCompExps,
                              LayoutConstraints[] newConstraints,
                              int index)
    {
        if (index < 0 || index > componentCodeExpressions.size())
            index = componentCodeExpressions.size();

        CodeStructure codeStructure = layoutContext.getCodeStructure();

        for (int i=0; i < newCompExps.length; i++) {
            int ii = index + i;

            CodeExpression compExp = newCompExps[i];
            componentCodeExpressions.add(ii, compExp);

            LayoutConstraints constr = newConstraints != null ?
                                       newConstraints[i] : null;
            if (constr == null)
                constr = createDefaultConstraints();

            componentConstraints.add(ii, constr);

            CodeGroup componentCode =
                codeStructure.createCodeGroup();
            createComponentCode(componentCode, compExp, ii);
            componentCodeGroups.add(ii, componentCode);
        }
    }

    /** Removes one component from the layout (at metadata level).
     * The code structures describing the layout is updated immediately.
     * @param index index of the component in the layout
     */
    public void removeComponent(int index) {
        componentCodeExpressions.remove(index);
        componentCodeGroups.remove(index);
        componentConstraints.remove(index);
    }

    /** Removes all components from the layout (at metadata level).
     * The code structures describing the layout is updated immediately.
     */
    public void removeAll() {
        if (componentCodeExpressions != null)
            componentCodeExpressions.clear();
        if (componentCodeGroups != null)
            componentCodeGroups.clear();
        if (componentConstraints != null)
            componentConstraints.clear();
    }

    /** Indicates whether there's some change in the layout in comparison
     * with the default layout of given container. If there's no change, no
     * code needs to be delegate (e.g. default FlowLayout in JPanel).
     * Note this is related to the container layout only, not to components.
     * @param defaultContainer instance of the default container to compare with
     * @param defaultContainerDelegate effective container delegate of the
     *        default container (e.g. like content pane of JFrame)
     * @return whether the current layout is different from the default one
     */
    public boolean isLayoutChanged(Container defaultContainer,
                                   Container defaultContainerDelegate)
    {
        if (isDedicated())
            return false;

        Class layoutClass = getSupportedClass();
        LayoutManager lm = defaultContainerDelegate.getLayout();

        if (layoutClass == null)
            return lm != null;
        if (lm == null)
            return true;

        // 
        if (!layoutClass.isAssignableFrom(lm.getClass()))
            return true;

        FormProperty[] props = getAllProperties();
        for (int i=0; i < props.length; i++)
            if (props[i].isChanged())
                return true;

        return false;
    }

    /** Gets layout constraints for a component at the given index.
     * @param index index of the component in the layout
     * @return layout constraints of given component
     */
    public LayoutConstraints getConstraints(int index) {
        return index < 0 || index >= componentConstraints.size() ? null :
               (LayoutConstraints) componentConstraints.get(index);
    }

    /** This method is called when switching layout - giving an opportunity to
     * convert the previous constrainst of components to constraints of the new
     * layout (this layout). The default implementation does nothing.
     * @param previousConstraints [input] layout constraints of components in
     *                                    the previous layout
     * @param currentConstraints [output] array of converted constraints for
     *                                    the new layout - to be filled
     * @param components [input] real components in a real container having the
     *                           previous layout
     */
    public void convertConstraints(LayoutConstraints[] previousConstraints,
                                   LayoutConstraints[] currentConstraints,
                                   Component[] components)
    {
    }

    /** Sets up the layout (without adding components) on a real container,
     * according to the internal metadata representation.
     * @param container instance of a real container to be set
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     */
    public void setLayoutToContainer(Container container,
                                     Container containerDelegate)
    {
        if (isDedicated())
            return;

        LayoutManager lm = null;
        try {
            if (containerDelegate == layoutContext.getPrimaryContainerDelegate()) {
                if (metaLayout != null) // use the instance of MetaLayout
                    lm = (LayoutManager) metaLayout.getBeanInstance();
            }
            else { // use cloned layout instance
                lm = cloneLayoutInstance(container, containerDelegate);
            }
        }
        catch (Exception ex) { // should not happen
            ex.printStackTrace();
        }

        if (lm != null)
            containerDelegate.setLayout(lm);
    }

    /** Adds real components to given container (according to layout
     * constraints stored for the components).
     * @param container instance of a real container to be added to
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @param components components to be added
     * @param index position at which to add the components to container
     */
    public void addComponentsToContainer(Container container,
                                         Container containerDelegate,
                                         Component[] components,
                                         int index)
    {
        for (int i=0; i < components.length; i++) {
            LayoutConstraints constr = getConstraints(i + index);
            if (constr != null)
                containerDelegate.add(components[i],
                                      constr.getConstraintsObject(),
                                      i + index);
            else
                containerDelegate.add(components[i], i + index);
        }
    }

    /** Removes a real component from a real container.
     * @param container instance of a real container
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @param component component to be removed
     * @return whether it was possible to remove the component (some containers
     *         may not support removing individual components reasonably)
     */
    public boolean removeComponentFromContainer(Container container,
                                                Container containerDelegate,
                                                Component component)
    {
        containerDelegate.remove(component);
        component.setBounds(0, 0, 0, 0);
        return true;
    }

    /** Removes all components from given real container.
     * @param container instance of a real container to be cleared
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @return whether it was possible to clear the container (some containers
     *         may not support this)
     */
    public boolean clearContainer(Container container,
                                  Container containerDelegate)
    {
        Component[] components = containerDelegate.getComponents();
        containerDelegate.removeAll();
        for (int i=0; i < components.length; i++)
            components[i].setBounds(0, 0, 0, 0);
        return true;
    }

    /** This method is called when user clicks on the container in form
     * designer. The layout delegate may do something with the container,
     * e.g. for JTabbedPane it might switch the selected TAB. The default
     * implementation does nothing.
     * @param p Point of click in the container
     * @param real instance of the container when the click occurred
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     */
    public void processMouseClick(Point p,
                                  Container container,
                                  Container containerDelegate)
    {
    }

    /** This method is called when a component is selected in Component
     * Inspector. If the layout delegate is interested in such information,
     * it may store it and use it e.g. in arrangeContainer method.
     * The default implementation does nothing.
     * @param index position (index) of the selected component in container
     */
    public void selectComponent(int index) {
    }

    /** In this method, the layout delegate has a chance to "arrange" real
     * container instance additionally - some other way that cannot be
     * done through layout properties and added components. For example, the
     * selected component index can be applied here (see delegates for
     * CardLayout and JTabbedPane). The default implementation does nothing.
     * @param container instance of a real container to be arranged
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     */
    public void arrangeContainer(Container container,
                                 Container containerDelegate)
    {
    }

    /** This method should calculate layout constraints for a component dragged
     * over a container (or just for mouse cursor being moved over container,
     * without any component). This method is useful for "constraints oriented"
     * layout managers (like e.g. BorderLayout or GridBagLayout).
     * @param container instance of a real container over/in which the
     *        component is dragged
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @param component the real component being dragged, can be null
     * @param index position (index) of the component in its current container;
     *        -1 if there's no dragged component
     * @param posInCont position of mouse in the container delegate
     * @param posInComp position of mouse in the dragged component; null if
     *        there's no dragged component
     * @return new LayoutConstraints object corresponding to the position of
     *         the component in the container; may return null if the layout
     *         does not use component constraints, or if default constraints
     *         should be used
     */
    public LayoutConstraints getNewConstraints(Container container,
                                               Container containerDelegate,
                                               Component component,
                                               int index,
                                               Point posInCont,
                                               Point posInComp)
    {
        return null;
    }

    /** This method should calculate position (index) for a component dragged
     * over a container (or just for mouse cursor being moved over container,
     * without any component). This method is useful for layout managers that
     * don't use component constraints (like e.g. FlowLayout or GridLayout)
     * @param container instance of a real container over/in which the
     *        component is dragged
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @param component the real component being dragged, can be null
     * @param index position (index) of the component in its current container;
     *        -1 if there's no dragged component
     * @param posInCont position of mouse in the container delegate
     * @param posInComp position of mouse in the dragged component; null if
     *        there's no dragged component
     * @return index corresponding to the position of the component in the
     *         container; may return -1 if the layout rather uses component
     *         constraints, or if a default index should be used
     */
    public int getNewIndex(Container container,
                           Container containerDelegate,
                           Component component,
                           int index,
                           Point posInCont,
                           Point posInComp)
    {
        return -1;
    }

    /** This method should paint a feedback for a component dragged over
     * a container (or just for mouse cursor being moved over container,
     * without any component). In principle, it should present given component
     * layout constraints or index graphically.
     * @param container instance of a real container over/in which the
     *        component is dragged
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame) - here the feedback is painted
     * @param component the real component being dragged, can be null
     * @param newConstraints component layout constraints to be presented
     * @param newIndex component's index position to be presented
     *        (if newConstraints == null)
     * @param g Graphics object for painting (with color and line style set)
     * @return whether any feedback was painted (may return false if the
     *         constraints or index are invalid, or if the painting is not
     *         implemented)
     */
    public boolean paintDragFeedback(Container container, 
                                     Container containerDelegate,
                                     Component component,
                                     LayoutConstraints newConstraints,
                                     int newIndex,
                                     Graphics g)
    {
        return false;
    }

    /** Provides resizing options for given component. It can combine the
     * bit-flag constants RESIZE_UP, RESIZE_DOWN, RESIZE_LEFT, RESIZE_RIGHT.
     * @param container instance of a real container in which the
     *        component is to be resized
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @param component real component to be resized
     * @param index position of the component in its container
     * @return resizing options for the component; 0 if no resizing is possible
     */
    public int getResizableDirections(Container container,
                                      Container containerDelegate,
                                      Component component,
                                      int index)
    {
        return 0;
    }

    /** This method should calculate layout constraints for a component being
     * resized.
     * @param container instance of a real container in which the
     *        component is resized
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @param component real component being resized
     * @param index position of the component in its container
     * @param sizeChanges Insets object with size differences
     * @param posInCont position of mouse in the container delegate
     * @return component layout constraints for resized component; null if
     *         resizing is not possible or not implemented
     */
    public LayoutConstraints getResizedConstraints(Container container,
                                                   Container containerDelegate,
                                                   Component component,
                                                   int index,
                                                   Insets sizeChanges,
                                                   Point posInCont)
    {
        return null;
    }

    /** Cloning method - creates a copy of the layout delegate.
     * @param targetContext LayoutSupportContext for the new layout delegate
     * @param compExpressions array of CodeExpression objects representing the
     *        components for the new layout delegate (corresponding to the
     *        current ones)
     * @return cloned layout delegate instance
     */
    public LayoutSupportDelegate cloneLayoutSupport(
                                     LayoutSupportContext targetContext,
                                     CodeExpression[] targetComponents)
    {
        AbstractLayoutSupport clone = createLayoutSupportInstance();
        try {
            clone.initialize(targetContext, null, false);
        }
        catch (Exception ex) { // should not fail (not reading from code)
            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex);
            return null;
        }

        FormProperty[] sourceProperties = getAllProperties();
        FormProperty[] targetProperties = clone.getAllProperties();
        FormUtils.copyProperties(sourceProperties,
                                 targetProperties,
                                 FormUtils.CHANGED_ONLY
                                   | FormUtils.DISABLE_CHANGE_FIRING);

        int compCount = getComponentCount();
        LayoutConstraints[] constraints = new LayoutConstraints[compCount];
        for (int i=0; i < compCount; i++) {
            LayoutConstraints constr = getConstraints(i);
            constraints[i] = constr != null ? constr.cloneConstraints() : null;
        }

        clone.addComponents(targetComponents, constraints, 0);

        return clone;
    }

    // ------------------
    // extended API for AbstractLayoutSupport subclasses

    /** Creates a default instance of LayoutManager (for internal use).
     * Override this method if the layout manager is not a bean (cannot
     * be created from default constructor).
     * @return new (default) instance of supported layout manager
     */
    protected LayoutManager createDefaultLayoutInstance()
        throws Exception
    {
        return (LayoutManager)
               CreationFactory.createDefaultInstance(getSupportedClass());
    }

    /** Cloning method - creates a clone of the reference LayoutManager
     * instance (for external use). Override this method if the layout manager
     * is not a bean (cannot be created from default constructor, and copied
     * using properties).
     * @param container instance of a real container in whose container
     *        delegate the layout manager will be probably used
     * @param containerDelegate effective container delegate of the container
     *        (e.g. like content pane of JFrame)
     * @return new instance of layout manager representing the layout (with
     *         all properties set)
     */
    protected LayoutManager cloneLayoutInstance(Container container,
                                                Container containerDelegate)
        throws Exception
    {
        return metaLayout == null ? null :
               (LayoutManager) metaLayout.cloneBeanInstance(null);
    }

    /** Cloning method - creates a new instance of this layout support, just
     * not initialized yet.
     * @return new instance of this layout support
     */
    protected AbstractLayoutSupport createLayoutSupportInstance() {
        try {
            return (AbstractLayoutSupport) getClass().newInstance();
        }
        catch (Exception ex) { // should not happen for AbstractLayoutSupport subclasses
            return null;
        }
    }

    /** This methods returns the code expression to be used for container on
     * which the layout is set and to which components are added. This can be
     * either container, or container delegate expression. In fact, it is
     * container delegate in most cases (so this method needs to be overridden
     * very rarely). But there's e.g. JScrollPane which has its viewport as the
     * container delegate, but we work with the JScrollPane (whole container).
     * @return code expression representing the effective container
     */
    protected CodeExpression getActiveContainerCodeExpression() {
        return layoutContext.getContainerDelegateCodeExpression();
    }

    /** Cleans all data before the delegate is initialized.
     */
    protected void clean() {
        if (componentCodeExpressions != null)
            componentCodeExpressions.clear();
        else componentCodeExpressions = new ArrayList();

        if (componentCodeGroups != null)
            componentCodeGroups.clear();
        else componentCodeGroups = new ArrayList();

        if (componentConstraints != null)
            componentConstraints.clear();
        else componentConstraints = new ArrayList();

        if (setLayoutCode != null)
            setLayoutCode.removeAll();
        else setLayoutCode = layoutContext.getCodeStructure().createCodeGroup();

        layoutBeanCode = null;
        metaLayout = null;

        allProperties = null;
    }

    /** This method is used for "reading layout from code", called from
     * initialize method. It recognizes relevant code which sets the layout
     * manager on the container and reads the layout information from the code.
     * This includes the code for setting up the layout manager itself and the
     * code for setting the layout manger to container. For setting up just the
     * layout manager bean, the method readInitLayoutCode is used.
     * Reading components code is not done here.
     * @param layoutCode CodeGroup to be filled with relevant layout code
     * @see readInitLayoutCode method
     */
    protected void readLayoutCode(CodeGroup layoutCode) {
        if (isDedicated())
            return;

        CodeGroup initLayoutCode =
            getCodeStructure().createCodeGroup();
        CodeStatement setLayoutStatement = null;

        Iterator it = CodeStructure.getDefinedStatementsIterator(
                                        getActiveContainerCodeExpression());
        CodeStatement[] statements = CodeStructure.filterStatements(
                                                     it, getSetLayoutMethod());
        if (statements.length > 0) { // read from code
            setLayoutStatement = statements[0];
            readInitLayoutCode(setLayoutStatement.getStatementParameters()[0],
                               initLayoutCode);
        }
        else { // create new
            CodeExpression layoutExp = createInitLayoutCode(initLayoutCode);
            if (layoutExp != null)
                setLayoutStatement = CodeStructure.createStatement(
                         getActiveContainerCodeExpression(),
                         getSetLayoutMethod(),
                         new CodeExpression[] { layoutExp });
        }

        if (setLayoutStatement != null) {
            layoutCode.addGroup(initLayoutCode);
            layoutCode.addStatement(setLayoutStatement);
        }
    }

    /** This method is called from readLayoutCode to read the layout manager
     * bean code (i.e. code for constructor and properties).
     * @param layoutExp CodeExpressin of the layout manager
     * @param initLayoutCode CodeGroup to be filled with relevant
     *        initialization code
     */
    protected void readInitLayoutCode(CodeExpression layoutExp,
                                      CodeGroup initLayoutCode)
    {
        if (metaLayout == null)
            return;

        layoutBeanCode = new BeanCodeManager(
            getSupportedClass(),
            getAllProperties(),
            CreationDescriptor.PLACE_ALL | CreationDescriptor.CHANGED_ONLY,
            false, // don't force empty constructor
            false, // disable changes firing when properties are restored
            layoutExp,
            initLayoutCode);
    }

    /** Creates code structures for a new layout manager (opposite to
     * readInitLayoutCode).
     * @param initLayoutCode CodeGroup to be filled with relevant
     *        initialization code
     * @return created CodeExpression representing the layout manager
     */
    protected CodeExpression createInitLayoutCode(CodeGroup initLayoutCode) {
        if (metaLayout == null)
            return null;

        layoutBeanCode = new BeanCodeManager(
            getSupportedClass(),
            getAllProperties(),
            CreationDescriptor.PLACE_ALL | CreationDescriptor.CHANGED_ONLY,
            false,
            layoutContext.getCodeStructure(),
            CodeVariable.LOCAL,
            initLayoutCode);

        return layoutBeanCode.getCodeExpression();
    }

    /** This method is used for scanning code structures and recognizing
     * components added to containers and their constraints. It's called from
     * initialize method. When a relevant code statement is found, then the
     * CodeExpression of component is get and added to component, and also the
     * layout constraints information is read (using separate
     * readConstraintsCode method).
     * @param statement CodeStatement to be tested if it contains relevant code
     * @param componentCode CodeGroup to be filled with all component code
     * @return CodeExpression representing found component; null if the
     *         statement is not relevant
     */
    protected CodeExpression readComponentCode(CodeStatement statement,
                                               CodeGroup componentCode)
    {
        CodeExpression compExp;
        CodeGroup constrCode;
        LayoutConstraints constr;

        // look for Container.add(Component) or Container.add(Component, Object)
        if (getSimpleAddMethod().equals(statement.getMetaObject())) {
            compExp = statement.getStatementParameters()[0];
            constrCode = null;
            constr = null;
        }
        else if (getAddWithConstraintsMethod().equals(
                                 statement.getMetaObject()))
        {
            CodeExpression[] params = statement.getStatementParameters();

            compExp = params[0];
            constrCode = getCodeStructure().createCodeGroup();
            constr = readConstraintsCode(params[1], constrCode, compExp);
        }
        else return null;

        componentConstraints.add(constr);
        if (constrCode != null)
            componentCode.addGroup(constrCode);
        componentCode.addStatement(statement);

        return compExp;
    }

    /** This method is called from readComponentCode method to read layout
     * constraints of a component from code.
     * @param constrExp CodeExpression object of the constraints (taken from
     *        add method in the code)
     * @param constrCode CodeGroup to be filled with the relevant constraints
     *        initialization code
     * @param compExp CodeExpression of the component for which the constraints
     *        are read
     * @return LayoutConstraints based on information read form code
     */
    protected LayoutConstraints readConstraintsCode(CodeExpression constrExp,
                                                    CodeGroup constrCode,
                                                    CodeExpression compExp)
    {
        return null; // no default implementation possible
    }

    /** Creates code for a component added to the layout (opposite to
     * readComponentCode method).
     * @param componentCode CodeGroup to be filled with complete component code
     *        (code for initializing the layout constraints and adding the
     *        component to the layout)
     * @param compExp CodeExpression object representing component
     * @param index position of the component in the layout
     */
    protected void createComponentCode(CodeGroup componentCode,
                                       CodeExpression compExp,
                                       int index)
    {
        CodeGroup constrCode = getCodeStructure().createCodeGroup();
        LayoutConstraints constr = getConstraints(index);

        // first create init code for the constraints object
        CodeExpression constrExp = createConstraintsCode(
                                       constrCode, constr, compExp, index);

        // create "add" code for the component
        CodeStatement compAddStatement;
        if (constrExp != null) { // add with constraints
            compAddStatement = CodeStructure.createStatement(
                    getActiveContainerCodeExpression(),
                    getAddWithConstraintsMethod(),
                    new CodeExpression[] { compExp, constrExp });
        }
        else { // add without constraints
            compAddStatement = CodeStructure.createStatement(
                    getActiveContainerCodeExpression(),
                    getSimpleAddMethod(),
                    new CodeExpression[] { compExp });
        }

        componentCode.addGroup(constrCode);
        componentCode.addStatement(compAddStatement);
    }

    /** Called from createComponentCode method, creates code for a component
     * layout constraints (opposite to readConstraintsCode).
     * @param constrCode CodeGroup to be filled with constraints code
     * @param constr layout constraints metaobject representing the constraints
     * @param compExp CodeExpression object representing the component
     * @return created CodeExpression representing the layout constraints
     */
    protected CodeExpression createConstraintsCode(CodeGroup constrCode,
                                                   LayoutConstraints constr,
                                                   CodeExpression compExp,
                                                   int index)
    {
        return null; // no default implementation possible
    }

    /** This method is called to get a default component layout constraints
     * metaobject in case it is not provided (e.g. in addComponents method).
     * @return the default LayoutConstraints object for the supported layout;
     *         null if no component constraints are used
     */
    protected LayoutConstraints createDefaultConstraints() {
        return null; // no default implementation possible
    }

    /** Method to obtain just one propetry of given name.
     * @return layout property of given name
     */
    protected Node.Property getProperty(String propName) {
        return metaLayout == null ? null :
                                    metaLayout.getPropertyByName(propName);
    }

    /** This method can be overridden to provide other layout properties than
     * the standard ones of LayoutManager handled automatically as a bean.
     * This method is called from getPropertySets() implementation to obtain
     * the default property set for the layout (assuming there's only one
     * property set). So it is also possible to override (more generally)
     * getPropertySets() instead.
     * @return array of alternative properties of the layout
     */
    protected FormProperty[] getProperties() {
        return null; // use default "bean" properties
    }

    // ---------------
    // useful methods for subclasses

    /** Gets the LayoutSupportContext instance set in initialize method.
     * @return the attached LayoutSupportContext object providing necessary
     *         context information
     */
    protected final LayoutSupportContext getLayoutContext() {
        return layoutContext;
    }

    /** Gets the main CodeStructure object (holding all code structure data)
     * for creating/reading the code. Obtained from the layout context.
     * @return main CodeStructure object (for working with code structure data)
     */
    protected final CodeStructure getCodeStructure() {
        return layoutContext.getCodeStructure();
    }

    /** Gets the internal list of layout constraints of components in the
     * layout. (The list contains instances of LayoutConstraints).
     * @return list of internally stored layout constraints of components
     */
    protected final java.util.List getConstraintsList() {
        return componentConstraints;
    }

    /** This methods collects properties from all property sets to one array.
     * @return all properties of the layout in an array
     */
    protected final FormProperty[] getAllProperties() {
        if (allProperties == null)
            getPropertySets();

        return allProperties;
    }

    /** This method should be used by subclasses if they need to re-create
     * the reference layout manager instance (see BoxLayoutSupport for example).
     */
    protected final void updateLayoutInstance() {
        Container cont = layoutContext.getPrimaryContainer();
        Container contDel = layoutContext.getPrimaryContainerDelegate();

        LayoutManager lm = null;
        try {
            lm = cloneLayoutInstance(cont, contDel);
        }
        catch (Exception ex) { // should not happen
            ex.printStackTrace();
        }

        if (lm != null && metaLayout != null)
            metaLayout.updateInstance(lm);
    
        layoutContext.updatePrimaryContainer();
    }

    /** This method finds the CodeStatement object representing setLayout
     * method call on the container.
     * @return CodeStatement of setLayout method call on the container
     */
    protected final CodeStatement getSetLayoutStatement() {
        Iterator it = CodeStructure.getDefinedStatementsIterator(
                                        getActiveContainerCodeExpression());
        CodeStatement[] found = CodeStructure.filterStatements(
                                                  it, getSetLayoutMethod());
        return found != null && found.length > 0 ? found[0] : null;
    }

    // ---------
    // utility methods

    /** Used only internally.
     */
    protected static ResourceBundle getBundle() {
        return org.openide.util.NbBundle.getBundle(AbstractLayoutSupport.class);
    }

    /** Gets java.lang.reflect.Method object representing the simple
     * Container.add(Component) method. This is needed when working with
     * code structures.
     * @return java.lang.reflect.Method representing Container.add(Component)
     */
    protected static Method getSimpleAddMethod() {
        if (simpleAddMethod == null) {
            try {
                simpleAddMethod = Container.class.getMethod(
                                      "add", // NOI18N
                                      new Class[] { Component.class });
            }
            catch (NoSuchMethodException ex) { // should not happen
                ex.printStackTrace();
            }
        }
        return simpleAddMethod;
    }

    /** Gets java.lang.reflect.Method object representing the method for adding
     * component with layout constraints - Container.add(Component, Object).
     * This is needed when working with code structures.
     * @return java.lang.reflect.Method object representing
     *         Container.add(Component, Object) method
     */
    protected static Method getAddWithConstraintsMethod() {
        if (addWithConstraintsMethod == null) {
            try {
                addWithConstraintsMethod = Container.class.getMethod(
                                               "add", // NOI18N
                                               new Class[] { Component.class,
                                                             Object.class });
            }
            catch (NoSuchMethodException ex) { // should not happen
                ex.printStackTrace();
            }
        }
        return addWithConstraintsMethod;
    }

    /** Gets java.lang.reflect.Method object representing
     * Container.setLayout(LayoutManager) method. This is needed when working
     * with code structures.
     * @return java.lang.reflect.Method object representing
     *         Container.setLayout(LayoutManager) method
     */
    protected static Method getSetLayoutMethod() {
        if (setLayoutMethod == null) {
            try {
                setLayoutMethod = Container.class.getMethod(
                                    "setLayout", // NOI18N
                                    new Class[] { LayoutManager.class });
            }
            catch (NoSuchMethodException ex) { // should not happen
                ex.printStackTrace();
            }
        }
        return setLayoutMethod;
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.