net.sourceforge.vulcan.web.struts.forms.PluginConfigForm.java Source code

Java tutorial

Introduction

Here is the source code for net.sourceforge.vulcan.web.struts.forms.PluginConfigForm.java

Source

/*
 * Vulcan Build Manager
 * Copyright (C) 2005-2012 Chris Eldredge
 * 
 * This program 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 2 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 Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package net.sourceforge.vulcan.web.struts.forms;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import net.sourceforge.vulcan.StateManager;
import net.sourceforge.vulcan.dto.PluginConfigDto;
import net.sourceforge.vulcan.dto.PluginProfileDto;
import net.sourceforge.vulcan.dto.PluginConfigDto.Widget;
import net.sourceforge.vulcan.integration.ConfigChoice;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.upload.FormFile;
import org.apache.struts.validator.ValidatorForm;

public final class PluginConfigForm extends ValidatorForm implements DispatchForm {
    private static final Log log = LogFactory.getLog(PluginConfigForm.class);

    private static final class EnumConverter implements Converter {
        @SuppressWarnings("unchecked")
        public Object convert(@SuppressWarnings("rawtypes") Class target, Object arg) {
            return Enum.valueOf(target, (String) arg);
        }
    };

    public static final String HIDDEN_PASSWORD_VALUE = "****************";

    private transient FormFile pluginFile;
    private transient PluginConfigDto pluginConfig;
    private transient PluginConfigDto originalPluginConfig;
    private transient List<PropertyDescriptor> propertyDescriptors = new ArrayList<PropertyDescriptor>();
    private transient Set<PluginProfileDto> renamedProfiles = new HashSet<PluginProfileDto>();

    private boolean projectPlugin;
    private String projectName;

    private String action;
    private String pluginId;
    private String focus = "pluginConfig";
    private String target;

    private Map<String, String> types = new HashMap<String, String>();
    private Map<String, List<String>> choices = new HashMap<String, List<String>>();
    private Map<String, String> hiddenPasswords = new HashMap<String, String>();

    private List<String> availableProjects;
    private List<String> breadCrumbs = new ArrayList<String>();

    private boolean forceDirty;

    public String getName() {
        return pluginId;
    }

    public String getOriginalName() {
        return null;
    }

    public String getTargetType() {
        return "plugin";
    }

    public void introspect(HttpServletRequest request) throws IntrospectionException, IllegalAccessException,
            InvocationTargetException, NoSuchMethodException, InstantiationException {
        Class<?> cls = null;

        if ("pluginConfig".equals(focus)) {
            cls = pluginConfig.getClass();
            this.breadCrumbs.clear();
            this.breadCrumbs.add("Setup");

            if (isProjectPlugin()) {
                this.breadCrumbs.add("Projects");
                this.breadCrumbs.add(projectName);
                this.breadCrumbs.add(this.pluginConfig.getPluginName());
            } else {
                this.breadCrumbs.add("Plugins");
                this.breadCrumbs.add(this.pluginConfig.getPluginName());
            }
        } else {
            cls = PropertyUtils.getPropertyType(this, focus);
            if (cls.isArray()) {
                cls = cls.getComponentType();
            }
        }

        final String prefix = focus + ".";
        final PropertyDescriptor[] pds;

        if (PluginConfigDto.class.isAssignableFrom(cls)) {
            final PluginConfigDto pluginConfig = (PluginConfigDto) getFocusObject();
            final List<PropertyDescriptor> tmp = pluginConfig.getPropertyDescriptors(request.getLocale());
            pds = tmp.toArray(new PropertyDescriptor[tmp.size()]);

            if (pluginConfig instanceof PluginProfileDto) {
                ((PluginProfileDto) pluginConfig).checkPoint();
            }
        } else {
            final BeanInfo beanInfo = Introspector.getBeanInfo(cls);
            Introspector.flushFromCaches(cls);
            pds = beanInfo.getPropertyDescriptors();
        }

        if (isNested()) {
            for (PropertyDescriptor pd : propertyDescriptors) {
                if (focus.startsWith(pd.getName())) {
                    breadCrumbs.add(pd.getDisplayName());
                }
            }
        }

        types.clear();
        choices.clear();
        propertyDescriptors.clear();
        hiddenPasswords.clear();

        for (PropertyDescriptor pd : pds) {
            final String name = prefix + pd.getName();
            final PropertyDescriptor cp = new PropertyDescriptor(pd.getName(), pd.getReadMethod(),
                    pd.getWriteMethod());
            cp.setShortDescription(pd.getShortDescription());
            cp.setDisplayName(pd.getDisplayName());
            cp.setName(name);
            propertyDescriptors.add(cp);
            types.put(name, getTypeAndPrepare(name, pd));
        }

        putBreadCrumbsInRequest(request);
    }

    public Object getFocusObject() {
        return getProperty(focus);
    }

    public String[] getBreadCrumbs() {
        return breadCrumbs.toArray(new String[breadCrumbs.size()]);
    }

    public void putBreadCrumbsInRequest(HttpServletRequest request) {
        request.setAttribute("location", StringUtils.join(this.breadCrumbs.iterator(), " > "));
    }

    @Override
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        setAction(ConfigForm.translateAction(getAction()));
        restoreUnmodifiedPasswords();
        return super.validate(mapping, request);
    }

    @Override
    public void reset(ActionMapping mapping, HttpServletRequest request) {
        projectPlugin = false;
        pluginFile = null;
        target = null;
        focus = "pluginConfig";

        if (request == null) {
            return;
        }

        final String action = request.getParameter("action");
        if (action != null) {
            final String lower = action.toLowerCase();
            if (lower.equals("back") || lower.equals("update")) {
                resetPrimitiveArraysAndBooleans();
            }
        }

        loadProjectChoices(request);
    }

    public void resortArrayIfNecessary() {
        if (!focus.endsWith("]")) {
            return;
        }

        final Object arrayElement = getFocusObject();

        if (arrayElement instanceof Comparable<?>) {
            final String arrayProperty = focus.substring(0, focus.lastIndexOf('['));

            final Object[] array = (Object[]) getProperty(arrayProperty);

            Arrays.sort(array);
        }
    }

    public String getPluginId() {
        return pluginId;
    }

    public void setPluginId(String pluginId) {
        this.pluginId = pluginId;
    }

    public void setPluginConfig(HttpServletRequest request, PluginConfigDto pluginConfig, boolean dirty)
            throws IntrospectionException, IllegalAccessException, InvocationTargetException, NoSuchMethodException,
            InstantiationException {
        this.originalPluginConfig = (PluginConfigDto) pluginConfig.copy();
        this.pluginConfig = (PluginConfigDto) pluginConfig.copy();
        this.pluginId = this.pluginConfig.getPluginId();
        this.renamedProfiles.clear();

        this.forceDirty = dirty;
        introspect(request);
    }

    public boolean isDirty() {
        return forceDirty || !originalPluginConfig.equals(pluginConfig);
    }

    public PluginConfigDto getPluginConfig() {
        return pluginConfig;
    }

    public FormFile getPluginFile() {
        return pluginFile;
    }

    public void setPluginFile(FormFile pluginFile) {
        this.pluginFile = pluginFile;
    }

    public Set<PluginProfileDto> getRenamedProfiles() {
        return renamedProfiles;
    }

    public List<PropertyDescriptor> getAllProperties() {
        return propertyDescriptors;
    }

    public Map<String, String> getTypes() {
        return types;
    }

    public void setTypes(Map<String, String> types) {
        this.types = types;
    }

    public Map<String, List<String>> getChoices() {
        return choices;
    }

    public void setChoices(Map<String, List<String>> choices) {
        this.choices = choices;
    }

    public boolean isProjectPlugin() {
        return projectPlugin;
    }

    public String getProjectName() {
        return projectName;
    }

    public void setProjectName(String projectName) {
        this.projectName = projectName;
    }

    public void setProjectPlugin(boolean projectPlugin) {
        this.projectPlugin = projectPlugin;
    }

    public String getAction() {
        return action;
    }

    public void setAction(String action) {
        this.action = action;
    }

    public String getFocus() {
        return focus;
    }

    public void setFocus(String focus) {
        this.focus = focus;
    }

    public String getTarget() {
        return target;
    }

    public void setTarget(String removeObject) {
        this.target = removeObject;
    }

    public boolean isNested() {
        return !"pluginConfig".equals(focus);
    }

    Object getProperty(String property) {
        try {
            return PropertyUtils.getProperty(this, property);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @SuppressWarnings("unchecked")
    private String getTypeAndPrepare(String propertyName, PropertyDescriptor pd) {
        final Class<?> c = pd.getPropertyType();
        final ConfigChoice choicesType = (ConfigChoice) pd.getValue(PluginConfigDto.ATTR_CHOICE_TYPE);

        if (choicesType != null) {
            switch (choicesType) {
            case PROJECTS:
                choices.put(propertyName, availableProjects);
                break;
            case INLINE:
                choices.put(propertyName, (List<String>) pd.getValue(PluginConfigDto.ATTR_AVAILABLE_CHOICES));
            }
            if (c.isArray()) {
                return "choice-array";
            }
            return "enum";
        }

        final Widget widget = (Widget) pd.getValue(PluginConfigDto.ATTR_WIDGET_TYPE);

        if (Widget.PASSWORD.equals(widget)) {
            hidePassword(propertyName);
        }

        if (c.isArray()) {
            final Class<?> componentType = c.getComponentType();
            if (isPrimitive(componentType)) {
                return "primitive-array";
            } else if (Enum.class.isAssignableFrom(componentType)) {
                populateEnumChoices(propertyName, componentType);
                return "choice-array";
            }
            return "object-array";
        } else if (isBoolean(c)) {
            return "boolean";
        } else if (isPrimitive(c)) {
            if (widget != null) {
                return widget.name().toLowerCase();
            }
            return "primitive";
        } else if (Enum.class.isAssignableFrom(c)) {
            populateEnumChoices(propertyName, c);
            if (widget != null) {
                return widget.name().toLowerCase();
            }
            return "enum";
        }
        return "object";
    }

    private void hidePassword(String propertyName) {
        try {
            final String password = (String) PropertyUtils.getProperty(this, propertyName);
            if (!StringUtils.isBlank(password)) {
                hiddenPasswords.put(propertyName, password);
                PropertyUtils.setProperty(this, propertyName, HIDDEN_PASSWORD_VALUE);
            }
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        }
    }

    private void restoreUnmodifiedPasswords() {
        for (String propertyName : hiddenPasswords.keySet()) {
            restorePasswordIfNecessary(propertyName);
        }
    }

    private void restorePasswordIfNecessary(String propertyName) {
        try {
            final String submittedPassword = (String) PropertyUtils.getProperty(this, propertyName);
            if (HIDDEN_PASSWORD_VALUE.equals(submittedPassword)) {
                PropertyUtils.setProperty(this, propertyName, hiddenPasswords.get(propertyName));
            }
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            log.error("Exception accessing configuration data", e);
        }
    }

    private boolean isBoolean(Class<?> c) {
        return Boolean.class.equals(c) || Boolean.TYPE.equals(c);
    }

    private boolean isPrimitive(Class<?> cls) {
        if (cls.isPrimitive()) {
            return true;
        } else if ("java.lang".equals(cls.getPackage().getName())) {
            return true;
        } else if (Date.class.isAssignableFrom(cls)) {
            return true;
        }

        return false;
    }

    private void populateEnumChoices(String propertyName, Class<?> c) {
        final Enum<?>[] values;

        try {
            final Method m = c.getMethod("values", (Class[]) null);
            values = (Enum[]) m.invoke(null, (Object[]) null);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        ConvertUtils.register(new EnumConverter(), c);

        final List<String> list = new ArrayList<String>();

        for (Enum<?> e : values) {
            list.add(e.name());
        }

        choices.put(propertyName, list);
    }

    private void loadProjectChoices(HttpServletRequest request) {
        final StateManager mgr = ConfigForm.getStateManager(getServlet());
        availableProjects = mgr.getProjectConfigNames();
    }

    private void resetPrimitiveArraysAndBooleans() {
        final List<PropertyDescriptor> props = getAllProperties();

        for (PropertyDescriptor pd : props) {
            final String name = pd.getName();
            final String type = types.get(name);
            if (type.equals("primitive-array") || type.equals("choice-array")) {
                final Object emptyArray = Array.newInstance(pd.getPropertyType().getComponentType(), 0);

                try {
                    BeanUtils.setProperty(this, name, emptyArray);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            } else if (type.equals("boolean")) {
                try {
                    BeanUtils.setProperty(this, name, Boolean.FALSE);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        propertyDescriptors = new ArrayList<PropertyDescriptor>();
    }
}