]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/impl/StringVariantBinding.java
Fixing several binding-related bugs
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / impl / StringVariantBinding.java
1 /*******************************************************************************
2  * Copyright (c) 2010- Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  * 
9  * Contributors:
10  *    VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.databoard.binding.impl;
13
14 import java.io.IOException;
15 import java.nio.ByteBuffer;
16 import java.util.Set;
17
18 import org.simantics.databoard.Bindings;
19 import org.simantics.databoard.Datatypes;
20 import org.simantics.databoard.binding.Binding;
21 import org.simantics.databoard.binding.IntegerBinding;
22 import org.simantics.databoard.binding.LongBinding;
23 import org.simantics.databoard.binding.StringBinding;
24 import org.simantics.databoard.binding.VariantBinding;
25 import org.simantics.databoard.binding.error.BindingException;
26 import org.simantics.databoard.binding.mutable.MutableVariant;
27 import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
28 import org.simantics.databoard.serialization.Serializer;
29 import org.simantics.databoard.serialization.SerializerConstructionException;
30 import org.simantics.databoard.serialization.SerializerScheme;
31 import org.simantics.databoard.type.Datatype;
32 import org.simantics.databoard.util.Base64;
33 import org.simantics.databoard.util.URIUtil;
34 import org.simantics.databoard.util.binary.BinaryMemory;
35 import org.simantics.databoard.util.binary.BinaryReadable;
36
37 /**
38  * This class binding Variant to a filename/URL compatible String.
39  * The value is human-readable for strings, integers and longs. 
40  * For datatypes the value is Base64 encoded binary.
41  * 
42  *  Filenames have the following encoding:
43  *    S<string>       String types, if string doesn't have the following 
44  *                    control characters " : < > | ? * # \ / % [0..31] [128..] are escaped with %<hex><hex>
45  *    I<integer>      Integer types
46  *    L<long>         Long types
47  *    B<base64>       All other cases. The value is binary encoded and presented as single line Base64 string.
48  *                    Base64 encoding has url and filename safe options enabled. 
49  *
50  * See StringVariantBindingExample
51  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
52  */
53 public class StringVariantBinding extends VariantBinding {
54         
55         SerializerScheme serializationFactory;
56         VariantBinding variantBinding;
57         
58         public StringVariantBinding(SerializerScheme serializationFactory, VariantBinding variantBinding) {
59                 this.serializationFactory = serializationFactory;
60                 this.variantBinding = variantBinding;
61         }
62         
63         @Override
64         public Object create(Binding binding, Object value) throws BindingException {
65                 if (binding instanceof StringBinding && binding.type().equals(Datatypes.STRING)) {
66                         StringBinding sb = (StringBinding) binding;
67                         return "S"+URIUtil.encodeURI( sb.getValue(value) );
68                 }
69                 
70                 if (binding instanceof IntegerBinding && binding.type().equals(Datatypes.INTEGER)) {
71                         IntegerBinding ib = (IntegerBinding) binding;
72                         return "I"+ib.getValue_(value);
73                 }
74                 
75                 if (binding instanceof LongBinding && binding.type().equals(Datatypes.LONG)) {
76                         LongBinding lb = (LongBinding) binding;
77                         return "L"+lb.getValue_(value);
78                 }
79                 
80                 try {
81                         MutableVariant v = new MutableVariant(binding, value);
82                         byte[] data = serializationFactory.getSerializerUnchecked( variantBinding ).serialize(v);
83                         return "B"+Base64.encodeBytes(data, Base64.URL_SAFE);
84                 } catch (RuntimeSerializerConstructionException e) {
85                         throw new BindingException(e);
86                 } catch (IOException e) {
87                         throw new BindingException(e);
88                 }
89         }
90
91         @Override
92         public Binding getContentBinding(Object variant) throws BindingException {
93                 String str = (String) variant;
94                 if (str.startsWith("S")) {
95                         return Bindings.STRING;
96                 }
97                 if (str.startsWith("I")) {
98                         return Bindings.INTEGER;
99                 }
100                 if (str.startsWith("L")) {
101                         return Bindings.LONG;
102                 }
103                 if (str.startsWith("B")) {
104                         try {
105                                 byte[]  data = Base64.decode(str.substring(1), Base64.URL_SAFE);
106                                 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) ) ;
107                                 Datatype type = (Datatype) getDatatypeSerializer().deserialize(readable);
108                                 return Bindings.getMutableBinding(type);
109                         } catch (IOException e) {
110                                 throw new BindingException(e);
111                         }                       
112                 }
113                 
114                 throw new BindingException("Invalid string");
115         }
116
117         @Override
118         public Datatype getContentType(Object variant) throws BindingException {
119                 String str = (String) variant;
120                 if (str.startsWith("S")) {
121                         return Bindings.STRING.type();
122                 }
123                 if (str.startsWith("I")) {
124                         return Bindings.INTEGER.type();
125                 }
126                 if (str.startsWith("L")) {
127                         return Bindings.LONG.type();
128                 }
129                 if (str.startsWith("B")) {
130                         try {
131                                 byte[]  data = Base64.decode(str.substring(1), Base64.URL_SAFE);
132                                 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) ) ;
133                                 return (Datatype) getDatatypeSerializer().deserialize(readable);
134                         } catch (IOException e) {
135                                 throw new BindingException(e);
136                         }                       
137                 }
138                 
139                 throw new BindingException("Invalid string");
140         }
141
142         @Override
143         public Object getContent(Object obj, Binding binding)
144                         throws BindingException {
145                 if (obj==null) throw new BindingException("null value is not Variant");
146                 if (obj instanceof String == false) throw new BindingException("wrong class, String expected");
147                 
148                 String str = (String) obj;
149                 
150                 if (str.startsWith("S")) {
151                         if (binding instanceof StringBinding == false) throw new BindingException("StringBinding expected, got "+binding.getClass().getSimpleName());
152                         String value = URIUtil.decodeURI( str.substring(1) );
153                         StringBinding sb = (StringBinding) binding;
154                         return sb.create(value);
155                 }
156                 
157                 if (str.startsWith("I")) {
158                         if (binding instanceof IntegerBinding == false) throw new BindingException("IntegerBinding expected, got "+binding.getClass().getSimpleName());
159                         try {
160                                 Integer value = Integer.valueOf( str.substring(1) );
161                                 IntegerBinding ib = (IntegerBinding) binding;
162                                 return ib.create(value);                                
163                         } catch (NumberFormatException nfe) {
164                                 throw new BindingException(nfe);
165                         }
166                 }
167
168                 if (str.startsWith("L")) {
169                         if (binding instanceof LongBinding == false) throw new BindingException("LongBinding expected, got "+binding.getClass().getSimpleName());
170                         try {
171                                 Long value = Long.valueOf( str.substring(1) );
172                                 LongBinding lb = (LongBinding) binding;
173                                 return lb.create(value);                                
174                         } catch (NumberFormatException nfe) {
175                                 throw new BindingException(nfe);
176                         }
177                 }
178                 
179                 if (str.startsWith("B")) {
180                         try {
181                                 byte[] data = Base64.decode(str.substring(1), Base64.URL_SAFE);
182                                 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) ) ;
183                                 Datatype type = (Datatype) getDatatypeSerializer().deserialize(readable);
184                                 if (!type.equals(binding.type()))
185                                         throw new BindingException("Binding for "+type.toSingleLineString()+" expected, but got "+binding.type());
186                                 return Bindings.getSerializer( binding ).deserialize(readable);
187                         } catch (IOException ioe) {
188                                 throw new BindingException(ioe);
189                         } catch (RuntimeSerializerConstructionException e) {
190                                 throw new BindingException(e);
191                         } catch (SerializerConstructionException e) {
192                                 throw new BindingException(e);
193                         }
194                 }
195                 
196                 throw new BindingException("Invalid value");            
197         }
198
199         @Override
200         public Object getContent(Object obj) throws BindingException {
201                 if (obj==null) throw new BindingException("null value is not Variant");
202                 if (obj instanceof String == false) throw new BindingException("wrong class, String expected");
203                 
204                 String str = (String) obj;
205                 
206                 if (str.startsWith("S")) {
207                         /*String value =*/ URIUtil.decodeURI( str.substring(1) );                       
208                         return Bindings.STRING;
209                 }
210                 
211                 if (str.startsWith("I")) {
212                         try {
213                                 /*Integer value =*/ Integer.valueOf( str.substring(1) );
214                                 return Bindings.INTEGER;                                
215                         } catch (NumberFormatException nfe) {
216                                 throw new BindingException(nfe);
217                         }
218                 }
219
220                 if (str.startsWith("L")) {
221                         try {
222                                 /*Long value =*/ Long.valueOf( str.substring(1) );
223                                 return Bindings.LONG;                           
224                         } catch (NumberFormatException nfe) {
225                                 throw new BindingException(nfe);
226                         }
227                 }
228                 
229                 if (str.startsWith("B")) {
230                         try {
231                                 byte[] data = Base64.decode(str.substring(1), Base64.URL_SAFE);
232                                 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) );
233                                 MutableVariant variant = (MutableVariant) Bindings.getSerializerUnchecked( Bindings.MUTABLE_VARIANT).deserialize(readable);
234                                 return variant.getValue();
235                         } catch (IOException ioe) {
236                                 throw new BindingException(ioe);
237                         } catch (RuntimeSerializerConstructionException e) {
238                                 throw new BindingException(e);
239                         }
240                 }
241                 
242                 throw new BindingException("Invalid value");            
243         }
244
245         @Override
246         public void setContent(Object variant, Binding binding, Object value)
247                         throws BindingException {
248                 throw new BindingException("Cannot set value to an immutable String");
249         }
250
251         @Override
252         public void assertInstaceIsValid(Object obj, Set<Object> validInstances)
253                         throws BindingException {
254                 if (obj==null) throw new BindingException("null value is not Variant");
255                 if (obj instanceof String == false) throw new BindingException("wrong class, String expected");
256                 Object value = getContent(obj);
257                 Binding binding = getContentBinding(obj);
258                 binding.assertInstaceIsValid(value);
259         }
260
261         @Override
262         public boolean isInstance(Object obj) {         
263                 return obj instanceof String;
264         }
265
266         static Serializer DATATYPE_SERIALIZER; 
267         static Serializer getDatatypeSerializer()
268         {
269                 if (DATATYPE_SERIALIZER == null) DATATYPE_SERIALIZER = Bindings.getSerializerUnchecked( Bindings.getBindingUnchecked(Datatype.class) );
270                 return DATATYPE_SERIALIZER;
271         }
272
273         @Override
274         protected boolean baseEquals(Object obj) {
275                 StringVariantBinding o = (StringVariantBinding)obj;
276                 return super.baseEquals(obj) && o.serializationFactory == serializationFactory && o.variantBinding == variantBinding;
277         }
278 }