/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fib.view.widget;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JLabel;
import org.openflexo.antar.binding.AbstractBinding.BindingEvaluationContext;
import org.openflexo.antar.binding.BindingVariable;
import org.openflexo.antar.binding.TypeUtils;
import org.openflexo.antar.expr.NullReferenceException;
import org.openflexo.antar.expr.TypeMismatchException;
import org.openflexo.fib.controller.FIBController;
import org.openflexo.fib.controller.FIBCustomDynamicModel;
import org.openflexo.fib.controller.FIBSelectable;
import org.openflexo.fib.model.DataBinding;
import org.openflexo.fib.model.FIBCustom;
import org.openflexo.fib.model.FIBCustom.FIBCustomAssignment;
import org.openflexo.fib.model.FIBCustom.FIBCustomComponent;
import org.openflexo.fib.view.FIBWidgetView;
import org.openflexo.swing.CustomPopup.ApplyCancelListener;
/**
* Defines an abstract custom widget
*
* @author sguerin
*
*/
public class FIBCustomWidget<J extends JComponent, T> extends FIBWidgetView<FIBCustom, J, T> implements ApplyCancelListener,
BindingEvaluationContext {
private static final Logger logger = Logger.getLogger(FIBCustomWidget.class.getPackage().getName());
private FIBCustomComponent<T, J> customComponent;
private final JLabel ERROR_LABEL = new JLabel("<Cannot instanciate component>");
public FIBCustomWidget(FIBCustom model, FIBController controller) {
super(model, controller);
try {
customComponent = makeCustomComponent(model.getComponentClass(), TypeUtils.getBaseClass(model.getDataType()), controller);
} catch (ClassCastException e) {
logger.warning("Could not instanciate component: ClassCastException, see logs for details");
e.printStackTrace();
}
if (customComponent != null) {
customComponent.addApplyCancelListener(this);
}
updateFont();
}
private FIBCustomComponent<T, J> makeCustomComponent(Class<FIBCustomComponent<T, J>> customComponentClass, Class<T> dataClass,
FIBController controller) {
if (customComponentClass == null) {
logger.warning("Could not instanciate custom component : no component class found");
return new NotFoundComponent();
}
Class[] types = new Class[1];
types[0] = dataClass;
try {
boolean found = false;
Constructor<FIBCustomComponent<T, J>> constructor = null;
while (!found && types[0] != null) {
try {
constructor = customComponentClass.getConstructor(types);
found = true;
} catch (NoSuchMethodException e) {
types[0] = types[0].getSuperclass();
}
}
if (constructor == null) {
for (Constructor c : customComponentClass.getConstructors()) {
if (c.getGenericParameterTypes().length == 1) {
constructor = c;
break;
}
}
}
if (constructor == null) {
logger.warning("Could not instanciate class " + customComponentClass + " : no valid constructor found, (searched "
+ customComponentClass.getSimpleName() + "(" + dataClass.getSimpleName() + ")...)");
return new NotFoundComponent();
}
Object[] args = new Object[1];
args[0] = null;
FIBCustomComponent<T, J> returned = constructor.newInstance(args);
returned.init(getComponent(), controller);
if (getDynamicModel() != null) {
getDynamicModel().customComponent = returned;
}
return returned;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
/**
* Must be overriden in sub-classes if required
*
* @param value
*/
protected void performModelUpdating(Object value) {
}
public void performModelUpdating() {
performModelUpdating(getValue());
}
public T getEditedValue() {
return getValue();
}
/**
* Update the model given the actual state of the widget
*/
@Override
public synchronized boolean updateModelFromWidget() {
return updateModelFromWidget(false);
}
public synchronized boolean updateModelFromWidget(boolean forceUpdate) {
if (forceUpdate || notEquals(getValue(), customComponent.getEditedObject())) {
setValue(customComponent.getEditedObject());
if (getWidget().getValueChangedAction().isValid()) {
getWidget().getValueChangedAction().execute(getController());
}
return true;
}
return false;
}
@Override
public J getJComponent() {
if (customComponent == null) {
return (J) ERROR_LABEL;
}
return customComponent.getJComponent();
}
@Override
public J getDynamicJComponent() {
if (customComponent != null) {
return customComponent.getJComponent();
}
return null;
}
private void performAssignments() {
for (FIBCustomAssignment assign : getWidget().getAssignments()) {
DataBinding variableDB = assign.getVariable();
DataBinding valueDB = assign.getValue();
if (valueDB != null && valueDB.getBinding() != null && valueDB.getBinding().isBindingValid()) {
Object value = null;
try {
value = valueDB.getBinding().getBindingValue(getController());
} catch (TypeMismatchException e) {
e.printStackTrace();
} catch (NullReferenceException e) {
e.printStackTrace();
}
if (variableDB.getBinding().isBindingValid()) {
// System.out.println("Assignment " + assign + " set value with " + value);
variableDB.getBinding().setBindingValue(value, this);
}
}
}
}
@Override
public boolean updateWidgetFromModel() {
// We need here to "force" update while some assignments may be required
// if (notEquals(getValue(), customComponent.getEditedObject())) {
/*if (getWidget().getComponentClass().getName().endsWith("FIBForegroundStyleSelector")) {
logger.info("GET updateWidgetFromModel() with " + getValue() + " for " + customComponent);
}*/
if (customComponent != null) {
// performAssignments();
try {
customComponent.setEditedObject(getValue());
} catch (ClassCastException e) {
customComponent.setEditedObject(null);
logger.warning("Unexpected ClassCastException in " + customComponent + ": " + e.getMessage());
// e.printStackTrace();
}
// Perform assignement AFTER the edited value was set !!!
// Tried to to it before raised to severe issues
performAssignments();
try {
customComponent.setRevertValue(getValue());
} catch (ClassCastException e) {
customComponent.setRevertValue(null);
logger.warning("Unexpected ClassCastException in " + customComponent + ": " + e.getMessage());
// e.printStackTrace();
}
}
return true;
// }
// return false;
}
@Override
public void fireApplyPerformed() {
if (logger.isLoggable(Level.FINE)) {
logger.fine("fireApplyPerformed() in FIBCustomWidget, value=" + customComponent.getEditedObject());
}
// In this case, we force model updating
updateModelFromWidget(true);
}
@Override
public void fireCancelPerformed() {
}
@Override
public Object getValue(BindingVariable variable) {
if (variable.getVariableName().equals("component")) {
return customComponent;
}
return null;
}
@Override
public boolean isSelectableComponent() {
return customComponent instanceof FIBSelectable;
}
@Override
public FIBSelectable getSelectableComponent() {
if (isSelectableComponent()) {
return (FIBSelectable) customComponent;
}
return null;
}
@Override
public FIBCustomDynamicModel<T, FIBCustomComponent<T, J>> createDynamicModel() {
return new FIBCustomDynamicModel<T, FIBCustomComponent<T, J>>(null);
}
@Override
public FIBCustomDynamicModel<T, FIBCustomComponent<T, J>> getDynamicModel() {
return (FIBCustomDynamicModel<T, FIBCustomComponent<T, J>>) super.getDynamicModel();
}
protected class NotFoundComponent implements FIBCustomComponent {
private JLabel label;
public NotFoundComponent() {
label = new JLabel("Component not defined");
}
@Override
public void init(FIBCustom component, FIBController controller) {
}
@Override
public void addApplyCancelListener(ApplyCancelListener l) {
}
@Override
public Object getEditedObject() {
return null;
}
@Override
public JComponent getJComponent() {
return label;
}
@Override
public Class<T> getRepresentedType() {
return null;
}
@Override
public T getRevertValue() {
return null;
}
@Override
public void removeApplyCancelListener(ApplyCancelListener l) {
}
@Override
public void setEditedObject(Object object) {
}
@Override
public void setRevertValue(Object object) {
}
}
}