
/*
 * Copyright (C) 2014 Archie L. Cobbs. All rights reserved.
 *
 * $Id$
 */

package org.jsimpledb.util;

import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableSet;

/**
 * Support superclass for {@link NavigableSet} implementations that join together multiple other {@link NavigableSet}s
 * having equivalent {@link Comparator}s and for which {@link #size} is an expensive operation.
 *
 * @param <E> element type
 */
abstract class AbstractMultiNavigableSet<E> extends AbstractNavigableSet<E> {

    protected final ArrayList<? extends NavigableSet<E>> list;
    protected final Comparator<? super E> comparator;

    /**
     * Convenience constructor for the case where there are no lower or upper bounds.
     *
     * @param sets sets to be combined
     * @throws IllegalArgumentException if the {@code sets} is empty
     * @throws IllegalArgumentException if the {@link NavigableSet}s in {@code sets} do not have equal {@link Comparator}s
     */
    protected AbstractMultiNavigableSet(Iterable<? extends NavigableSet<E>> sets) {
        this(sets, AbstractMultiNavigableSet.getComparator(sets), new Bounds<E>());
    }

    /**
     * Primary constructor.
     *
     * @param sets sets to be combined
     * @param comparator common comparator
     * @param bounds range restriction
     * @throws IllegalArgumentException if the {@code sets} is empty
     * @throws IllegalArgumentException if the {@link NavigableSet}s in {@code sets} do not have equal {@link Comparator}s
     * @throws IllegalArgumentException if {@code bounds} is null
     */
    protected AbstractMultiNavigableSet(Iterable<? extends NavigableSet<E>> sets,
      Comparator<? super E> comparator, Bounds<E> bounds) {
        super(bounds);
        this.list = Lists.newArrayList(sets);
        this.comparator = comparator;
    }

    @Override
    public Comparator<? super E> comparator() {
        return this.comparator;
    }

    /**
     * Get and verify a common {@link Comparator} (possibly null).
     */
    private static <E> Comparator<? super E> getComparator(Iterable<? extends NavigableSet<E>> sets) {
        final Iterator<? extends NavigableSet<E>> i = sets.iterator();
        if (!i.hasNext())
            throw new IllegalArgumentException("empty sets");
        final Comparator<? super E> comparator = i.next().comparator();
        while (i.hasNext()) {
            final Comparator<? super E> comparator2 = i.next().comparator();
            if (!(comparator == null ? comparator2 == null : comparator.equals(comparator2)))
                throw new IllegalArgumentException("sets have unequal comparators");
        }
        return comparator;
    }

    @Override
    protected final NavigableSet<E> createSubSet(boolean reverse, Bounds<E> newBounds) {
        final ArrayList<NavigableSet<E>> newList = new ArrayList<NavigableSet<E>>(this.list.size());
        for (NavigableSet<E> set : this.list) {
            final NavigableSet<E> newSet;
            if (newBounds.getLowerBoundType() != BoundType.NONE && newBounds.getUpperBoundType() == BoundType.NONE)
                newSet = set.tailSet(newBounds.getLowerBound(), newBounds.getLowerBoundType().isInclusive());
            else if (newBounds.getLowerBoundType() == BoundType.NONE && newBounds.getUpperBoundType() != BoundType.NONE)
                newSet = set.headSet(newBounds.getUpperBound(), newBounds.getUpperBoundType().isInclusive());
            else if (newBounds.getLowerBoundType() != BoundType.NONE && newBounds.getUpperBoundType() != BoundType.NONE) {
                newSet = set.subSet(newBounds.getLowerBound(), newBounds.getLowerBoundType().isInclusive(),
                  newBounds.getUpperBound(), newBounds.getUpperBoundType().isInclusive());
            } else
                newSet = set;
            newList.add(set);
        }
        return this.createSubSet(reverse, newBounds, newList);
    }

    /**
     * Create a (possibly reversed) view of this instance with (possibly) tighter lower and/or upper bounds.
     * The {@code newBounds} are consistent with the new ordering (i.e., reversed if {@code reverse} is true)
     * and have already been range-checked against {@linkplain #bounds this instance's current bounds}.
     *
     * @param reverse whether the new set's ordering should be reversed relative to this instance's ordering
     * @param newBounds new bounds
     * @param list list of this instance's nested sets, restricted to {@code newBounds}
     * @throws IllegalArgumentException if {@code newBounds} is null
     */
    protected abstract NavigableSet<E> createSubSet(boolean reverse, Bounds<E> newBounds, List<NavigableSet<E>> list);
}


@@ -3037,50 +3037,8 @@
 ) %7B%0A
-            final NavigableSet%3CE%3E newSet;%0A
     
@@ -3100,61 +3100,38 @@
 NONE
- && newBounds.getUpperBoundType() == BoundType.NONE)%0A
+) %7B%0A                try %7B%0A    
     
@@ -3134,36 +3134,33 @@
                 
-newS
+s
 et = set.tailSet
@@ -3249,243 +3249,154 @@
     
-else if (newBounds.getLowerBoundType() == BoundType.NONE && newBounds.getUpperBoundType() != BoundType.NONE)%0A                newSet = set.headSet(newBounds.getUpperBound(), newBounds.getUpperBoundType().isInclusive());%0A            else
+    %7D catch (IllegalArgumentException e) %7B%0A                    // ignore - lower bound is out of range%0A                %7D%0A            %7D%0A           
  if 
@@ -3401,35 +3401,35 @@
 f (newBounds.get
-Low
+Upp
 erBoundType() !=
@@ -3447,60 +3447,31 @@
 NONE
- && newBounds.getUpperBoundType() != BoundType.NONE)
+) %7B%0A                try
  %7B%0A 
@@ -3477,36 +3477,37 @@
                 
-newS
+    s
 et = set.subSet(
@@ -3503,11 +3503,12 @@
 set.
-sub
+head
 Set(
@@ -3512,35 +3512,35 @@
 et(newBounds.get
-Low
+Upp
 erBound(), newBo
@@ -3539,35 +3539,35 @@
 ), newBounds.get
-Low
+Upp
 erBoundType().is
@@ -3577,17 +3577,18 @@
 lusive()
-,
+);
 %0A       
@@ -3600,85 +3600,107 @@
     
-  newBounds.getUpperBound(), newBounds.getU
+%7D catch (IllegalArgumentException e) %7B%0A                    // ignore - u
 pper
-B
+ b
 ound
-Type().isInclusive());%0A  
+ is out of range%0A
     
@@ -3709,21 +3709,16 @@
     
-%7D else%0A
       
+%7D%0A
     
@@ -3727,21 +3727,11 @@
     
-newSet = set;
+  %7D
 %0A   

Fix two bugs in createSubSet(): - Handle IllegalArgumentException thrown from headSet() and tailSet() when new bound is out of range. - We were not even using the individual restricted sub-sets anyway.