dynamicrefactoring.interfaz.dynamic.DynamicRefactoringWindow.java Source code

Java tutorial

Introduction

Here is the source code for dynamicrefactoring.interfaz.dynamic.DynamicRefactoringWindow.java

Source

/*<Dynamic Refactoring Plugin For Eclipse 2.0 - Plugin that allows to perform refactorings 
on Java code within Eclipse, as well as to dynamically create and manage new refactorings>
    
Copyright (C) 2009  Laura Fuente De La Fuente
    
This file is part of Foobar
    
Foobar 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 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 Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

package dynamicrefactoring.interfaz.dynamic;

import java.io.File;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javamoon.core.JavaModel;
import moon.core.NamedObject;
import moon.core.ObjectMoon;
import moon.core.instruction.CodeFragment;

import org.apache.log4j.Logger;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Scrollable;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;

import repository.moon.MOONRefactoring;

import com.google.common.base.Preconditions;

import dynamicrefactoring.RefactoringConstants;
import dynamicrefactoring.RefactoringImages;
import dynamicrefactoring.domain.DynamicRefactoring;
import dynamicrefactoring.domain.DynamicRefactoringDefinition;
import dynamicrefactoring.domain.InputParameter;
import dynamicrefactoring.domain.RefactoringExample;
import dynamicrefactoring.domain.RefactoringException;
import dynamicrefactoring.integration.ModelGenerator;
import dynamicrefactoring.integration.NamedObjectHandler;
import dynamicrefactoring.interfaz.ComboEditor;

/**
 * Ventana generada dinmicamente para la configuracin de una refactorizacin
 * dinmica antes de su ejecucin.
 * 
 * @author <A HREF="mailto:lfd0002@alu.ubu.es">Laura Fuente de la Fuente</A>
 * @author <A HREF="mailto:sfd0009@alu.ubu.es">Sonia Fuente de la Fuente</A>
 * @author <A HREF="mailto:ehp0001@alu.ubu.es">Enrique Herrero Paredes</A>
 */
public class DynamicRefactoringWindow extends Dialog {

    /**
     * Elemento de registro de errores y otros eventos de la clase.
     */
    private static final Logger logger = Logger.getLogger(DynamicRefactoringWindow.class);

    /**
     * Campo de texto utilizado para la introduccin manual de valores.
     */
    private Text t_Input;

    /**
     * Campo desplegable utilizado para la seleccin de valores entre un
     * conjunto.
     */
    private Combo combo;

    /**
     * Campo de texto asociado a la entrada principal de la refactorizacin.
     */
    private Text t_Root;

    /**
     * Tabla asociativa en la que se almacenan los valores ya cargados hasta el
     * momento para las entradas.
     */
    protected Hashtable<String, Object> inputValues;

    /**
     * Tabla asociativa en la que se almacenan los posibles valores asociados a
     * una lista desplegable, en el mismo orden en que sta los muestra.
     * 
     * <p>
     * Se utiliza como clave el cdigo <i>hash</i> del desplegable, y como valor
     * la lista de objetos cuya representacin muestra.
     * </p>
     */
    private final HashMap<Integer, List<Object>> comboValues;

    /**
     * Tabla asociativa que permite obtener el campo (campo de texto o desplegable)
     * en que se muestra el valor de una determinada entrada.
     * 
     * <p>Se utiliza como clave el nombre de la entrada, y como valor, una 
     * referencia al campo en que se muestra su valor.</p>
     */
    private final HashMap<String, Scrollable> inputFields;

    /**
     * Tabla asociativa en la que se almacenan los atributos de cada una de las 
     * entradas, utilizando como clave de cada entrada su nombre.
     */
    private final HashMap<String, InputParameter> inputAttributes;

    /**
     * Tabla asociativa que permite actualizar los valores de las entradas que
     * dependen de la seleccin de un determinado desplegable. Se utiliza como
     * clave un objeto de envoltura que contiene el <code>hashcode</code> del
     * combo en cuestin, y como valor un <i>array</i> con los nombres de las
     * entradas que dependen de dicho desplegable.
     */
    private final HashMap<Integer, String[]> comboDependencies;

    /**
     * Lista de atributos que se han ido cargando a travs de una serie de
     * llamadas recursivas consecutivas.
     */
    private ArrayList<String> recursiveInputs;

    /**
     * La definicin de la refactorizacin.
     */
    protected DynamicRefactoringDefinition refactoringDefinition;

    /**
     * Entrada principal de la refactorizacin.
     */
    protected ObjectMoon currentObject;

    /**
     * Modelo MOON-Java sobre el que se refactoriza.
     */
    protected JavaModel model;

    /**
     * Variable auxiliar para la situacin dinmica de los campos.
     */
    private int count = 0;

    /**
     * Desplazamiento que diferencia la situacin de los elementos de la
     * interfaz entre una entrada principal que hereda de NamedObject y otra que
     * hereda de CodeFragment.
     */
    private int desplazamiento = 0;

    /**
     * Crea la ventana de dilogo.
     * 
     * @param currentObject
     *            objeto que constituye la entrada principal de la
     *            refactorizacin.
     * 
     * @param refactoring
     *            definicin de la refactorizacin.
     * 
     */
    public DynamicRefactoringWindow(ObjectMoon currentObject, DynamicRefactoringDefinition refactoring) {
        super(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());

        Preconditions.checkNotNull(currentObject, "The main object in the refactoring cannot be null.");

        this.refactoringDefinition = refactoring;
        this.currentObject = currentObject;

        inputValues = new Hashtable<String, Object>();
        inputFields = new HashMap<String, Scrollable>();
        comboValues = new HashMap<Integer, List<Object>>();
        comboDependencies = new HashMap<Integer, String[]>();
        inputAttributes = refactoringDefinition.getInputsAsHash();

        model = ModelGenerator.getInstance().getModel();
    }

    /**
     * Obtiene el campo de texto asociado a una entrada.
     * 
     * @param inputName el nombre de la entrada.
     * 
     * @return el campo de texto asociado a la entrada, si es que lo tiene.
     * <code>null</code> si no.
     */
    public Text getField(String inputName) {
        Scrollable field = inputFields.get(inputName);
        if (field != null && field instanceof Text)
            return (Text) field;
        else
            return null;
    }

    /**
     * Crea el contenido de la ventana de dilogo.
     * 
     * @param parent
     *            el elemento compuesto padre que contendr el rea de dilogo.
     * 
     * @return el control del rea de dilogo.
     * 
     * @see Dialog#createDialogArea
     */
    @Override
    protected Control createDialogArea(Composite parent) {

        Composite container = (Composite) super.createDialogArea(parent);
        container.setLayout(new FormLayout());

        final Composite composite = new Composite(container, SWT.NONE);
        final FormData fd_composite = new FormData();
        fd_composite.bottom = new FormAttachment(100, -5);
        fd_composite.right = new FormAttachment(100, -5);
        fd_composite.left = new FormAttachment(0, 5);
        fd_composite.top = new FormAttachment(0, 5);
        composite.setLayoutData(fd_composite);
        final GridLayout gridLayout = new GridLayout();
        composite.setLayout(gridLayout);

        // Se aaden las pestaas a la interfaz.
        final TabFolder tabFolder = new TabFolder(composite, SWT.NONE);
        tabFolder.setLayoutData(new GridData(867, 393));

        final TabItem parametersTabItem = new TabItem(tabFolder, SWT.NONE);
        parametersTabItem.setText(Messages.DynamicRefactoringWindow_Parameters);

        new DynamicRefactoringTab(tabFolder, refactoringDefinition);

        if (!refactoringDefinition.getExamples().isEmpty()) {
            new DynamicExamplesTab(tabFolder, refactoringDefinition);
        }

        final ScrolledComposite scrolledComposite2 = new ScrolledComposite(tabFolder,
                SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        parametersTabItem.setControl(scrolledComposite2);
        scrolledComposite2.setExpandHorizontal(true);
        scrolledComposite2.setExpandVertical(true);

        // Pestaa de parmetros.
        final Composite cParameters = new Composite(scrolledComposite2, SWT.NONE);
        cParameters.setLocation(0, 0);
        cParameters.setSize(872, 390);
        scrolledComposite2.setContent(cParameters);

        // Se busca la entrada raz.
        for (InputParameter nextInput : refactoringDefinition.getInputs()) {
            // Para su entrada de tipo "raz".
            if (nextInput.isMain()) { //$NON-NLS-1$
                if (!checkMainInput(nextInput)) {
                    return null;
                }
                final CLabel lb_Root = new CLabel(cParameters, SWT.NONE);
                lb_Root.setAlignment(SWT.CENTER);
                lb_Root.setText(nextInput.getName().replaceAll("_", " ")); //$NON-NLS-1$ //$NON-NLS-2$
                lb_Root.setBounds(342, 38, 183, 19);

                if (currentObject instanceof NamedObject) {
                    t_Root = new Text(cParameters, SWT.BORDER);
                    t_Root.setBounds(35, 65, 790, 25);
                    t_Root.setText(((NamedObject) currentObject).getName().toString());
                    t_Root.setEditable(false);
                } else {
                    desplazamiento = 50;
                    final StyledText b_Root = new StyledText(cParameters, SWT.BORDER | SWT.V_SCROLL);
                    b_Root.setBounds(35, 65, 790, 75);
                    b_Root.setText(((CodeFragment) currentObject).getText());
                    b_Root.setEditable(false);
                }

                break;
            }
        }

        int rootInputs = 0;
        // Para el resto de entradas.      
        for (InputParameter nextInput : refactoringDefinition.getInputs()) {

            if (!nextInput.isMain()) {

                // Para entradas de tipo from.
                if (nextInput.getFrom() != null && nextInput.getFrom().length() > 0) {

                    // Si an no se ha obtenido su valor.
                    if (!inputValues.containsKey(nextInput.getName())) {

                        // Se busca la entrada a la que apunta el campo "from".
                        InputParameter fromInput = inputAttributes.get(nextInput.getFrom());

                        // Si se encuentra la entrada apuntada por el "from".
                        if (fromInput != null) {

                            // Si es la entrada principal o raz.
                            if (fromInput.isMain()) { //$NON-NLS-1$
                                if (!loadFromMain(cParameters, nextInput))
                                    return null;
                            }

                            // Si el "from" apunta al modelo.
                            else if (fromInput.getType().equals("moon.core.Model")) { //$NON-NLS-1$
                                if (!loadFromModel(cParameters, nextInput))
                                    return null;
                            }

                            // Para el resto de entradas.
                            else {
                                recursiveInputs = new ArrayList<String>();
                                InputParameter source = inputAttributes.get(nextInput.getFrom());
                                // Se inicia su obtencin.
                                recursiveDependencyResolution(cParameters, source, nextInput);
                            }
                        }

                        // Si no se encuentra la entrada a la que apunta el campo "from".
                        else {
                            Object[] messageArgs = { "\"" + nextInput.getFrom() + "\"", nextInput.getName() }; //$NON-NLS-1$ //$NON-NLS-2$
                            MessageFormat formatter = new MessageFormat(""); //$NON-NLS-1$
                            formatter.applyPattern(Messages.DynamicRefactoringWindow_ReferencedObjectNotFound);

                            // La refactorizacin contiene un error.
                            String message = formatter.format(messageArgs) + ".\n" + //$NON-NLS-1$
                                    Messages.DynamicRefactoringWindow_ReviewDefinition + ".\n"; //$NON-NLS-1$
                            logger.error(message);
                            exitWithError(message);
                            return null;
                        }
                    }
                }
                // Si no es la entrada principal, ni tiene campo "from"
                else {
                    // Si no es el propio modelo.
                    if (!nextInput.getType().equals(RefactoringConstants.MODEL_PATH)) {

                        final CLabel lb_Input = new CLabel(cParameters, SWT.NONE);
                        lb_Input.setBounds(342, desplazamiento + 105 + 67 * count, 183, 19);
                        lb_Input.setAlignment(SWT.CENTER);
                        lb_Input.setText(nextInput.getName().replaceAll("_", " ")); //$NON-NLS-1$ //$NON-NLS-2$

                        // Es una entrada cuyo valor debe introducir el usuario
                        // de forma manual (campo de texto).
                        t_Input = new Text(cParameters, SWT.BORDER);
                        t_Input.setBounds(35, desplazamiento + 132 + 67 * count, 790, 25);
                        t_Input.addModifyListener(new TextModifyListener());
                        // Se indica que el valor de la entrada se almacena en
                        // este campo de texto.
                        inputFields.put(nextInput.getName(), t_Input);

                        count++;
                    }
                }
            }
            // Se ha encontrado una entrada marcada como principal.
            else if (++rootInputs > 1) {
                // Se han encontrado varias entradas sealadas como principales.
                // La refactorizacin contiene un error.
                Object[] messageArgs = { "\"" + Messages.DynamicRefactoringWindow_Root + "\"" }; //$NON-NLS-1$ //$NON-NLS-2$
                MessageFormat formatter = new MessageFormat(""); //$NON-NLS-1$
                formatter.applyPattern(Messages.DynamicRefactoringWindow_SeveralInputsMarked);

                String message = formatter.format(messageArgs) + ".\n" //$NON-NLS-1$
                        + Messages.DynamicRefactoringWindow_ReviewDefinition + ".\n"; //$NON-NLS-1$
                logger.error(message);
                exitWithError(message);
                return null;
            }

        }
        scrolledComposite2.setMinSize(cParameters.computeSize(SWT.DEFAULT, SWT.DEFAULT));

        // Se han terminado de procesar todas las entradas.

        return container;
    }

    /**
     * Carga el valor de una entrada a partir de la entrada principal.
     * 
     * @param cParameters
     *            <code>Composite</code> que contendr las nuevas etiqueta y
     *            campo de texto o desplegable la entrada cargada.
     * @param loadedInput
     *            entrada cuyo valor se intenta cargar.
     * 
     * @return <code>true</code> si se pudo cargar; <code>false</code> en caso
     *         contrario.
     */
    private boolean loadFromMain(final Composite cParameters, InputParameter loadedInput) {

        try {
            Method method = currentObject.getClass().getMethod(loadedInput.getMethod(), (Class[]) null);
            Object values = method.invoke(currentObject, (Object[]) null);

            if (values != null) {

                final CLabel lb_Input = new CLabel(cParameters, SWT.NONE);
                lb_Input.setBounds(342, desplazamiento + 105 + 67 * count, 183, 19);
                lb_Input.setAlignment(SWT.CENTER);
                lb_Input.setText(loadedInput.getName().replaceAll("_", " ")); //$NON-NLS-1$ //$NON-NLS-2$

                // Si se ha obtenido un nico valor.
                if (isSingleValue(values)) {
                    t_Input = new Text(cParameters, SWT.BORDER);
                    t_Input.setBounds(35, desplazamiento + 132 + 67 * count, 790, 25);
                    t_Input.setText(NamedObjectHandler.getName(values));
                    t_Input.setEditable(false);
                    inputValues.put(loadedInput.getName(), values);
                    // Se indica que el valor de la entrada lo almacena este campo.
                    inputFields.put(loadedInput.getName(), t_Input);
                }
                // Otras entradas aparte del modelo tambin pueden
                // devolver conjuntos de valores.
                else {
                    combo = new Combo(cParameters, SWT.BORDER);
                    combo.setBounds(35, desplazamiento + 132 + 67 * count, 790, 25);
                    combo.setVisibleItemCount(10);
                    combo.addFocusListener(new ComboEditor());

                    // Se procesa el contenido del conjunto de valores.
                    fillInCombo(values, combo, loadedInput.getName());
                    combo.addSelectionListener(new ComboSelectionListener());
                    // Se indica que el valor de esta entrada se almacena en esta
                    // lista desplegable.
                    inputFields.put(loadedInput.getName(), combo);
                }
            }
            // Se incrementa en uno el espacio necesario.
            count++;
            return true;
        } catch (Exception e) {
            String message = Messages.DynamicRefactoringWindow_ErrorInvokingAccessMethod + ".\n" + e.getMessage(); //$NON-NLS-1$
            logger.error(message);
            exitWithError(message);
            return false;
        }
    }

    /**
     * Carga la lista de posibles valores de una entrada a partir del modelo
     * MOON.
     * 
     * @param cParameters
     *            <code>Composite</code> en que se integrarn la nueva etiqueta
     *            y el nuevo desplegable con los valores de la entrada.
     * @param nextInput
     *            entrada cuyo valor se debe obtener.
     * 
     * @return <code>true</code> si se pudo cargar; <code>false</code> en caso
     *         contrario.
     */
    private boolean loadFromModel(final Composite cParameters, InputParameter nextInput) {

        try {
            Method method = model.getClass().getMethod(nextInput.getMethod(), (Class[]) null);

            Object values = method.invoke(model, (Object[]) null);

            if (values != null) {
                final CLabel lb_Input = new CLabel(cParameters, SWT.NONE);
                lb_Input.setBounds(342, desplazamiento + 105 + 67 * count, 183, 19);
                lb_Input.setAlignment(SWT.CENTER);
                lb_Input.setText(nextInput.getName().replaceAll("_", " ")); //$NON-NLS-1$ //$NON-NLS-2$

                // Los mtodos utilizados del modelo devuelven
                // siempre conjuntos de valores.
                combo = new Combo(cParameters, SWT.BORDER);
                combo.setBounds(35, desplazamiento + 132 + 67 * count, 790, 25);
                combo.setVisibleItemCount(10);
                combo.addFocusListener(new ComboEditor());

                // Se procesa el contenido del conjunto de valores.
                fillInCombo(values, combo, nextInput.getName());
                combo.addSelectionListener(new ComboSelectionListener());
                // Se indica que el valor de la entrada se almacena en esta lista
                // desplegable.
                inputFields.put(nextInput.getName(), combo);
            }
            // Se incrementa en uno el espacio necesario.
            count++;
            return true;
        } catch (Exception e) {
            Object[] messageArgs = { nextInput.getName() };
            MessageFormat formatter = new MessageFormat(""); //$NON-NLS-1$
            formatter.applyPattern(Messages.DynamicRefactoringWindow_ErrorObtainingValues);

            String message = formatter.format(messageArgs) + ".\n" + e.getMessage(); //$NON-NLS-1$
            logger.error(message);
            MessageDialog.openError(getShell(), Messages.DynamicRefactoringWindow_Error, message);
            return false;
        }
    }

    /**
     * Comprueba si una entrada identificada como <code>root</code> (la entrada
     * principal de la refactorizacin) conforma con el tipo del objeto
     * seleccionado como entrada principal de la refactorizacin en tiempo de
     * ejecucin.
     * 
     * <p>
     * El objeto seleccionado deber ser del mismo tipo o de un subtipo del tipo
     * con que se define la entrada raz de la refactorizacin.
     * </p>
     * 
     * <p>
     * Si la comprobacin falla, se mostrar un mensaje de error y se forzar el
     * cierre de la ventana de dilogo de refactorizacin, puesto que es un
     * error grave que impide la ejecucin normal de la refactorizacin.
     * 
     * @param testedInput
     *            entrada identificada como <code>root</code> y que conforma con
     *            el formato utilizado en la definicin de refactorizaciones.
     * 
     * @return <code>true</code> si los tipos se pueden comparar y conforman uno
     *         con otro; <code>false</code> en caso contrario.
     * 
     * @see DynamicRefactoringDefinition#getInputs() para estudiar el formato de
     *      especificacin de las entradas de una refactorizacin dinmica.
     */
    private boolean checkMainInput(InputParameter testedInput) {
        // El tipo de la entrada debe coincidir con el esperado.
        try {
            Class<?> mainClass = Class.forName(testedInput.getType());
            // La clase de la entrada debe ser una superclase del tipo
            // que tiene el argumento principal seleccionado en Eclipse.
            if (!mainClass.isAssignableFrom(currentObject.getClass())) {
                Object[] messageArgs = { "(" + currentObject.getClass().getCanonicalName() + ")", //$NON-NLS-1$ //$NON-NLS-2$
                        "(" + mainClass.getCanonicalName() + ")" }; //$NON-NLS-1$ //$NON-NLS-2$
                MessageFormat formatter = new MessageFormat(""); //$NON-NLS-1$
                formatter.applyPattern(Messages.DynamicRefactoringWindow_ObjectNotComplies);

                exitWithError(formatter.format(messageArgs) + "."); //$NON-NLS-1$
                return false;
            }
            return true;
        } catch (ClassNotFoundException exception) {
            String message = Messages.DynamicRefactoringWindow_ClassNotLoaded + ".\n" + exception.getMessage(); //$NON-NLS-1$
            logger.error(message);
            exitWithError(message);
            return false;
        }
    }

    /**
     * Aade los botones a la barra de botones de este dilogo.
     * 
     * @param parent
     *            el elemento compuesto de la barra de botones.
     * 
     * @see Dialog#createButtonsForButtonBar
     */
    @Override
    protected void createButtonsForButtonBar(Composite parent) {
        createButton(parent, IDialogConstants.OK_ID, Messages.DynamicRefactoringWindow_Run, true);
        createButton(parent, IDialogConstants.CANCEL_ID, Messages.DynamicRefactoringWindow_Close, false);
    }

    /**
     * Devuelve el tamao inicial que se debe utilizar.
     * 
     * @return el tamao inicial de la ventana.
     * 
     * @see Dialog#getInitialSize()
     */
    @Override
    protected Point getInitialSize() {
        return new Point(900, 512);
    }

    /**
     * Configura la shell proporcionada, preparndola para la apertura de esta
     * ventana sobre ella.
     * 
     * Mtodo plantilla (patrn de diseo Mtodo Plantilla).
     * 
     * @param newShell
     *            la shell que se configura.
     */
    @Override
    protected void configureShell(Shell newShell) {
        super.configureShell(newShell);
        newShell.setText(Messages.DynamicRefactoringWindow_Refactoring + refactoringDefinition.getName());
        newShell.setImage(RefactoringImages.getConfigureIcon());
    }

    /**
     * Muestra una ventana de dilogo con un mensaje de error y cierra la
     * ventana actual.
     * 
     * @param errorMsg
     *            mensaje de error que se debe mostrar.
     */
    private void exitWithError(String errorMsg) {
        MessageDialog.openError(getShell(), Messages.DynamicRefactoringWindow_Error, errorMsg);
        close();
    }

    /**
     * Analiza el tipo de ejecucin de un objeto que debera contener un
     * conjunto de elementos y, si es as, los obtiene uno a uno y los aade
     * como cadenas de texto al contenido de una lista desplegable.
     * 
     * <p>
     * El contenido de la lista desplegable depender de la implementacin que
     * los elementos del conjunto proporcionen para el mtodo de
     * <code>Object</code> <code>toString()</code>, ya que es el que se emplear
     * para obtener su representacin.
     * </p>
     * 
     * @param values
     *            objeto que debe contener un conjunto de elementos.
     * @param combo
     *            lista desplegable que se puebla con las representaciones
     *            textuales de los objetos encontrados.
     * @param inputName
     *            nombre de la entrada para la que se rellena el desplegable.
     * 
     * @throws ClassNotFoundException
     *             si no se consigue acceder a la clase de algunas de las clases
     *             de conjuntos de Java.
     */
    private void fillInCombo(Object values, Combo combo, String inputName) throws ClassNotFoundException {

        // Durante la creacin de refactorizaciones, a la hora de escoger
        // mtodos
        // que devuelvan conjuntos de valores, se tienen en cuenta dos tipos de
        // conjuntos de datos de Java.

        Class<?> collection = Class.forName(RefactoringConstants.COLLECTION_PATH);
        Class<?> iterator = Class.forName(RefactoringConstants.ITERATOR_PATH);

        Class<?> container = values.getClass();

        ArrayList<Object> elements = new ArrayList<Object>();

        // Si los valores estn contenidos en una coleccin.
        if (collection.isAssignableFrom(container))
            for (Object next : ((java.util.Collection<?>) values))
                elements.add(next);

        // Si los valores estn contenidos en un iterador.
        else if (iterator.isAssignableFrom(container)) {
            Iterator<?> valueIterator = (java.util.Iterator<?>) values;
            while (valueIterator.hasNext())
                elements.add(valueIterator.next());
        }

        List<Object> orderedElements = NamedObjectHandler.getSortedList(elements);
        inputValues.put(inputName, orderedElements.get(0));
        comboValues.put(Integer.valueOf(combo.hashCode()), orderedElements);
        for (Object next : orderedElements)
            combo.add(NamedObjectHandler.getName(next));
        combo.select(0);
    }

    /**
     * Analiza el tipo de ejecucin de un objeto para determinar si contiene un
     * nico elemento o, por el contrario, se trata de algn tipo de conjunto.
     * 
     * <p>
     * Si no se trata de un subtipo de <code>java.util.List</code>, ni de
     * <code>java.util.Iterator</code>, ni de <code>java.util.Collection</code>,
     * se considera un valor nico. Estos tres son los tipos de conjuntos que
     * devuelven los mtodos del modelo que acceden a listas, tablas,
     * colecciones, etc.
     * </p>
     * 
     * @param object
     *            objeto cuyo carcter de elemento nico se comprueba.
     * 
     * @return <code>true</code> si se considera que se trata de un elemento
     *         nico; <code>false</code> en caso contrario.
     * 
     * @throws ClassNotFoundException
     *             si no se consigue acceder a la clase de algunas de las clases
     *             de conjuntos de Java.
     */
    public static boolean isSingleValue(Object object) throws ClassNotFoundException {

        // Durante la creacin de refactorizaciones, a la hora de escoger
        // mtodos
        // que devuelvan conjuntos de valores, se tienen en cuenta tres tipos de
        // conjuntos de datos de Java.

        Class<?> collection = Class.forName(RefactoringConstants.COLLECTION_PATH);
        Class<?> iterator = Class.forName(RefactoringConstants.ITERATOR_PATH);

        Class<?> elementType = object.getClass();

        if (!collection.isAssignableFrom(elementType) && !iterator.isAssignableFrom(elementType))
            return true;

        return false;
    }

    /**
     * Obtiene un mtodo con un cierto nombre a partir de la definicin de una
     * clase cargada a partir de su nombre completamente cualificado.
     * 
     * @param from
     *            nombre completamente cualificado del tipo de la clase cuyo
     *            mtodo se debe cargar.
     * @param method
     *            nombre simple del mtodo que se debe cargar.
     * 
     * @return el mtodo obtenido por reflexin a partir de la clase de nombre
     *         dado.
     * 
     * @throws ClassNotFoundException
     *             si no se encuentra la clase.
     * @throws NoSuchMethodException
     *             si no se encuentra el mtodo en la clase.
     */
    private Method loadMethod(String from, String method) throws ClassNotFoundException, NoSuchMethodException {

        // Se intenta cargar la clase del tipo de origen.
        Class<?> inputClass = Class.forName(from);
        // Se intenta cargar el mtodo con el nombre dado y sin parmetros.
        return inputClass.getMethod(method, (Class[]) null);
    }

    /**
     * Convierte los objetos en cadenas con su nombre cualificado.
     * 
     * @param inputParameters
     *            <code>HashMap</code> con el nombre de los parmetros como
     *            clave y el objeto qeu lo representa como valor.
     * @return <code>HashMap</code> con el nombre de los parmetros como clave y
     *         su nombre cualificado como valor.
     */
    public static HashMap<String, String> getInputParameters(Map<String, Object> inputParameters) {
        HashMap<String, String> params = new HashMap<String, String>();

        // Guardamos la informacin de los parametros de entrada para
        // escribirlos en el plan
        //de refactorizaciones.
        for (Map.Entry<String, Object> param : inputParameters.entrySet()) {
            if (!param.getKey().equals("Model")) {
                if (param.getValue() instanceof moon.core.NamedObject)
                    params.put(param.getKey(), ((NamedObject) param.getValue()).getUniqueName().toString());
                else if (param.getValue() instanceof moon.core.instruction.CodeFragment) {
                    CodeFragment code = (CodeFragment) param.getValue();
                    params.put(param.getKey(),
                            code.getLine() + "," + code.getColumn() + "," + code.getEndLine() + ","
                                    + code.getEndColumn() + "," + code.getClassDef().getUniqueName() + ","
                                    + code.getText());
                } else
                    params.put(param.getKey(), param.getValue().toString());
            } else {
                // no necesito guardar informacin sobre el modelo ya que al
                // exportar el plan
                // el modelo sobre el que se trabajar ser diferente.
                params.put(param.getKey(), "");
            }
        }
        return params;
    }

    /**
     * Resuelve recursivamente el valor de una entrada que depende de otra
     * entrada distinta de la entrada principal y del modelo.
     * 
     * @param cParameters
     *            elemento sobre el que se situar el campo de texto o
     *            desplegable asociado a la nueva entrada una vez resuelta.
     * @param sourceInput
     *            <i>array</i> de cadenas con los atributos de la entrada a
     *            partir de la que se obtiene el valor para la nueva entrada.
     * @param newInput
     *            <i>array</i> de cadenas con los atributos de la nueva entrada
     *            que se debe intentar resolver.
     * 
     * @return <code>true</code> si se logr resolver correctamente la nueva
     *         entrada a partir de la entrada de origen; <code>false</code> en
     *         caso contrario.
     */
    private boolean recursiveDependencyResolution(Composite cParameters, InputParameter sourceInput,
            InputParameter newInput) {

        if (recursiveInputs.contains(newInput.getName())) {
            Object[] messageArgs = { newInput.getName() };
            MessageFormat formatter = new MessageFormat(""); //$NON-NLS-1$
            formatter.applyPattern(Messages.DynamicRefactoringWindow_CycleFound);

            String message = formatter.format(messageArgs) + ".\n" + //$NON-NLS-1$
                    Messages.DynamicRefactoringWindow_ReviewDefinition + ".\n"; //$NON-NLS-1$
            logger.error(message);
            exitWithError(message);
            return false;
        }
        recursiveInputs.add(newInput.getName());

        try {
            if (!inputValues.containsKey(sourceInput.getName())) {
                InputParameter recursiveSource = inputAttributes.get(sourceInput.getFrom());
                if (recursiveSource != null) {
                    if (recursiveSource.getType().equals("moon.core.Model")) { //$NON-NLS-1$
                        loadFromModel(cParameters, sourceInput);
                    } else if (recursiveSource.isMain()) { //$NON-NLS-1$
                        loadFromMain(cParameters, sourceInput);
                    } else {
                        recursiveDependencyResolution(cParameters, inputAttributes.get(sourceInput.getFrom()),
                                sourceInput);
                    }
                }
            }

            // Puede que no se haya cargado an el valor de origen porque deba
            // ser
            // introducido de forma manual. Solo se continua si se ha cargado.
            if (inputValues.containsKey(sourceInput.getName())) {

                Method runtimeMeth = loadMethod(sourceInput.getType(), newInput.getMethod());
                Object values = runtimeMeth.invoke(inputValues.get(sourceInput.getName()), (Object[]) null);

                if (values != null) {

                    // Si el valor se ha obtenido a partir de una entrada
                    // desplegable, hay que registrar esta dependencia.
                    Scrollable sourceField = inputFields.get(sourceInput.getName());
                    if (sourceField != null && sourceField instanceof Combo) {
                        String[] dependent = comboDependencies.get(Integer.valueOf(sourceField.hashCode()));
                        String[] updated;
                        if (dependent != null) {
                            updated = Arrays.copyOf(dependent, dependent.length + 1);
                            // Se aade el nombre de la nueva entrada a la lista
                            // de
                            // entradas dependientes del desplegable.
                            updated[updated.length] = newInput.getName();
                        } else
                            updated = new String[] { newInput.getName() };
                        comboDependencies.put(sourceField.hashCode(), updated);
                    }

                    final CLabel lb_Input = new CLabel(cParameters, SWT.NONE);
                    lb_Input.setBounds(342, desplazamiento + 105 + 67 * count, 183, 19);
                    lb_Input.setAlignment(SWT.CENTER);
                    lb_Input.setText(newInput.getName().replaceAll("_", " ")); //$NON-NLS-1$ //$NON-NLS-2$

                    // Si se ha obtenido un nico valor.
                    if (isSingleValue(values)) {
                        t_Input = new Text(cParameters, SWT.BORDER);
                        t_Input.setBounds(35, desplazamiento + 132 + 67 * count, 790, 25);
                        t_Input.setText(NamedObjectHandler.getName(values));
                        t_Input.setEditable(false);
                        inputValues.put(newInput.getName(), values);
                        // Se indica que el valor de esta entrada lo tiene este campo.
                        inputFields.put(newInput.getName(), t_Input);
                    }
                    // Otras entradas aparte del modelo tambin pueden
                    // devolver conjuntos de valores.
                    else {
                        combo = new Combo(cParameters, SWT.BORDER);
                        combo.setBounds(35, desplazamiento + 132 + 67 * count, 790, 25);
                        combo.setVisibleItemCount(10);
                        combo.addFocusListener(new ComboEditor());

                        // Se procesa el contenido del conjunto de valores.
                        fillInCombo(values, combo, newInput.getName());
                        combo.addSelectionListener(new ComboSelectionListener());
                        // Se indica que el valor de esta entrada se almacena en
                        // esta lista desplegable.
                        inputFields.put(newInput.getName(), combo);
                    }

                    // Se incrementa en uno el espacio necesario.
                    count++;
                }
            }
            // No se admiten dependencias de campos cuyo valor deba ser
            // introducido por el usuario.
            else {
                Object[] messageArgs = { newInput.getName() };
                MessageFormat formatter = new MessageFormat(""); //$NON-NLS-1$
                formatter.applyPattern(Messages.DynamicRefactoringWindow_DependsOnUser);

                String message = formatter.format(messageArgs) + ".\n" + //$NON-NLS-1$
                        Messages.DynamicRefactoringWindow_ReviewDefinition + ".\n"; //$NON-NLS-1$
                logger.error(message);
                exitWithError(message);
                return false;
            }

            return true;
        } catch (ClassNotFoundException exception) {
            String message = Messages.DynamicRefactoringWindow_ErrorLoadingClass + ".\n" + exception.getMessage(); //$NON-NLS-1$
            logger.error(message);
            exitWithError(message);
            return false;
        } catch (Exception exception) {
            String message = Messages.DynamicRefactoringWindow_ErrorInvokingMethodOnInput + ".\n" //$NON-NLS-1$
                    + exception.getMessage();
            logger.error(message);
            exitWithError(message);
            return false;
        }
    }

    /**
     * Recalcula el valor asociado a una entrada.
     * 
     * <p>
     * Solo debe llamarse para entradas que dependan de terceras entradas
     * distintas de la entrada principal de la refactorizacin y del modelo,
     * puesto que ninguna de las dos puede cambiar y se obtendra siempre, por
     * tanto, el mismo valor.
     * </p>
     * 
     * @param input
     *            entrada cuyo valor se debe recalcular.
     */
    private void recompute(InputParameter input) {

        // Se obtiene la entrada de la que depende la actual.
        InputParameter sourceInput = inputAttributes.get(input.getFrom());

        // Se obtiene el valor de la entrada de la que depende la actual.
        Object source = inputValues.get(sourceInput.getName());

        try {
            // Solo se actualiza el valor si el valor de origen ya est
            // disponible.
            if (source != null) {
                // Se carga el mtodo con el que se obtendr el valor de la
                // entrada.
                Method method = loadMethod(sourceInput.getType(), input.getMethod());
                // Se calcula el nuevo valor.
                Object values = method.invoke(source, (Object[]) null);

                if (values != null) {

                    Scrollable field = inputFields.get(input.getName());
                    if (field instanceof Text) {
                        // Se actualiza el valor de la entrada.
                        inputValues.put(input.getName(), values);
                        ((Text) field).setText(NamedObjectHandler.getName(values));
                    } else if (field instanceof Combo)
                        // Se rellena de nuevo el desplegable.
                        // El propio mtodo actualiza las tablas de entradas.
                        fillInCombo(values, (Combo) field, input.getName());
                }
            }
        } catch (Exception exception) {
            String message = Messages.DynamicRefactoringWindow_ErrorInvokingMethodOnInput + ".\n" //$NON-NLS-1$
                    + exception.getMessage();
            logger.error(message);
            exitWithError(message);
            close();
        }
    }

    /**
     * Determina si se han introducido todos los valores necesarios para
     * configurar la refactorizacin.
     * 
     * @return <code>true</code> si se ha introducido ya un valor para cada
     *         entrada necesaria para la refactorizacin; <code>false</code> en
     *         caso contrario.
     */
    private boolean isComplete() {
        if (inputAttributes.entrySet().size() > inputValues.entrySet().size() + 2)
            return false;
        return true;
    }

    /**
     * Elimina los htmls generados por java2html para visualizar los ejemplos
     * en un navegador html.
     */
    private void deleteHTMLS() {
        //directorio de la refactorizacion
        String dirRefactoring = new File(refactoringDefinition.getExamplesAbsolutePath().get(0).getBefore())
                .getParent();

        for (RefactoringExample ejemplos : refactoringDefinition.getExamples()) {
            File html_fich = new File(
                    dirRefactoring + File.separator + ejemplos.getBefore().replace("txt", "java") + ".html");
            if (html_fich.exists())
                html_fich.delete();
            html_fich = new File(
                    dirRefactoring + File.separator + ejemplos.getAfter().replace("txt", "java") + ".html");
            if (html_fich.exists())
                html_fich.delete();
        }

        if (new File(dirRefactoring + File.separator + "AllClasses.html").exists()) {
            new File(dirRefactoring + File.separator + "AllClasses.html").delete();
        }

        if (new File(dirRefactoring + File.separator + "default.index.html").exists()) {
            new File(dirRefactoring + File.separator + "default.index.html").delete();
        }

        if (new File(dirRefactoring + File.separator + "front.html").exists()) {
            new File(dirRefactoring + File.separator + "front.html").delete();
        }

        if (new File(dirRefactoring + File.separator + "index.html").exists()) {
            new File(dirRefactoring + File.separator + "index.html").delete();
        }

        if (new File(dirRefactoring + File.separator + "packages.html").exists()) {
            new File(dirRefactoring + File.separator + "packages.html").delete();
        }

        if (new File(dirRefactoring + File.separator + "stylesheet.css").exists()) {
            new File(dirRefactoring + File.separator + "stylesheet.css").delete();
        }

        if (new File(dirRefactoring + File.separator + "consola.txt").exists()) {
            new File(dirRefactoring + File.separator + "consola.txt").delete();
        }

    }

    /**
     * Cierra la ventana de dilogo.
     * 
     * @return indica si se ha podido cerrar la ventana correctamente.
     */
    @Override
    public boolean close() {
        //Es necesario hacer limpieza de los ficheros html generados cuando
        //hay ejemplos que mostrar.
        if (!refactoringDefinition.getExamples().isEmpty())
            deleteHTMLS();
        return super.close();
    }

    /**
     * Notifica que el botn de este dilogo con el identificador especificado
     * ha sido pulsado.
     * 
     * @param buttonId
     *            el identificador del botn que ha sido pulsado (vanse las
     *            constantes <code>IDialogConstants.*ID</code>).
     * 
     * @see Dialog#buttonPressed
     * @see IDialogConstants
     */
    @Override
    protected void buttonPressed(int buttonId) {

        // Independiente del tipo de botn que pusemos en este dilogo, es
        //necesario hacer limpieza de los ficheros html generados cuando
        //hay ejemplos que mostrar.
        if (!refactoringDefinition.getExamples().isEmpty())
            deleteHTMLS();

        if (buttonId == IDialogConstants.OK_ID) {

            if (isComplete()) {

                try {
                    InputProcessor processor = new InputProcessor(this);

                    MOONRefactoring.resetModel();

                    DynamicRefactoring refactoring = new DynamicRefactoring(refactoringDefinition, model,
                            processor.getInputs());

                    this.close();

                    DynamicRefactoringRunner runner = new DynamicRefactoringRunner(refactoring);
                    runner.setInputParameters(getInputParameters(processor.getInputs()));
                    runner.runRefactoring();

                    return;
                } catch (RefactoringException exception) {
                    String message = Messages.DynamicRefactoringWindow_ErrorBuilding + ":\n\n" //$NON-NLS-1$
                            + exception.getMessage();
                    logger.error(message);
                    exitWithError(message);
                    return;
                }
            } else
                MessageDialog.openWarning(getShell(), Messages.DynamicRefactoringWindow_Warning,
                        Messages.DynamicRefactoringWindow_FieldsMissing + "."); //$NON-NLS-1$
        } else
            super.buttonPressed(buttonId);
    }

    /**
     * Recibe notificaciones cuando cambia la seleccin en uno de los
     * desplegables asociado a una de las entradas.
     * 
     * @author <A HREF="mailto:sfd0009@alu.ubu.es">Sonia Fuente de la Fuente</A>
     * @author <A HREF="mailto:ehp0001@alu.ubu.es">Enrique Herrero Paredes</A>
     */
    private class ComboSelectionListener implements SelectionListener {

        /**
         * Recibe una notificacin de que un elemento del desplegable observado
         * ha sido seleccionado.
         * 
         * <p>
         * Inicia la actualizacin de todas las entradas cuyo valor es
         * dependiente del del desplegable.
         * </p>
         * 
         * @param e
         *            el evento de seleccin disparado en la ventana.
         * 
         * @see SelectionListener#widgetSelected(SelectionEvent)
         */
        @Override
        public void widgetSelected(SelectionEvent e) {
            if (e.getSource() instanceof Combo) {
                Combo observed = (Combo) e.getSource();

                // Se obtiene el nombre de la entrada asociada al desplegable.
                String thisInput = ""; //$NON-NLS-1$
                for (Entry<String, Scrollable> entry : inputFields.entrySet()) {
                    if (entry.getValue() == observed) {
                        thisInput = entry.getKey();
                        break;
                    }
                }
                // Se obtiene la lista de valores asociada al desplegable.
                List<Object> values = comboValues.get(Integer.valueOf(observed.hashCode()));
                // Se actualiza el valor de la entrada asociada al desplegable.
                // Se toma el valor de la posicin indicada por la seleccin.
                inputValues.put(thisInput, values.get(observed.getSelectionIndex()));

                // Se obtienen los nombes de las entradas dependientes del combo.
                String[] dependent = comboDependencies.get(Integer.valueOf(observed.hashCode()));
                if (dependent != null)
                    for (String inputName : dependent) {
                        // Se obtienen los atributos completos de la entrada.
                        InputParameter input = inputAttributes.get(inputName);
                        recompute(input);
                    }
            }
        }

        /**
         * Comportamiento para el evento de seleccin por defecto.
         * 
         * @see SelectionListener#widgetDefaultSelected(SelectionEvent)
         * 
         * @param e evento de seleccin.
         */
        @Override
        public void widgetDefaultSelected(SelectionEvent e) {
            widgetSelected(e);
        }
    }

    /**
     * Recibe notificaciones cuando cambia la seleccin en uno de los campos de
     * texto editables asociado a una de las entradas.
     * 
     * @author <A HREF="mailto:sfd0009@alu.ubu.es">Sonia Fuente de la Fuente</A>
     * @author <A HREF="mailto:ehp0001@alu.ubu.es">Enrique Herrero Paredes</A>
     */
    private class TextModifyListener implements ModifyListener {

        /**
         * Recibe una notificacin de que un elemento del desplegable observado
         * ha sido seleccionado.
         * 
         * <p>
         * Inicia la actualizacin del valor asociado al campo de texto
         * editable.
         * </p>
         * 
         * @param e
         *            el evento de modificacin disparado en la ventana.
         * 
         * @see SelectionListener#widgetSelected(SelectionEvent)
         */
        @Override
        public void modifyText(ModifyEvent e) {
            if (e.getSource() instanceof Text) {
                Text observed = (Text) e.getSource();

                // Se obtiene el nombre de la entrada asociada al desplegable.
                String thisInput = ""; //$NON-NLS-1$
                for (Entry<String, Scrollable> entry : inputFields.entrySet()) {
                    if (entry.getValue() == observed) {
                        thisInput = entry.getKey();
                        break;
                    }
                }
                // Se obtiene el valor asociado al campo de texto.
                String value = observed.getText().trim();
                // Se actualiza el valor de la entrada asociada al campo.
                inputValues.put(thisInput, value);
            }
        }
    }
}