net.erdfelt.android.sdkfido.configer.Configer.java Source code

Java tutorial

Introduction

Here is the source code for net.erdfelt.android.sdkfido.configer.Configer.java

Source

/*******************************************************************************
 *    Copyright 2012 - Joakim Erdfelt
 *
 *    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.
 *******************************************************************************/
package net.erdfelt.android.sdkfido.configer;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;

public class Configer {
    private static final Logger LOG = Logger.getLogger(Configer.class.getName());
    public static final File RC_DIR = new File(SystemUtils.getUserHome(), ".sdkfido");
    private BeanUtilsBean bub;
    private Object obj;
    private Map<String, Configurable> configurables = new HashMap<String, Configurable>();
    // File to persist configuration to/from
    private File persistFile;
    // Resource Bundle for various descriptive definitions of the configurable fields
    private ResourceBundle defsBundle;
    private Set<String> scopes = new TreeSet<String>();
    private Method rawArgAdder;

    public Configer(Object o) {
        this.obj = o;
        this.bub = BeanUtilsBean.getInstance();

        // Setup default persistent storage file
        this.persistFile = new File(RC_DIR, "config.properties");

        // Load descriptive definitions
        try {
            defsBundle = ResourceBundle.getBundle(o.getClass().getName());
            Enumeration<String> keys = defsBundle.getKeys();
            while (keys.hasMoreElements()) {
                String key = keys.nextElement();
                if (key.startsWith("scope.")) {
                    String scope = key.substring("scope.".length());
                    scopes.add(scope);
                }
            }
        } catch (MissingResourceException e) {
            LOG.log(Level.WARNING, e.getMessage(), e);
        }

        // Identify all of the configurable fields
        walkFields(null, o.getClass(), null);

        // Identify the method to use for accepting raw arguments
        findRawArgsMethod(o.getClass());
    }

    public void addManualConfigurable(String option, String type, String description) {
        String key = option;
        if (key.startsWith("--")) {
            key = key.substring(2);
        }
        Configurable cfrb = new Configurable(key, type, description);
        this.configurables.put(key, cfrb);
    }

    public Configurable getConfigurable(String key) {
        return this.configurables.get(key);
    }

    public Set<Configurable> getConfigurables() {
        Set<Configurable> confs = new TreeSet<Configurable>(new ConfigurableComparator());
        confs.addAll(configurables.values());
        return confs;
    }

    public String getDefinition(String key) {
        try {
            return defsBundle.getString(key);
        } catch (MissingResourceException e) {
            LOG.warning("Unable to find description under ResourceBundle key: " + key);
            return "";
        }
    }

    public File getPersistFile() {
        return persistFile;
    }

    public String getScopeDescription(String scope) {
        return getDefinition("scope." + scope);
    }

    public List<String> getScopeIds() {
        List<String> scopelist = new ArrayList<String>(scopes);
        Collections.sort(scopelist);
        return scopelist;
    }

    public String getValue(String key) {
        Configurable cbl = configurables.get(key);
        if (cbl == null) {
            return null;
        }
        try {
            return this.bub.getProperty(this.obj, key);
        } catch (Throwable t) {
            LOG.log(Level.WARNING, "Unable to get value for key: " + key, t);
            return null;
        }
    }

    public boolean hasConfigurable(String key) {
        return configurables.containsKey(key);
    }

    private void findRawArgsMethod(Class<?> clazz) {
        for (Method method : clazz.getDeclaredMethods()) {
            ConfigArguments arg = method.getAnnotation(ConfigArguments.class);
            if (arg == null) {
                continue; // skip, not tagged
            }

            int mods = method.getModifiers();
            if (!Modifier.isPublic(mods)) {
                continue; // skip, not public
            }

            if (Modifier.isStatic(mods)) {
                continue; // skip, dont support static
            }

            Class<?>[] params = method.getParameterTypes();
            if (params == null) {
                continue; // skip, needs params
            }

            if (params.length != 1) {
                continue; // skip, needs 1 param
            }

            if (!(params[0].equals(String.class))) {
                continue; // skip, param must be String
            }

            if (this.rawArgAdder != null) {
                StringBuilder err = new StringBuilder();
                err.append("Not allowed to have multiple @ConfigArguments defined: ");
                err.append("\n  Duplicate found at ").append(clazz.getName()).append("#").append(method.getName());
                err.append("\n  Original found at ").append(rawArgAdder.getDeclaringClass().getName()).append("#")
                        .append(rawArgAdder.getName());
                throw new IllegalStateException(err.toString());
            }

            this.rawArgAdder = method;
        }
    }

    private final void walkFields(String prefix, Class<?> clazz, String scope) {
        for (Field field : clazz.getDeclaredFields()) {
            ConfigOption opt = field.getAnnotation(ConfigOption.class);
            if (opt == null) {
                continue; // skip
            }

            String key = StringUtils.defaultString(prefix) + field.getName();
            if (opt.suboption()) {
                walkFields(key + ".", field.getType(), opt.scope());
            } else {
                Configurable cfgrbl = new Configurable(field, opt, key, scope);
                configurables.put(key, cfgrbl);

                scopes.add(cfgrbl.getScope());

                if (field.getType().isEnum()) {
                    // register converter for this field type.
                    Class<?> enumclass = field.getType();
                    this.bub.getConvertUtils().register(new EnumConverter(enumclass), enumclass);
                }
            }
        }
    }

    public void persist() throws IOException {
        Properties props = new Properties();

        String key, value;
        for (Configurable conf : configurables.values()) {
            if (conf.isInternal()) {
                continue; // skip Configuration internal configurables
            }
            key = conf.getKey();
            value = getValue(key);
            props.put(key, StringUtils.defaultString(value));
        }

        FileWriter writer = null;
        try {
            writer = new FileWriter(this.persistFile);
            props.store(writer, "Written by " + this.getClass().getName());
        } finally {
            IOUtils.closeQuietly(writer);
        }
    }

    /**
     * Restore from persistent storage.
     * 
     * @throws IOException
     *             if unable to read persisted storage file.
     * @see #setPersistFile(File)
     * @see #persist()
     */
    public void restore() throws IOException {
        if (!this.persistFile.exists()) {
            return; // nothing to load. skip it.
        }
        FileReader reader = null;
        try {
            reader = new FileReader(this.persistFile);
            Properties props = new Properties();
            props.load(reader);

            @SuppressWarnings("unchecked")
            Enumeration<String> keys = (Enumeration<String>) props.propertyNames();
            while (keys.hasMoreElements()) {
                String key = keys.nextElement();
                setValue(key, props.getProperty(key));
            }
        } finally {
            IOUtils.closeQuietly(reader);
        }
    }

    public void setPersistFile(File persistFile) {
        if (persistFile == null) {
            throw new NullPointerException("persistFile cannot be null");
        }
        this.persistFile = persistFile;
    }

    public void setValue(String key, String value) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalArgumentException("Key is unset or blank");
        }
        Configurable cbl = configurables.get(key);
        if (cbl == null) {
            throw new IllegalArgumentException("Not a valid configurable key [" + key + "]");
        }

        try {
            this.bub.setProperty(this.obj, key, value);
        } catch (Throwable t) {
            LOG.log(Level.WARNING, t.getMessage(), t);
        }
    }

    public boolean hasRawArgsDefined() {
        return (rawArgAdder != null);
    }

    public void addRawArg(String arg) {
        if (rawArgAdder == null) {
            return;
        }

        Object params[] = { arg };

        try {
            rawArgAdder.invoke(this.obj, params);
        } catch (Throwable t) {
            LOG.log(Level.WARNING,
                    "Unable to add raw arg: " + this.obj.getClass().getName() + "#" + rawArgAdder.getName(), t);
        }
    }

    public String getRawArgsName() {
        if (rawArgAdder != null) {
            ConfigArguments ca = rawArgAdder.getAnnotation(ConfigArguments.class);
            if ((ca != null) && (StringUtils.isNotBlank(ca.name()))) {
                return ca.name();
            }
        }
        return "arguments";
    }
}