1616package com .google .cloud .bigtable .data .v2 .models ;
1717
1818import com .google .api .core .InternalExtensionOnly ;
19+ import com .google .common .base .Objects ;
1920import com .google .common .base .Preconditions ;
2021import com .google .protobuf .ByteString ;
22+ import com .google .protobuf .UnsafeByteOperations ;
2123import javax .annotation .Nonnull ;
2224
2325/**
@@ -197,25 +199,65 @@ public R of(String startClosed, String endOpen) {
197199 }
198200
199201 /** Creates a new {@link Range} with the specified exclusive start and the current end. */
200- public R startOpen (String start ) {
202+ public R startOpen (@ Nonnull String start ) {
201203 return startOpen (wrap (start ));
202204 }
203205
204206 /** Creates a new {@link Range} with the specified inclusive start and the current end. */
205- public R startClosed (String start ) {
207+ public R startClosed (@ Nonnull String start ) {
206208 return startClosed (wrap (start ));
207209 }
208210
209211 /** Creates a new {@link Range} with the specified exclusive end and the current start. */
210- public R endOpen (String end ) {
212+ public R endOpen (@ Nonnull String end ) {
211213 return endOpen (wrap (end ));
212214 }
213215
214216 /** Creates a new {@link Range} with the specified inclusive end and the current start. */
215- public R endClosed (String end ) {
217+ public R endClosed (@ Nonnull String end ) {
216218 return endClosed (wrap (end ));
217219 }
218220
221+ @ Override
222+ public R startOpen (@ Nonnull ByteString start ) {
223+ Preconditions .checkNotNull (start );
224+ if (start .isEmpty ()) {
225+ return startUnbounded ();
226+ } else {
227+ return super .startOpen (start );
228+ }
229+ }
230+
231+ @ Override
232+ public R startClosed (@ Nonnull ByteString start ) {
233+ Preconditions .checkNotNull (start );
234+ if (start .isEmpty ()) {
235+ return startUnbounded ();
236+ } else {
237+ return super .startClosed (start );
238+ }
239+ }
240+
241+ @ Override
242+ public R endOpen (@ Nonnull ByteString end ) {
243+ Preconditions .checkNotNull (end );
244+ if (end .isEmpty ()) {
245+ return endUnbounded ();
246+ } else {
247+ return super .endOpen (end );
248+ }
249+ }
250+
251+ @ Override
252+ public R endClosed (@ Nonnull ByteString end ) {
253+ Preconditions .checkNotNull (end );
254+ if (end .isEmpty ()) {
255+ return endUnbounded ();
256+ } else {
257+ return super .endClosed (end );
258+ }
259+ }
260+
219261 @ SuppressWarnings ("unchecked" )
220262 @ Override
221263 protected R clone () {
@@ -244,10 +286,79 @@ public static TimestampRange create(long closedStart, long openEnd) {
244286 private TimestampRange (BoundType startBound , Long start , BoundType endBound , Long end ) {
245287 super (startBound , start , endBound , end );
246288 }
289+
290+ @ Override
291+ public boolean equals (Object o ) {
292+ if (this == o ) {
293+ return true ;
294+ }
295+ if (o == null || getClass () != o .getClass ()) {
296+ return false ;
297+ }
298+ TimestampRange range = (TimestampRange ) o ;
299+
300+ if (getStartBound () != range .getStartBound () || getEndBound () != range .getEndBound ()) {
301+ return false ;
302+ }
303+ if (getStartBound () != BoundType .UNBOUNDED && !Objects .equal (getStart (), range .getStart ())) {
304+ return false ;
305+ }
306+ if (getEndBound () != BoundType .UNBOUNDED && !Objects .equal (getEnd (), range .getEnd ())) {
307+ return false ;
308+ }
309+ return true ;
310+ }
311+
312+ @ Override
313+ public int hashCode () {
314+ return Objects .hashCode (
315+ getStartBound (),
316+ getStartBound () == BoundType .UNBOUNDED ? null : getStart (),
317+ getEndBound (),
318+ getEndBound () == BoundType .UNBOUNDED ? null : getEnd ());
319+ }
247320 }
248321
249322 /** Concrete Range for ByteStrings */
250323 public static final class ByteStringRange extends AbstractByteStringRange <ByteStringRange > {
324+ public static ByteStringRange prefix (String prefix ) {
325+ return prefix (ByteString .copyFromUtf8 (prefix ));
326+ }
327+
328+ public static ByteStringRange prefix (ByteString prefix ) {
329+ if (prefix .isEmpty ()) {
330+ return unbounded ();
331+ }
332+
333+ int offset = prefix .size () - 1 ;
334+ int curByte = 0xFF ;
335+
336+ while (offset >= 0 ) {
337+ curByte = prefix .byteAt (offset ) & 0xFF ;
338+ if (curByte != 0xFF ) {
339+ break ;
340+ }
341+ offset --;
342+ }
343+
344+ if (offset < 0 ) {
345+ // We got an 0xFFFF... (only FFs) stopRow value which is
346+ // the last possible prefix before the end of the table.
347+ // So set it to stop at the 'end of the table'
348+ return unbounded ().startClosed (prefix );
349+ }
350+
351+ ByteString endPrefix = offset == 0 ? ByteString .EMPTY : prefix .substring (0 , offset );
352+ ByteString endSuffix = UnsafeByteOperations .unsafeWrap (new byte [] {(byte ) (curByte + 1 )});
353+ ByteString end = endPrefix .concat (endSuffix );
354+
355+ ByteStringRange range = ByteStringRange .unbounded ().startClosed (prefix );
356+ if (!end .isEmpty ()) {
357+ range .endOpen (end );
358+ }
359+ return range ;
360+ }
361+
251362 public static ByteStringRange unbounded () {
252363 return new ByteStringRange (BoundType .UNBOUNDED , null , BoundType .UNBOUNDED , null );
253364 }
@@ -265,5 +376,36 @@ private ByteStringRange(
265376 BoundType startBound , ByteString start , BoundType endBound , ByteString end ) {
266377 super (startBound , start , endBound , end );
267378 }
379+
380+ @ Override
381+ public boolean equals (Object o ) {
382+ if (this == o ) {
383+ return true ;
384+ }
385+ if (o == null || getClass () != o .getClass ()) {
386+ return false ;
387+ }
388+ ByteStringRange range = (ByteStringRange ) o ;
389+
390+ if (getStartBound () != range .getStartBound () || getEndBound () != range .getEndBound ()) {
391+ return false ;
392+ }
393+ if (getStartBound () != BoundType .UNBOUNDED && !Objects .equal (getStart (), range .getStart ())) {
394+ return false ;
395+ }
396+ if (getEndBound () != BoundType .UNBOUNDED && !Objects .equal (getEnd (), range .getEnd ())) {
397+ return false ;
398+ }
399+ return true ;
400+ }
401+
402+ @ Override
403+ public int hashCode () {
404+ return Objects .hashCode (
405+ getStartBound (),
406+ getStartBound () == BoundType .UNBOUNDED ? null : getStart (),
407+ getEndBound (),
408+ getEndBound () == BoundType .UNBOUNDED ? null : getEnd ());
409+ }
268410 }
269411}
0 commit comments