Skip to content

Commit 5d3cd53

Browse files
mbwhitejt-nti
authored andcommitted
[FABCJ-160] Char support
Java has an additional primtive type of char (unsigned 16bit singel character) this wasn't supported so was considered to be an object. Now treated as a specific format of string. Signed-off-by: Matthew B. White <[email protected]>
1 parent e32e404 commit 5d3cd53

File tree

6 files changed

+240
-27
lines changed

6 files changed

+240
-27
lines changed

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/execution/JSONTransactionSerializer.java

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
/**
3232
* Used as a the default serialisation for transmission from SDK to Contract
3333
*/
34-
@Serializer()
34+
@Serializer()
3535
public class JSONTransactionSerializer implements SerializerInterface {
3636
private static Logger logger = Logger.getLogger(JSONTransactionSerializer.class.getName());
3737
private TypeRegistry typeRegistry = TypeRegistry.getRegistry();
@@ -62,7 +62,12 @@ public byte[] toBuffer(Object value, TypeSchema ts) {
6262
buffer = array.toString().getBytes(UTF_8);
6363
break;
6464
case "string":
65-
buffer = ((String) value).getBytes(UTF_8);
65+
String format = ts.getFormat();
66+
if (format!=null && format.contentEquals("uint16")){
67+
buffer = Character.valueOf((char)value).toString().getBytes(UTF_8);
68+
} else {
69+
buffer = ((String) value).getBytes(UTF_8);
70+
}
6671
break;
6772
case "number":
6873
case "integer":
@@ -71,21 +76,21 @@ public byte[] toBuffer(Object value, TypeSchema ts) {
7176
buffer = (value).toString().getBytes(UTF_8);
7277
}
7378
} else {
74-
// at this point we can assert that the value is
79+
// at this point we can assert that the value is
7580
// representing a complex data type
76-
// so we can get this from
77-
// the type registry, and get the list of propertyNames
81+
// so we can get this from
82+
// the type registry, and get the list of propertyNames
7883
// it should have
7984
DataTypeDefinition dtd = this.typeRegistry.getDataType(ts);
8085
Set<String> keySet = dtd.getProperties().keySet();
8186
String[] propNames = keySet.toArray(new String[keySet.size()]);
82-
87+
8388
// Note: whilst the current JSON library does pretty much
8489
// everything is required, this part is hard.
85-
// we want to create a JSON Object based on the value,
90+
// we want to create a JSON Object based on the value,
8691
// with certain property names.
8792

88-
// Based on the constructors available we need to have a two
93+
// Based on the constructors available we need to have a two
8994
// step process, create a JSON Object, then create the object
9095
// we really want based on the propNames
9196
JSONObject obj = new JSONObject(new JSONObject(value),propNames);
@@ -98,17 +103,17 @@ public byte[] toBuffer(Object value, TypeSchema ts) {
98103
/**
99104
* We need to take the JSON array, and if there are complex datatypes within it
100105
* ensure that they don't get spurious JSON properties appearing
101-
*
106+
*
102107
* This method needs to be general so has to copy with nested arrays
103108
* and with primitive and Object types
104109
*/
105110
private JSONArray normalizeArray(JSONArray jsonArray, TypeSchema ts){
106111
JSONArray normalizedArray;
107-
112+
108113
// Need to work with what type of array this is
109114
TypeSchema items = ts.getItems();
110115
String type = items.getType();
111-
116+
112117
if (type != null && type != "array" ){
113118
// primitive - can return this directly
114119
normalizedArray = jsonArray;
@@ -120,16 +125,16 @@ private JSONArray normalizeArray(JSONArray jsonArray, TypeSchema ts){
120125
normalizedArray.put(i,normalizeArray(jsonArray.getJSONArray(i),items));
121126
}
122127
} else {
123-
// get the permitted propeties in the type,
128+
// get the permitted propeties in the type,
124129
// then loop over the array and ensure they are correct
125130
DataTypeDefinition dtd = this.typeRegistry.getDataType(items);
126131
Set<String> keySet = dtd.getProperties().keySet();
127132
String[] propNames = keySet.toArray(new String[keySet.size()]);
128-
133+
129134
normalizedArray = new JSONArray();
130135
// array of objects
131136
// iterate over said array
132-
for (int i=0; i<jsonArray.length(); i++){
137+
for (int i=0; i<jsonArray.length(); i++){
133138
JSONObject obj = new JSONObject(jsonArray.getJSONObject(i),propNames);
134139
normalizedArray.put(i,obj);
135140
}
@@ -167,10 +172,10 @@ public Object fromBuffer(byte[] buffer, TypeSchema ts) {
167172
/** We need to be able to map between the primative class types
168173
* and the object variants. In the case where this is needed
169174
* Java auto-boxing doesn't actually help.
170-
*
175+
*
171176
* For other types the parameter is passed directly back
172-
*
173-
* @param primitive class for the primitive
177+
*
178+
* @param primitive class for the primitive
174179
* @return Class for the Object variant
175180
*/
176181
private Class<?> mapPrimitive(Class<?> primitive){
@@ -220,7 +225,12 @@ private Object _convert(String stringData, TypeSchema ts)
220225
}
221226

222227
if (type.contentEquals("string")) {
223-
value = stringData;
228+
String strformat = ts.getFormat();
229+
if (strformat!=null && strformat.contentEquals("uint16")){
230+
value = stringData.charAt(0);
231+
} else {
232+
value = stringData;
233+
}
224234
} else if (type.contentEquals("integer")) {
225235
String intFormat = ts.getFormat();
226236
if (intFormat.contentEquals("int32")) {
@@ -263,7 +273,7 @@ private Object _convert(String stringData, TypeSchema ts)
263273
}
264274

265275
/** Create new instance of the specificied object from the supplied JSON String
266-
*
276+
*
267277
* @param format Details of the format needed
268278
* @param jsonString JSON string
269279
* @param ts TypeSchema
@@ -282,7 +292,6 @@ Object createComponentInstance(String format, String jsonString, TypeSchema ts)
282292
JSONObject json = new JSONObject(jsonString);
283293
// request validation of the type may throw an exception if validation fails
284294
ts.validate(json);
285-
286295
try {
287296
Map<String, PropertyDefinition> fields = dtd.getProperties();
288297
for (Iterator<PropertyDefinition> iterator = fields.values().iterator(); iterator.hasNext();) {

fabric-chaincode-shim/src/main/java/org/hyperledger/fabric/contract/metadata/TypeSchema.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,13 @@ public Class<?> getTypeClass(TypeRegistry typeRegistry) {
9898
}
9999

100100
if (type.contentEquals("string")) {
101-
clz = String.class;
101+
String format = getFormat();
102+
if (format!=null && format.contentEquals("uint16")){
103+
clz = char.class;
104+
} else {
105+
clz = String.class;
106+
}
107+
102108
} else if (type.contentEquals("integer")) {
103109
// need to check the format
104110
String format = getFormat();
@@ -162,7 +168,7 @@ public static TypeSchema typeConvert(Class<?> clz) {
162168
if (clz.isArray()) {
163169
returnschema.put("type", "array");
164170
schema = new TypeSchema();
165-
171+
166172
// double check the componentType
167173
Class<?> componentClass = clz.getComponentType();
168174
if (componentClass.isArray()){
@@ -171,7 +177,7 @@ public static TypeSchema typeConvert(Class<?> clz) {
171177
} else {
172178
returnschema.put("items", schema);
173179
}
174-
180+
175181
className = componentClass.getTypeName();
176182
} else {
177183
schema = returnschema;
@@ -181,6 +187,11 @@ public static TypeSchema typeConvert(Class<?> clz) {
181187
case "java.lang.String":
182188
schema.put("type", "string");
183189
break;
190+
case "char":
191+
case "java.lang.Character":
192+
schema.put("type","string");
193+
schema.put("format","uint16");
194+
break;
184195
case "byte":
185196
case "java.lang.Byte":
186197
schema.put("type", "integer");
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*/
4+
package org.hyperledger.fabric.contract;
5+
6+
import org.hyperledger.fabric.contract.annotation.DataType;
7+
import org.hyperledger.fabric.contract.annotation.Property;
8+
9+
@DataType()
10+
public class AllTypesAsset {
11+
12+
@Property
13+
private byte theByte = 125;
14+
15+
@Property
16+
private short theShort = 30000;
17+
18+
@Property
19+
private int theInt = 1234577123;
20+
21+
@Property
22+
private long theLong = 12384233333123L;
23+
24+
@Property
25+
private float theFloat = 3.1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164f;
26+
27+
@Property
28+
private double theDouble = 3.1415926535_8979323846_2643383279_5028841971_6939937510_5820974944_5923078164_0628620899_8628034825_3421170679d;
29+
30+
@Property
31+
private boolean theBoolean = false;
32+
33+
@Property
34+
private char theChar = 'a';
35+
36+
@Property
37+
private String theString = "Hello World";
38+
39+
@Property
40+
private MyType theCustomObject = new MyType().setValue("Hello World");
41+
42+
public byte getTheByte() {
43+
return theByte;
44+
}
45+
46+
public void setTheByte(byte aByte) {
47+
this.theByte = aByte;
48+
}
49+
50+
public short getTheShort() {
51+
return theShort;
52+
}
53+
54+
public void setTheShort(short aShort) {
55+
this.theShort = aShort;
56+
}
57+
58+
public int getTheInt() {
59+
return theInt;
60+
}
61+
62+
public void setTheInt(int aInt) {
63+
this.theInt = aInt;
64+
}
65+
66+
public long getTheLong() {
67+
return theLong;
68+
}
69+
70+
public void setTheLong(long aLong) {
71+
this.theLong = aLong;
72+
}
73+
74+
public float getTheFloat() {
75+
return theFloat;
76+
}
77+
78+
public void setTheFloat(float aFloat) {
79+
this.theFloat = aFloat;
80+
}
81+
82+
public double getTheDouble() {
83+
return theDouble;
84+
}
85+
86+
public void setTheDouble(double aDouble) {
87+
this.theDouble = aDouble;
88+
}
89+
90+
public boolean isTheBoolean() {
91+
return theBoolean;
92+
}
93+
94+
public void setBoolean(boolean aBoolean) {
95+
this.theBoolean = aBoolean;
96+
}
97+
98+
public char getTheChar() {
99+
return theChar;
100+
}
101+
102+
public void setTheChar(char aChar) {
103+
this.theChar = aChar;
104+
}
105+
106+
public String getTheString() {
107+
return theString;
108+
}
109+
110+
public void setString(String aString) {
111+
this.theString = aString;
112+
}
113+
114+
public MyType getTheCustomObject() {
115+
return theCustomObject;
116+
}
117+
118+
public void setTheCustomObject(MyType customObject) {
119+
this.theCustomObject = customObject;
120+
}
121+
122+
public boolean equals(AllTypesAsset obj){
123+
return
124+
theByte == obj.getTheByte() &&
125+
theShort == obj.getTheShort() &&
126+
theInt == obj.getTheInt() &&
127+
theLong == obj.getTheLong() &&
128+
theFloat == obj.getTheFloat() &&
129+
theDouble == obj.getTheDouble() &&
130+
theBoolean == obj.isTheBoolean() &&
131+
theString.equals(obj.getTheString());
132+
}
133+
134+
public String toString() {
135+
StringBuilder builder = new StringBuilder(System.lineSeparator());
136+
builder.append("byte="+theByte).append(System.lineSeparator());
137+
builder.append("short="+theShort).append(System.lineSeparator());
138+
builder.append("int="+theInt).append(System.lineSeparator());
139+
builder.append("long="+theLong).append(System.lineSeparator());
140+
builder.append("float="+theFloat).append(System.lineSeparator());
141+
builder.append("double="+theDouble).append(System.lineSeparator());
142+
builder.append("boolean="+theBoolean).append(System.lineSeparator());
143+
builder.append("char="+theChar).append(System.lineSeparator());
144+
builder.append("String="+theString).append(System.lineSeparator());
145+
builder.append("Mytype="+theCustomObject).append(System.lineSeparator());
146+
147+
return builder.toString();
148+
}
149+
}

fabric-chaincode-shim/src/test/java/org/hyperledger/fabric/contract/MyType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,31 @@
88

99
import org.hyperledger.fabric.contract.annotation.DataType;
1010
import org.hyperledger.fabric.contract.annotation.Property;
11+
import org.json.JSONPropertyIgnore;
1112

1213
@DataType
1314
public class MyType {
1415

1516
@Property()
1617
private String value;
1718

19+
1820
private String state="";
1921

2022
public final static String STARTED = "STARTED";
2123
public final static String STOPPED = "STOPPED";
2224

25+
@JSONPropertyIgnore()
2326
public void setState(String state){
2427
this.state = state;
2528
}
2629

30+
@JSONPropertyIgnore()
2731
public boolean isStarted(){
2832
return state.equals(STARTED);
2933
}
3034

35+
@JSONPropertyIgnore()
3136
public boolean isStopped(){
3237
return state.equals(STARTED);
3338
}

0 commit comments

Comments
 (0)