Java tutorial
/* * The MIT License (MIT) * * Copyright (c) 2013-2015 Jeff Nelson, Cinchapi Software Collective * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package org.cinchapi.concourse.server.model; import java.util.Comparator; import java.util.List; import org.cinchapi.concourse.server.concurrent.RangeToken; import org.cinchapi.concourse.thrift.Operator; import com.google.common.collect.BoundType; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Lists; import com.google.common.collect.Range; /** * A collection of utilities to augment the {@link Range} class with respect to * coverage of {@link Value Values}. * * @author jnelson */ public final class Ranges { /** * Compare {@code a} and {@code b} by their lower endpoints and, if * necessary, bounds. * * @param a * @param b * @return a negative Value, zero, or a positive Value as {@code a} is * less than, equal to, or greater than {@code b}. */ public static int compareToLower(Range<Value> a, Range<Value> b) { return ComparisonChain.start().compare(getLowerEndpoint(a), getLowerEndpoint(b)) .compare(getLowerBoundType(a), getLowerBoundType(b), LOWER_BOUND_COMPARATOR).result(); } /** * Compare {@code a} and {@code b} by their upper endpoints and, if * necessary, bounds. * * @param a * @param b * @return a negative Value, zero, or a positive Value as {@code a} is * less than, equal to, or greater than {@code b}. */ public static int compareToUpper(Range<Value> a, Range<Value> b) { return ComparisonChain.start().compare(getUpperEndpoint(a), getUpperEndpoint(b)) .compare(getUpperBoundType(a), getUpperBoundType(b), UPPER_BOUND_COMPARATOR).result(); } /** * Convert the given {@code range} for {@code key} to a matching * {@link RangeToken}. * * @param key * @param range * @return the RangeToken */ public static RangeToken convertToRangeToken(Text key, Range<Value> range) { Value lower = getLowerEndpoint(range); Value upper = getUpperEndpoint(range); boolean lowerClosed = getLowerBoundType(range) == BoundType.CLOSED; boolean upperClosed = getUpperBoundType(range) == BoundType.CLOSED; // We use the length of the values array in the RangeToken as a hack to // signal what kind of non-traditional (i.e. closedOpen) bounds to use // on the BETWEEN range when using the Guava data type. int size; if (!lowerClosed && !upperClosed) { size = 3; } else if (lowerClosed && upperClosed) { size = 4; } else if (!lowerClosed && upperClosed) { size = 5; } else { size = 2; } Value[] values = new Value[size]; values[0] = lower; values[1] = upper; for (int i = 2; i < values.length; i++) { values[i] = Value.NEGATIVE_INFINITY; } return RangeToken.forReading(key, Operator.BETWEEN, values); } /** * Equivalent to {@link Range#lowerBoundType()} except that * {@link BoundType#CLOSED} is returned if the lower endpoint is equals to * {@link Value#NEGATIVE_INFINITY}. * * @param range * @return the lower bound type */ public static BoundType getLowerBoundType(Range<Value> range) { Value lower = getLowerEndpoint(range); if (lower == Value.NEGATIVE_INFINITY) { return BoundType.CLOSED; } else { return range.lowerBoundType(); } } /** * Equivalent to {@link Range#lowerEndpoint()} except that * {@link Value#NEGATIVE_INFINITY} is returned if the {@code range} does not * have a defined lower bound. * * @param range * @return the lower endpoint */ public static Value getLowerEndpoint(Range<Value> range) { if (!range.hasLowerBound()) { return Value.NEGATIVE_INFINITY; } else { return range.lowerEndpoint(); } } /** * Equivalent to {@link Range#upperBoundType()} except that * {@link BoundType#CLOSED} is returned if the upper endpoint is equals to * {@link Value#POSITVE_INFINITY}. * * @param range * @return the upper bound type */ public static BoundType getUpperBoundType(Range<Value> range) { Value upper = getUpperEndpoint(range); if (upper == Value.POSITIVE_INFINITY) { return BoundType.CLOSED; } else { return range.upperBoundType(); } } /** * Equivalent to {@link Range#upperEndpoint()} except that * {@link Value#POSITVE_INFINITY} is returned if the {@code range} does not * have a defined upper bound. * * @param range * @return the upper endpoint */ public static Value getUpperEndpoint(Range<Value> range) { if (!range.hasUpperBound()) { return Value.POSITIVE_INFINITY; } else { return range.upperEndpoint(); } } /** * Return a new {@link Range} that is the merger (e.g. union) of {@code a} * and {@code b}. The new {@link Range} maintains both the lower and higher * endpoint/bound between the two inputs. * * @param a * @param b * @return the union of {@code a} and {@code b} */ public static Range<Value> merge(Range<Value> a, Range<Value> b) { if (a.isConnected(b)) { boolean aStart = compareToLower(a, b) < 0; boolean aEnd = compareToUpper(a, b) > 0; boolean lower = getLowerBoundType(aStart ? a : b) == BoundType.CLOSED; boolean upper = getUpperBoundType(aStart ? a : b) == BoundType.CLOSED; if (lower && upper) { return Range.closed(getLowerEndpoint(aStart ? a : b), getUpperEndpoint(aEnd ? a : b)); } else if (!lower && upper) { return Range.closedOpen(getLowerEndpoint(aStart ? a : b), getUpperEndpoint(aEnd ? a : b)); } else if (lower && !upper) { return Range.openClosed(getLowerEndpoint(aStart ? a : b), getUpperEndpoint(aEnd ? a : b)); } else { return Range.open(getLowerEndpoint(aStart ? a : b), getUpperEndpoint(aEnd ? a : b)); } } else { return null; } } /** * Return the ranges that include the points that are in {@code a} or the * {@code b} one and not in their intersection. The return set will include * between 0 and 2 ranges that together include all the points that meet * this criteria. * <p> * <strong>NOTE:</strong> If the two ranges do not intersect, then a * collection containing both of them is returned (since they already form * their xor). * </p> * * @param a * @param b * @return the set or ranges that make uValue the symmetric difference * between this range and the {@code other} one */ public static Iterable<Range<Value>> xor(Range<Value> a, Range<Value> b) { List<Range<Value>> ranges = Lists.newArrayList(); try { Range<Value> intersection = a.intersection(b); boolean aStart = compareToLower(a, b) < 0; boolean aEnd = compareToUpper(a, b) > 0; boolean lower = getLowerBoundType(aStart ? a : b) == BoundType.CLOSED; boolean upper = getUpperBoundType(aEnd ? a : b) == BoundType.CLOSED; boolean interLower = getLowerBoundType(intersection) == BoundType.OPEN; boolean interUpper = getUpperBoundType(intersection) == BoundType.OPEN; Range<Value> first; if (lower && interLower) { first = Range.closed(getLowerEndpoint(aStart ? a : b), getLowerEndpoint(intersection)); } else if (!lower && interLower) { first = Range.openClosed(getLowerEndpoint(aStart ? a : b), getLowerEndpoint(intersection)); } else if (lower && !interLower) { first = Range.closedOpen(getLowerEndpoint(aStart ? a : b), getLowerEndpoint(intersection)); } else { first = Range.open(getLowerEndpoint(aStart ? a : b), getLowerEndpoint(intersection)); } Range<Value> second; if (interUpper && upper) { second = Range.closed(getUpperEndpoint(intersection), getUpperEndpoint(aEnd ? a : b)); } else if (!interUpper && upper) { second = Range.openClosed(getUpperEndpoint(intersection), getUpperEndpoint(aEnd ? a : b)); } else if (interUpper && !interUpper) { second = Range.closedOpen(getUpperEndpoint(intersection), getUpperEndpoint(aEnd ? a : b)); } else { second = Range.open(getUpperEndpoint(intersection), getUpperEndpoint(aEnd ? a : b)); } if (!first.isEmpty()) { ranges.add(first); } if (!second.isEmpty()) { ranges.add(second); } } catch (IllegalArgumentException e) { // ranges dont intersect ranges.add(a); ranges.add(b); } return ranges; } /** * A comparator to sort the lower bound of Ranges. */ private final static Comparator<BoundType> LOWER_BOUND_COMPARATOR = new Comparator<BoundType>() { @Override public int compare(BoundType o1, BoundType o2) { if (o1 == o2) { return 0; } else if (o1 == BoundType.CLOSED) { return -1; } else { return 1; } } }; /** * A comparator to sort the upper bound of Ranges. */ private final static Comparator<BoundType> UPPER_BOUND_COMPARATOR = new Comparator<BoundType>() { @Override public int compare(BoundType o1, BoundType o2) { if (o1 == o2) { return 0; } else if (o1 == BoundType.CLOSED) { return 1; } else { return -1; } } }; private Ranges() { /* noop */} }