All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jaxx.runtime.context.DefaultApplicationContext Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * *##% 
 * JAXX Runtime
 * Copyright (C) 2008 - 2009 CodeLutin
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser 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 Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * ##%*
 */
package jaxx.runtime.context;

import jaxx.runtime.*;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.annotation.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The default context to be used for an application.
 *
 * This extends the {@link DefaultJAXXContext} and add a possibility to auto-instanciate
 * some classes asked via {@link #getContextValue(java.lang.Class)} and
 * {@link #getContextValue(java.lang.Class, java.lang.String)} methods.
 *
 * To registred a such class, just annotate your class with {@link AutoLoad}.
 * 
 * @author chemit
 * @since 1.2
 * @see DefaultJAXXContext
 */
public class DefaultApplicationContext extends DefaultJAXXContext {

    /**
     * A class annotated @AutoLoad is used by context to auto
     * instanciate the class when required.
     *
     * Note : A such class always have a public default constructor.
     *
     * @author  chemit
     * @version 1.0, 21/02/09
     * @since 1.2
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public static @interface AutoLoad {
        //TODO use this to add a method to init 

        String initMethod() default "";
    }

    /**
     * A class annotated @MethodAccess is used by context to obtain the value
     * of an entry via a declared method.
     *
     * Note : A such class must have a method called {@link #methodName()} with 
     * a single String parameter.
     *
     * @author  chemit
     * @version 1.0, 21/02/09
     * @since 1.2
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    public static @interface MethodAccess {

        /**
         * Define a forward to a target class. When the target class
         * will be asked with method {@link JAXXContext#getContextValue(java.lang.Class, java.lang.String)}
         * it will be delegating to this class.
         *
         * @return the forwarded class
         */
        Class target() default Object.class;

        /**
         * @return the name of the method to access data
         */
        String methodName();
    }
    /**
     * Map of forwarded classes (key) to classes (values).
     */
    protected Map, Class> forwards;

    public DefaultApplicationContext() {
        super();
        forwards = new HashMap, Class>();
        pcs = new PropertyChangeSupport(this);
    }

//    public DefaultApplicationContext(JAXXObject ui) {
//        super();
//    }
    /** to use log facility, just put in your code: log.info(\"...\"); */
    static private final Log log = LogFactory.getLog(DefaultApplicationContext.class);

    /** to manage properties modifications */
    protected PropertyChangeSupport pcs;

    @SuppressWarnings({"unchecked"})
    @Override
    public  T getContextValue(Class clazz, String name) {
        Object value;
        MethodAccess access;

        Class realClass;

        if (forwards.containsKey(clazz)) {
            // this is a forward class            
            realClass = forwards.get(clazz);
            // always call the forwarder with no name
            value = getContextValue(realClass, null);
            if (log.isDebugEnabled()) {
                log.debug("detect forward from " + clazz + " to " + realClass + " (" + value + ")");
            }

        } else {
            realClass = clazz;
            value = super.getContextValue(realClass, name);
        }

        //TC-20091007 TODO Make possible use of named autoload entries
        //(add a parameter on AutoLoad annotation)
        if (value == null) {
            AutoLoad anno = clazz.getAnnotation(AutoLoad.class);

            if (anno == null) {
                // no annotation, so do nothing
                return null;
            }

            if (name != null) {
                throw new IllegalArgumentException("an " + AutoLoad.class.getName() + " can not have a named context but was call with this one : " + name);
            }
            value = newInstance(clazz);
            if (!anno.initMethod().trim().isEmpty()){
                // apply method on class
                newAccess(clazz, value, anno.initMethod().trim());
            }
            if (log.isDebugEnabled()) {
                log.debug("new instance " + clazz + " : " + value);
            }
            // save new instance            
            setContextValue(value, null);
        }

        access = realClass.getAnnotation(MethodAccess.class);

        if (access != null) {
            if (name == null) {
                if (access.target() != Object.class) {
                    // register forward
                    Class targetClass = access.target();
                    if (!forwards.containsKey(targetClass)) {
                        // register forward
                        forwards.put(targetClass, clazz);
                        if (log.isDebugEnabled()) {
                            log.debug("register forward from " + targetClass + " to " + clazz);
                        }
                    }
                }
            } else {
                // specialized access
                value = newAccess(realClass, value, access.methodName(), name);
            }
        }
        return (T) value;
    }

    @Override
    public  void removeContextValue(Class klazz, String name) {
        Entry, Class> entry;
        if (name == null && forwards.containsValue(klazz)) {
            // remove forward
            Iterator, Class>> itr = forwards.entrySet().iterator();
            while (itr.hasNext()) {
                entry = itr.next();
                if (entry.getValue().equals(klazz)) {
                    itr.remove();
                    if (log.isDebugEnabled()) {
                        log.debug("removed forward from " + entry.getKey() + " to " + klazz);
                    }
                    break;
                }
            }
        }
        //FIXME should update forwards state
        super.removeContextValue(klazz, name);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(propertyName, listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(propertyName, listener);
    }

    public synchronized boolean hasListeners(String propertyName) {
        return pcs.hasListeners(propertyName);
    }

    public synchronized PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {
        return pcs.getPropertyChangeListeners(propertyName);
    }

    public synchronized PropertyChangeListener[] getPropertyChangeListeners() {
        return pcs.getPropertyChangeListeners();
    }

    protected Object newInstance(Class clazz) throws IllegalArgumentException {

        Object value;

        Constructor constructor;
        try {
            constructor = clazz.getConstructor();
            // auto instanciate the class
            if (constructor == null) {
                throw new IllegalArgumentException(clazz + " has no public constructor");
            }
        } catch (NoSuchMethodException ex) {
            throw new IllegalArgumentException(ex);
        } catch (SecurityException ex) {
            throw new IllegalArgumentException(ex);
        }
        try {
            value = constructor.newInstance();

        } catch (InstantiationException ex) {
            throw new IllegalArgumentException(ex);
        } catch (IllegalAccessException ex) {
            throw new IllegalArgumentException(ex);
        } catch (InvocationTargetException ex) {
            throw new IllegalArgumentException(ex);
        }
        return value;
    }

    protected Object newAccess(Class clazz, Object parent, String methodName, String name) throws IllegalArgumentException {
        Object value;
        try {
            Method m = clazz.getMethod(methodName, String.class);
            value = m.invoke(parent, name);
            return value;
        } catch (NoSuchMethodException ex) {
            throw new IllegalArgumentException(ex);
        } catch (SecurityException ex) {
            throw new IllegalArgumentException(ex);
        } catch (IllegalAccessException ex) {
            throw new IllegalArgumentException(ex);
        } catch (InvocationTargetException ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    protected Object newAccess(Class clazz, Object parent, String methodName) throws IllegalArgumentException {
        Object value;
        try {
            Method m = clazz.getMethod(methodName);
            value = m.invoke(parent);
            return value;
        } catch (NoSuchMethodException ex) {
            throw new IllegalArgumentException(ex);
        } catch (SecurityException ex) {
            throw new IllegalArgumentException(ex);
        } catch (IllegalAccessException ex) {
            throw new IllegalArgumentException(ex);
        } catch (InvocationTargetException ex) {
            throw new IllegalArgumentException(ex);
        }
    }

    protected void firePropertyChange(String name, Object oldValue, Object newValue) {
        pcs.firePropertyChange(name, oldValue, newValue);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy