Skip to content

Commit ddb9f2f

Browse files
avinash-anandmp911de
authored andcommitted
feature: adding support for point type
[#282][#283]
1 parent 6c65157 commit ddb9f2f

File tree

5 files changed

+318
-1
lines changed

5 files changed

+318
-1
lines changed

src/main/java/io/r2dbc/postgresql/codec/DefaultCodecs.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ public DefaultCodecs(ByteBufAllocator byteBufAllocator) {
9090
new ShortArrayCodec(byteBufAllocator),
9191
new StringArrayCodec(byteBufAllocator),
9292
new IntegerArrayCodec(byteBufAllocator),
93-
new LongArrayCodec(byteBufAllocator)
93+
new LongArrayCodec(byteBufAllocator),
94+
95+
new PointCodec(byteBufAllocator)
9496
));
9597
}
9698

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright 2017-2020 the original author or authors.
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+
* https://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+
17+
package io.r2dbc.postgresql.codec;
18+
19+
/**
20+
* <p>It maps to the point datatype in org.postgresql.</p>
21+
*
22+
* <p> It uses double to represent the coordinates.</p>
23+
*/
24+
public class Point {
25+
26+
private final double x;
27+
28+
private final double y;
29+
30+
/**
31+
* @param x coordinate
32+
* @param y coordinate
33+
*/
34+
public Point(double x, double y) {
35+
this.x = x;
36+
this.y = y;
37+
}
38+
39+
public double getX() {
40+
return x;
41+
}
42+
43+
public double getY() {
44+
return y;
45+
}
46+
47+
/**
48+
* Translate the point by the supplied amount.
49+
*
50+
* @param x integer amount to add on the x axis
51+
* @param y integer amount to add on the y axis
52+
* @return - new point with translated values
53+
*/
54+
public Point translate(int x, int y) {
55+
return translate((double) x, (double) y);
56+
}
57+
58+
/**
59+
* Translate the point by the supplied amount.
60+
*
61+
* @param x double amount to add on the x axis
62+
* @param y double amount to add on the y axis
63+
* @return - new point with translated values
64+
*/
65+
public Point translate(double x, double y) {
66+
return new Point(this.x + x, this.y + y);
67+
}
68+
69+
/**
70+
* Moves the point to the supplied coordinates.
71+
*
72+
* @param x integer coordinate
73+
* @param y integer coordinate
74+
*/
75+
public Point move(int x, int y) {
76+
return setLocation(x, y);
77+
}
78+
79+
/**
80+
* Moves the point to the supplied coordinates.
81+
*
82+
* @param x double coordinate
83+
* @param y double coordinate
84+
* @return - new point with provided coordinates
85+
*/
86+
public Point move(double x, double y) {
87+
return new Point(x, y);
88+
}
89+
90+
/**
91+
* Moves the point to the supplied coordinates.
92+
*
93+
* @param x integer coordinate
94+
* @param y integer coordinate
95+
* @return - return new Point with these coordinates
96+
*/
97+
public Point setLocation(int x, int y) {
98+
return move((double) x, (double) y);
99+
}
100+
101+
public boolean equals(Object obj) {
102+
if (obj instanceof Point) {
103+
Point p = (Point) obj;
104+
return x == p.x && y == p.y;
105+
}
106+
return false;
107+
}
108+
109+
public int hashCode() {
110+
long v1 = Double.doubleToLongBits(x);
111+
long v2 = Double.doubleToLongBits(y);
112+
return (int) (v1 ^ v2 ^ (v1 >>> 32) ^ (v2 >>> 32));
113+
}
114+
115+
public String toString() {
116+
return "(" + x + "," + y + ")";
117+
}
118+
119+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2017-2020 the original author or authors.
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+
* https://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+
17+
package io.r2dbc.postgresql.codec;
18+
19+
import io.netty.buffer.ByteBuf;
20+
import io.netty.buffer.ByteBufAllocator;
21+
import io.r2dbc.postgresql.client.Parameter;
22+
import io.r2dbc.postgresql.message.Format;
23+
import io.r2dbc.postgresql.type.PostgresqlObjectId;
24+
import io.r2dbc.postgresql.util.Assert;
25+
import io.r2dbc.postgresql.util.ByteBufUtils;
26+
import reactor.util.annotation.Nullable;
27+
28+
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
29+
import static io.r2dbc.postgresql.type.PostgresqlObjectId.POINT;
30+
31+
public class PointCodec extends AbstractCodec<Point> {
32+
33+
private final ByteBufAllocator byteBufAllocator;
34+
35+
PointCodec(ByteBufAllocator byteBufAllocator) {
36+
super(Point.class);
37+
this.byteBufAllocator = Assert.requireNonNull(byteBufAllocator, "byteBufAllocator must not be null");
38+
}
39+
40+
@Override
41+
boolean doCanDecode(PostgresqlObjectId type, @Nullable Format format) {
42+
Assert.requireNonNull(type, "type must not be null");
43+
44+
return POINT == type;
45+
}
46+
47+
@Override
48+
Point doDecode(ByteBuf buffer, PostgresqlObjectId dataType, Format format, Class<? extends Point> type) {
49+
Assert.requireNonNull(buffer, "byteBuf must not be null");
50+
Assert.requireNonNull(type, "type must not be null");
51+
Assert.requireNonNull(format, "format must not be null");
52+
53+
if (format == FORMAT_BINARY) {
54+
double x = buffer.readDouble();
55+
double y = buffer.readDouble();
56+
return new Point(x, y);
57+
} else {
58+
String decodedAsString = ByteBufUtils.decode(buffer);
59+
String parenRemovedVal = decodedAsString.replaceAll("[()]", "");
60+
String[] coordinatesAsString = parenRemovedVal.split(",");
61+
double x = Double.parseDouble(coordinatesAsString[0]);
62+
double y = Double.parseDouble(coordinatesAsString[1]);
63+
return new Point(x, y);
64+
}
65+
}
66+
67+
/**
68+
* @param value the {@code value}.
69+
* @return Point in String format as understood by postgresql - (x,y)
70+
*/
71+
@Override
72+
Parameter doEncode(Point value) {
73+
Assert.requireNonNull(value, "value must not be null");
74+
return create(POINT, FORMAT_BINARY, () -> this.byteBufAllocator.buffer(lengthInBytes()).writeDouble(value.getX()).writeDouble(value.getY()));
75+
}
76+
77+
@Override
78+
public Parameter encodeNull() {
79+
return createNull(POINT, FORMAT_BINARY);
80+
}
81+
82+
public int lengthInBytes() {
83+
return 16;
84+
}
85+
86+
}

src/test/java/io/r2dbc/postgresql/AbstractCodecIntegrationTests.java

+6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import io.r2dbc.postgresql.api.PostgresqlStatement;
2424
import io.r2dbc.postgresql.codec.EnumCodec;
2525
import io.r2dbc.postgresql.codec.Json;
26+
import io.r2dbc.postgresql.codec.Point;
2627
import io.r2dbc.spi.Blob;
2728
import io.r2dbc.spi.Clob;
2829
import io.r2dbc.spi.Connection;
@@ -360,6 +361,11 @@ void offsetDateTime() {
360361
testCodec(OffsetDateTime.class, OffsetDateTime.now(), (actual, expected) -> assertThat(actual.isEqual(expected)).isTrue(), "TIMESTAMP WITH TIME ZONE");
361362
}
362363

364+
@Test
365+
void point() {
366+
testCodec(Point.class, new Point(1.12, 2.12), "POINT");
367+
}
368+
363369
@Test
364370
void shortArray() {
365371
testCodec(Short[].class, new Short[]{100, 200, 300}, "INT2[]");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright 2017-2020 the original author or authors.
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+
* https://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+
17+
package io.r2dbc.postgresql.codec;
18+
19+
import io.netty.buffer.ByteBuf;
20+
import io.r2dbc.postgresql.client.Parameter;
21+
import io.r2dbc.postgresql.client.ParameterAssert;
22+
import org.junit.jupiter.api.Test;
23+
24+
import static io.r2dbc.postgresql.client.Parameter.NULL_VALUE;
25+
import static io.r2dbc.postgresql.message.Format.FORMAT_BINARY;
26+
import static io.r2dbc.postgresql.message.Format.FORMAT_TEXT;
27+
import static io.r2dbc.postgresql.type.PostgresqlObjectId.POINT;
28+
import static io.r2dbc.postgresql.type.PostgresqlObjectId.VARCHAR;
29+
import static io.r2dbc.postgresql.util.TestByteBufAllocator.TEST;
30+
import static org.assertj.core.api.Assertions.assertThat;
31+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
32+
33+
class PointCodecTest {
34+
35+
@Test
36+
void constructorNoByteBufAllocator() {
37+
assertThatIllegalArgumentException().isThrownBy(() -> new PointCodec(null))
38+
.withMessage("byteBufAllocator must not be null");
39+
}
40+
41+
@Test
42+
void doCanDecodeNoType() {
43+
assertThatIllegalArgumentException().isThrownBy(() -> new PointCodec(TEST).doCanDecode(null, FORMAT_BINARY))
44+
.withMessage("type must not be null");
45+
}
46+
47+
@Test
48+
void doCanDecode() {
49+
PointCodec codec = new PointCodec(TEST);
50+
51+
assertThat(codec.doCanDecode(VARCHAR, FORMAT_BINARY)).isFalse();
52+
assertThat(codec.doCanDecode(POINT, FORMAT_TEXT)).isTrue();
53+
assertThat(codec.doCanDecode(POINT, FORMAT_BINARY)).isTrue();
54+
}
55+
56+
@Test
57+
void doDecodeNoByteBuf() {
58+
assertThatIllegalArgumentException().isThrownBy(() -> new PointCodec(TEST).doDecode(null, POINT, FORMAT_BINARY, Point.class))
59+
.withMessage("byteBuf must not be null");
60+
}
61+
62+
@Test
63+
void doDecodeNoType() {
64+
assertThatIllegalArgumentException().isThrownBy(() -> new PointCodec(TEST).doDecode(TEST.buffer(), POINT, FORMAT_BINARY, null))
65+
.withMessage("type must not be null");
66+
}
67+
68+
@Test
69+
void doDecodeNoFormat() {
70+
assertThatIllegalArgumentException().isThrownBy(() -> new PointCodec(TEST).doDecode(TEST.buffer(), POINT, null, Point.class))
71+
.withMessage("format must not be null");
72+
}
73+
74+
@Test
75+
void doDecode() {
76+
PointCodec codec = new PointCodec(TEST);
77+
Point point = new Point(1.12, 2.12);
78+
ByteBuf pointAsBinary = TEST.buffer(codec.lengthInBytes()).writeDouble(1.12).writeDouble(2.12);
79+
assertThat(codec.doDecode(pointAsBinary, POINT, FORMAT_BINARY, Point.class)).isEqualTo(point);
80+
}
81+
82+
@Test
83+
void doEncodeNoValue() {
84+
assertThatIllegalArgumentException().isThrownBy(() -> new PointCodec(TEST).doEncode(null))
85+
.withMessage("value must not be null");
86+
}
87+
88+
@Test
89+
void doEncode() {
90+
PointCodec codec = new PointCodec(TEST);
91+
ByteBuf pointAsBinary = TEST.buffer(codec.lengthInBytes()).writeDouble(1.12).writeDouble(2.12);
92+
93+
ParameterAssert.assertThat(codec.doEncode(new Point(1.12, 2.12)))
94+
.hasFormat(FORMAT_BINARY)
95+
.hasType(POINT.getObjectId())
96+
.hasValue(pointAsBinary);
97+
}
98+
99+
@Test
100+
void encodeNull() {
101+
ParameterAssert.assertThat(new PointCodec(TEST).encodeNull())
102+
.isEqualTo(new Parameter(FORMAT_BINARY, POINT.getObjectId(), NULL_VALUE));
103+
}
104+
}

0 commit comments

Comments
 (0)