1 /*******************************************************************************
\r
2 * Copyright (c) 2010- Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.databoard.binding.impl;
\r
14 import java.io.IOException;
\r
15 import java.nio.ByteBuffer;
\r
16 import java.util.Set;
\r
18 import org.simantics.databoard.Bindings;
\r
19 import org.simantics.databoard.Datatypes;
\r
20 import org.simantics.databoard.binding.Binding;
\r
21 import org.simantics.databoard.binding.IntegerBinding;
\r
22 import org.simantics.databoard.binding.LongBinding;
\r
23 import org.simantics.databoard.binding.StringBinding;
\r
24 import org.simantics.databoard.binding.VariantBinding;
\r
25 import org.simantics.databoard.binding.error.BindingException;
\r
26 import org.simantics.databoard.binding.mutable.MutableVariant;
\r
27 import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
\r
28 import org.simantics.databoard.serialization.Serializer;
\r
29 import org.simantics.databoard.serialization.SerializerConstructionException;
\r
30 import org.simantics.databoard.serialization.SerializerFactory;
\r
31 import org.simantics.databoard.type.Datatype;
\r
32 import org.simantics.databoard.util.Base64;
\r
33 import org.simantics.databoard.util.URIUtil;
\r
34 import org.simantics.databoard.util.binary.BinaryMemory;
\r
35 import org.simantics.databoard.util.binary.BinaryReadable;
\r
38 * This class binding Variant to a filename/URL compatible String.
\r
39 * The value is human-readable for strings, integers and longs.
\r
40 * For datatypes the value is Base64 encoded binary.
\r
42 * Filenames have the following encoding:
\r
43 * S<string> String types, if string doesn't have the following
\r
44 * control characters " : < > | ? * # \ / % [0..31] [128..] are escaped with %<hex><hex>
\r
45 * I<integer> Integer types
\r
46 * L<long> Long types
\r
47 * B<base64> All other cases. The value is binary encoded and presented as single line Base64 string.
\r
48 * Base64 encoding has url and filename safe options enabled.
\r
50 * See StringVariantBindingExample
\r
51 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
\r
53 public class StringVariantBinding extends VariantBinding {
\r
55 SerializerFactory serializationFactory;
\r
56 VariantBinding variantBinding;
\r
58 public StringVariantBinding(SerializerFactory serializationFactory, VariantBinding variantBinding) {
\r
59 this.serializationFactory = serializationFactory;
\r
60 this.variantBinding = variantBinding;
\r
64 public Object create(Binding binding, Object value) throws BindingException {
\r
65 if (binding instanceof StringBinding && binding.type().equals(Datatypes.STRING)) {
\r
66 StringBinding sb = (StringBinding) binding;
\r
67 return "S"+URIUtil.encodeURI( sb.getValue(value) );
\r
70 if (binding instanceof IntegerBinding && binding.type().equals(Datatypes.INTEGER)) {
\r
71 IntegerBinding ib = (IntegerBinding) binding;
\r
72 return "I"+ib.getValue_(value);
\r
75 if (binding instanceof LongBinding && binding.type().equals(Datatypes.LONG)) {
\r
76 LongBinding lb = (LongBinding) binding;
\r
77 return "L"+lb.getValue_(value);
\r
81 MutableVariant v = new MutableVariant(binding, value);
\r
82 byte[] data = serializationFactory.getSerializerUnchecked( variantBinding ).serialize(v);
\r
83 return "B"+Base64.encodeBytes(data, Base64.URL_SAFE);
\r
84 } catch (RuntimeSerializerConstructionException e) {
\r
85 throw new BindingException(e);
\r
86 } catch (IOException e) {
\r
87 throw new BindingException(e);
\r
92 public Binding getContentBinding(Object variant) throws BindingException {
\r
93 String str = (String) variant;
\r
94 if (str.startsWith("S")) {
\r
95 return Bindings.STRING;
\r
97 if (str.startsWith("I")) {
\r
98 return Bindings.INTEGER;
\r
100 if (str.startsWith("L")) {
\r
101 return Bindings.LONG;
\r
103 if (str.startsWith("B")) {
\r
105 byte[] data = Base64.decode(str.substring(1), Base64.URL_SAFE);
\r
106 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) ) ;
\r
107 Datatype type = (Datatype) getDatatypeSerializer().deserialize(readable);
\r
108 return Bindings.getMutableBinding(type);
\r
109 } catch (IOException e) {
\r
110 throw new BindingException(e);
\r
114 throw new BindingException("Invalid string");
\r
118 public Datatype getContentType(Object variant) throws BindingException {
\r
119 String str = (String) variant;
\r
120 if (str.startsWith("S")) {
\r
121 return Bindings.STRING.type();
\r
123 if (str.startsWith("I")) {
\r
124 return Bindings.INTEGER.type();
\r
126 if (str.startsWith("L")) {
\r
127 return Bindings.LONG.type();
\r
129 if (str.startsWith("B")) {
\r
131 byte[] data = Base64.decode(str.substring(1), Base64.URL_SAFE);
\r
132 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) ) ;
\r
133 return (Datatype) getDatatypeSerializer().deserialize(readable);
\r
134 } catch (IOException e) {
\r
135 throw new BindingException(e);
\r
139 throw new BindingException("Invalid string");
\r
143 public Object getContent(Object obj, Binding binding)
\r
144 throws BindingException {
\r
145 if (obj==null) throw new BindingException("null value is not Variant");
\r
146 if (obj instanceof String == false) throw new BindingException("wrong class, String expected");
\r
148 String str = (String) obj;
\r
150 if (str.startsWith("S")) {
\r
151 if (binding instanceof StringBinding == false) throw new BindingException("StringBinding expected, got "+binding.getClass().getSimpleName());
\r
152 String value = URIUtil.decodeURI( str.substring(1) );
\r
153 StringBinding sb = (StringBinding) binding;
\r
154 return sb.create(value);
\r
157 if (str.startsWith("I")) {
\r
158 if (binding instanceof IntegerBinding == false) throw new BindingException("IntegerBinding expected, got "+binding.getClass().getSimpleName());
\r
160 Integer value = Integer.valueOf( str.substring(1) );
\r
161 IntegerBinding ib = (IntegerBinding) binding;
\r
162 return ib.create(value);
\r
163 } catch (NumberFormatException nfe) {
\r
164 throw new BindingException(nfe);
\r
168 if (str.startsWith("L")) {
\r
169 if (binding instanceof LongBinding == false) throw new BindingException("LongBinding expected, got "+binding.getClass().getSimpleName());
\r
171 Long value = Long.valueOf( str.substring(1) );
\r
172 LongBinding lb = (LongBinding) binding;
\r
173 return lb.create(value);
\r
174 } catch (NumberFormatException nfe) {
\r
175 throw new BindingException(nfe);
\r
179 if (str.startsWith("B")) {
\r
181 byte[] data = Base64.decode(str.substring(1), Base64.URL_SAFE);
\r
182 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) ) ;
\r
183 Datatype type = (Datatype) getDatatypeSerializer().deserialize(readable);
\r
184 if (!type.equals(binding.type()))
\r
185 throw new BindingException("Binding for "+type.toSingleLineString()+" expected, but got "+binding.type());
\r
186 return Bindings.getSerializer( binding ).deserialize(readable);
\r
187 } catch (IOException ioe) {
\r
188 throw new BindingException(ioe);
\r
189 } catch (RuntimeSerializerConstructionException e) {
\r
190 throw new BindingException(e);
\r
191 } catch (SerializerConstructionException e) {
\r
192 throw new BindingException(e);
\r
196 throw new BindingException("Invalid value");
\r
200 public Object getContent(Object obj) throws BindingException {
\r
201 if (obj==null) throw new BindingException("null value is not Variant");
\r
202 if (obj instanceof String == false) throw new BindingException("wrong class, String expected");
\r
204 String str = (String) obj;
\r
206 if (str.startsWith("S")) {
\r
207 /*String value =*/ URIUtil.decodeURI( str.substring(1) );
\r
208 return Bindings.STRING;
\r
211 if (str.startsWith("I")) {
\r
213 /*Integer value =*/ Integer.valueOf( str.substring(1) );
\r
214 return Bindings.INTEGER;
\r
215 } catch (NumberFormatException nfe) {
\r
216 throw new BindingException(nfe);
\r
220 if (str.startsWith("L")) {
\r
222 /*Long value =*/ Long.valueOf( str.substring(1) );
\r
223 return Bindings.LONG;
\r
224 } catch (NumberFormatException nfe) {
\r
225 throw new BindingException(nfe);
\r
229 if (str.startsWith("B")) {
\r
231 byte[] data = Base64.decode(str.substring(1), Base64.URL_SAFE);
\r
232 BinaryReadable readable = new BinaryMemory( ByteBuffer.wrap(data) );
\r
233 MutableVariant variant = (MutableVariant) Bindings.getSerializerUnchecked( Bindings.MUTABLE_VARIANT).deserialize(readable);
\r
234 return variant.getValue();
\r
235 } catch (IOException ioe) {
\r
236 throw new BindingException(ioe);
\r
237 } catch (RuntimeSerializerConstructionException e) {
\r
238 throw new BindingException(e);
\r
242 throw new BindingException("Invalid value");
\r
246 public void setContent(Object variant, Binding binding, Object value)
\r
247 throws BindingException {
\r
248 throw new BindingException("Cannot set value to an immutable String");
\r
252 public void assertInstaceIsValid(Object obj, Set<Object> validInstances)
\r
253 throws BindingException {
\r
254 if (obj==null) throw new BindingException("null value is not Variant");
\r
255 if (obj instanceof String == false) throw new BindingException("wrong class, String expected");
\r
256 Object value = getContent(obj);
\r
257 Binding binding = getContentBinding(obj);
\r
258 binding.assertInstaceIsValid(value);
\r
262 public boolean isInstance(Object obj) {
\r
263 return obj instanceof String;
\r
266 static Serializer DATATYPE_SERIALIZER;
\r
267 static Serializer getDatatypeSerializer()
\r
269 if (DATATYPE_SERIALIZER == null) DATATYPE_SERIALIZER = Bindings.getSerializerUnchecked( Bindings.getBindingUnchecked(Datatype.class) );
\r
270 return DATATYPE_SERIALIZER;
\r
274 protected boolean baseEquals(Object obj) {
\r
275 StringVariantBinding o = (StringVariantBinding)obj;
\r
276 return super.baseEquals(obj) && o.serializationFactory == serializationFactory && o.variantBinding == variantBinding;
\r