Committed by
Gerrit Code Review
Implement compact serialization for a set of discrete resources
This is for ONOS-4281. Change-Id: I08a9fc4fd334c499c7a09d2960145743a798094e
Showing
6 changed files
with
412 additions
and
1 deletions
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package org.onosproject.net.resource; | ||
| 17 | + | ||
| 18 | +import com.google.common.annotations.Beta; | ||
| 19 | +import com.google.common.collect.ImmutableSet; | ||
| 20 | + | ||
| 21 | +import java.util.Objects; | ||
| 22 | +import java.util.Optional; | ||
| 23 | +import java.util.Set; | ||
| 24 | + | ||
| 25 | +import static com.google.common.base.Preconditions.checkArgument; | ||
| 26 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 27 | +import static com.google.common.base.Preconditions.checkState; | ||
| 28 | + | ||
| 29 | +/** | ||
| 30 | + * Represents a set of discrete type resources. | ||
| 31 | + * This class is intended to be used by ConsistentResourceStore though it is exposed to the public. | ||
| 32 | + */ | ||
| 33 | +@Beta | ||
| 34 | +public final class DiscreteResourceSet { | ||
| 35 | + private final Set<DiscreteResource> values; | ||
| 36 | + private final DiscreteResourceCodec codec; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * Creates an instance with resources and the codec for them. | ||
| 40 | + * | ||
| 41 | + * @param values resources to be contained in the instance | ||
| 42 | + * @param codec codec for the specified resources | ||
| 43 | + * @return an instance | ||
| 44 | + */ | ||
| 45 | + public static DiscreteResourceSet of(Set<DiscreteResource> values, DiscreteResourceCodec codec) { | ||
| 46 | + checkNotNull(values); | ||
| 47 | + checkNotNull(codec); | ||
| 48 | + checkArgument(!values.isEmpty()); | ||
| 49 | + | ||
| 50 | + return new DiscreteResourceSet(ImmutableSet.copyOf(values), codec); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + /** | ||
| 54 | + * Creates the instance representing an empty resource set. | ||
| 55 | + * | ||
| 56 | + * @return an empty resource set | ||
| 57 | + */ | ||
| 58 | + public static DiscreteResourceSet empty() { | ||
| 59 | + return new DiscreteResourceSet(ImmutableSet.of(), NoOpCodec.INSTANCE); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + private DiscreteResourceSet(Set<DiscreteResource> values, DiscreteResourceCodec codec) { | ||
| 63 | + this.values = values; | ||
| 64 | + this.codec = codec; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + private DiscreteResourceSet() { | ||
| 68 | + this.values = null; | ||
| 69 | + this.codec = null; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + /** | ||
| 73 | + * Returns resources contained in this instance. | ||
| 74 | + * | ||
| 75 | + * @return resources | ||
| 76 | + */ | ||
| 77 | + public Set<DiscreteResource> values() { | ||
| 78 | + return values; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + /** | ||
| 82 | + * Returns the parent resource of the resources contained in this instance. | ||
| 83 | + * | ||
| 84 | + * @return the parent resource of the resources | ||
| 85 | + */ | ||
| 86 | + public DiscreteResourceId parent() { | ||
| 87 | + if (values.isEmpty()) { | ||
| 88 | + // Dummy value avoiding null | ||
| 89 | + return ResourceId.ROOT; | ||
| 90 | + } | ||
| 91 | + Optional<DiscreteResourceId> parent = values.iterator().next().id().parent(); | ||
| 92 | + checkState(parent.isPresent()); | ||
| 93 | + | ||
| 94 | + return parent.get(); | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + /** | ||
| 98 | + * Returns the codec for the resources contained in this instance. | ||
| 99 | + * | ||
| 100 | + * @return the codec for the resources | ||
| 101 | + */ | ||
| 102 | + public DiscreteResourceCodec codec() { | ||
| 103 | + return codec; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + @Override | ||
| 107 | + public int hashCode() { | ||
| 108 | + return Objects.hash(values, codec); | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + @Override | ||
| 112 | + public boolean equals(Object obj) { | ||
| 113 | + if (this == obj) { | ||
| 114 | + return true; | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + if (obj == null || getClass() != obj.getClass()) { | ||
| 118 | + return false; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + final DiscreteResourceSet other = (DiscreteResourceSet) obj; | ||
| 122 | + return Objects.equals(this.values, other.values) | ||
| 123 | + && Objects.equals(this.codec, other.codec); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package org.onosproject.net.resource; | ||
| 17 | + | ||
| 18 | +import com.esotericsoftware.kryo.Kryo; | ||
| 19 | +import com.esotericsoftware.kryo.Serializer; | ||
| 20 | +import com.esotericsoftware.kryo.io.Input; | ||
| 21 | +import com.esotericsoftware.kryo.io.Output; | ||
| 22 | +import com.google.common.annotations.Beta; | ||
| 23 | +import com.google.common.collect.DiscreteDomain; | ||
| 24 | +import com.google.common.collect.Range; | ||
| 25 | +import com.google.common.collect.TreeRangeSet; | ||
| 26 | +import org.onlab.util.ClosedOpenRange; | ||
| 27 | + | ||
| 28 | +import java.util.ArrayList; | ||
| 29 | +import java.util.List; | ||
| 30 | +import java.util.Set; | ||
| 31 | +import java.util.stream.Collectors; | ||
| 32 | +import java.util.stream.IntStream; | ||
| 33 | + | ||
| 34 | +/** | ||
| 35 | + * Kryo serializer for {@link DiscreteResourceSet}. | ||
| 36 | + */ | ||
| 37 | +@Beta | ||
| 38 | +public final class DiscreteResourceSetSerializer extends Serializer<DiscreteResourceSet> { | ||
| 39 | + | ||
| 40 | + public DiscreteResourceSetSerializer() { | ||
| 41 | + super(false, true); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @Override | ||
| 45 | + public void write(Kryo kryo, Output output, DiscreteResourceSet object) { | ||
| 46 | + TreeRangeSet<Integer> rangeSet = TreeRangeSet.create(); | ||
| 47 | + object.values().stream() | ||
| 48 | + .map(x -> object.codec().encode(x)) | ||
| 49 | + .map(Range::singleton) | ||
| 50 | + .map(x -> x.canonical(DiscreteDomain.integers())) | ||
| 51 | + .forEach(rangeSet::add); | ||
| 52 | + List<ClosedOpenRange> ranges = rangeSet.asRanges().stream() | ||
| 53 | + .map(ClosedOpenRange::of) | ||
| 54 | + .collect(Collectors.toList()); | ||
| 55 | + kryo.writeObject(output, ranges); | ||
| 56 | + kryo.writeClassAndObject(output, object.codec()); | ||
| 57 | + kryo.writeObject(output, object.parent()); | ||
| 58 | + } | ||
| 59 | + | ||
| 60 | + @Override | ||
| 61 | + public DiscreteResourceSet read(Kryo kryo, Input input, Class<DiscreteResourceSet> type) { | ||
| 62 | + @SuppressWarnings("unchecked") | ||
| 63 | + List<ClosedOpenRange> ranges = kryo.readObject(input, ArrayList.class); | ||
| 64 | + DiscreteResourceCodec codec = (DiscreteResourceCodec) kryo.readClassAndObject(input); | ||
| 65 | + DiscreteResourceId parent = kryo.readObject(input, DiscreteResourceId.class); | ||
| 66 | + | ||
| 67 | + if (ranges.isEmpty()) { | ||
| 68 | + return DiscreteResourceSet.empty(); | ||
| 69 | + } | ||
| 70 | + | ||
| 71 | + Set<DiscreteResource> resources = ranges.stream() | ||
| 72 | + .flatMapToInt(x -> IntStream.range(x.lowerBound(), x.upperBound())) | ||
| 73 | + .mapToObj(x -> codec.decode(parent, x)) | ||
| 74 | + .collect(Collectors.toSet()); | ||
| 75 | + | ||
| 76 | + return DiscreteResourceSet.of(resources, codec); | ||
| 77 | + } | ||
| 78 | +} |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package org.onosproject.net.resource; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * Represents no-op codec intended to used in an empty discrete resource set only. | ||
| 20 | + * It's not supposed to be used by other classes. | ||
| 21 | + */ | ||
| 22 | +public class NoOpCodec implements DiscreteResourceCodec { | ||
| 23 | + public static final DiscreteResourceCodec INSTANCE = new NoOpCodec(); | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public int encode(DiscreteResource resource) { | ||
| 27 | + return 0; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + @Override | ||
| 31 | + public DiscreteResource decode(DiscreteResourceId parent, int value) { | ||
| 32 | + return Resource.ROOT; | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public boolean equals(Object obj) { | ||
| 37 | + if (obj == this) { | ||
| 38 | + return true; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + if (obj == null || getClass() != obj.getClass()) { | ||
| 42 | + return false; | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + return true; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + @Override | ||
| 49 | + public int hashCode() { | ||
| 50 | + return NoOpCodec.class.hashCode(); | ||
| 51 | + } | ||
| 52 | +} |
| ... | @@ -31,6 +31,7 @@ import org.onlab.packet.MacAddress; | ... | @@ -31,6 +31,7 @@ import org.onlab.packet.MacAddress; |
| 31 | import org.onlab.packet.TpPort; | 31 | import org.onlab.packet.TpPort; |
| 32 | import org.onlab.packet.VlanId; | 32 | import org.onlab.packet.VlanId; |
| 33 | import org.onlab.util.Bandwidth; | 33 | import org.onlab.util.Bandwidth; |
| 34 | +import org.onlab.util.ClosedOpenRange; | ||
| 34 | import org.onlab.util.Frequency; | 35 | import org.onlab.util.Frequency; |
| 35 | import org.onlab.util.KryoNamespace; | 36 | import org.onlab.util.KryoNamespace; |
| 36 | import org.onlab.util.Match; | 37 | import org.onlab.util.Match; |
| ... | @@ -191,12 +192,18 @@ import org.onosproject.net.meter.MeterId; | ... | @@ -191,12 +192,18 @@ import org.onosproject.net.meter.MeterId; |
| 191 | import org.onosproject.net.resource.ContinuousResource; | 192 | import org.onosproject.net.resource.ContinuousResource; |
| 192 | import org.onosproject.net.resource.ContinuousResourceId; | 193 | import org.onosproject.net.resource.ContinuousResourceId; |
| 193 | import org.onosproject.net.resource.DiscreteResource; | 194 | import org.onosproject.net.resource.DiscreteResource; |
| 195 | +import org.onosproject.net.resource.DiscreteResourceCodec; | ||
| 194 | import org.onosproject.net.resource.DiscreteResourceId; | 196 | import org.onosproject.net.resource.DiscreteResourceId; |
| 197 | +import org.onosproject.net.resource.DiscreteResourceSet; | ||
| 198 | +import org.onosproject.net.resource.DiscreteResourceSetSerializer; | ||
| 199 | +import org.onosproject.net.resource.MplsCodec; | ||
| 200 | +import org.onosproject.net.resource.NoOpCodec; | ||
| 195 | import org.onosproject.net.resource.ResourceAllocation; | 201 | import org.onosproject.net.resource.ResourceAllocation; |
| 196 | import org.onosproject.net.packet.DefaultOutboundPacket; | 202 | import org.onosproject.net.packet.DefaultOutboundPacket; |
| 197 | import org.onosproject.net.packet.DefaultPacketRequest; | 203 | import org.onosproject.net.packet.DefaultPacketRequest; |
| 198 | import org.onosproject.net.packet.PacketPriority; | 204 | import org.onosproject.net.packet.PacketPriority; |
| 199 | import org.onosproject.net.provider.ProviderId; | 205 | import org.onosproject.net.provider.ProviderId; |
| 206 | +import org.onosproject.net.resource.VlanCodec; | ||
| 200 | import org.onosproject.security.Permission; | 207 | import org.onosproject.security.Permission; |
| 201 | import org.onosproject.store.Timestamp; | 208 | import org.onosproject.store.Timestamp; |
| 202 | import org.onosproject.store.primitives.MapUpdate; | 209 | import org.onosproject.store.primitives.MapUpdate; |
| ... | @@ -521,7 +528,12 @@ public final class KryoNamespaces { | ... | @@ -521,7 +528,12 @@ public final class KryoNamespaces { |
| 521 | org.onlab.packet.MplsLabel.class, | 528 | org.onlab.packet.MplsLabel.class, |
| 522 | org.onlab.packet.MPLS.class | 529 | org.onlab.packet.MPLS.class |
| 523 | ) | 530 | ) |
| 524 | - | 531 | + .register(ClosedOpenRange.class) |
| 532 | + .register(new DiscreteResourceSetSerializer(), DiscreteResourceSet.class) | ||
| 533 | + .register(DiscreteResourceCodec.class) | ||
| 534 | + .register(VlanCodec.class) | ||
| 535 | + .register(MplsCodec.class) | ||
| 536 | + .register(NoOpCodec.class) | ||
| 525 | .build(); | 537 | .build(); |
| 526 | 538 | ||
| 527 | 539 | ... | ... |
| ... | @@ -24,6 +24,7 @@ import org.junit.After; | ... | @@ -24,6 +24,7 @@ import org.junit.After; |
| 24 | import org.junit.Before; | 24 | import org.junit.Before; |
| 25 | import org.junit.BeforeClass; | 25 | import org.junit.BeforeClass; |
| 26 | import org.junit.Test; | 26 | import org.junit.Test; |
| 27 | +import org.onlab.packet.MplsLabel; | ||
| 27 | import org.onlab.packet.VlanId; | 28 | import org.onlab.packet.VlanId; |
| 28 | import org.onlab.util.Bandwidth; | 29 | import org.onlab.util.Bandwidth; |
| 29 | import org.onlab.util.Frequency; | 30 | import org.onlab.util.Frequency; |
| ... | @@ -62,6 +63,9 @@ import org.onosproject.net.flow.FlowId; | ... | @@ -62,6 +63,9 @@ import org.onosproject.net.flow.FlowId; |
| 62 | import org.onosproject.net.flow.FlowRule; | 63 | import org.onosproject.net.flow.FlowRule; |
| 63 | import org.onosproject.net.flow.FlowRuleBatchEntry; | 64 | import org.onosproject.net.flow.FlowRuleBatchEntry; |
| 64 | import org.onosproject.net.intent.IntentId; | 65 | import org.onosproject.net.intent.IntentId; |
| 66 | +import org.onosproject.net.resource.DiscreteResource; | ||
| 67 | +import org.onosproject.net.resource.DiscreteResourceSet; | ||
| 68 | +import org.onosproject.net.resource.MplsCodec; | ||
| 65 | import org.onosproject.net.resource.ResourceAllocation; | 69 | import org.onosproject.net.resource.ResourceAllocation; |
| 66 | import org.onosproject.net.resource.Resources; | 70 | import org.onosproject.net.resource.Resources; |
| 67 | import org.onosproject.net.provider.ProviderId; | 71 | import org.onosproject.net.provider.ProviderId; |
| ... | @@ -80,11 +84,15 @@ import org.onlab.packet.Ip4Prefix; | ... | @@ -80,11 +84,15 @@ import org.onlab.packet.Ip4Prefix; |
| 80 | import org.onlab.packet.Ip6Prefix; | 84 | import org.onlab.packet.Ip6Prefix; |
| 81 | import org.onlab.packet.MacAddress; | 85 | import org.onlab.packet.MacAddress; |
| 82 | import org.onlab.util.KryoNamespace; | 86 | import org.onlab.util.KryoNamespace; |
| 87 | +import org.onosproject.net.resource.VlanCodec; | ||
| 83 | 88 | ||
| 84 | import java.nio.ByteBuffer; | 89 | import java.nio.ByteBuffer; |
| 85 | import java.util.Arrays; | 90 | import java.util.Arrays; |
| 86 | import java.util.Collections; | 91 | import java.util.Collections; |
| 87 | import java.time.Duration; | 92 | import java.time.Duration; |
| 93 | +import java.util.Set; | ||
| 94 | +import java.util.stream.Collectors; | ||
| 95 | +import java.util.stream.IntStream; | ||
| 88 | 96 | ||
| 89 | import static java.util.Arrays.asList; | 97 | import static java.util.Arrays.asList; |
| 90 | import static org.junit.Assert.*; | 98 | import static org.junit.Assert.*; |
| ... | @@ -356,6 +364,38 @@ public class KryoSerializerTest { | ... | @@ -356,6 +364,38 @@ public class KryoSerializerTest { |
| 356 | } | 364 | } |
| 357 | 365 | ||
| 358 | @Test | 366 | @Test |
| 367 | + public void testVlanIdResourceSet() { | ||
| 368 | + DiscreteResource port = Resources.discrete(DID1, P1).resource(); | ||
| 369 | + | ||
| 370 | + Set<DiscreteResource> vlans = IntStream.range(0, 4096) | ||
| 371 | + .mapToObj(x -> VlanId.vlanId((short) x)) | ||
| 372 | + .map(x -> Resources.discrete(port.id(), x).resource()) | ||
| 373 | + .collect(Collectors.toSet()); | ||
| 374 | + | ||
| 375 | + DiscreteResourceSet sut = DiscreteResourceSet.of(vlans, new VlanCodec()); | ||
| 376 | + testSerializedEquals(sut); | ||
| 377 | + } | ||
| 378 | + | ||
| 379 | + @Test | ||
| 380 | + public void testMplsLabelResourceSet() { | ||
| 381 | + DiscreteResource port = Resources.discrete(DID1, P1).resource(); | ||
| 382 | + | ||
| 383 | + Set<DiscreteResource> labels = IntStream.range(0, 1024 * 1024) | ||
| 384 | + .mapToObj(MplsLabel::mplsLabel) | ||
| 385 | + .map(x -> Resources.discrete(port.id(), x).resource()) | ||
| 386 | + .collect(Collectors.toSet()); | ||
| 387 | + | ||
| 388 | + DiscreteResourceSet sut = DiscreteResourceSet.of(labels, new MplsCodec()); | ||
| 389 | + testSerializedEquals(sut); | ||
| 390 | + } | ||
| 391 | + | ||
| 392 | + @Test | ||
| 393 | + public void testEmptyResourceSet() { | ||
| 394 | + DiscreteResourceSet sut = DiscreteResourceSet.empty(); | ||
| 395 | + testSerializedEquals(sut); | ||
| 396 | + } | ||
| 397 | + | ||
| 398 | + @Test | ||
| 359 | public void testResourceId() { | 399 | public void testResourceId() { |
| 360 | testSerializedEquals(Resources.discrete(DID1, P1).id()); | 400 | testSerializedEquals(Resources.discrete(DID1, P1).id()); |
| 361 | } | 401 | } | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | +package org.onlab.util; | ||
| 17 | + | ||
| 18 | +import com.google.common.annotations.Beta; | ||
| 19 | +import com.google.common.collect.DiscreteDomain; | ||
| 20 | +import com.google.common.collect.Range; | ||
| 21 | + | ||
| 22 | +import java.util.Objects; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * Represent a closed-open range. | ||
| 26 | + * The primary user of this class is the ResourceService implementation. | ||
| 27 | + */ | ||
| 28 | +@Beta | ||
| 29 | +public final class ClosedOpenRange { | ||
| 30 | + private final int lowerBound; // inclusive | ||
| 31 | + private final int upperBound; // exclusive | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * Creates a range from a Guava's range. | ||
| 35 | + * | ||
| 36 | + * @param range Guava's range | ||
| 37 | + * @return this range | ||
| 38 | + */ | ||
| 39 | + public static ClosedOpenRange of(Range<Integer> range) { | ||
| 40 | + return new ClosedOpenRange( | ||
| 41 | + range.canonical(DiscreteDomain.integers()).lowerEndpoint(), | ||
| 42 | + range.canonical(DiscreteDomain.integers()).upperEndpoint()); | ||
| 43 | + } | ||
| 44 | + | ||
| 45 | + /** | ||
| 46 | + * Create a range with a lower bound and an upper bound. | ||
| 47 | + * | ||
| 48 | + * @param lowerBound lower bound (inclusive) | ||
| 49 | + * @param upperBound upper bound (exclusive) | ||
| 50 | + * @return this range | ||
| 51 | + */ | ||
| 52 | + public static ClosedOpenRange of(int lowerBound, int upperBound) { | ||
| 53 | + return new ClosedOpenRange(lowerBound, upperBound); | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + private ClosedOpenRange(int lowerBound, int upperBound) { | ||
| 57 | + this.lowerBound = lowerBound; | ||
| 58 | + this.upperBound = upperBound; | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * Returns the lower bound. | ||
| 63 | + * | ||
| 64 | + * @return the lower bound | ||
| 65 | + */ | ||
| 66 | + public int lowerBound() { | ||
| 67 | + return lowerBound; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * Returns the upper bound. | ||
| 72 | + * | ||
| 73 | + * @return the upper bound | ||
| 74 | + */ | ||
| 75 | + public int upperBound() { | ||
| 76 | + return upperBound; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Override | ||
| 80 | + public int hashCode() { | ||
| 81 | + return Objects.hash(lowerBound, upperBound); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @Override | ||
| 85 | + public boolean equals(Object obj) { | ||
| 86 | + if (this == obj) { | ||
| 87 | + return true; | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + if (!(obj instanceof ClosedOpenRange)) { | ||
| 91 | + return false; | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + final ClosedOpenRange other = (ClosedOpenRange) obj; | ||
| 95 | + return Objects.equals(this.lowerBound, other.lowerBound) | ||
| 96 | + && Objects.equals(this.upperBound, other.upperBound); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + @Override | ||
| 100 | + public String toString() { | ||
| 101 | + return "[" + lowerBound + ".." + upperBound + ")"; | ||
| 102 | + } | ||
| 103 | +} |
-
Please register or login to post a comment