dynamicrefactoring.domain.metadata.imp.ElementCatalog.java Source code

Java tutorial

Introduction

Here is the source code for dynamicrefactoring.domain.metadata.imp.ElementCatalog.java

Source

/*<Dynamic Refactoring Plugin For Eclipse 3 - Plugin that allows to perform refactorings 
on Java code within Eclipse, as well as to dynamically create and manage new refactorings and classify them.>
    
Copyright (C) 2011  Mryam Gmez e ?igo Mediavilla
    
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.domain.metadata.imp;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;

import dynamicrefactoring.domain.metadata.condition.CategoryCondition;
import dynamicrefactoring.domain.metadata.interfaces.Category;
import dynamicrefactoring.domain.metadata.interfaces.Classification;
import dynamicrefactoring.domain.metadata.interfaces.ClassifiedElements;
import dynamicrefactoring.domain.metadata.interfaces.ClassifiedFilterableCatalog;
import dynamicrefactoring.domain.metadata.interfaces.Element;

/**
 * Permite clasificar elementos por categoras y aplicar filtros sobre ellos.
 * 
 * @author <A HREF="mailto:ims0011@alu.ubu.es">Iigo Mediavilla Saiz</A>
 * @author <A HREF="mailto:mgs0110@alu.ubu.es">Mryam Gmez San Martn</A>
 * 
 * @param <K>
 *            Tipo del catlogo
 */
public final class ElementCatalog<K extends Element> implements ClassifiedFilterableCatalog<K> {

    /**
     * Lista de condiciones que conforman el filtro actual aplicado.
     */
    private List<Predicate<K>> filter;

    /**
     * Elementos clasificados.
     */
    private Map<Category, Set<K>> classifiedElements;

    /**
     * Elementos clasificados filtrados.
     */
    private Map<Category, Set<K>> filteredClassifiedElements;

    /**
     * Clasificacion que divide por categorias los elementos.
     */
    private final Classification classification;

    /**
     * Crea un catalogo de elementos con los elementos que se le pasa sin ningun
     * filtro y clasificando los elementos en las categorias pasadas.
     * 
     * @param allElements
     *            elementos que componen el catalogo
     * @param classification
     *            clasificacion en la que se basa el catlogo
     */
    public ElementCatalog(Set<K> allElements, Classification classification) {
        this(allElements, classification, new ArrayList<Predicate<K>>());

    }

    /**
     * Crea un catalog de elementos con los elementos que se le pasa con los
     * filtros pasados y clasificando los elementos en las categorias pasadas.
     * 
     * @param allElements
     *            elementos que componen el catalogo
     * @param classification
     *            clasificacin en la que se basa el catlogo
     * @param filterConditionList
     *            condiciones de filtrado del nuevo catalogo de elementos
     */
    public ElementCatalog(Set<K> allElements, Classification classification,
            List<Predicate<K>> filterConditionList) {
        Preconditions.checkArgument(
                classification.isMultiCategory()
                        || !hasMultiCategoryElements(allElements, classification.getCategories()),
                "Some of the elements belong to more than one category" + ""
                        + " though the classification doesn't allow multiple-category elements");
        this.filter = new ArrayList<Predicate<K>>();
        this.classification = classification;
        initializeClassifiedElements(classification.getCategories());
        initializeFilteredClassifiedElements(classification.getCategories());
        classify(allElements);
        for (Predicate<K> condition : filterConditionList) {
            addConditionToFilter(condition);
        }
    }

    /**
     * Devuelve si alguno de los elementos pertenece a mas de una de las
     * categorias.
     * 
     * @param allElements
     *            conjunto de elementos
     * @param categories
     *            conjunto de categorias
     * @return si alguno de los elementos pertenece a mas de una de las
     *         categorias
     */
    private boolean hasMultiCategoryElements(Set<K> allElements, Set<Category> categories) {
        for (K element : allElements) {
            if (Sets.intersection(element.getCategories(), categories).size() > 1) {
                return true;
            }
        }
        return false;
    }

    /**
     * Inicializa el catlogo de elementos.
     * 
     * @param classificationCategories
     *            conjunto de categoras de la clasificacin
     */
    private void initializeClassifiedElements(Set<Category> classificationCategories) {
        classifiedElements = new HashMap<Category, Set<K>>();
        for (Category c : classificationCategories) {
            classifiedElements.put(c, new HashSet<K>());
        }
        classifiedElements.put(Category.NONE_CATEGORY, new HashSet<K>());
    }

    /**
     * Inicializa el catlogo de elementos filtrados.
     * 
     * @param classificationCategories
     *            conjunto de categoras de la clasificacin
     */
    private void initializeFilteredClassifiedElements(Set<Category> classificationCategories) {
        filteredClassifiedElements = new HashMap<Category, Set<K>>();
        for (Category c : classificationCategories) {
            filteredClassifiedElements.put(c, new HashSet<K>());
        }
        filteredClassifiedElements.put(Category.NONE_CATEGORY, new HashSet<K>());
    }

    /**
     * Clasifica los elementos pasados dentro de las categorias definidas en el
     * catalogo.
     * 
     * @param elementsToClassify
     *            elementos a clasificar
     */
    private void classify(Collection<K> elementsToClassify) {
        ArrayList<K> elementsLeftWithNoCategory = new ArrayList<K>(elementsToClassify);
        for (Category category : classifiedElements.keySet()) {
            Collection<K> belongToCategory = Collections2.filter(elementsToClassify,
                    new CategoryCondition<K>(category));
            classifiedElements.get(category).addAll(belongToCategory);
            elementsLeftWithNoCategory.removeAll(belongToCategory);
        }
        classifiedElements.get(Category.NONE_CATEGORY).addAll(elementsLeftWithNoCategory);
    }

    @Override
    public void addConditionToFilter(Predicate<K> condition) {
        for (Entry<Category, Set<K>> entry : classifiedElements.entrySet()) {
            Collection<K> toFilter = new HashSet<K>(
                    Collections2.filter(entry.getValue(), Predicates.not(condition)));
            classifiedElements.get(entry.getKey()).removeAll(toFilter);
            filteredClassifiedElements.get(entry.getKey()).addAll(toFilter);
        }
        filter.add(condition);
    }

    /**
     * Devuelve un predicado con todas las condiciones del filtro juntas.
     * 
     * @return predicado con todas las condiciones
     */
    private Predicate<K> getPredicateForAllConditions() {
        return Predicates.and(filter);
    }

    @Override
    public void removeConditionFromFilter(Predicate<K> conditionToRemove) {
        // comprobamos si cada uno sigue estando filtrado con el filtro
        // resultante
        // de haber eliminado la condicion que se va a eliminar
        filter.remove(conditionToRemove);
        for (Entry<Category, Set<K>> entry : filteredClassifiedElements.entrySet()) {
            Collection<K> toUnFilter = new HashSet<K>(
                    Collections2.filter(entry.getValue(), getPredicateForAllConditions()));
            filteredClassifiedElements.get(entry.getKey()).removeAll(toUnFilter);
            classifiedElements.get(entry.getKey()).addAll(toUnFilter);
        }

    }

    /**
     * Genera un catalogo nuevo con los mismos elementos y categorias pero
     * inicialmente ninguna condicion.
     * 
     * @return catalogo con los mismos elementos y clasificaciones que el actual
     *         pero sin condiciones
     */
    @Override
    public ClassifiedFilterableCatalog<K> removeAllFilterConditions() {
        return new ElementCatalog<K>(getAllElements(), classification);
    }

    /**
     * Obtiene la lista de elementos clasificados.
     * 
     * @return lista de elementos clasificados
     */
    @Override
    public ClassifiedElements<K> getClassificationOfElements() {
        HashMap<Category, Set<K>> toReturn = new HashMap<Category, Set<K>>();

        // Hacemos copias defensivas de los sets
        for (Category category : classifiedElements.keySet()) {
            toReturn.put(category, ImmutableSet.copyOf(classifiedElements.get(category)));
        }

        return new SimpleClassifiedElements<K>(new SimpleUniLevelClassification(classification.getName(),
                classification.getDescription(), toReturn.keySet(), classification.isMultiCategory()), toReturn);
    }

    /**
     * Obtiene la lista de elementos filtrados clasificados.
     * 
     * @return lista de elementos filtrados clasificados
     */
    @Override
    public ClassifiedElements<K> getClassificationOfFilteredElements() {
        HashMap<Category, Set<K>> toReturn = new HashMap<Category, Set<K>>();

        // Hacemos copias defensivas de los sets
        for (Category category : filteredClassifiedElements.keySet()) {
            toReturn.put(category, ImmutableSet.copyOf(filteredClassifiedElements.get(category)));
        }

        return new SimpleClassifiedElements<K>(new SimpleUniLevelClassification(classification.getName(),
                classification.getDescription(), toReturn.keySet(), classification.isMultiCategory()), toReturn);
    }

    /**
     * Devuelve todas las condiciones de filtro.
     * 
     * @return todas las condiciones de filtro
     */
    @Override
    public List<Predicate<K>> getAllFilterConditions() {
        return new ArrayList<Predicate<K>>(filter);
    }

    /**
     * Construye una catlogo con los mismos filtros y elementos que el actual
     * pero con una clasificacion distinta.
     * 
     * @param classification
     *            clasificacion a aplicar al nuevo catalogo
     * @return nuevo catalogo en el que solo cambia que se le aplica una nueva
     *         clasificacion
     */
    @Override
    public ClassifiedFilterableCatalog<K> newInstance(Classification classification) {
        return new ElementCatalog<K>(getAllElements(), classification, filter);
    }

    /**
     * Obtiene todos los elementos del catalogo.
     * 
     * @return todos los elementos contenidos en el catalogo.
     */
    protected Set<K> getAllElements() {
        Set<K> allElements = new HashSet<K>();
        for (Category c : classifiedElements.keySet()) {
            allElements.addAll(classifiedElements.get(c));
        }
        for (Category c : filteredClassifiedElements.keySet()) {
            allElements.addAll(filteredClassifiedElements.get(c));
        }

        return allElements;
    }

    /**
     * Devuelve la clasificacin que divide en categorias los elementos de este
     * catalogo.
     * 
     * @return clasificacion que divide en categorias el catalogo.
     */
    @Override
    public Classification getClassification() {
        return new SimpleUniLevelClassification(classification.getName(), classification.getDescription(),
                classification.getCategories(), classification.isMultiCategory());
    }

    /**
     * Determina si se encuentra vacio el filtro de condiciones.
     * 
     * @return devuelve verdadero si el filtro se encuentra vacio de
     *         condiciones, falso en caso contrario.
     */
    @Override
    public boolean isEmptyFilter() {
        return filter.isEmpty();
    }

    /**
     * Determina si existen elementos filtrados.
     * 
     * @return devuelve verdadero si exiten elementos filtrados, falso en caso
     *         contrario.
     */
    @Override
    public boolean hasFilteredElements() {
        Set<K> allFilteredElements = new HashSet<K>();
        for (Category c : filteredClassifiedElements.keySet()) {
            allFilteredElements.addAll(filteredClassifiedElements.get(c));
        }
        return !allFilteredElements.isEmpty();
    }
}