001// Licensed under the MIT license. See LICENSE file in the project root for full license information. 002 003package de.bytefish.pgbulkinsert.pgsql.model.range; 004 005import java.util.Objects; 006 007// https://github.com/npgsql/npgsql/blob/d4132d0d546594629bcef658bcb1418b4a8624cc/src/Npgsql/NpgsqlTypes/NpgsqlRange.cs 008public class Range<TElementType> { 009 010 private int flags; 011 012 private TElementType lowerBound; 013 014 private TElementType upperBound; 015 016 public Range(TElementType lowerBound, TElementType upperBound) { 017 this(lowerBound, true, false, upperBound, true, false); 018 } 019 020 public Range(TElementType lowerBound, boolean lowerBoundIsInclusive, TElementType upperBound, boolean upperBoundIsInclusive) { 021 this(lowerBound, lowerBoundIsInclusive, false, upperBound, upperBoundIsInclusive, false); 022 } 023 024 public Range(TElementType lowerBound, boolean lowerBoundIsInclusive, boolean lowerBoundInfinite, 025 TElementType upperBound, boolean upperBoundIsInclusive, boolean upperBoundInfinite) { 026 this(lowerBound, upperBound, evaluateBoundaryFlags(lowerBoundIsInclusive, upperBoundIsInclusive, lowerBoundInfinite, upperBoundInfinite)); 027 } 028 029 030 private Range(TElementType lowerBound, TElementType upperBound, int flags) { 031 this.lowerBound = (flags & RangeFlags.LowerBoundInfinite) != 0 ? null : lowerBound; 032 this.upperBound = (flags & RangeFlags.UpperBoundInfinite) != 0 ? null : upperBound; 033 this.flags = flags; 034 035 // TODO Check this! 036 if (lowerBound == null) { 037 this.flags |= RangeFlags.LowerBoundInfinite; 038 } 039 040 if (upperBound == null) { 041 this.flags |= RangeFlags.UpperBoundInfinite; 042 } 043 044 if (isEmptyRange(lowerBound, upperBound, flags)) { 045 this.lowerBound = null; 046 this.upperBound = null; 047 this.flags = RangeFlags.Empty; 048 } 049 } 050 051 private boolean isEmptyRange(TElementType lowerBound, TElementType upperBound, int flags) { 052 // --------------------------------------------------------------------------------- 053 // We only want to check for those conditions that are unambiguously erroneous: 054 // 1. The bounds must not be default values (including null). 055 // 2. The bounds must be definite (non-infinite). 056 // 3. The bounds must be inclusive. 057 // 4. The bounds must be considered equal. 058 // 059 // See: 060 // - https://github.com/npgsql/npgsql/pull/1939 061 // - https://github.com/npgsql/npgsql/issues/1943 062 // --------------------------------------------------------------------------------- 063 064 if ((flags & RangeFlags.Empty) == RangeFlags.Empty) 065 return true; 066 067 if ((flags & RangeFlags.Infinite) == RangeFlags.Infinite) 068 return false; 069 070 if ((flags & RangeFlags.Inclusive) == RangeFlags.Inclusive) 071 return false; 072 073 return Objects.equals(lowerBound, upperBound); 074 } 075 076 077 private static int evaluateBoundaryFlags(boolean lowerBoundIsInclusive, boolean upperBoundIsInclusive, boolean lowerBoundInfinite, boolean upperBoundInfinite) { 078 079 int result = RangeFlags.None; 080 081 // This is the only place flags are calculated. 082 if (lowerBoundIsInclusive) 083 result |= RangeFlags.LowerBoundInclusive; 084 if (upperBoundIsInclusive) 085 result |= RangeFlags.UpperBoundInclusive; 086 if (lowerBoundInfinite) 087 result |= RangeFlags.LowerBoundInfinite; 088 if (upperBoundInfinite) 089 result |= RangeFlags.UpperBoundInfinite; 090 091 // PostgreSQL automatically converts inclusive-infinities. 092 // See: https://www.postgresql.org/docs/current/static/rangetypes.html#RANGETYPES-INFINITE 093 if ((result & RangeFlags.LowerInclusiveInfinite) == RangeFlags.LowerInclusiveInfinite) { 094 result &= ~RangeFlags.LowerBoundInclusive; 095 } 096 097 if ((result & RangeFlags.UpperInclusiveInfinite) == RangeFlags.UpperInclusiveInfinite) { 098 result &= ~RangeFlags.UpperBoundInclusive; 099 } 100 101 return result; 102 } 103 104 public int getFlags() { 105 return flags; 106 } 107 108 public boolean isEmpty() { 109 return (flags & RangeFlags.Empty) != 0; 110 } 111 112 public boolean isLowerBoundInfinite() { 113 return (flags & RangeFlags.LowerBoundInfinite) != 0; 114 } 115 116 public boolean isUpperBoundInfinite() { 117 return (flags & RangeFlags.UpperBoundInfinite) != 0; 118 } 119 120 public TElementType getLowerBound() { 121 return lowerBound; 122 } 123 124 public void setLowerBound(TElementType lowerBound) { 125 this.lowerBound = lowerBound; 126 } 127 128 public TElementType getUpperBound() { 129 return upperBound; 130 } 131 132 public void setUpperBound(TElementType upperBound) { 133 this.upperBound = upperBound; 134 } 135}