groovy.swing.factory.BindFactory.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of driver-cql-shaded Show documentation
Show all versions of driver-cql-shaded Show documentation
A Shaded CQL ActivityType driver for http://nosqlbench.io/
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package groovy.swing.factory
import groovy.swing.SwingBuilder
import groovy.swing.binding.AbstractButtonProperties
import groovy.swing.binding.JComboBoxProperties
import groovy.swing.binding.JComponentProperties
import groovy.swing.binding.JListProperties
import groovy.swing.binding.JScrollBarProperties
import groovy.swing.binding.JSliderProperties
import groovy.swing.binding.JSpinnerProperties
import groovy.swing.binding.JTableProperties
import groovy.swing.binding.JTextComponentProperties
import org.codehaus.groovy.binding.AggregateBinding
import org.codehaus.groovy.binding.BindingUpdatable
import org.codehaus.groovy.binding.ClosureSourceBinding
import org.codehaus.groovy.binding.ClosureTriggerBinding
import org.codehaus.groovy.binding.EventTriggerBinding
import org.codehaus.groovy.binding.FullBinding
import org.codehaus.groovy.binding.MutualPropertyBinding
import org.codehaus.groovy.binding.PropertyBinding
import org.codehaus.groovy.binding.SourceBinding
import org.codehaus.groovy.binding.TargetBinding
import org.codehaus.groovy.binding.TriggerBinding
import java.util.Map.Entry
/**
* @author Danno Ferrin
* @since Groovy 1.1
*/
public class BindFactory extends AbstractFactory {
public static final String CONTEXT_DATA_KEY = "BindFactoryData";
final Map 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")
Object update = attributes.get("update")
Map bindContext = builder.context.get(CONTEXT_DATA_KEY) ?: [:]
if (bindContext.isEmpty()) {
builder.context.put(CONTEXT_DATA_KEY, bindContext)
}
TargetBinding tb = null
if (target != null) {
Object targetProperty = attributes.remove("targetProperty") ?: value
if (! (targetProperty instanceof CharSequence)) {
throw new IllegalArgumentException("Invalid value for targetProperty: (or node value)." +
" Value for this attribute must be a String but it is "+ (targetProperty != null? targetProperty.getClass().getName() : null))
}
tb = new PropertyBinding(target, targetProperty.toString(), update)
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
Object property = attributes.remove("sourceProperty") ?: value
if (! (property instanceof CharSequence)) {
throw new IllegalArgumentException("Invalid value for sourceProperty: (or node value). " +
"Value for this attribute must be a String but it is "+ (property != null? property.getClass().getName() : null))
}
if (source == null) {
// if we have a sourceProperty but no source then we're in trouble
throw new IllegalArgumentException("Missing value for source: even though sourceProperty: (or node value) "+
"was specified. Please check you didn't write bind(model.someProperty) instead of bind{ model.someProperty }")
}
PropertyBinding pb = new PropertyBinding(source, property.toString(), update)
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)
def ctb = new ClosureTriggerBinding(syntheticBindings)
bindContext.put(ctb, newAttributes)
attributes.clear()
return ctb
} 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")])
}
bindContext.get(fb, [:]).put('update', update)
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 onHandleNodeAttributes(FactoryBuilderSupport builder, Object node, Map attributes) {
attributes.remove('update')
true
}
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")
}
def update = bindAttrs.get('update')
FullBinding fb
if (value instanceof MutualPropertyBinding) {
fb = (FullBinding) value
PropertyBinding psb = new PropertyBinding(node, property, update)
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, update)
} else if (value instanceof TargetBinding) {
PropertyBinding psb = new PropertyBinding(node, property, update)
fb = getTriggerBinding(psb).createBinding(psb, value)
finishContextualBinding(fb, builder, bindAttrs, id)
} else if (value instanceof ClosureTriggerBinding) {
PropertyBinding psb = new PropertyBinding(node, property, update)
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) {
bindAttrs.remove('update')
Object bindValue = bindAttrs.remove("bind")
List propertiesToBeSkipped = ['group']
bindAttrs.each {k, v -> if (!(k in propertiesToBeSkipped)) fb."$k" = v}
if ((bindAttrs.group instanceof AggregateBinding) && (fb instanceof BindingUpdatable)) {
bindAttrs.group.addBinding(fb)
}
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)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy