/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package com.bc.ceres.swing.binding.internal;
import com.bc.ceres.binding.BindingException;
import com.bc.ceres.core.Assert;
import com.bc.ceres.swing.binding.Binding;
import com.bc.ceres.swing.binding.BindingContext;
import com.bc.ceres.swing.binding.BindingProblem;
import com.bc.ceres.swing.binding.BindingProblemListener;
import com.bc.ceres.swing.binding.ComponentAdapter;
import javax.swing.JComponent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
public final class BindingImpl implements Binding, PropertyChangeListener {
private final BindingContext context;
private final String name;
private final ComponentAdapter componentAdapter;
private List<JComponent> secondaryComponents;
private boolean adjustingComponents;
private BindingProblem problem;
public BindingImpl(BindingContext context, String name, ComponentAdapter componentAdapter) {
Assert.notNull(context, "context");
Assert.notNull(name, "name");
Assert.notNull(componentAdapter, "componentAdapter");
this.context = context;
this.name = name;
this.componentAdapter = componentAdapter;
}
public void bindProperty() {
context.addPropertyChangeListener(name, this);
}
public void unbindProperty() {
context.removePropertyChangeListener(name, this);
}
@Override
public BindingProblem getProblem() {
return problem;
}
@Override
public void clearProblem() {
BindingProblem oldProblem = this.problem;
if (oldProblem != null) {
this.problem = null;
fireProblemCleared(oldProblem);
}
}
@Override
public BindingProblem reportProblem(BindingException cause) {
Assert.notNull(cause, "cause");
final BindingProblem newProblem = new BindingProblemImpl(this, cause);
BindingProblem oldProblem = this.problem;
if (oldProblem == null || !newProblem.equals(oldProblem)) {
this.problem = newProblem;
fireProblemReported(newProblem, oldProblem);
}
return newProblem;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
adjustComponents();
}
@Override
public ComponentAdapter getComponentAdapter() {
return componentAdapter;
}
@Override
public final BindingContext getContext() {
return context;
}
@Override
public final String getPropertyName() {
return name;
}
@Override
public Object getPropertyValue() {
return context.getPropertySet().getValue(getPropertyName());
}
@Override
public void setPropertyValue(Object value) {
try {
context.getPropertySet().setValue(getPropertyName(), value);
clearProblem();
} catch (IllegalArgumentException e) {
if (e.getCause() instanceof BindingException) {
reportProblem((BindingException) e.getCause());
}
}
}
@Override
public boolean isAdjustingComponents() {
return adjustingComponents;
}
@Override
public void adjustComponents() {
if (!adjustingComponents) {
// Make sure, componentAdapter is not already unbound
if (componentAdapter.getBinding() != null) {
try {
adjustingComponents = true;
componentAdapter.adjustComponents();
// Now property is in sync with UI
clearProblem();
} finally {
adjustingComponents = false;
}
}
}
}
/**
* Gets all Swing components this binding is associated with.
*
* @return The component array.
* @see #addComponent(javax.swing.JComponent)
*/
@Override
public JComponent[] getComponents() {
if (secondaryComponents == null) {
return componentAdapter.getComponents();
} else {
JComponent[] primaryComponents = componentAdapter.getComponents();
JComponent[] allComponents = new JComponent[primaryComponents.length + secondaryComponents.size()];
System.arraycopy(primaryComponents, 0, allComponents, 0, primaryComponents.length);
int j = primaryComponents.length;
for (JComponent component : secondaryComponents) {
allComponents[j] = component;
j++;
}
return allComponents;
}
}
/**
* Attaches a secondary Swing component to this binding.
*
* @param component The secondary component.
* @see #removeComponent(javax.swing.JComponent)
*/
@Override
public void addComponent(JComponent component) {
synchronized (this) {
if (secondaryComponents == null) {
secondaryComponents = new ArrayList<JComponent>(3);
}
if (!secondaryComponents.contains(component)) {
secondaryComponents.add(component);
}
}
}
/**
* Detaches a secondary Swing component from this binding.
*
* @param component The secondary component.
* @see #addComponent(javax.swing.JComponent)
*/
@Override
public void removeComponent(JComponent component) {
if (secondaryComponents != null) {
secondaryComponents.remove(component);
}
}
void fireProblemReported(BindingProblem newProblem, BindingProblem oldProblem) {
for (BindingProblemListener listener : context.getProblemListeners()) {
listener.problemReported(newProblem, oldProblem);
}
}
void fireProblemCleared(BindingProblem oldProblem) {
for (BindingProblemListener listener : context.getProblemListeners()) {
listener.problemCleared(oldProblem);
}
}
}