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.accessor.binary;
15 import java.io.IOException;
16 import java.util.Collection;
17 import java.util.LinkedList;
18 import java.util.List;
19 import java.util.concurrent.Executor;
20 import java.util.concurrent.locks.Lock;
22 import org.simantics.databoard.accessor.Accessor;
23 import org.simantics.databoard.accessor.CloseableAccessor;
24 import org.simantics.databoard.accessor.ParametrisedAccessor;
25 import org.simantics.databoard.accessor.error.AccessorConstructionException;
26 import org.simantics.databoard.accessor.error.AccessorException;
27 import org.simantics.databoard.accessor.error.ReferenceException;
28 import org.simantics.databoard.accessor.event.Event;
29 import org.simantics.databoard.accessor.event.InvalidatedEvent;
30 import org.simantics.databoard.accessor.file.FileAccessor;
31 import org.simantics.databoard.accessor.impl.AccessorParams;
32 import org.simantics.databoard.accessor.impl.ListenerEntry;
33 import org.simantics.databoard.accessor.interestset.InterestSet;
34 import org.simantics.databoard.accessor.reference.ChildReference;
35 import org.simantics.databoard.adapter.AdaptException;
36 import org.simantics.databoard.adapter.AdapterConstructionException;
37 import org.simantics.databoard.binding.Binding;
38 import org.simantics.databoard.serialization.Serializer;
39 import org.simantics.databoard.serialization.SerializerConstructionException;
40 import org.simantics.databoard.type.ArrayType;
41 import org.simantics.databoard.type.BooleanType;
42 import org.simantics.databoard.type.ByteType;
43 import org.simantics.databoard.type.Datatype;
44 import org.simantics.databoard.type.DoubleType;
45 import org.simantics.databoard.type.FloatType;
46 import org.simantics.databoard.type.IntegerType;
47 import org.simantics.databoard.type.LongType;
48 import org.simantics.databoard.type.MapType;
49 import org.simantics.databoard.type.OptionalType;
50 import org.simantics.databoard.type.RecordType;
51 import org.simantics.databoard.type.StringType;
52 import org.simantics.databoard.type.UnionType;
53 import org.simantics.databoard.type.VariantType;
54 import org.simantics.databoard.util.binary.BinaryFile;
55 import org.simantics.databoard.util.binary.Blob;
56 import org.simantics.databoard.util.binary.RandomAccessBinary;
59 * BinaryObject is an accessor to a binary object, usually a random access file.
60 * BinaryObject cannot handle files of recursive types.
62 * The file can be opened once. It may not be modified by any other instance other than
63 * accessor while accessors are beign used. You must not create more than one
64 * instance of BinaryObjects for a file.
81 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
83 public abstract class BinaryObject implements Accessor, FileAccessor, CloseableAccessor, ParametrisedAccessor {
85 /** Hard link to the parent object, <code>null</code> if this is root */
86 protected Accessor parent;
88 protected ListenerEntry listeners = null;
89 /** Random access binary object */
92 protected Datatype type;
95 /** Accessor params */
96 protected AccessorParams params;
98 BinaryObject(Accessor parent, Blob blob, Datatype type, AccessorParams params) {
102 this.params = params;
104 if (parent!=null && parent instanceof BinaryObject) {
105 file = ((BinaryObject)parent).file();
107 RandomAccessBinary sourceBinary = b.getSource();
108 if (sourceBinary instanceof BinaryFile) {
109 BinaryFile bf = (BinaryFile) sourceBinary;
115 public Datatype type() {
119 public void flush() throws AccessorException {
122 } catch (IOException e) {
123 throw new AccessorException(e);
128 public void reset() throws AccessorException {
131 } catch (IOException e) {
132 throw new AccessorException(e);
136 public RandomAccessBinary getSource() {
137 RandomAccessBinary result = b;
138 while (result instanceof Blob) result = ((Blob)result).getParent();
143 * Close the random access file beneath
145 public void close() throws AccessorException {
149 ((FileAccessor) parent).close();
155 RandomAccessBinary rab = getSource();
159 } catch (IOException e) {
160 throw new AccessorException(e);
166 public boolean isOpen() {
171 * Get file if the binary object is based on binary file.
173 * @return file or <code>null</code>
179 public RandomAccessBinary getBinary() {
184 public AccessorParams getParams() {
189 public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException {
190 listeners = ListenerEntry.link(listeners, listener, interestSet, path, executor);
193 protected ListenerEntry detachListener(Listener listener) throws AccessorException {
194 ListenerEntry e = listeners;
195 ListenerEntry p = null;
198 if (e.listener == listener) {
199 // The match was the first entry of the linked list
204 // Some other entry, unlink e
215 public void removeListener(Listener listener) throws AccessorException {
216 detachListener(listener);
220 * Write a new value and flush the buffer.
226 public void setValue(Binding binding, Object newValue)
227 throws AccessorException {
231 setValueNoflush(binding, newValue);
233 } catch (IOException e) {
234 throw new AccessorException(e);
241 * Write a new value and don't flush the buffer
245 * @throws AccessorException
247 public abstract void setValueNoflush(Binding binding, Object newValue) throws AccessorException;
249 public boolean setValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
251 Accessor a = getComponent(path);
252 a.setValue(binding, obj);
254 } catch (ReferenceException re) {
256 } catch (AccessorConstructionException e) {
257 throw new AccessorException(e);
262 public Object getValue(Binding binding) throws AccessorException {
266 Serializer s = params.serializerScheme.getSerializer( binding );
268 return s.deserialize(b, null);
269 } catch (IOException e) {
270 throw new AccessorException(e);
271 } catch (SerializerConstructionException e) {
272 throw new AccessorException(e);
278 public void getValue(Binding binding, Object obj) throws AccessorException {
282 Serializer s = params.serializerScheme.getSerializer( binding );
284 s.deserializeTo(b, null, obj);
285 } catch (IOException e) {
286 throw new AccessorException(e);
287 } catch (SerializerConstructionException e) {
288 throw new AccessorException(e);
295 public boolean getValue(ChildReference path, Binding binding, Object obj) throws AccessorException {
297 Accessor a = getComponent(path);
298 a.getValue(binding, obj);
300 } catch (ReferenceException re) {
302 } catch (AccessorConstructionException e) {
303 throw new AccessorException(e);
307 public Object getValue(ChildReference path, Binding binding) throws AccessorException {
309 Accessor a = getComponent(path);
310 return a.getValue(binding);
311 } catch (ReferenceException re) {
313 } catch (AccessorConstructionException e) {
314 throw new AccessorException(e);
318 Object adapt(Object value, Binding domain, Binding range) throws AdaptException, AdapterConstructionException {
319 return params.adapterScheme.getAdapter(domain, range, true, false).adapt(value);
323 * Send notification that this accessor has been detached from the parent
325 void invalidatedNotification() {
326 ListenerEntry le = listeners;
328 InterestSet is = le.getInterestSet();
329 if (is.inNotifications()) {
330 InvalidatedEvent e = new InvalidatedEvent();
338 * Apply a change set that has events for the particular accessor.
339 * There are no sub-accessor events. Does not flush buffer.
342 * @param makeRollback
343 * @return rollback-event
344 * @throws AccessorException
346 abstract Event applyLocal(Event e, boolean makeRollback) throws AccessorException;
349 public void apply(List<Event> cs, LinkedList<Event> rollback) throws AccessorException {
353 boolean makeRollback = rollback != null;
356 BinaryObject a = e.reference == null ? this : (BinaryObject) getComponent(e.reference);
358 Event rbe = a.applyLocal(e, makeRollback);
360 rbe.reference = e.reference;
361 rollback.addFirst( rbe );
364 // Flush after successful transaction
367 } catch (IOException e1) {
368 throw new AccessorException(e1);
370 } catch (AccessorConstructionException ae) {
371 // Attempt to flush, don't report if fails
372 // The contract is that all events that did go thru, created a rollback
373 // and those must be flushed
376 } catch (IOException e1) {
378 throw new AccessorException(ae);
386 public String toString() {
388 Datatype type = type();
389 // Binding binding = params.bindingScheme.getBinding(type);
390 // Object instance = getValue(binding);
391 // return "Accessor("+binding.printValueDefinition(instance, true)+")";
392 return this.getClass()+"("+type+")";
393 // } catch (AccessorException e) {
394 // return "Accessor(error="+e.getMessage()+")";
395 // } catch (BindingException e) {
396 // return "Accessor(error="+e.getMessage()+")";
397 // } catch (IOException e) {
398 // return "Accessor(error="+e.getMessage()+")";
399 // } catch (BindingConstructionException e) {
400 // return "Accessor(error="+e.getMessage()+")";
404 public static BinaryObject createAccessor(RandomAccessBinary binary, Datatype type, AccessorParams params) throws AccessorConstructionException
407 Blob blob = binary instanceof Blob ? (Blob) binary : new Blob(binary);
409 if (type instanceof BooleanType) {
410 return new BinaryBoolean(null, blob, (BooleanType) type, params);
412 if (type instanceof ByteType) {
413 return new BinaryByte(null, blob, (ByteType)type, params);
415 if (type instanceof IntegerType) {
416 return new BinaryInteger(null, blob, (IntegerType)type, params);
418 if (type instanceof LongType) {
419 return new BinaryLong(null, blob, (LongType)type, params);
421 if (type instanceof FloatType) {
422 return new BinaryFloat(null, blob, (FloatType)type, params);
424 if (type instanceof DoubleType) {
425 return new BinaryDouble(null, blob, (DoubleType)type, params);
427 if (type instanceof StringType) {
428 return new BinaryString(null, blob, (StringType)type, params);
430 if (type instanceof OptionalType) {
431 return new BinaryOptional(null, blob, (OptionalType)type, params);
433 if (type instanceof UnionType) {
434 return new BinaryUnion(null, blob, (UnionType)type, params);
436 if (type instanceof RecordType) {
437 return new BinaryRecord(null, blob, (RecordType)type, params);
439 if (type instanceof VariantType) {
440 return new BinaryVariant(null, blob, (VariantType)type, params);
442 if (type instanceof MapType) {
443 return new BinaryMap(null, blob, (MapType)type, params);
445 if (type instanceof ArrayType) {
446 return new BinaryArray(null, blob, (ArrayType)type, params);
449 throw new AccessorConstructionException("Can not create accessor to "+type);
450 } catch (IOException e) {
451 throw new AccessorConstructionException(e);
455 BinaryObject createSubAccessor(Datatype type, long position, long length, AccessorParams params)
456 throws AccessorConstructionException {
457 Blob sb = b.createSubBlob(position, length);
459 if (type instanceof BooleanType) {
460 return new BinaryBoolean(this, sb, (BooleanType) type, params);
462 if (type instanceof ByteType) {
463 return new BinaryByte(this, sb, (ByteType)type, params);
465 if (type instanceof IntegerType) {
466 return new BinaryInteger(this, sb, (IntegerType)type, params);
468 if (type instanceof LongType) {
469 return new BinaryLong(this, sb, (LongType)type, params);
471 if (type instanceof FloatType) {
472 return new BinaryFloat(this, sb, (FloatType)type, params);
474 if (type instanceof DoubleType) {
475 return new BinaryDouble(this, sb, (DoubleType)type, params);
477 if (type instanceof StringType) {
478 return new BinaryString(this, sb, (StringType)type, params);
480 if (type instanceof OptionalType) {
481 return new BinaryOptional(this, sb, (OptionalType)type, params);
483 if (type instanceof UnionType) {
484 return new BinaryUnion(this, sb, (UnionType)type, params);
486 if (type instanceof VariantType) {
487 return new BinaryVariant(this, sb, (VariantType)type, params);
489 if (type instanceof ArrayType) {
490 return new BinaryArray(this, sb, (ArrayType)type, params);
492 if (type instanceof MapType) {
493 return new BinaryMap(this, sb, (MapType)type, params);
495 if (type instanceof RecordType) {
496 return new BinaryRecord(this, sb, (RecordType)type, params);
498 throw new AccessorConstructionException("Can not create accessor to "+type);
501 protected void emitEvent(ListenerEntry le, Event e) {
502 e.reference = ChildReference.concatenate(le.path, e.reference);
506 protected void emitEvents(ListenerEntry le, Collection<Event> events) {
507 for (Event e : events)
508 e.reference = ChildReference.concatenate(le.path, e.reference);
509 le.emitEvents(events);
513 * Get lock if available.
515 * @return lock or <tt>null</tt>
517 public Lock getReadLock() {
518 return params.readLock;
522 * Get lock if available.
524 * @return lock or <tt>null</tt>
526 public Lock getWriteLock() {
527 return params.writeLock;
532 * Lock the lock if there is a lock.
534 protected void readLock() {
535 if (params.readLock!=null) params.readLock.lock();
539 * Unlock the lock if one exists
541 protected void readUnlock() {
542 if (params.readLock!=null) params.readLock.unlock();
546 * Lock the lock if there is a lock.
548 protected void writeLock() {
549 if (params.writeLock!=null) params.writeLock.lock();
553 * Unlock the lock if one exists
555 protected void writeUnlock() {
556 if (params.writeLock!=null) params.writeLock.unlock();