/* * JBoss, Home of Professional Open Source * Copyright 2013, Red Hat, Inc. and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.richfaces.resource.optimizer.ordering; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.google.common.collect.Sets; /** * <p> * Stores partial orderings in order to be able derive complete ordering. * </p> * * <p> * When storing new partial ordering, checks that new partial ordering does not violates partial orderings stored before. * </p> * * @author <a href="http://community.jboss.org/people/lfryc">Lukas Fryc</a> */ public class PartialOrderToCompleteOrder<T> { // all items stored in partial orderings for quick access private Set<T> allItems = Sets.newLinkedHashSet(); // partial orderings used to check for ordering violation private List<PartialOrdering> partialOrderings = new LinkedList<PartialOrdering>(); // map from items to their dependencies private Map<T, Set<T>> dependencies = Maps.newLinkedHashMap(); /** * <p> * Stores collection as partial ordering. * </p> * * <p> * Checks that this collection will not violate another partial orderings stored before. * </p> * * @param collection as partial order */ public void addPartialOrdering(Collection<T> collection) { if (collection == null || collection.isEmpty()) { return; } checkCurrentPartialOrders(collection); allItems.addAll(collection); partialOrderings.add(new PartialOrdering(collection)); registerDependencies(Lists.newLinkedList(collection)); } /** * Provides all items which was stored in collections as partial orderings * * @return all items which was stored in collections as partial orderings */ public Set<T> getAllItems() { return allItems; } /** * <p> * Provides current complete ordering derived from partial orderings. * </p> * * @return current complete ordering derived from partial orderings. */ public CompleteOrdering getCompleteOrdering() { return new CompleteOrdering(); } /** * Get all items completely ordered. * * @return all items completely ordered. */ public Collection<T> getCompletelyOrderedItems() { return new CompleteOrdering().sortedCopy(allItems); } /** * Class representing result of deriving complete ordering from stored partial orderings. */ public class CompleteOrdering extends Ordering<T> { private Set<T> ordered = getCurrentOrder(); private Predicate<T> IS_ORDERED = new Predicate<T>() { public boolean apply(T item) { return ordered.contains(item); } }; public int compare(T left, T right) { if (!ordered.contains(left) || !ordered.contains(right)) { throw new IllegalStateException(); } if (left.equals(right)) { return 0; } for (T item : ordered) { if (item.equals(left)) { return -1; } else if (item.equals(right)) { return +1; } } throw new IllegalStateException(); } /** * <p> * Returns new iterable sorted according to this complete ordering. * </p> * * <p> * All items which are unknown in this ordering are stored on the end of returned collection in the same order like in * iterable. * </p> */ @SuppressWarnings("unchecked") @Override public <E extends T> List<E> sortedCopy(Iterable<E> iterable) { List<T> originList = (List<T>) Lists.newLinkedList(iterable); Collection<T> onlyOrdered = Collections2.filter(originList, IS_ORDERED); Collection<T> onlyNotOrdered = Collections2.filter(originList, Predicates.not(IS_ORDERED)); List<T> itemsInOrder = super.sortedCopy(onlyOrdered); itemsInOrder.addAll(onlyNotOrdered); return (List<E>) itemsInOrder; } private Set<T> getCurrentOrder() { Set<T> result = Sets.newLinkedHashSet(); DependencyResolver resolver = new DependencyResolver(); for (int i = 0; i < dependencies.size(); i++) { List<T> nodesWithoutDependencies = resolver.findNodesWithoutDependencies(); result.addAll(nodesWithoutDependencies); resolver.removeNodes(nodesWithoutDependencies); } if (resolver.getSize() > 0) { throw new IllegalStateException(); } return result; } private class DependencyResolver { private Map<T, Set<T>> deps = deepCopyOfDependencies(); private List<T> findNodesWithoutDependencies() { List<T> list = Lists.newLinkedList(); for (Entry<T, Set<T>> entry : deps.entrySet()) { if (entry.getValue().isEmpty()) { list.add(entry.getKey()); } } return list; } private void removeNodes(List<T> nodes) { for (Set<T> values : deps.values()) { values.removeAll(nodes); } for (T node : nodes) { deps.remove(node); } } private Map<T, Set<T>> deepCopyOfDependencies() { Map<T, Set<T>> result = Maps.newLinkedHashMap(); for (Entry<T, Set<T>> entry : dependencies.entrySet()) { result.put(entry.getKey(), Sets.newHashSet(entry.getValue())); } return result; } public int getSize() { return deps.size(); } } } private void checkCurrentPartialOrders(Collection<T> collection) { for (PartialOrdering p : partialOrderings) { List<T> filtered = p.filter(collection); if (!p.isStrictlyOrdered(filtered)) { throw new IllegalPartialOrderingException("\ncollection: " + collection + "\n" + p); } } } private void registerDependencies(Collection<T> collection) { List<T> reversedOrder = Lists.reverse(Lists.newLinkedList(collection)); Set<T> newItemDependencies = Sets.newLinkedHashSet(collection); for (T newItem : reversedOrder) { newItemDependencies.remove(newItem); registerDependenciesForItem(newItem, Sets.newLinkedHashSet(newItemDependencies)); } } private void registerDependenciesForItem(T item, Set<T> newItemDependencies) { if (!dependencies.containsKey(item)) { dependencies.put(item, Sets.<T>newHashSet()); } Set<T> itemDependences = dependencies.get(item); itemDependences.addAll(newItemDependencies); } private class PartialOrdering extends Ordering<T> { private LinkedList<T> order = Lists.newLinkedList(); private HashSet<T> items = Sets.newHashSet(); public PartialOrdering(Collection<T> collection) { order = Lists.newLinkedList(collection); items = Sets.newHashSet(collection); } public int compare(T left, T right) { if (!items.contains(left)) { throw new IllegalArgumentException("'" + left + "' is not part of this partial ordering"); } if (!items.contains(right)) { throw new IllegalArgumentException("'" + right + "' is not part of this partial ordering"); } return order.indexOf(left) - order.indexOf(right); } public List<T> filter(Collection<T> collection) { List<T> list = new LinkedList<T>(collection); list.retainAll(items); return list; } @Override public String toString() { return "PartialOrder" + order; } } }