com.maydesk.base.util.PDBinding.java Source code

Java tutorial

Introduction

Here is the source code for com.maydesk.base.util.PDBinding.java

Source

/* This file is part of the MayDesk project.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.*/

package com.maydesk.base.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;

import lombok.soplets.Beanable;
import nextapp.echo.app.Component;
import nextapp.echo.app.event.ActionEvent;
import nextapp.echo.app.event.ActionListener;
import nextapp.echo.app.text.TextComponent;

import org.hibernate.Session;

import com.maydesk.base.PDDesktop;
import com.maydesk.base.PDHibernateFactory;
import com.maydesk.base.gui.PDCheckBox;
import com.maydesk.base.model.MBase;
import com.maydesk.base.model.MBaseWithTitle;
import com.maydesk.base.model.MDataLink;
import com.maydesk.base.widgets.PDTextField;

/**
 * @author chrismay
 */
public class PDBinding {

    private Hashtable<IChangeSupportable, MyChangeData> changeData = new Hashtable<IChangeSupportable, MyChangeData>();
    private Hashtable<String, MyChangeData> changeDataByFieldName = new Hashtable<String, MyChangeData>();
    private MBase target;
    private Class<? extends MBase> targetClass;
    private ActionListener titleChangeListener;
    private ActionListener somethingChangedListener;
    private ActionListener saveDoneListener;
    private Set<PDBinding> children = new HashSet<PDBinding>();
    private MDataLink dataLink;
    private boolean isDeleted;

    public void setStatusDeleted(boolean deleted) {
        isDeleted = deleted;
        fireChangeEvent();
    }

    public PDBinding(Class<? extends MBase> targetClass) {
        this.targetClass = targetClass;
    }

    public void setTitleChangeListener(ActionListener titleChangeListener) {
        this.titleChangeListener = titleChangeListener;
    }

    public void setSaveDoneListener(ActionListener saveDoneListener) {
        this.saveDoneListener = saveDoneListener;
    }

    public void setSomethingChangedListener(ActionListener somethingChangedListener) {
        this.somethingChangedListener = somethingChangedListener;
        for (PDBinding childBinding : children) {
            childBinding.setSomethingChangedListener(somethingChangedListener);
        }
    }

    public void doChange(IChangeSupportable component, Object newValue) {
        if (target == null)
            return;

        Object oldValue = null;
        MyChangeData cd = changeData.get(component);
        try {
            Component c = (Component) component;
            // store the old value (or retrieve the original one)
            oldValue = cd.getter.invoke(target, new Object[] {});
            if (cd.originalValue == null) {
                cd.originalValue = oldValue;
            } else {
                oldValue = cd.originalValue;
            }
            if (newValue == null && oldValue == null) {
                return;
            }

            // set the border of the component
            if (newValue != null && newValue.equals(oldValue)) {
                cd.changed = false;
                component.setBorder(PDBorderFactory.getBorder());
            } else {
                cd.changed = true;
                component.setBorder(PDBorderFactory.getBorderActive());
            }
            fireChangeEvent();

            // apply value to target entity
            cd.setter.invoke(target, newValue);

            // update the label of the list entry
            if (titleChangeListener != null && cd.listen && target instanceof MBaseWithTitle) {
                ((MBaseWithTitle) target).setCachedTitle(((MBaseWithTitle) target).createCachedTitle());
                ((MBaseWithTitle) target).setCachedDescription(((MBaseWithTitle) target).createCachedDescription());
                ActionEvent e = new ActionEvent(target, null);
                titleChangeListener.actionPerformed(e);
            }

            // display the flash label (top-right corner of workspace)
            if (PDDesktop.getInstance() != null) {
                PDDesktop.getInstance().showSaving();
            }

            // MActionValueChange action = new MActionValueChange();
            // action.setValueClass(cd.getter.getReturnType().getCanonicalName());
            // action.setOldValue(oldValue);
            // action.setNewValue(newValue);
            // action.setUser(PDUserSession.getInstance().getUser());
            // action.setChangeSupport(this);
            // action.setTargetClass(target.getClass().getCanonicalName());
            // action.setTargetId(target.getId());
            // action.setTargetField(cd.fieldName);
            // PDActionManager.getInstance().addAction(action);
        } catch (Exception e1) {
            System.out.println(oldValue + " -> " + newValue);
            e1.printStackTrace();
        }
    }

    public void fireChangeEvent() {
        // activate the save/cancel button
        if (somethingChangedListener != null) {
            ActionEvent e = new ActionEvent(hasChanges(), null);
            somethingChangedListener.actionPerformed(e);
        }
    }

    public MBase getTarget() {
        return target;
    }

    public Class getTargetClass() {
        return targetClass;
    }

    public void read(MBase target) {
        this.target = target;
        if (target == null) {
            throw new IllegalArgumentException("Target is null");
        }
        for (IChangeSupportable component : changeData.keySet()) {
            MyChangeData cd = changeData.get(component);
            if (cd == null)
                return;
            try {
                Object value = cd.getter.invoke(target, new Object[0]);
                component.setValue(value);
            } catch (Exception e1) {
                System.out.println("Field: " + cd.fieldName);
                e1.printStackTrace();
            }
        }
        if (titleChangeListener != null) {
            ActionEvent e = new ActionEvent(target, null);
            titleChangeListener.actionPerformed(e);
        }
    }

    // public void redoChange(MActionValueChange action) {
    // MyChangeData cd = changeDataByFieldName.get(action.getTargetField());
    // Object newValue = action.getNewValue();
    // try {
    // cd.setter.invoke(target, new Object[] { newValue });
    // if (valueListener != null) { // && cd.listen) {
    // valueListener.actionPerformed(null);
    // }
    // cd.component.setValue(newValue);
    // ApplicationInstance.getActive().setFocusedComponent((Component) cd.component);
    // } catch (Exception e1) {
    // e1.printStackTrace();
    // }
    // PDHibernateFactory.getSession().update(target);
    // }

    public <T extends IChangeSupportable<?>> T register(T component, Beanable sopletRef) {
        return register(component, sopletRef, false);
    }

    public <T extends IChangeSupportable<?>> T register(final T component, Beanable sopletRef, boolean listen) {
        String name = null;
        if (sopletRef instanceof SimpleName) {
            name = ((SimpleName) sopletRef).name();
        } else {
            name = ((Enum) sopletRef).name();
        }
        String fieldName2 = name.substring(0, 1).toUpperCase() + name.substring(1);

        MyChangeData cd = new MyChangeData();
        cd.listen = listen;
        cd.soplet = sopletRef;
        cd.fieldName = fieldName2;

        component.setChangeSupport(this);
        changeData.put(component, cd);
        changeDataByFieldName.put(fieldName2, cd);
        try {
            String prefix = component instanceof PDCheckBox ? "is" : "get";
            cd.getter = targetClass.getMethod(prefix + fieldName2, new Class[] {});

            Class valueClass = cd.getter.getReturnType();
            cd.setter = targetClass.getMethod("set" + fieldName2, new Class[] { valueClass });

            if (component instanceof IChangeSupportableWithAction) {
                // immediate save on Enter or on Blur
                IChangeSupportableWithAction cswa = (IChangeSupportableWithAction) component;
                cswa.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        doChange(component, component.getValue());
                    }
                });
            }

            if (component instanceof PDTextField) {
                if (sopletRef instanceof Enum) {
                    Integer length = null; //PDUtil.getOverrideInt((Enum) sopletRef, "length");
                    //if (length == null) {
                    length = sopletRef.length();
                    //}
                    if (length != null && length > 0) {
                        ((PDTextField) component).setMaximumLength(length);
                    }
                }
            }
            ;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return component;
    }

    // public void undoChange(MActionValueChange action) {
    // MyChangeData cd = changeDataByFieldName.get(action.getTargetField());
    // Object newValue = action.getOldValue();
    // Object oldValue = action.getNewValue();
    //
    // try {
    // cd.setter.invoke(target, new Object[]{newValue});
    // if (valueListener != null) { // && cd.listen) {
    // valueListener.actionPerformed(null);
    // }
    // cd.component.setValue(newValue);
    // } catch (Exception e1) {
    // System.out.println("undo: " + oldValue + " -> " + newValue);
    // e1.printStackTrace();
    // }
    // PDHibernateFactory.getSession().update(target);
    // }

    class MyChangeData {
        private Beanable soplet;
        private String fieldName;
        private Method getter;
        private boolean listen;
        private Method setter;
        private Object originalValue;
        private boolean changed;
    }

    public boolean applyChanges() {
        // validate values
        boolean validationOK = true;
        for (IChangeSupportable component : changeData.keySet()) {
            component.setBorder(PDBorderFactory.getBorder());
            if (component instanceof TextComponent) {
                ((TextComponent) component).setToolTipText(""); // FIXME
            }

            MyChangeData cd = changeData.get(component);
            if (cd == null)
                continue;

            // validation
            try {
                if (!(cd.soplet instanceof Enum))
                    continue;
                Annotation sopletAnnotation = cd.soplet.getClass().getField(((Enum) cd.soplet).name())
                        .getAnnotations()[0];
                if (sopletAnnotation == null)
                    continue;
                Method m = sopletAnnotation.getClass().getMethod("validator");
                if (m == null)
                    continue;
                Enum<?> validator = (Enum<?>) m.invoke(sopletAnnotation);
                if (validator == null)
                    continue;
                Method validate = validator.getClass().getMethod("validate", Object.class);
                validate.setAccessible(true);
                Object value = component.getValue();
                Boolean validationResult = (Boolean) validate.invoke(validator, value);
                if (Boolean.TRUE.equals(validationResult))
                    continue;

                // validation error found
                component.setBorder(PDBorderFactory.getBorderError());
                if (component instanceof TextComponent) {
                    Field field = validator.getDeclaringClass().getField(validator.name());
                    Annotation ann = field.getAnnotations()[0];
                    Method m2 = ann.getClass().getMethod("textEN");
                    if (m2 != null) {
                        String errorMessage = (String) m2.invoke(ann);
                        ((TextComponent) component).setToolTipText(errorMessage);
                    }
                }
                validationOK = false;
            } catch (NoSuchMethodException ex) {
                // no validator
            } catch (InvocationTargetException ex) {
                // no validator
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        if (!validationOK) {
            return false;
        }

        if (hasChanges()) {
            if (target instanceof MBaseWithTitle) {
                ((MBaseWithTitle) target).updateCachedValues();
            }
            Session session = PDHibernateFactory.getSession();
            if (target.getId() == 0) {
                session.save(target);
                if (dataLink != null) {
                    session.flush();
                    session.refresh(target);
                    dataLink.setTargetId(target.getId());
                    session.saveOrUpdate(dataLink);
                }
            } else {
                session.update(target);
            }
        }
        for (PDBinding childBinding : children) {
            childBinding.applyChanges();
        }
        resetChanges();
        if (saveDoneListener != null) {
            ActionEvent e = new ActionEvent(target, null);
            saveDoneListener.actionPerformed(e);
        }
        return true;
    }

    public boolean hasChanges() {
        if (target == null) {
            return false;
        }
        if (isDeleted && target.getId() > 0) {
            return true; // deleted an (existing!) object
        } else if (target.getId() == 0) {
            return true; // newly created object
        }

        for (IChangeSupportable c : changeData.keySet()) {
            MyChangeData cd = changeData.get(c);
            if (cd.changed) {
                return true;
            }
        }
        for (PDBinding childBinding : children) {
            if (childBinding.hasChanges()) {
                return true;
            }
        }
        return false;
    }

    public void resetChanges() {
        isDeleted = false;
        for (IChangeSupportable c : changeData.keySet()) {
            c.setBorder(PDBorderFactory.getBorder());
            MyChangeData cd = changeData.get(c);
            cd.changed = false;
            cd.originalValue = null;
        }
        for (PDBinding childBinding : children) {
            childBinding.resetChanges();
        }
    }

    public void addChild(PDBinding childBinding) {
        children.add(childBinding);
    }

    public void resetChildren() {
        children.clear();
    }

    public Set<PDBinding> getChildren() {
        return children;
    }

    public ActionListener getSomethingChangedListener() {
        return somethingChangedListener;
    }

    public MBase readDataLink(MDataLink dataLink) {
        this.dataLink = dataLink;
        MBase model = null;
        if (dataLink.getTargetId() == -1) {
            try {
                model = (MBase) Class.forName(dataLink.getTargetClass()).newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            model = MBase.loadByDataLink(dataLink);
        }
        if (model != null)
            read(model);
        return model;
    }
}