2
2
3
3
import com .github .pgasync .impl .Oid ;
4
4
5
+ import java .lang .reflect .Array ;
5
6
import java .util .ArrayList ;
6
7
import java .util .List ;
7
8
import java .util .function .BiFunction ;
8
9
import java .util .function .Function ;
9
10
10
11
import static java .nio .charset .StandardCharsets .UTF_8 ;
11
12
13
+ // TODO: change internal value format from byte[] to PgValue(TEXT|BINARY)
14
+ @ SuppressWarnings ({"unchecked" ,"rawtypes" })
12
15
enum ArrayConversions {
13
16
;
14
17
15
- protected static Oid getElementOid (final Oid oid ) {
16
- try {
17
- return Oid .valueOf (oid .name ().replaceFirst ("_ARRAY" , "" ));
18
- } catch (IllegalArgumentException e ) {
19
- return Oid .UNSPECIFIED ;
20
- }
18
+ public static byte [] fromArray (final Object [] elements , final Function <Object ,byte []> printFn ) {
19
+ return appendArray (new StringBuilder (), elements , printFn ).toString ().getBytes (UTF_8 );
21
20
}
22
21
23
- protected static Class getElementType (Class arrayType ) {
24
- while (arrayType .getComponentType () != null ) {
25
- arrayType = arrayType .getComponentType ();
22
+ static StringBuilder appendArray (StringBuilder sb , final Object elements , final Function <Object ,byte []> printFn ) {
23
+ sb .append ('{' );
24
+
25
+ int nElements = Array .getLength (elements );
26
+ for (int i = 0 ; i < nElements ; i ++) {
27
+ if (i > 0 ) {
28
+ sb .append (',' );
29
+ }
30
+
31
+ Object o = Array .get (elements , i );
32
+ if (o == null ) {
33
+ sb .append ("NULL" );
34
+ } else if (o .getClass ().isArray ()) {
35
+ sb = appendArray (sb , o , printFn );
36
+ } else {
37
+ sb = appendEscaped (sb , new String (printFn .apply (o ), UTF_8 ));
38
+ }
26
39
}
27
- return arrayType ;
40
+
41
+ return sb .append ('}' );
28
42
}
29
43
30
- @ SuppressWarnings ("unchecked" )
31
- public static <TArray > TArray toArray (
32
- Class <TArray > arrayType , Oid oid , byte [] value , BiFunction <Oid , byte [], Object > parse ) {
44
+ static StringBuilder appendEscaped (final StringBuilder b , final String s ) {
45
+ b .append ('"' );
46
+ for (int j = 0 ; j < s .length (); j ++) {
47
+ char c = s .charAt (j );
48
+ if (c == '"' || c == '\\' ) {
49
+ b .append ('\\' );
50
+ }
33
51
34
- Oid elementOid = getElementOid ( oid );
35
- return ( TArray ) buildArray (
36
- getElementType ( arrayType ), buildArrayList ( value ), 0 , - 1 , ( s ) -> parse . apply ( elementOid , s ) );
52
+ b . append ( c );
53
+ }
54
+ return b . append ( '"' );
37
55
}
38
56
39
- protected static Object [] buildArray (
40
- Class elementType , PgArrayList input , int index , int count , Function <byte [], Object > parse ) {
41
-
42
- if (count < 0 )
43
- count = input .size ();
57
+ public static <T > T toArray (Class <T > type , Oid oid , byte [] value , BiFunction <Oid ,byte [],Object > parse ) {
58
+ char [] text = new String (value , UTF_8 ).toCharArray ();
59
+ List <List <Object >> holder = new ArrayList <>(1 );
44
60
45
- int dimensionCount = input .dimensionsCount ;
46
- int [] dimensions = new int [dimensionCount ];
47
- for (int i = 0 ; i < dimensionCount ; i ++) {
48
- dimensions [i ] = (i == 0 ? count : 0 );
61
+ if (readArray (text , skipBounds (text , 0 ), (List ) holder ) != text .length ) {
62
+ throw new IllegalStateException ("Failed to read array" );
49
63
}
50
64
51
- Object [] ret = (Object [])java .lang .reflect .Array .newInstance (elementType , dimensions );
65
+ return (T ) toNestedArrays (holder .get (0 ), getElementType (type ), getElementOid (oid ), parse );
66
+ }
52
67
53
- for (int length = 0 ; length < count ; length ++) {
54
- Object o = input .get (index ++);
55
- if (o != null ) {
56
- if (1 < dimensionCount ) {
57
- o = buildArray (elementType , (PgArrayList ) o , 0 , -1 , parse );
58
- } else {
59
- o = parse .apply (((String ) o ).getBytes (UTF_8 ));
60
- }
68
+ static int skipBounds (final char [] text , final int start ) {
69
+ if (text [start ] != '[' ) {
70
+ return start ;
71
+ }
72
+ for (int end = start + 1 ;;) {
73
+ if (text [end ++] == '=' ) {
74
+ return end ;
61
75
}
62
- ret [length ] = o ;
63
76
}
64
- return ret ;
65
77
}
66
78
67
- // TODO: split and simplify
68
- @ SuppressWarnings ("unchecked" )
69
- protected static PgArrayList buildArrayList (byte [] byteValue ) {
70
- PgArrayList arrayList = new PgArrayList ();
71
-
72
- char delim = ',' ;
73
-
74
- if (byteValue != null ) {
75
- char [] chars = new String (byteValue ).toCharArray ();
76
- StringBuffer buffer = null ;
77
- boolean insideString = false ;
78
- boolean wasInsideString = false ; // needed for checking if NULL
79
- // value occurred
80
- List dims = new ArrayList (); // array dimension arrays
81
- PgArrayList curArray = arrayList ; // currently processed array
82
-
83
- // Starting with 8.0 non-standard (beginning index
84
- // isn't 1) bounds the dimensions are returned in the
85
- // data formatted like so "[0:3]={0,1,2,3,4}".
86
- // Older versions simply do not return the bounds.
87
- //
88
- // Right now we ignore these bounds, but we could
89
- // consider allowing these index values to be used
90
- // even though the JDBC spec says 1 is the first
91
- // index. I'm not sure what a client would like
92
- // to see, so we just retain the old behavior.
93
- int startOffset = 0 ;
94
-
95
- if (chars [0 ] == '[' ) {
96
- while (chars [startOffset ] != '=' ) {
97
- startOffset ++;
98
- }
99
- startOffset ++; // skip =
79
+ static int readArray (final char [] text , final int start , List <Object > result ) {
80
+ List <Object > values = new ArrayList <>();
81
+ for (int i = start + 1 ;;) {
82
+ final char c = text [i ];
83
+ if (c == '}' ) {
84
+ result .add (values );
85
+ return i + 1 ;
86
+ } else if (c == ',' || Character .isWhitespace (c )) {
87
+ i ++;
88
+ } else if (c == '"' ) {
89
+ i = readString (text , i , values );
90
+ } else if (c == '{' ) {
91
+ i = readArray (text , i , values );
92
+ } else if (c == 'N' ) {
93
+ i = readNull (i , values );
94
+ } else {
95
+ i = readValue (text , i , values );
100
96
}
97
+ }
98
+ }
101
99
102
- for (int i = startOffset ; i < chars .length ; i ++) {
103
- // escape character that we need to skip
104
- if (chars [i ] == '\\' )
105
- i ++;
106
-
107
- // subarray start
108
- else if (!insideString && chars [i ] == '{' ) {
109
- if (dims .size () == 0 ) {
110
- dims .add (arrayList );
111
- } else {
112
- PgArrayList a = new PgArrayList ();
113
- PgArrayList p = ((PgArrayList ) dims .get (dims .size () - 1 ));
114
- p .add (a );
115
- dims .add (a );
116
- }
117
- curArray = (PgArrayList ) dims .get (dims .size () - 1 );
118
-
119
- // number of dimensions
120
- for (int t = i + 1 ; t < chars .length ; t ++) {
121
- if (Character .isWhitespace (chars [t ]))
122
- continue ;
123
- else if (chars [t ] == '{' )
124
- curArray .dimensionsCount ++;
125
- else break ;
126
- }
127
-
128
- buffer = new StringBuffer ();
129
- continue ;
130
- }
131
- // quoted element
132
- else if (chars [i ] == '"' ) {
133
- insideString = !insideString ;
134
- wasInsideString = true ;
135
- continue ;
136
- }
137
- // white space
138
- else if (!insideString && Character .isWhitespace (chars [i ])) {
139
- continue ;
140
- }
141
- // array end or element end
142
- else if ((!insideString && (chars [i ] == delim || chars [i ] == '}' )) || i == chars .length - 1 ) {
143
- // when character that is a part of array element
144
- if (chars [i ] != '"' && chars [i ] != '}' && chars [i ] != delim && buffer != null ) {
145
- buffer .append (chars [i ]);
146
- }
147
-
148
- String b = buffer == null ? null : buffer .toString ();
149
-
150
- // add element to current array
151
- if (b != null && (b .length () > 0 || wasInsideString )) {
152
- curArray .add (!wasInsideString && b .equals ("NULL" ) ? null : b );
153
- }
154
-
155
- wasInsideString = false ;
156
- buffer = new StringBuffer ();
157
-
158
- // when end of an array
159
- if (chars [i ] == '}' ) {
160
- dims .remove (dims .size () - 1 );
161
-
162
- // when multi-dimension
163
- if (dims .size () > 0 ) {
164
- curArray = (PgArrayList ) dims .get (dims .size () - 1 );
165
- }
166
-
167
- buffer = null ;
168
- }
169
- continue ;
170
- }
171
-
172
- if (buffer != null )
173
- buffer .append (chars [i ]);
100
+ static int readValue (final char [] text , final int start , List <Object > result ) {
101
+ StringBuilder str = new StringBuilder ();
102
+ for (int i = start ;; i ++) {
103
+ char c = text [i ];
104
+ if (c == ',' || c == '}' || Character .isWhitespace (c )) {
105
+ result .add (str .toString ());
106
+ return i ;
174
107
}
108
+ str .append (c );
175
109
}
176
- return arrayList ;
177
110
}
178
111
179
- public static byte [] fromArray (final Object [] elements ) {
180
- StringBuffer sb = new StringBuffer ();
181
- appendArray (sb , elements );
182
- return sb .toString ().getBytes (UTF_8 );
112
+ static int readNull (final int i , final List <Object > result ) {
113
+ result .add (null );
114
+ return i + 4 ;
183
115
}
184
116
185
- protected static void appendArray (final StringBuffer sb , final Object elements ) {
186
- sb . append ( '{' );
187
-
188
- int nElements = java . lang . reflect . Array . getLength ( elements ) ;
189
- for ( int i = 0 ; i < nElements ; i ++ ) {
190
- if ( i > 0 ) {
191
- sb . append ( ',' ) ;
117
+ static int readString (final char [] text , final int start , final List < Object > result ) {
118
+ StringBuilder str = new StringBuilder ( );
119
+ for ( int i = start + 1 ;;) {
120
+ char c = text [ i ++] ;
121
+ if ( c == '"' ) {
122
+ result . add ( str . toString ());
123
+ return i ;
192
124
}
125
+ if (c == '\\' ) {
126
+ c = text [i ++];
127
+ }
128
+ str .append (c );
129
+ }
130
+ }
193
131
194
- Object o = java .lang .reflect .Array .get (elements , i );
195
- if (o == null ) {
196
- sb .append ("NULL" );
197
- } else if (o .getClass ().isArray ()) {
198
- appendArray (sb , o );
132
+ static Oid getElementOid (final Oid oid ) {
133
+ try {
134
+ return Oid .valueOf (oid .name ().replaceFirst ("_ARRAY" , "" ));
135
+ } catch (IllegalArgumentException e ) {
136
+ return Oid .UNSPECIFIED ;
137
+ }
138
+ }
139
+
140
+ static Class getElementType (Class arrayType ) {
141
+ while (arrayType .getComponentType () != null ) {
142
+ arrayType = arrayType .getComponentType ();
143
+ }
144
+ return arrayType ;
145
+ }
146
+
147
+ static <T > T [] toNestedArrays (List <Object > result , Class <?> type , Oid oid , BiFunction <Oid , byte [], Object > parse ) {
148
+ Object [] arr = (Object []) Array .newInstance (type , getNestedDimensions (result ));
149
+ for (int i = 0 ; i < result .size (); i ++) {
150
+ Object elem = result .get (i );
151
+ if (elem == null ) {
152
+ arr [i ] = null ;
153
+ } else if (elem .getClass ().equals (String .class )) {
154
+ arr [i ] = parse .apply (oid , ((String ) elem ).getBytes (UTF_8 ));
199
155
} else {
200
- String s = o .toString ();
201
- escapeArrayElement (sb , s );
156
+ arr [i ] = toNestedArrays ((List <Object >) elem , type , oid , parse );
202
157
}
203
158
}
204
- sb . append ( '}' ) ;
159
+ return ( T []) arr ;
205
160
}
206
161
207
- protected static void escapeArrayElement ( final StringBuffer b , final String s ) {
208
- b . append ( '"' );
209
- for ( int j = 0 ; j < s . length (); j ++) {
210
- char c = s . charAt ( j );
211
- if ( c == '"' || c == '\\' ) {
212
- b . append ( '\\' ) ;
213
- }
162
+ static int [] getNestedDimensions ( List < Object > result ) {
163
+ if ( result . isEmpty ()) {
164
+ return new int []{ 0 };
165
+ }
166
+ if (!( result . get ( 0 ) instanceof List ) ) {
167
+ return new int []{ result . size () } ;
168
+ }
214
169
215
- b .append (c );
170
+ List <Integer > dimensions = new ArrayList <>();
171
+ dimensions .add (result .size ());
172
+
173
+ Object value = result .get (0 );
174
+ while (value instanceof List ) {
175
+ List nested = (List ) value ;
176
+ dimensions .add (nested .size ());
177
+ value = nested .isEmpty () ? null : nested .get (0 );
216
178
}
217
- b .append ('"' );
218
- }
219
179
220
- private static class PgArrayList extends ArrayList {
221
- private static final long serialVersionUID = 2052783752654562677L ;
180
+ return toIntArray ( dimensions );
181
+ }
222
182
223
- int dimensionsCount = 1 ;
183
+ static int [] toIntArray (List <Integer > list ) {
184
+ int [] arr = new int [list .size ()];
185
+ for (int i = 0 ; i < arr .length ; i ++) {
186
+ arr [i ] = list .get (i );
187
+ }
188
+ return arr ;
224
189
}
225
- }
190
+ }
0 commit comments