calculadora.controlador.ControladorComandos.java Source code

Java tutorial

Introduction

Here is the source code for calculadora.controlador.ControladorComandos.java

Source

/*
 * Copyright 2013 Antonio Lpez Marn and Antonio Rodrigo Gea Lpez.
 * Calculadora CLI 2.0
 * 
 * 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 calculadora.controlador;

import java.util.Map;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.ParseException;

/**
 * ControladorComandos etapa de interrogatorios controla los comandos de la
 * linea de entrada y segun el comando se comunica con el controlador de la
 * calculadora {@link ControladorCalc}.
 *
 * Comunicacion directa con el controlador de la calculadora y devuelve los
 * resultados al {@link ControladorCLI}
 *
 * Todos los metodos son privados excepto el constructor y la busqueda de
 * comandos.
 *
 * @author Antonio Lpez Marn
 * @author Antonio Rodrigo Gea Lpez
 * @see ControladorCalc Controlador de la calculadora.
 */
public class ControladorComandos {
    //Comunicacion con el controlador de la calculadora

    private ControladorCalc controlCalc;
    //Recoger linea comandos
    private CommandLine cmdLine;
    /*
     * Constantes de los comandos de facil acceso.
     * 
     * Si hubiera que cambiar algun comando no habria que buscarlo
     * en el codigo sino cambiarlo aqui, y no podra ser modificado
     * por el programa en toda la ejecucion.
     * 
     */
    //Opcion de ayuda
    private final String HELP = "help";
    //Opcion de salida
    private final String EXIT = "exit";
    //OperacionesAritmetricas
    private final String SUMA = "s";
    private final String RESTA = "r";
    private final String MULTI = "m";
    private final String DIVISION = "d";
    private final String RESTO = "re";
    private final String POTENCIA = "p";
    private final String RAIZ = "ra";
    //OperacionesTrigonometricas
    private final String SENO = "seno";
    private final String COSENO = "coseno";
    private final String TANGENTE = "tangente";
    //Tablas de verdad
    private final String AND = "and";
    private final String OR = "or";
    private final String XOR = "xor";
    private final String NOT = "not";
    //Propiedades
    private final String PROPERTY = "D";

    /**
     * Constructor que le pasan por argumentos la linea de comandos para buscar
     * el comando que recibe.
     *
     * Y creo el objeto de la controladora de la calculadora para poder
     * notificar que operacion se desea realizar segun el comando que le pasen.
     *
     * @param cmdLine la linea de comandos de commons cli
     */
    public ControladorComandos(CommandLine cmdLine) throws ParseException {
        this.cmdLine = cmdLine;
        controlCalc = new ControladorCalc();
        buenosComandos(); //Compruebo que el cmd es correcto de entrada
    }

    /**
     * Ya que no estan los comandos agrupados para poder abrir una linea de
     * comandos, hay que controlarlo con este pequeo metodo y ademas la
     * informacion del error, si lo hay, puede ser mas informativa.
     *
     * Tambien compruebo que no hay argumentos que no reconoce el programa.
     *
     * NOTA: Se refactorizo este metodo ya que tenia una complegidad ciclomatica
     * de 14 demasiado grande.
     *
     * @throws ParseException demasiados comandos
     */
    private void buenosComandos() throws ParseException {
        String[] badArgs = cmdLine.getArgs();
        //Si hay argumentos no reconocidos
        if (badArgs.length > 0) {
            if (!badArgs[0].equals("")) { //Si es distinto de un ENTER.
                throw new ParseException(
                        "'" + badArgs[0] + "' no " + "se reconoce como comando interno del programa.");
            }
        }
        //Obtengo las opciones del cmd
        Option[] opt = cmdLine.getOptions();

        //Si es igual a dos que las compruebe
        if (opt.length == 2) {
            hasBuenosComandos(opt);
        } else if (opt.length == 3) { //Si es igual a tres directamente error
            throw errorComandos();
        }
    }

    /**
     * Metodo que devuelve una excepcion de error de comandos.
     *
     * @return ParseException devuelve la excepcion con el mensaje pertinente
     * @throws ParseException error de comandos.
     */
    private ParseException errorComandos() throws ParseException {
        return new ParseException("Demasiados comandos, " + "solo se puede realizar una " + "operacion cada vez.");
    }

    /**
     * Controlo si un comando con posix largo se escribio a la vez que otro
     * posix largo como puede ser (--help) junto con --(seno) de error.
     *
     * Controlo si un comando posix largo se escribio a la vez que otro de posix
     * corto que no sean --help y -D(ya que si ocurriera esto mostraria la
     * ayuda), y viceversa tiene que dar error.
     *
     * @param opt dos opciones
     * @see Option
     * @throws ParseException Demasiados comandos
     */
    private void hasBuenosComandos(Option[] opt) throws ParseException {
        //Obtengo la primera y la segunda opcion
        Option op1 = opt[0];
        Option op2 = opt[1];

        boolean isLongOpt1 = op1.hasLongOpt();
        boolean isLongOpt2 = op2.hasLongOpt();

        //Si los dos comandos son posix largo error
        if (isLongOpt1 && isLongOpt2) {
            throw errorComandos(); //Error de comandos
            //Si el primero es posix largo y el segundo no pero son el comando
            //-help y -D no debe ocurrir nada
        } else if ((isLongOpt1 && !isLongOpt2 && op1.getLongOpt().equals(HELP) && op2.getOpt().equals(PROPERTY))) {
            //No debe hacer nada importante esto vacio.
            //Si el primero es poxis largo y el segundo posix corto y viceversa
        } else if (isLongOpt1 && !isLongOpt2 || !isLongOpt1 && isLongOpt2) {
            throw errorComandos(); //Error de comandos
        }
    }

    /**
     * Compruebo que los argumentos de un comando son correctos, en este caso
     * deben de haber dos argumentos.
     *
     * @param args
     * @throws IllegalArgumentException
     */
    private void dosArgs(String[] args) throws IllegalArgumentException {
        if (args.length != 2) { //Imposible ser distinto de dos, error
            throw new IllegalArgumentException("No hay suficientes argumentos" + " de entrada, se requieren dos.");
        }
    }

    /**
     * Compruebo que los argumentos de un comando son correctos, en este caso
     * debe de haber un argumento.
     *
     * @param args
     * @throws IllegalArgumentException
     */
    private void unArg(String[] args) throws IllegalArgumentException {
        if (args.length != 1) {
            throw new IllegalArgumentException("Los argumentos de " + "entrada son erroneos, se requiere uno.");
        }
    }

    /**
     * Paso los numeros de dos argumentos a un array de numeros double,
     * asegurandome que son correctos, si hay algun error son lanzados.
     *
     *
     * @param numeros los argumentos que deben ser numeros
     * @return nums dos numeros pasados en double
     * @throws IllegalArgumentException error de argumentos
     * @throws NumberFormatException error al parsear los argumentos
     * @see Double#parseDouble(java.lang.String)
     */
    private double[] dosNumerosDouble(String[] numeros) throws IllegalArgumentException, NumberFormatException {
        //Verificamos que los argumentos son correctos
        dosArgs(numeros);

        //Siempre sera 2
        double[] nums = new double[2];

        //Mete los numeros en un array de double
        for (int i = 0; i < nums.length; i++) {
            nums[i] = Double.parseDouble(numeros[i]);
        }

        return nums;
    }

    /**
     * Recoge dos booleanos de los argumentos y los pasa a un array tipo
     * booleano {@link Boolean}, si hay algun error se lanzan.
     *
     *
     * @param booleanos array de parametros
     * @return boo array con dos boleanos
     * @throws IllegalArgumentException
     * @throws NumberFormatException
     * @see Boolean#parseBoolean(java.lang.String)
     */
    private boolean[] dosBooleanos(String[] booleanos) throws IllegalArgumentException, NumberFormatException {

        //Verificamos que los argumentos son corerctos
        //sino salta excepcion
        dosArgs(booleanos);

        //Siempre va a ser dos
        boolean[] boo = new boolean[2];

        //Mete los argumentos booleanos en el array
        for (int i = 0; i < booleanos.length; i++) {
            //Si los argumentos son distintos de true y false..
            if (!booleanos[i].equalsIgnoreCase("true") && !booleanos[i].equalsIgnoreCase("false")) {
                //Para poder personalizar el mensaje
                throw errorBooleano();
            }
            boo[i] = Boolean.parseBoolean(booleanos[i]);
        }

        return boo;
    }

    /**
     * Metodo que devuelve una excepcion diciendo el error de tipo de booleanos.
     *
     * @return IllegalArgumentException con el mensaje de error.
     * @see IllegalArgumentException
     */
    private IllegalArgumentException errorBooleano() {
        return new IllegalArgumentException("Tipo de argumentos erroneos, " + "escribe booleanos (true o false).");
    }

    /**
     * Busca el comando que se le paso al constructor y llama a la calculadora
     * para que ejecute el comando con los argumentos de entrada.
     *
     * Este metodo utiliza el <b>return</b> para devolver los resultados una vez
     * encuentra un comando devuelve el resultado y no pasa por los demas
     * sentencias.
     *
     * Se debe controlar cada uno de los comandos y pasarle los numeros a la
     * calculadora.
     *
     * Nota: -Si en la linea de comandos de entrada no hay ningun comando de la
     * aplicacion no devuelve una cadena vacia para que no muestre nada en
     * pantalla.
     *
     * -Si la opcion es de ayuda se le pasa una cadena unica que interpretara el
     * controlador principal para mostrar la ayuda.
     *
     * -Nunca se devuelve null
     *
     * Refacotorizado varias veces, antes estaba toda la busqueda concentrada en
     * el metodo.
     *
     * @see ControladorCalc#Todos los metodos
     * @see Object#toString()
     * @return String el resultado de la operacion
     * @throws IllegalArgumentException argumentos no son correctos
     * @throws NumberFormatException tipos de argumentos erroneos
     */
    public String busquedaDeComandos() throws IllegalArgumentException, NumberFormatException {

        String resultado = "";
        //Resultado de las posibles operaciones
        Double result;
        Boolean resultBoo;

        //Si el comando es help
        if (cmdLine.hasOption(HELP)) {
            if (cmdLine.hasOption(PROPERTY)) {
                //Devuelvo un valor unico posible para decirle al controlador
                //principal que tiene que mostrar la ayuda de el comando -D
                resultado = "AYUDAPROPERTY";
            } else {
                //Devuelvo un valor unico posible para decirle al controlador
                //principal que tiene que mostrar la ayuda a la vista
                resultado = "AYUDA";
            }
        } else //Si el comando es exit
        if (cmdLine.hasOption(EXIT)) {
            //Devuelvo un valor unico posible para decirle al controlador
            //principal que tiene que salirse de la linea de comandos
            resultado = "EXIT";
        } else /* Propiedades */ if (cmdLine.hasOption("D")) {
            //Devuelve el resultado de la conversion
            resultado = conversor();
        } else if ((result = operacionesAritmetricas()) != null) {
            /* OperacionesAritmetricas */
            //Resultado de las posibles operaciones
            resultado = result.toString();
        } else if ((result = operacionesTrigonometricas()) != null) {
            /* OperacionesTrigonometricas */
            resultado = result.toString();
        } else if ((resultBoo = tablasVerdad()) != null) {
            /* Tablas de verdad */
            //Resultado de las posibles operaciones booleanas
            resultado = resultBoo.toString();
        }

        //Devuelve el resultado 
        return resultado;
    }

    /**
     * Metodo de operaciones aritmetricas que devuelve un {@link Double} en
     * forma de Objeto para luego poder parsear rapidamente a cadena y poder
     * devolver el resultado.
     *
     * Devuelve <b>null</b> si el comando no es ninguno de las operaciones
     * aritmetricas.
     *
     * @see Double
     * @see Double#parseDouble(java.lang.String)
     * @see Integer#parseInt(java.lang.String)
     * @return Double resultado de las operaciones
     * @throws IllegalArgumentException argumentos no son correctos
     * @throws NumberFormatException tipos de argumentos erroneos
     */
    private Double operacionesAritmetricas() throws IllegalArgumentException, NumberFormatException {
        //Preparo el array de numeros
        double[] numeros;
        //Resultado a devolver
        Double resultado = null;

        //Comando de suma
        if (cmdLine.hasOption(SUMA)) {
            //Utilizo el metodo para no duplicar codigo
            numeros = dosNumerosDouble(cmdLine.getOptionValues(SUMA));

            //le paso a el controlador el primer numero y el segundo
            resultado = controlCalc.calcularSuma(numeros[0], numeros[1]);
        } else //Comando de resta
        if (cmdLine.hasOption(RESTA)) {
            numeros = dosNumerosDouble(cmdLine.getOptionValues(RESTA));

            resultado = controlCalc.calcularResta(numeros[0], numeros[1]);
        } else //Comando de multiplicacion
        if (cmdLine.hasOption(MULTI)) {
            numeros = dosNumerosDouble(cmdLine.getOptionValues(MULTI));

            resultado = controlCalc.calcularMultipilicacion(numeros[0], numeros[1]);
        } else //Comando de division
        if (cmdLine.hasOption(DIVISION)) {
            numeros = dosNumerosDouble(cmdLine.getOptionValues(DIVISION));

            resultado = controlCalc.calcularDivision(numeros[0], numeros[1]);
        } else //Comando de resto
        if (cmdLine.hasOption(RESTO)) {
            numeros = dosNumerosDouble(cmdLine.getOptionValues(RESTO));

            resultado = controlCalc.calcularResto(numeros[0], numeros[1]);
        } else //Comando de potencia
        if (cmdLine.hasOption(POTENCIA)) {
            numeros = dosNumerosDouble(cmdLine.getOptionValues(POTENCIA));

            resultado = controlCalc.calcularPotencia(numeros[0], numeros[1]);
        } else //Comando de raiz
        if (cmdLine.hasOption(RAIZ)) {
            String[] nums = cmdLine.getOptionValues(RAIZ);

            dosArgs(nums);
            //Paso cada uno a su tipo
            double radicando = Double.parseDouble(nums[0]);
            int indice = Integer.parseInt(nums[1]);

            resultado = controlCalc.calcularRaiz(radicando, indice);
        }

        return resultado;
    }

    /**
     * Metodo de operaciones trigonometricas que devuelve un {@link Double} en
     * forma de Objeto para luego poder parsear rapidamente a cadena y poder
     * devolver el resultado.
     *
     * Devuelve <b>null</b> si el comando no es ninguno de las operaciones
     * trigonometricas.
     *
     * @see Double
     * @see Double#parseDouble(java.lang.String)
     * @return Double resultado de las operaciones
     * @throws IllegalArgumentException argumentos no son correctos
     * @throws NumberFormatException tipos de argumentos erroneos
     */
    private Double operacionesTrigonometricas() throws IllegalArgumentException, NumberFormatException {
        //Preparo el angulo que pueden utilizar para los proximos metodos
        double angulo;
        //Resultado
        Double resultado = null;

        //Comando de seno
        if (cmdLine.hasOption(SENO)) {
            unArg(cmdLine.getOptionValues(SENO));
            angulo = Double.parseDouble(cmdLine.getOptionValue(SENO));

            resultado = controlCalc.calcularseno(angulo);
        } else //Comando de coseno
        if (cmdLine.hasOption(COSENO)) {
            unArg(cmdLine.getOptionValues(COSENO));
            angulo = Double.parseDouble(cmdLine.getOptionValue(COSENO));

            resultado = controlCalc.calcularCoseno(angulo);
        } else //Comando de la tangente
        if (cmdLine.hasOption(TANGENTE)) {
            unArg(cmdLine.getOptionValues(TANGENTE));
            angulo = Double.parseDouble(cmdLine.getOptionValue(TANGENTE));

            resultado = controlCalc.calcularTangente(angulo);
        }

        return resultado;
    }

    /**
     * Metodo de operaciones de las tablas de verdad que devuelve un
     * {@link Double} en forma de Objeto para luego poder parsear rapidamente a
     * cadena y poder devolver el resultado.
     *
     * Devuelve <b>null</b> si el comando no es ninguno de las operaciones de
     * las tablas de verdad.
     *
     * @see Double
     * @see Boolean#parseBoolean(java.lang.String)
     * @return Boolean resultado de las operaciones
     * @throws IllegalArgumentException argumentos no son correctos
     * @throws NumberFormatException tipos de argumentos erroneos
     */
    private Boolean tablasVerdad() throws IllegalArgumentException, NumberFormatException {
        //Preparo el array de booleanos
        boolean[] verdades;
        Boolean resultado = null;

        //Comando de and
        if (cmdLine.hasOption(AND)) {
            verdades = dosBooleanos(cmdLine.getOptionValues(AND));

            resultado = controlCalc.calcularAnd(verdades[0], verdades[1]);
        } else //Comando de or
        if (cmdLine.hasOption(OR)) {
            verdades = dosBooleanos(cmdLine.getOptionValues(OR));

            resultado = controlCalc.calcularOr(verdades[0], verdades[1]);
        } else //Comando de xor
        if (cmdLine.hasOption(XOR)) {
            verdades = dosBooleanos(cmdLine.getOptionValues(XOR));

            resultado = controlCalc.calcularXor(verdades[0], verdades[1]);
        } else //Comando de not
        if (cmdLine.hasOption(NOT)) {
            unArg(cmdLine.getOptionValues(NOT));

            String boleano = cmdLine.getOptionValue(NOT);
            //Compruebo si es true o false, sino lanzo excepcion.
            if (!boleano.equalsIgnoreCase("true") && !boleano.equalsIgnoreCase("false")) {
                throw errorBooleano();
            }
            //Si todo correcto lo paso a boleano
            boolean boo = Boolean.parseBoolean(boleano);

            resultado = controlCalc.calcularNot(boo);
        }

        return resultado;
    }

    /**
     * Metodo que utiliza el metodo {@link ControladorComandos#conversor()} para
     * lanzar la excepcion de si un valor de la propiedad son incorrectos.
     *
     *
     * @return new IllegalArgumentException
     * @throws IllegalArgumentException valores son no son correctos
     */
    private IllegalArgumentException errorPropiedad() throws IllegalArgumentException {
        return new IllegalArgumentException("Valor/es de la propiedad incorrecto");
    }

    /**
     * Metodo del comando -D para Conversin entre binario, octal, decimal y
     * hexadecimal: origen y destino se establecen con propiedades al estilo
     * java (Dconversion.source=hex Dconversion.dest=bin).
     *
     * Recupera las propiedades con sus valores con el operador a convertir
     * segun las propiedades y devuelve el resultado.
     *
     * @see Map
     * @see Map#get(java.lang.Object)
     * @see String#valueOf(java.lang.Object)
     * @see ControladorComandos#conversorOrigenDestino(java.lang.String,
     * java.lang.String, java.lang.String)
     * @return String el resultado convertido en el destino
     * @throws IllegalArgumentException error de argumentos o valores
     * @throws NumberFormatException error de tipo de argumentos
     */
    private String conversor() throws IllegalArgumentException, NumberFormatException {
        //Mapa de las propiedades con sus valores del comando -D
        Map propiedades = cmdLine.getOptionProperties(PROPERTY);
        //Recojo los valores de las propiedades origen y destino
        Object source = propiedades.get("convert.source");
        Object dest = propiedades.get("convert.dest");

        //Recojo los argumentos, el ultimo elemento debe ser el operando(numero)
        String[] values = cmdLine.getOptionValues(PROPERTY);
        String value = values[values.length - 1];

        if (source == null || dest == null) {
            throw new IllegalArgumentException(
                    "Propiedades erroneas, " + "debes expecificar el origen y el destino correctamente."
                            + "\nPara mas informacion comando de ayuda seguido " + "de -Dconvert.");
        }
        //Los valores origen y destino salvados de ser nulos
        String origen = source.toString();
        String destino = dest.toString();

        return conversorOrigenDestino(origen, destino, value);
    }

    /**
     * Segun los valores de las propiedades se comunica con la calculadora y
     * devuelve el resultado.
     *
     * @param origen valor de origen de la conversion
     * @param destino valor de destino de la conversion
     * @param value el operador a convertir
     * @return String el resultado de la conversion
     * @throws IllegalArgumentException error de argumentos o valores
     * @throws NumberFormatException error de tipo de argumentos
     */
    private String conversorOrigenDestino(String origen, String destino, String value)
            throws IllegalArgumentException, NumberFormatException {
        //Variable que almacena el resultado y lo devuelve.
        String solucion = "";

        //Calcula segun los valores de las propiedades
        switch (origen) {
        case "dec":
            switch (destino) {
            case "bin":
                solucion = controlCalc.calcularDecBin(Long.parseLong(value));
                break;
            case "oct":
                solucion = controlCalc.calcularDecOct(Long.parseLong(value));
                break;
            case "hex":
                solucion = controlCalc.calcularDecHex(Long.parseLong(value));
                break;
            default:
                throw errorPropiedad();
            }
            break;
        case "bin":
            switch (destino) {
            case "dec":
                solucion = String.valueOf(controlCalc.calcularBinDec(value));
                break;
            case "oct":
                solucion = controlCalc.calcularBinOct(value);
                break;
            case "hex":
                solucion = controlCalc.calcularBinHex(value);
                break;
            default:
                throw errorPropiedad();
            }

            break;
        case "oct":
            switch (destino) {
            case "dec":
                solucion = String.valueOf(controlCalc.calcularOctDec(value));
                break;
            case "bin":
                solucion = controlCalc.calcularOctBin(value);
                break;
            case "hex":
                solucion = controlCalc.calcularOctHex(value);
                break;
            default:
                throw errorPropiedad();
            }

            break;
        case "hex":
            switch (destino) {
            case "dec":
                solucion = String.valueOf(controlCalc.calcularHexDec(value));
                break;
            case "bin":
                solucion = controlCalc.calcularHexBin(value);
                break;
            case "oct":
                solucion = controlCalc.calcularHexOct(value);
                break;
            default:
                throw errorPropiedad();
            }
            break;
        default:
            throw errorPropiedad();
        }
        //Devuelve la solucion
        return solucion;
    }
}