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

com.trigonic.utils.spring.cmdline.CommandLineMetaData Maven / Gradle / Ivy

/*
 * Copyright 2011 the original author or authors.
 *
 * 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 com.trigonic.utils.spring.cmdline;

import java.beans.PropertyDescriptor;
import java.io.PrintStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import joptsimple.OptionParser;
import joptsimple.internal.ColumnarData;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanDefinitionStoreException;

public class CommandLineMetaData {
    private Map options = new HashMap();
    private Map operands = new HashMap();
    
    public CommandLineMetaData(Class beanClass) {
        populateOptionMethods(beanClass);
        populateOptionFields(beanClass);
        populateOperandMethods(beanClass);
        populateOperandFields(beanClass);
        validateOperands();
    }

    private void validateOperands() {
        int expectedIndex = 0;
        boolean remainderConsumed = false;
        for (Entry entry : operands.entrySet()) {
            if (remainderConsumed) {
                throw new IllegalArgumentException(String.format("Operand index [%d] illegally follows multi-value operand"));
            }
            Operand operand = entry.getKey();
            if (operand.index() < expectedIndex) {
                throw new IllegalArgumentException(String.format("Duplicate operand index [%d]", operand.index()));
            } else if (operand.index() > expectedIndex) {
                throw new IllegalArgumentException(String.format("Missing operand index [%d]", expectedIndex));
            }
            expectedIndex = operand.index() + 1;
            OperandHandler operandHandler = entry.getValue();
            remainderConsumed = operandHandler.hasMultipleValues();
        }
    }

    public Collection getOptionHandlers() {
        return options.values();
    }
    
    public Collection getOperandHandlers() {
        Set result = new TreeSet(new OperandHandlerOrderComparator());
        result.addAll(operands.values());
        return result;
    }

    public void register(OptionParser parser) {
        for (OptionHandler optionHandler : getOptionHandlers()) {
            optionHandler.register(parser);
        }
    }
    
    public void printOperandsOn(PrintStream err) {
        Collection operands = getOperandHandlers();
        if (operands.size() > 0) {
            ColumnarData columnarData = new ColumnarData("Operands", "Description");
            for (OperandHandler operand : operands) {
                columnarData.addRow(operand.getName(), operand.getDescription());
            }
            err.println();
            err.println(columnarData.format());
        }
    }

    private  void checkWriteableProperty(T annotation, Class beanClass, Field field) {
        PropertyDescriptor property = BeanUtils.getPropertyDescriptor(beanClass, field.getName());
        if (property == null || property.getWriteMethod() == null) {
            throw new BeanDefinitionStoreException("@" + annotation.getClass().getSimpleName() + " annotation cannot be applied to fields without matching setters");
        }
    }

    private PropertyDescriptor getPropertyForMethod(String annotationType, Annotation annotation, Method method) {
        PropertyDescriptor property = BeanUtils.findPropertyForMethod(method);
        if (property == null) {
            throw new BeanDefinitionStoreException(annotationType + " annotation cannot be applied to non-property methods");
        }
        return property;
    }

    private void populateOperandFields(Class beanClass) {
        Class superClass = beanClass.getSuperclass();
        if (!superClass.equals(Object.class)) {
            populateOperandFields(superClass);
        }
        for (Field field : beanClass.getDeclaredFields()) {
            Operand operand = field.getAnnotation(Operand.class);
            if (operand != null) {
                checkWriteableProperty(operand, beanClass, field);
                operands.put(operand, new OperandFieldHandler(operand, field));
            }
        }
    }

    private void populateOperandMethods(Class beanClass) {
        Class superClass = beanClass.getSuperclass();
        if (!superClass.equals(Object.class)) {
            populateOperandMethods(superClass);
        }
        for (Method method : beanClass.getDeclaredMethods()) {
            Operand operand = method.getAnnotation(Operand.class);
            if (operand != null) {
                operands.put(operand, new OperandPropertyHandler(operand, getPropertyForMethod("@Operand", operand, method), beanClass));
            }
        }
    }

    private void populateOptionFields(Class beanClass) {
        Class superClass = beanClass.getSuperclass();
        if (!superClass.equals(Object.class)) {
            populateOptionFields(superClass);
        }
        for (Field field : beanClass.getDeclaredFields()) {
            Option option = field.getAnnotation(Option.class);
            if (option != null) {
                checkWriteableProperty(option, beanClass, field);
                options.put(option, new OptionFieldHandler(option, field));
            }
        }
    }

    private void populateOptionMethods(Class beanClass) {
        Class superClass = beanClass.getSuperclass();
        if (!superClass.equals(Object.class)) {
            populateOptionMethods(superClass);
        }
        for (Method method : beanClass.getDeclaredMethods()) {
            Option option = method.getAnnotation(Option.class);
            if (option != null) {
                options.put(option, new OptionPropertyHandler(option, getPropertyForMethod("@Option", option, method), beanClass));
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy