1 /*******************************************************************************
2 * Copyright (c) 2010- Association for Decentralized Information Management in
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
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.databoard.binding.impl;
14 import java.io.IOException;
15 import java.nio.ByteBuffer;
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;
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.
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
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.
50 * See StringVariantBindingExample
51 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
53 public class StringVariantBinding extends VariantBinding {
55 SerializerScheme serializationFactory;
56 VariantBinding variantBinding;
58 public StringVariantBinding(SerializerScheme serializationFactory, VariantBinding variantBinding) {
59 this.serializationFactory = serializationFactory;
60 this.variantBinding = variantBinding;
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) );
70 if (binding instanceof IntegerBinding && binding.type().equals(Datatypes.INTEGER)) {
71 IntegerBinding ib = (IntegerBinding) binding;
72 return "I"+ib.getValue_(value);
75 if (binding instanceof LongBinding && binding.type().equals(Datatypes.LONG)) {
76 LongBinding lb = (LongBinding) binding;
77 return "L"+lb.getValue_(value);
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);
92 public Binding getContentBinding(Object variant) throws BindingException {
93 String str = (String) variant;
94 if (str.startsWith("S")) {
95 return Bindings.STRING;
97 if (str.startsWith("I")) {
98 return Bindings.INTEGER;
100 if (str.startsWith("L")) {
101 return Bindings.LONG;
103 if (str.startsWith("B")) {
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);
114 throw new BindingException("Invalid string");
118 public Datatype getContentType(Object variant) throws BindingException {
119 String str = (String) variant;
120 if (str.startsWith("S")) {
121 return Bindings.STRING.type();
123 if (str.startsWith("I")) {
124 return Bindings.INTEGER.type();
126 if (str.startsWith("L")) {
127 return Bindings.LONG.type();
129 if (str.startsWith("B")) {
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);
139 throw new BindingException("Invalid string");
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");
148 String str = (String) obj;
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);
157 if (str.startsWith("I")) {
158 if (binding instanceof IntegerBinding == false) throw new BindingException("IntegerBinding expected, got "+binding.getClass().getSimpleName());
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);
168 if (str.startsWith("L")) {
169 if (binding instanceof LongBinding == false) throw new BindingException("LongBinding expected, got "+binding.getClass().getSimpleName());
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);
179 if (str.startsWith("B")) {
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);
196 throw new BindingException("Invalid value");
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");
204 String str = (String) obj;
206 if (str.startsWith("S")) {
207 /*String value =*/ URIUtil.decodeURI( str.substring(1) );
208 return Bindings.STRING;
211 if (str.startsWith("I")) {
213 /*Integer value =*/ Integer.valueOf( str.substring(1) );
214 return Bindings.INTEGER;
215 } catch (NumberFormatException nfe) {
216 throw new BindingException(nfe);
220 if (str.startsWith("L")) {
222 /*Long value =*/ Long.valueOf( str.substring(1) );
223 return Bindings.LONG;
224 } catch (NumberFormatException nfe) {
225 throw new BindingException(nfe);
229 if (str.startsWith("B")) {
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);
242 throw new BindingException("Invalid value");
246 public void setContent(Object variant, Binding binding, Object value)
247 throws BindingException {
248 throw new BindingException("Cannot set value to an immutable String");
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);
262 public boolean isInstance(Object obj) {
263 return obj instanceof String;
266 static Serializer DATATYPE_SERIALIZER;
267 static Serializer getDatatypeSerializer()
269 if (DATATYPE_SERIALIZER == null) DATATYPE_SERIALIZER = Bindings.getSerializerUnchecked( Bindings.getBindingUnchecked(Datatype.class) );
270 return DATATYPE_SERIALIZER;
274 protected boolean baseEquals(Object obj) {
275 StringVariantBinding o = (StringVariantBinding)obj;
276 return super.baseEquals(obj) && o.serializationFactory == serializationFactory && o.variantBinding == variantBinding;