de.jwic.base.Control.java Source code

Java tutorial

Introduction

Here is the source code for de.jwic.base.Control.java

Source

/*
 * Copyright 2005 jWic group (http://www.jwic.de)
 * 
 * Licensed 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.
 * 
 * de.jwic.base.Control
 * $Id: Control.java,v 1.12 2010/02/07 14:24:24 lordsam Exp $
 */
package de.jwic.base;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONWriter;

/**
 * <p>Superclass for jWic controls placed into an jWic container. A Control represents
 * a part of an Application. All controls live in the SessionContext object, wich 
 * represents the application environment. A Control is connected to a 
 * {@link Container} object. 
 * 
 * <p>Control objects live persistent until the application is closed or the control
 * is removed from it's container.
 * 
 * <p>A control is created like a normal java object, using its constructor:
 * <code>ButtonControl button = new ButtonControl(container);</code> 
     
 * @author Florian Lippisch
 * @version $Revision: 1.12 $
 */
public abstract class Control implements Serializable, IControl {

    private static final long serialVersionUID = 1L;

    protected final static String DEFAULT_RENDERER = "jwic.renderer.Default";
    protected final static String DEFAULT_OUTER_RENDERER = "jwic.renderer.OuterContainer";

    private final static String ACTION_PREFIX = "action";

    /** Logger available to subclasses */
    protected transient Log log = LogFactory.getLog(getClass());

    protected boolean bolRequireRedraw = false;
    protected boolean bolVisible = true;

    private String name = null;
    private IControlContainer objContainer = null;
    private SessionContext objSessionContext = null;
    private String strControlID = null;
    private String rendererId = DEFAULT_RENDERER;

    protected Map<String, Field> fields = null;
    protected String templateName = null;

    /**
     * Constructs a new control instance and adds it to the specified
     * container with the specified name. If the name is <code>null</code>,
     * a unique name will be choosen by the container.
     */
    public Control(IControlContainer container, String name) {
        container.registerControl(this, name);
    }

    /**
     * Get new logger after deserialization.
     * @param s
     * @throws IOException
     */
    private void readObject(ObjectInputStream s) throws IOException {
        try {
            s.defaultReadObject();
        } catch (ClassNotFoundException e) {
            throw new IOException("ClassNotFound in readObject");
        }
        log = LogFactory.getLog(getClass());
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#actionPerformed(java.lang.String, java.lang.String)
     */
    public void actionPerformed(String actionId, String parameter) {

        String methodName;
        if (actionId.length() == 0) {
            return;
        } else if (actionId.length() == 1) {
            methodName = ACTION_PREFIX + actionId.toUpperCase();
        } else {
            methodName = ACTION_PREFIX + actionId.substring(0, 1).toUpperCase() + actionId.substring(1);
        }

        // try to find a method of the implementing control class that
        // equals the actionId

        Method actionMethod = null;
        Object[] arguments = new Object[] { parameter };
        try {
            actionMethod = getClass().getMethod(methodName, new Class[] { String.class });
        } catch (NoSuchMethodException e) {
            // if the parameter is empty, try to find a method without arguments
            if (parameter.length() == 0) {
                arguments = null;
                try {
                    actionMethod = getClass().getMethod(methodName, (Class[]) null);
                } catch (NoSuchMethodException e1) {
                    // no method exists -> will be handled later
                }
            }
        }
        if (actionMethod == null) {
            // FLI: I do now throw an exception if the desired method was not found. It should
            // not break existing applications because this method was abstract in earlier versions 
            // so it can not be called accidently.
            log.error("A method to handle the action '" + actionId + "' does not exist in this control. ("
                    + methodName + ")");
            throw new IllegalArgumentException("A method to handle the action '" + actionId
                    + "' does not exist in this control. (" + methodName + ")");
        } else {
            try {
                actionMethod.invoke(this, arguments);
            } catch (IllegalArgumentException e) {
                log.error("Error invoking method " + actionMethod.getName(), e);
            } catch (IllegalAccessException e) {
                log.error("Error invoking method " + actionMethod.getName(), e);
            } catch (InvocationTargetException e) {
                Throwable t = e.getCause();
                if (t instanceof RuntimeException) {
                    // forward the exception
                    throw (RuntimeException) t;
                } else if (t != null) {
                    String msg = "Error during invocation of action method " + actionMethod.getName();
                    log.error(msg, t);
                    throw new RuntimeException(msg, t);
                }
                String msg = "Unexpected InvocationTargetException";
                log.error(msg, e);
                throw new RuntimeException(msg);
            }
        }

    }

    /**
     * Adds a field to the fieldlist.
     * @param name
     * @param field
     */
    void addField(Field field) {

        if (fields == null) {
            fields = new HashMap<String, Field>();
        }

        if (field.getName() == null) {
            // generate a name.
            int idx = 0;
            String fName = Integer.toString(idx);
            while (fields.containsKey(fName)) {
                fName = Integer.toString(++idx);
            }
            field.setName(fName);
        }
        if (fields.containsKey(field.getName())) {
            throw new IllegalArgumentException("A field with the name '" + field.getName()
                    + "' already exists in control '" + getControlID() + "'.");
        }
        fields.put(field.getName(), field);
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#destroy()
     */
    public void destroy() {
        if (objContainer != null && objContainer.getControl(name) != null) {
            // remove this control from container
            objContainer.removeControl(name);
        } else {
            // remove from top control
            if (objSessionContext != null) {
                objSessionContext.removeTopControl(this);
            }
        }
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#removeField(java.lang.String)
     */
    public void removeField(String fieldname) {
        if (fields != null) {
            fields.remove(fieldname);
        }
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#removeField(de.jwic.base.Field)
     */
    public void removeField(Field field) {
        if (fields != null) {
            fields.remove(field.getName());
        }
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#getField(java.lang.String)
     */
    public Field getField(String fieldname) {
        if (fields != null) {
            return fields.get(fieldname);
        }
        return null;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#createActionURL(java.lang.String, java.lang.String)
     */
    public String createActionURL(String action, String acpara) {
        StringBuffer sb = new StringBuffer();
        sb.append("javascript:JWic.fireAction('");
        sb.append(getControlID());
        sb.append("', '");
        sb.append(action);
        sb.append("', '");
        sb.append(acpara);
        sb.append("');");

        return sb.toString();
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#getContainer()
     */
    public IControlContainer getContainer() {
        return objContainer;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#getControlID()
     */
    public java.lang.String getControlID() {
        return strControlID;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#getSessionContext()
     */
    public SessionContext getSessionContext() {
        return objSessionContext;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#isRequireRedraw()
     */
    public boolean isRequireRedraw() {
        return bolRequireRedraw;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#isVisible()
     */
    public boolean isVisible() {
        return bolVisible;
    }

    /**
     * This method is used to give the control a connection to the Container object
     * when it's added to a container.
     */
    void setContainer(IControlContainer newContainer) {
        objContainer = newContainer;
    }

    /**
     * Set the ID of the control. Invoked when the control is added to a container.
     * @param newControlID String
     */
    void setControlID(String newControlID) {
        strControlID = newControlID;
    }

    /**
     * Flags the control that it must be rendered again to reflect its
     * current state. Control implementors should use this method to
     * let the framework render the control again when a page update 
     * occures.
     */
    public void requireRedraw() {
        setRequireRedraw(true);
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#setRequireRedraw(boolean)
     */
    public void setRequireRedraw(boolean requireRedraw) {
        bolRequireRedraw = requireRedraw;
    }

    /**
     * This method is used to give the control a connection to the SessionContext object
     * when it's added to a container.
     */
    void setSessionContext(SessionContext newSessionContext) {
        objSessionContext = newSessionContext;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#setVisible(boolean)
     */
    public void setVisible(boolean newVisible) {
        if (bolVisible != newVisible) {
            // JBO 2005-09-06: for nicer ajax support container is not updated anymore
            setRequireRedraw(true);
        }
        bolVisible = newVisible;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#getName()
     */
    public final String getName() {
        return name;
    }

    /**
     * Sets the name of the Contorl.
     * @param name The name to set.
     */
    void setName(String name) {
        if (this.name != null && !this.name.equals(name)) {
            throw new IllegalStateException("The name of a control can not be changed.");
        }
        this.name = name;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#getRendererId()
     */
    public String getRendererId() {
        return rendererId;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#setRendererId(java.lang.String)
     */
    public void setRendererId(String rendererId) {
        this.rendererId = rendererId;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#getTemplateName()
     */
    public String getTemplateName() {
        if (templateName == null) {
            Class<?> clazz = getClass();
            for (String classname = clazz.getName(); classname.indexOf("$") != -1;) {
                clazz = clazz.getSuperclass();
                classname = clazz.getName();
            }
            return clazz.getName();
        }
        return templateName;
    }

    /* (non-Javadoc)
     * @see de.jwic.base.IControl#setTemplateName(java.lang.String)
     */
    public void setTemplateName(String templateName) {
        this.templateName = templateName;
    }

    /**
     * Builds Json object string of all methods marked with 
     * IncludeJsOption Annotation.
     * If Enums are used getCode needs to be implemented, which returns the value
     * @return
     */
    public String buildJsonOptions() {
        try {
            StringWriter sw = new StringWriter();
            JSONWriter writer = new JSONWriter(sw);
            writer.object();
            BeanInfo beanInfo;
            beanInfo = Introspector.getBeanInfo(getClass());

            PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();

            for (PropertyDescriptor pd : pds) {
                Method getter = pd.getReadMethod();
                if (getter != null && pd.getReadMethod().getAnnotation(IncludeJsOption.class) != null) {
                    Object o = getter.invoke(this);
                    if (o != null) {
                        if (o instanceof Enum) {
                            Method m = o.getClass().getMethod("getCode", null);
                            if (m != null) {
                                writer.key(pd.getDisplayName()).value(m.invoke(o));
                                continue;
                            }
                        } else if (o instanceof Date) {
                            writer.key(pd.getDisplayName())
                                    .value(new JsDateString((Date) o, getSessionContext().getTimeZone()));
                            continue;
                        }

                        // if name has been changed use that one.
                        IncludeJsOption annotation = pd.getReadMethod().getAnnotation(IncludeJsOption.class);
                        if (annotation.jsPropertyName() != null && annotation.jsPropertyName().length() > 0) {
                            writer.key(annotation.jsPropertyName());
                        } else {
                            writer.key(pd.getDisplayName());
                        }
                        writer.value(o);
                    }
                }
            }
            writer.endObject();
            return sw.toString();
        } catch (Exception e) {
            throw new RuntimeException("Error while configuring Json Option for " + this.getClass().getName());
        }
    }
}