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

Groovy example source code file (BindFactory.groovy)

This example Groovy source code file (BindFactory.groovy) 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.

Java - Groovy tags/keywords

boolean, closure, closuretriggerbinding, exception, fullbinding, fullbinding, map, object, object, propertybinding, propertybinding, string, string, triggerbinding

The Groovy BindFactory.groovy source code

/*
 * Copyright 2007-2009 the original author or authors.
 *
 * 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 groovy.swing.factory

import groovy.swing.SwingBuilder
import groovy.swing.binding.*
import java.util.Map.Entry
import org.codehaus.groovy.binding.*

/**
 * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin
 * @version $Revision: 21140 $
 * @since Groovy 1.1
 */
public class BindFactory extends AbstractFactory {

    public static final String CONTEXT_DATA_KEY = "BindFactoryData";

    final Map<String, TriggerBinding> syntheticBindings

    public BindFactory() {
        syntheticBindings = new HashMap()

        // covers JTextField.text
        // covers JTextPane.text
        // covers JTextArea.text
        // covers JEditorPane.text
        syntheticBindings.putAll(JTextComponentProperties.syntheticProperties)

        // covers JCheckBox.selected
        // covers JCheckBoxMenuItem.selected
        // covers JRadioButton.selected
        // covers JRadioButtonMenuItem.selected
        // covers JToggleButton.selected
        syntheticBindings.putAll(AbstractButtonProperties.syntheticProperties)

        // covers JSlider.value
        syntheticBindings.putAll(JSliderProperties.syntheticProperties)

        // covers JScrollBar.value
        syntheticBindings.putAll(JScrollBarProperties.syntheticProperties)

        // JComboBox.elements / items
        // JComboBox.selectedElement / selectedItem
        syntheticBindings.putAll(JComboBoxProperties.syntheticProperties)

        // JList.selectedElement / selectedItem / selectedElements / selectedItems / selectedIndex
        syntheticBindings.putAll(JListProperties.syntheticProperties)

        // JSpinner.value
        syntheticBindings.putAll(JSpinnerProperties.syntheticProperties)

        // other properties handled in JSR-295
        // JTable.elements
        // JTable.selectedElement
        // JTable.selectedElements
        syntheticBindings.putAll(JTableProperties.syntheticProperties);

        // JTree.root
        // JTree.selectedElement
        // JTree.selectedElements

        // covers JComponent.size
        // covers JComponent.width
        // covers JComponent.height
        // covers JComponent.bounds
        // covers JComponent.x
        // covers JComponent.y
        // covers JComponent.visible
        syntheticBindings.putAll(JComponentProperties.syntheticProperties)

    }

    /**
     * Accepted Properties...
     *
     * group?
     * source ((sourceProperty) | (sourceEvent sourceValue))
     * (target targetProperty)? (? use default javabeans property if targetProperty is not present?)
     *
     *
     * @param builder
     * @param name
     * @param value
     * @param attributes
     * @return the newly created instance
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
        Object source = attributes.remove("source")
        Object target = attributes.remove("target")
        Map bindContext = builder.context.get(CONTEXT_DATA_KEY) ?: [:]
        if (bindContext.isEmpty()) {
            builder.context.put(CONTEXT_DATA_KEY, bindContext)
        }

        TargetBinding tb = null
        if (target != null) {
            String targetProperty = attributes.remove("targetProperty") ?: value
            tb = new PropertyBinding(target, targetProperty)
            if (source == null) {
                // if we have a target but no source assume the build context is the source and return
                def result
                if (attributes.remove("mutual")) {
                  result = new MutualPropertyBinding(null, null, tb, this.&getTriggerBinding)
                } else {
                  result = tb
                }
                def newAttributes = [:]
                newAttributes.putAll(attributes)
                bindContext.put(result, newAttributes)
                attributes.clear()
                return result
            }
        }

        FullBinding fb
        boolean sea = attributes.containsKey("sourceEvent")
        boolean sva = attributes.containsKey("sourceValue")
        boolean spa = attributes.containsKey("sourceProperty") || value

        if (sea && sva && !spa) {
            // entirely event triggered binding
            Closure queryValue = (Closure) attributes.remove("sourceValue")
            ClosureSourceBinding csb = new ClosureSourceBinding(queryValue)
            String trigger = (String) attributes.remove("sourceEvent")
            EventTriggerBinding etb = new EventTriggerBinding(source, trigger)
            fb = etb.createBinding(csb, tb)
        } else if (spa && !(sea && sva)) {
            // partially property driven binding
            String property = attributes.remove("sourceProperty") ?: value
            PropertyBinding pb = new PropertyBinding(source, property)

            TriggerBinding trigger
            if (sea) {
                // source trigger comes from an event
                String triggerName = (String) attributes.remove("sourceEvent")
                trigger = new EventTriggerBinding(source, triggerName)
            } else {
                // source trigger comes from a property change
                // this method will also check for synthetic properties
                trigger = getTriggerBinding(pb)
            }

            SourceBinding sb;
            if (sva) {
                // source value comes from a value closure
                Closure queryValue = (Closure) attributes.remove("sourceValue")
                sb = new ClosureSourceBinding(queryValue)
            } else {
                // source value is the property value
                sb = pb
            }

            // check for a mutual binding (bi-directional)
            if (attributes.remove("mutual")) {
                fb = new MutualPropertyBinding(trigger, sb, tb, this.&getTriggerBinding)
            } else {
                fb = trigger.createBinding(sb, tb)
            }
        } else if (!(sea || sva || spa)) {
            // if no sourcing is defined then assume we are a closure binding and return
            def newAttributes = [:]
            newAttributes.putAll(attributes)
            bindContext.put(tb, newAttributes)
            attributes.clear()
            return new ClosureTriggerBinding(syntheticBindings)
        } else {
            throw new RuntimeException("Both sourceEvent: and sourceValue: cannot be specified along with sourceProperty: or a value argument")
        }

        if (attributes.containsKey("value")) {
            bindContext.put(fb, [value:attributes.remove("value")])
        }

        Object o = attributes.remove("bind")
        if (    ((o == null) && !attributes.containsKey('group'))
            || ((o instanceof Boolean) && ((Boolean)o).booleanValue()))
        {
            fb.bind()
        }

        if ((attributes.group instanceof AggregateBinding) && (fb instanceof BindingUpdatable)) {
            attributes.remove('group').addBinding(fb)
        }

        builder.addDisposalClosure(fb.&unbind)
        return fb
    }

    public void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object node) {
        super.onNodeCompleted(builder, parent, node);

        if (node instanceof FullBinding && node.sourceBinding && node.targetBinding) {
            try {
                node.update()
            } catch (Exception ignored) {
                // don't throw out to top
            }
            try {
                node.rebind()
            } catch (Exception ignored) {
                // don't throw out to top
            }
        }
    }

    public boolean isLeaf() {
        return false;
    }

    public boolean isHandlesNodeChildren() {
        return true;
    }

    public boolean onNodeChildren(FactoryBuilderSupport builder, Object node, Closure childContent) {
        if ((node instanceof FullBinding) && (node.converter == null)) {
            node.converter = childContent
            return false
        } else if (node instanceof ClosureTriggerBinding) {
            node.closure = childContent
            return false;
        } else if (node instanceof TriggerBinding) {
            def bindAttrs = builder.context.get(CONTEXT_DATA_KEY)[node] ?: [:]
            if (!bindAttrs.containsKey("converter")) {
                bindAttrs["converter"] = childContent
                return false;
            }
        }

        throw new RuntimeException("Binding nodes do not accept child content when a converter is already specified")
    }

    public TriggerBinding getTriggerBinding(PropertyBinding psb) {
        String property = psb.propertyName
        Class currentClass = psb.bean.getClass()
        while (currentClass != null) {
            // should we check interfaces as well?  if so at what level?
            def trigger = (TriggerBinding) syntheticBindings.get("$currentClass.name#$property" as String)
            if (trigger != null) {
                return trigger
            }
            currentClass = currentClass.getSuperclass()
        }
        //TODO inspect the bean info and throw an error if the property is not observable and not bind:false?
        return psb
    }

    public bindingAttributeDelegate(FactoryBuilderSupport builder, def node, def attributes) {
        Iterator iter = attributes.entrySet().iterator()
        Map bindContext = builder.context.get(CONTEXT_DATA_KEY) ?: [:]

        while (iter.hasNext()) {
            Entry entry = (Entry) iter.next()
            String property = entry.key.toString()
            Object value = entry.value

            def bindAttrs = bindContext.get(value) ?: [:]
            def idAttr = builder.getAt(SwingBuilder.DELEGATE_PROPERTY_OBJECT_ID) ?: SwingBuilder.DEFAULT_DELEGATE_PROPERTY_OBJECT_ID
            def id = bindAttrs.remove(idAttr)
            if (bindAttrs.containsKey("value")) {
                node."$property" = bindAttrs.remove("value")
            }

            FullBinding fb
            if (value instanceof MutualPropertyBinding) {
                fb = (FullBinding) value
                PropertyBinding psb = new PropertyBinding(node, property)
                if (fb.sourceBinding == null) {
                    fb.sourceBinding = psb
                    finishContextualBinding(fb, builder, bindAttrs, id)
                } else if (fb.targetBinding == null) {
                    fb.targetBinding = psb
                }
            } else if (value instanceof FullBinding) {
                fb = (FullBinding) value
                fb.targetBinding = new PropertyBinding(node, property)
            } else  if (value instanceof TargetBinding) {
                PropertyBinding psb = new PropertyBinding(node, property)
                fb = getTriggerBinding(psb).createBinding(psb, value)
                finishContextualBinding(fb, builder, bindAttrs, id)
            } else if (value instanceof ClosureTriggerBinding) {
                PropertyBinding psb = new PropertyBinding(node, property)
                fb = value.createBinding(value, psb);
                finishContextualBinding(fb, builder, bindAttrs, id)
            } else {
                continue
            }
            try {
                fb.update()
            } catch (Exception e) {
                // just eat it?
            }
            try {
                fb.rebind()
            } catch (Exception e) {
                // just eat it?
            }
            // this is why we cannot use entrySet().each { }
            iter.remove()
        }
    }

  private def finishContextualBinding(FullBinding fb, FactoryBuilderSupport builder, bindAttrs, id) {

    Object bindValue = bindAttrs.remove("bind")
    bindAttrs.each {k, v -> fb."$k" = v}

    if ((bindValue == null)
            || ((bindValue instanceof Boolean) && ((Boolean) bindValue).booleanValue())) {
      fb.bind()
    }

    builder.addDisposalClosure(fb.&unbind)

    // replaces ourselves in the variables
    // id: is lost to us by now, so we just assume that any storage of us is a goner as well
    //builder.getVariables().each{ Map.Entry me -> if (value.is(me.value)) me.setValue fb}
    if (id) builder.setVariable(id, fb)
  }

}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy BindFactory.groovy source code file:

... 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.