/*
* Copyright (c) 2014 tabletoptool.com team.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* rptools.com team - initial implementation
* tabletoptool.com team - further development
*/
package com.t3.client.swing;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import org.apache.log4j.Logger;
import yasb.Binder;
import yasb.core.AdapterException;
import yasb.core.BindingInfo;
import yasb.core.Property;
import yasb.core.UpdateTime;
import yasb.swing.AbstractComponentAdapter;
import yasb.swing.BindingResolver;
import com.jeta.forms.components.panel.FormPanel;
/**
* <p>
* This class acts as a "field binding front-end" for the {@link FormPanel}
* class.
* </p>
* <p>
* After instantiating an object and passing it the name of the Abeille form,
* call the {@link #bind(Object)} method and pass the data model instance as a
* parameter. This class will then copy the data from the model to the view
* using the field names specified when the Abeille form was created. View field
* names must start with "@" to be automatically associated with a corresponding
* model field. Anything in the field name from the first period to the end is
* ignored.
* </p>
* <p>
* As changes occur to the view (the user has edited the text fields or changed
* the value of a radiobutton), those changes will NOT propagate back to the
* model -- the application programmer must call {@link #commit()} for those
* changes to be recorded in the model. This allows for Cancel, OK, and Reset
* buttons, if desired.
* </p>
* <p>
* In all cases, the {@link Binder} class is the one that uses Reflection and
* standard JavaBean characteristics to link view fields with model fields.
* </p>
*
* @author tcroft
*
* @param <T>
*/
@SuppressWarnings("serial")
public class AbeillePanel<T> extends JPanel {
private static final Logger log = Logger.getLogger(AbeillePanel.class);
private final FormPanel panel;
private T model;
static {
Binder.setDefaultAdapter(JRadioButton.class, RadioButtonAdapter.class);
Binder.setBindingResolver(new BindingResolver() {
@Override
public BindingInfo getBindingInfo(Component view) {
String name = view.getName();
if (name == null || !name.startsWith("@")) {
return null;
}
// System.out.println("Name:" + name);
name = name.substring(1).trim(); // cut the "@"
int point = name.indexOf(".");
if (point >= 0)
name = name.substring(0, point).trim();
return new BindingInfo(name);
}
@Override
public void storeBindingInfo(Component view, BindingInfo info) {
}
});
}
public AbeillePanel(String panelForm) {
setLayout(new GridLayout());
panel = new FormPanel(panelForm);
add(panel);
}
public T getModel() {
return model;
}
/**
* Call any method on the class that matches "init*" that has zero arguments
*/
protected void panelInit() {
for (Method method : getClass().getMethods()) {
if (method.getName().startsWith("init")) {
try {
method.invoke(this, new Object[] {});
} catch (IllegalArgumentException e) {
log.error("Could not init method: " + method.getName(), e);
} catch (IllegalAccessException e) {
log.error("Could not init method: " + method.getName(), e);
} catch (InvocationTargetException e) {
log.error("Could not init method: " + method.getName(), e);
}
}
}
}
protected void replaceComponent(String panelName, String name, Component component) {
panel.getFormAccessor(panelName).replaceBean(name, component);
panel.reset();
}
protected Component getComponent(String name) {
return panel.getComponentByName(name);
}
/**
* Creates the link between the model and the view by calling
* {@link Binder#bindContainer(Class, java.awt.Container, UpdateTime)} and
* passing it the class of the model, the view component, and when to make
* the updates.
* <p>
* This code assumes that the updates will never occur automatically.
* </p>
* <p>
* Also, this code calls the protected method {@link #preModelBind()} to
* allow subclasses to modify the binding characteristics before copying the
* model fields to the view.
* </p>
*
* @param model
*/
public void bind(T model) {
if (this.model != null) {
throw new IllegalStateException("Already bound exception");
}
this.model = model;
Binder.bindContainer(model.getClass(), panel, UpdateTime.NEVER);
preModelBind();
Binder.modelToView(model, panel);
}
protected void preModelBind() {
// Do nothing
}
/**
* This method is invoked by the application code whenever it wants to copy
* data from the view to the model.
*
* @return <code>true</code> if successful, <code>false</code> otherwise
*/
public boolean commit() {
if (model != null) {
try {
Binder.viewToModel(model, panel);
} catch (AdapterException e) {
e.printStackTrace();
return false;
}
}
return true;
}
/**
* Breaks the binding between the model and the view.
*/
public void unbind() {
model = null;
}
public static class RadioButtonAdapter<T extends Enum<T>> extends AbstractComponentAdapter implements ItemListener {
private JRadioButton button;
private T selected;
// COMPONENT ADAPTER
@Override
protected Object getActualContent() {
try {
return getValue();
} catch (Exception e) {
// YLogger.logException(e);
return null;
}
}
@Override
protected T getValue() throws Exception {
return button.isSelected() ? selected : null;
}
@Override
protected void setupListener() {
button.addItemListener(this);
}
@Override
protected void showValue(Object value) {
if (value == selected) {
button.setSelected(true);
}
}
@Override
public void viewToModel(Object dataSource) throws AdapterException {
if (!button.isSelected()) {
return;
}
super.viewToModel(dataSource);
}
@Override
public void bind(Property property, Component view, UpdateTime updateTime) {
// System.out.println("bind:" + view.getName() + " - " + view);
if (view instanceof JRadioButton) {
button = (JRadioButton) view;
super.bind(property, view, updateTime);
String bindVal = button.getName();
bindVal = bindVal.substring(bindVal.indexOf(".") + 1);
selected = Enum.valueOf((Class<T>)property.getType(), bindVal);
}
}
// ITEM LISTENER
@Override
public void itemStateChanged(ItemEvent e) {
fireViewChanged();
fireViewEditValidated();
}
}
}