44 */
55package org .epics .vtype .gson ;
66
7+ import com .google .gson .JsonArray ;
78import com .google .gson .JsonElement ;
8- import org .epics .util .array .ListNumber ;
9+ import com .google .gson .JsonObject ;
10+ import org .epics .util .array .*;
911import org .epics .util .number .UByte ;
1012import org .epics .util .number .UInteger ;
1113import org .epics .util .number .ULong ;
1214import org .epics .util .number .UShort ;
13- import org .epics .vtype .EnumDisplay ;
14- import org .epics .vtype .VDouble ;
15- import org .epics .vtype .VEnum ;
16- import org .epics .vtype .VNumber ;
17- import org .epics .vtype .VNumberArray ;
18- import org .epics .vtype .VString ;
19- import org .epics .vtype .VStringArray ;
20- import org .epics .vtype .VType ;
15+ import org .epics .vtype .*;
2116
17+ import java .util .ArrayList ;
18+ import java .util .Collections ;
2219import java .util .List ;
2320
2421/**
@@ -61,6 +58,8 @@ static VType toVType(JsonElement json) {
6158 return toVStringArray (json );
6259 case "VEnum" :
6360 return toVEnum (json );
61+ case "VTable" :
62+ return toVTable (json );
6463 default :
6564 throw new UnsupportedOperationException ("Not implemented yet" );
6665 }
@@ -85,6 +84,8 @@ static JsonElement toJson(VType vType) {
8584 return toJson ((VStringArray ) vType );
8685 } else if (vType instanceof VEnum ) {
8786 return toJson ((VEnum ) vType );
87+ } else if (vType instanceof VTable ) {
88+ return toJson ((VTable ) vType );
8889 }
8990 throw new UnsupportedOperationException ("Not implemented yet" );
9091 }
@@ -261,4 +262,184 @@ else if(VTypeGsonMapper.NEG_INF_QUOTED.equals(valueAsString)){
261262 return Double .parseDouble (valueAsString );
262263 }
263264 }
265+
266+ /**
267+ * Deserializes the Gson representation of a {@link VTable}
268+ * @param jsonElement JSON representation
269+ * @return A {@link VTable} object.
270+ */
271+ static VTable toVTable (JsonElement jsonElement ) {
272+ JsonObject jsonObject = jsonElement .getAsJsonObject ();
273+ int columnCount = jsonObject .get ("columnCount" ).getAsInt ();
274+ List <String > listString = new ArrayList <>();
275+ List <Class <?>> listClass = new ArrayList <>();
276+ JsonArray columnNames = jsonObject .get ("columnNames" ).getAsJsonArray ();
277+ JsonArray columnTypes = jsonObject .get ("columnTypes" ).getAsJsonArray ();
278+ List <Object > listObject = new ArrayList <>();
279+ for (int i = 0 ; i < columnCount ; i ++) {
280+ listString .add (i , columnNames .get (i ).getAsString ());
281+ Class <?> clazz = getClass (columnTypes .get (i ).getAsString ());
282+ listClass .add (i , clazz );
283+ JsonArray value = jsonObject .get ("value" ).getAsJsonArray ();
284+ JsonArray ja = value .get (i ).getAsJsonArray ();
285+ clazz = listClass .get (i );
286+ if (ja == null || ja .size () == 0 || clazz == null ) {
287+ listObject .add (i , Collections .emptyList ());
288+ } else {
289+ String columnType = columnTypes .get (i ).getAsString ();
290+ switch (columnType ){
291+ case "bool" :
292+ listObject .add (i , GsonArrays .toListBoolean (ja ));
293+ break ;
294+ case "byte" :
295+ listObject .add (i , GsonArrays .toListByte (ja ));
296+ break ;
297+ case "ubyte" :
298+ listObject .add (i , GsonArrays .toListUByte (ja ));
299+ break ;
300+ case "short" :
301+ listObject .add (i , GsonArrays .toListShort (ja ));
302+ break ;
303+ case "ushort" :
304+ listObject .add (i , GsonArrays .toListUShort (ja ));
305+ break ;
306+ case "int" :
307+ listObject .add (i , GsonArrays .toListInt (ja ));
308+ break ;
309+ case "uint" :
310+ listObject .add (i , GsonArrays .toListUInteger (ja ));
311+ break ;
312+ case "long" :
313+ listObject .add (i , GsonArrays .toListLong (ja ));
314+ break ;
315+ case "ulong" :
316+ listObject .add (i , GsonArrays .toListULong (ja ));
317+ break ;
318+ case "float" :
319+ listObject .add (i , GsonArrays .toListFloat (ja ));
320+ break ;
321+ case "double" :
322+ listObject .add (i , GsonArrays .toListDouble (ja ));
323+ break ;
324+ case "string" :
325+ listObject .add (i , GsonArrays .toListString (ja ));
326+ break ;
327+ default :
328+ listObject .add (i , Collections .emptyList ());
329+ }
330+ }
331+ }
332+ return VTable .of (listClass , listString , listObject );
333+ }
334+
335+ /**
336+ * @param name A type name determined at serialization time. The "unsigned primitives" (ushort, uint...)
337+ * are allowed.
338+ * @return A {@link Class} derived from the name, where - for instance - int and uint both map to
339+ * {@link Integer#TYPE}. For unsupported type names <code>null</code> is returned.
340+ */
341+ static Class <?> getClass (String name ) {
342+ switch (name ) {
343+ case "bool" :
344+ return Boolean .TYPE ;
345+ case "int" :
346+ case "uint" :
347+ return Integer .TYPE ;
348+ case "long" :
349+ case "ulong" :
350+ return Long .TYPE ;
351+ case "short" :
352+ case "ushort" :
353+ return Short .TYPE ;
354+ case "byte" :
355+ case "ubyte" :
356+ return Byte .TYPE ;
357+ case "double" :
358+ return Double .TYPE ;
359+ case "float" :
360+ return Float .TYPE ;
361+ case "string" :
362+ return String .class ;
363+ default :
364+ return null ;
365+ }
366+ }
367+
368+ /**
369+ * Serializes a {@link VTable} object, though with the restriction that data is
370+ * structured as arrays, but not any other nested ot complex structure, e.g. arrays of arrays.
371+ * The array data may be of different types, but EPICS requires data columns to be of equal length.
372+ * @param vTable A {@link VTable} object
373+ * @return Gson representation.
374+ */
375+ static JsonObject toJson (VTable vTable ) {
376+ GsonVTypeBuilder jsonVTypeBuilder = new GsonVTypeBuilder ();
377+ int columnCount = vTable .getColumnCount ();
378+ List <String > columnNames = new ArrayList <>();
379+ List <String > columnTypes = new ArrayList <>();
380+ for (int i = 0 ; i < columnCount ; i ++) {
381+ columnNames .add (i , vTable .getColumnName (i ));
382+ columnTypes .add (i , getVTableDataType (vTable .getColumnData (i )));
383+ }
384+ jsonVTypeBuilder .addType (vTable )
385+ .add ("columnCount" , columnCount )
386+ .addListString ("columnNames" , columnNames );
387+
388+ jsonVTypeBuilder .addListString ("columnTypes" , columnTypes );
389+ JsonArray valueBuilder = new JsonArray ();
390+ for (int i = 0 ; i < columnCount ; i ++) {
391+ valueBuilder .add (GsonArrays .fromList (vTable .getColumnData (i )));
392+ }
393+ jsonVTypeBuilder .add ("value" , valueBuilder );
394+ return jsonVTypeBuilder .build ();
395+ }
396+
397+ /**
398+ * An NTTable's value columns are always of type struct_t[]. In order to be able to preserve
399+ * the type information when serializing, this method returns a string representation
400+ * of the array type.
401+ * @param data NTTable value field that is always of array type.
402+ * @return A string representation of the array type.
403+ */
404+ static String getVTableDataType (Object data ){
405+ if (data instanceof ArrayBoolean ){
406+ return "bool" ;
407+ }
408+ else if (data instanceof ArrayByte ){
409+ return "byte" ;
410+ }
411+ else if (data instanceof ArrayUByte ){
412+ return "ubyte" ;
413+ }
414+ else if (data instanceof ArrayShort ){
415+ return "short" ;
416+ }
417+ else if (data instanceof ArrayUShort ){
418+ return "ushort" ;
419+ }
420+ else if (data instanceof ArrayInteger ){
421+ return "int" ;
422+ }
423+ else if (data instanceof ArrayUInteger ){
424+ return "uint" ;
425+ }
426+ else if (data instanceof ArrayLong ){
427+ return "long" ;
428+ }
429+ else if (data instanceof ArrayULong ){
430+ return "ulong" ;
431+ }
432+ else if (data instanceof ArrayFloat ){
433+ return "float" ;
434+ }
435+ else if (data instanceof ArrayDouble ){
436+ return "double" ;
437+ }
438+ else if (data instanceof List ){
439+ return "string" ;
440+ }
441+ else {
442+ return "invalid" ;
443+ }
444+ }
264445}
0 commit comments