]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/wire/WireServer.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / wire / WireServer.java
index 8dd7fe27c1d02c15baabe6e8ba262bc97f75d7dd..a6490707c08c57520d6c7478521accc8d9572046 100644 (file)
-/*******************************************************************************\r
- * Copyright (c) 2010- Association for Decentralized Information Management in\r
- * Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- * \r
- * Contributors:\r
- *    VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
-package org.simantics.databoard.accessor.wire;\r
-\r
-import java.util.ArrayList;\r
-import java.util.Collection;\r
-import java.util.Collections;\r
-import java.util.HashMap;\r
-import java.util.LinkedList;\r
-import java.util.List;\r
-import java.util.Map;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.Methods;\r
-import org.simantics.databoard.accessor.Accessor;\r
-import org.simantics.databoard.accessor.Accessor.Listener;\r
-import org.simantics.databoard.accessor.ArrayAccessor;\r
-import org.simantics.databoard.accessor.MapAccessor;\r
-import org.simantics.databoard.accessor.OptionalAccessor;\r
-import org.simantics.databoard.accessor.RecordAccessor;\r
-import org.simantics.databoard.accessor.UnionAccessor;\r
-import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
-import org.simantics.databoard.accessor.error.AccessorException;\r
-import org.simantics.databoard.accessor.event.Event;\r
-import org.simantics.databoard.accessor.impl.AccessorParams;\r
-import org.simantics.databoard.accessor.interestset.InterestSet;\r
-import org.simantics.databoard.accessor.reference.ChildReference;\r
-import org.simantics.databoard.adapter.AdaptException;\r
-import org.simantics.databoard.annotations.Optional;\r
-import org.simantics.databoard.binding.ArrayBinding;\r
-import org.simantics.databoard.binding.Binding;\r
-import org.simantics.databoard.binding.MapBinding;\r
-import org.simantics.databoard.binding.RecordBinding;\r
-import org.simantics.databoard.binding.UnionBinding;\r
-import org.simantics.databoard.binding.error.BindingConstructionException;\r
-import org.simantics.databoard.binding.impl.ObjectArrayBinding;\r
-import org.simantics.databoard.binding.mutable.MutableVariant;\r
-import org.simantics.databoard.method.MethodInterface;\r
-import org.simantics.databoard.method.MethodInterface.Method;\r
-import org.simantics.databoard.method.MethodNotSupportedException;\r
-import org.simantics.databoard.method.MethodTypeBinding;\r
-import org.simantics.databoard.method.TcpConnection;\r
-import org.simantics.databoard.method.TcpConnection.ConnectionListener;\r
-import org.simantics.databoard.type.ArrayType;\r
-import org.simantics.databoard.type.Component;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.type.MapType;\r
-import org.simantics.databoard.type.UnionType;\r
-import org.simantics.databoard.util.BijectionMap;\r
-\r
-/**\r
- * WireServer exposes an accessor over TCP/IP socket. \r
- *\r
- * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
- */\r
-public class WireServer implements IWireServer {\r
-       \r
-       Accessor accessor;\r
-       Map<TcpConnection, ClientRecord> clients = Collections.synchronizedMap( new HashMap<TcpConnection, ClientRecord>() );\r
-       MethodInterface mi;\r
-       AccessorParams params = AccessorParams.DEFAULT;\r
-       \r
-       public WireServer(Accessor accessor) {\r
-               this.accessor = accessor;\r
-               try {\r
-                       this.mi = Methods.bindInterface(IWireServer.class, this);\r
-               } catch (BindingConstructionException e) {\r
-                       throw new RuntimeException(e);\r
-               }\r
-       }\r
-       \r
-       public MethodInterface getMethodInterface() {\r
-               return mi;\r
-       }\r
-       \r
-       static class ClientRecord {\r
-               TcpConnection connection;\r
-               MethodInterface clientMethodInterface;\r
-               Method onEventsMethod;\r
-               IWireClient clientMethods;\r
-               int accIdCounter = 0;\r
-               int listIdCounter = 0;          \r
-               BijectionMap<Integer, AccRecord> accessorTable = new BijectionMap<Integer, AccRecord>();\r
-               Map<Integer, LisRecord> listenerTable = new HashMap<Integer, LisRecord>();\r
-               \r
-               public void dispose() {                 \r
-               }               \r
-       }\r
-       \r
-       static class AccRecord {\r
-               int accId;\r
-               Accessor accessor;\r
-               ChildReference reference;\r
-               Datatype type;\r
-               Binding binding;\r
-       }       \r
-       \r
-       static class LisRecord {\r
-               int lisId;\r
-               int accId;\r
-               InterestSet is;\r
-               Listener listener;\r
-       }\r
-       \r
-       static class OnEventsRequest {\r
-               public @Optional Integer arg1;\r
-               public @Optional Event[] arg2;\r
-       }\r
-           \r
-       /**\r
-        * Get or create new client handler associated with current thread.\r
-        * The result value depends on the current thread.  \r
-        * \r
-        * @return client handler\r
-        * @throws WireException\r
-        */\r
-       public ClientRecord getClient() throws WireException {\r
-               TcpConnection connection = TcpConnection.getCurrentConnection();\r
-               if (connection == null) throw new WireException("Internal Error. This method must be invoked in ConnectionThread");\r
-               try {\r
-               synchronized(clients) {\r
-                       ClientRecord handler = clients.get( connection );\r
-                       if (handler == null) {\r
-\r
-                               RecordBinding requestBinding = (RecordBinding) Bindings.getBindingUnchecked( OnEventsRequest.class );\r
-                               Binding responseBinding = Bindings.INTEGER;\r
-                               UnionType ut = new UnionType();\r
-                               ut.components = new Component[0];\r
-                               UnionBinding errorBinding = (UnionBinding) params.bindingScheme.getBinding( ut );\r
-                               MethodTypeBinding onEventsBinding = new MethodTypeBinding("onEvents", requestBinding, responseBinding, errorBinding);\r
-                               \r
-                               try {\r
-                                       handler = new ClientRecord();\r
-                                       handler.clientMethodInterface = connection.getRemoteMethodInterface();\r
-                                       handler.onEventsMethod = handler.clientMethodInterface.getMethod( onEventsBinding );\r
-                                       handler.connection = connection;\r
-                                       handler.clientMethods = Methods.createProxy(IWireClient.class, handler.connection.getRemoteMethodInterface());\r
-                               } catch (MethodNotSupportedException e) {\r
-                                       throw new WireException(e);\r
-                               }\r
-                               \r
-                               connection.addConnectionListener( new ConnectionListener() {\r
-                                       @Override\r
-                                       public void onError(Exception error) {\r
-                                               TcpConnection connection = TcpConnection.getCurrentConnection();\r
-                                               ClientRecord handler = clients.remove(connection);\r
-                                               if (handler != null) handler.dispose();\r
-                                       }\r
-                                       @Override\r
-                                       public void onClosed() {\r
-                                               TcpConnection connection = TcpConnection.getCurrentConnection();\r
-                                               ClientRecord handler = clients.remove(connection);\r
-                                               if (handler != null) handler.dispose();\r
-                                       }});\r
-                               clients.put(connection, handler);\r
-                       }\r
-                       return handler;\r
-               }\r
-               } catch (BindingConstructionException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       AccRecord getAccessor(int accId) throws WireException {\r
-               ClientRecord client = getClient();\r
-               AccRecord acc = client.accessorTable.getRight(accId);\r
-               if (acc==null) throw new WireException("Invalid accessor id");\r
-               return acc;\r
-       }\r
-       \r
-       LisRecord getListener(int accId, int lisId) throws WireException {\r
-               ClientRecord client = getClient();\r
-               LisRecord lis = client.listenerTable.get(lisId);\r
-               return lis;\r
-       }\r
-       \r
-       // Assumption is that each method is invoked in a connection thread.\r
-       // Therefore object synchronization is unnecessary.\r
-       \r
-       @Override\r
-       public AccessorInfo openAccessor(ChildReference ref) throws WireException {\r
-               try {\r
-                       ClientRecord client = getClient();\r
-                       AccRecord acc = new AccRecord();\r
-                       acc.accessor = accessor.getComponent(ref);\r
-                       acc.accId = client.accIdCounter++;\r
-                       acc.reference = ref;\r
-                       acc.type = accessor.type();\r
-                       acc.binding = params.bindingScheme.getBinding(acc.type);\r
-                       client.accessorTable.map(acc.accId, acc);\r
-                       AccessorInfo ai = new AccessorInfo();\r
-                       ai.accessorId = acc.accId;\r
-                       ai.type = acc.type;\r
-                       return ai;\r
-               } catch (AccessorConstructionException e) {\r
-                       throw new WireException( e );\r
-               } catch (BindingConstructionException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public int closeAccessors(Integer[] accIds) throws WireException {\r
-               ClientRecord client = getClient();\r
-               for (Integer accId : accIds) {\r
-                       client.accessorTable.removeWithLeft(accId);\r
-               }\r
-               return 0;\r
-       }\r
-       @Override\r
-       public MutableVariant getValue(int accId) throws WireException {\r
-               try {\r
-                       AccRecord acc = getAccessor(accId);\r
-                       Object value = acc.accessor.getValue(acc.binding);\r
-                       return new MutableVariant(acc.binding, value);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException(e);\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public ApplyResult apply(int accId, Event[] changeSet, boolean rollback) {\r
-               ApplyResult result = new ApplyResult();\r
-               result.rollbackLog = rollback ? new LinkedList<Event>() : null;         \r
-               try {\r
-                       AccRecord acc = getAccessor(accId);\r
-                       List<Event> changeSetList = new ArrayList<Event>( changeSet.length );\r
-                       for (Event e : changeSet) changeSetList.add( e );\r
-                       acc.accessor.apply(changeSetList, result.rollbackLog);\r
-               } catch (WireException e) {\r
-                       result.error = e;\r
-               } catch (AccessorException e) {\r
-                       result.error = new WireException(e);\r
-               }               \r
-               return result;\r
-       }\r
-\r
-       @Override\r
-       public int addListener(int accId, InterestSet interestSet,\r
-                       ChildReference path) throws WireException {\r
-               try {\r
-                       \r
-                       ClientRecord client = getClient();\r
-                       AccRecord acc = getAccessor(accId);\r
-                       final LisRecord lis = new LisRecord();\r
-                       lis.accId = accId;\r
-                       lis.is = interestSet;\r
-                       lis.lisId = client.listIdCounter++;\r
-                       lis.listener = new Listener() {\r
-                               @Override\r
-                               public void onEvents(Collection<Event> events) {\r
-                                       try {\r
-                                               // Push event to client\r
-                                               ClientRecord client = getClient();\r
-                                               Event[] eventArray = events.toArray( new Event[events.size()] );\r
-\r
-//                                             client.clientMethods.onEvents(lis.lisId, eventArray);\r
-\r
-                                               // Invoke async.\r
-                                               OnEventsRequest req = new OnEventsRequest();\r
-                                               req.arg1 = lis.lisId;\r
-                                               req.arg2 = eventArray;\r
-                                               client.onEventsMethod.invoke( req );\r
-                                       } catch (WireException e) {\r
-                                       }\r
-                               }};\r
-                       acc.accessor.addListener(lis.listener, interestSet, path, null /*TODO Dispatch thread*/);\r
-                       client.listenerTable.put(lis.lisId, lis);\r
-                       return lis.lisId;\r
-               } catch (AccessorException e) {\r
-                       throw new WireException(e.getClass().getName()+": "+e.getMessage());\r
-               }               \r
-       }\r
-\r
-       @Override\r
-       public int removeListener(int lisId) throws WireException {\r
-               try {\r
-                       ClientRecord client = getClient();              \r
-                       LisRecord lis = client.listenerTable.remove(lisId);\r
-                       AccRecord acc = getAccessor(lis.accId);\r
-                       acc.accessor.removeListener(lis.listener);\r
-                       return 0;\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e ); \r
-               }               \r
-       }\r
-\r
-       @Override\r
-       public int size(int accId) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof ArrayAccessor) return ((ArrayAccessor)acc).size();\r
-                       if (acc instanceof RecordAccessor) return ((RecordAccessor)acc).count();\r
-                       if (acc instanceof MapAccessor) return ((MapAccessor)acc).size();\r
-                       if (acc instanceof UnionAccessor) return ((UnionAccessor)acc).count();\r
-                       throw new WireException("Cannot get size for "+acc.getClass().getName());\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public int clear(int accId) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof ArrayAccessor) {\r
-                               ArrayAccessor aa = ((ArrayAccessor)acc); \r
-                               aa.remove(0, aa.size());\r
-                               return 0;\r
-                       }\r
-                       if (acc instanceof MapAccessor) {\r
-                               ((MapAccessor)acc).clear();\r
-                               return 0;\r
-                       }                       \r
-                       throw new WireException("Cannot clear "+acc.getClass().getName());\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-/*\r
-       @Override\r
-       public void add(int accId, int index, Variant[] values) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof ArrayAccessor) {\r
-                               ArrayAccessor aa = (ArrayAccessor) acc;\r
-                               if (values.length==0) return;\r
-                               \r
-                               Binding b = values[0].getBinding();\r
-                               boolean sameBinding = true;\r
-                               for (Variant v : values) sameBinding &= v.getBinding()==b;\r
-                               \r
-                               if (sameBinding) {\r
-                                       Object[] objs = new Object[ values.length ];\r
-                                       for (int i=0; i<values.length; i++) {\r
-                                               objs[i] = values[i].getValue();\r
-                                       }\r
-                                       aa.add(index, b, objs);                                 \r
-                               } else {\r
-                                       // Add entry one by one\r
-                                       for (Variant v : values) {\r
-                                               aa.add(index, v.getClass(), v.getValue());\r
-                                               index++;\r
-                                       }\r
-                               }\r
-                               \r
-                       } else          \r
-                       throw new WireException("Cannot add to "+acc.getClass().getName());\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-*/\r
-\r
-       @Override\r
-       public boolean containsKey(int accId, MutableVariant key) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       return ma.containsKey(key.getBinding(), key.getValue());\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public boolean containsValue(int accId, MutableVariant value) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       return ma.containsValue(value.getBinding(), value.getValue());\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getFirstKey(int accId) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );\r
-                       Object key = ma.getFirstKey( keyBinding );\r
-                       return new MutableVariant(keyBinding, key);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               } catch (BindingConstructionException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getLastKey(int accId) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );\r
-                       Object key = ma.getLastKey( keyBinding );\r
-                       return new MutableVariant(keyBinding, key);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               } catch (BindingConstructionException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getLowerKey(int accId, MutableVariant key) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       Binding keyBinding = key.getBinding();\r
-                       Object lowerKey = ma.getLowerKey(keyBinding, key.getValue());\r
-                       if (lowerKey==null) return new MutableVariant(Bindings.VOID, null);\r
-                       return new MutableVariant(keyBinding, lowerKey);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }       \r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getFloorKey(int accId, MutableVariant key) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       Binding keyBinding = key.getBinding();\r
-                       Object floorKey = ma.getFloorKey(keyBinding, key.getValue());\r
-                       if (floorKey==null) return new MutableVariant(Bindings.VOID, null);;                    \r
-                       return new MutableVariant(keyBinding, floorKey);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }       \r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getCeilingKey(int accId, MutableVariant key) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       Binding keyBinding = key.getBinding();\r
-                       Object ceilingKey = ma.getCeilingKey(keyBinding, key.getValue());\r
-                       if (ceilingKey==null) return new MutableVariant(Bindings.VOID, null);;                  \r
-                       return new MutableVariant(keyBinding, ceilingKey);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }       \r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getHigherKey(int accId, MutableVariant key) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof MapAccessor == false) \r
-                               throw new WireException("Not a map");\r
-                       MapAccessor ma = (MapAccessor) acc;\r
-                       Binding keyBinding = key.getBinding();\r
-                       Object higherKey = ma.getHigherKey(keyBinding, key.getValue());\r
-                       if (higherKey==null) return new MutableVariant(Bindings.VOID, null);;                   \r
-                       return new MutableVariant(keyBinding, higherKey);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }       \r
-       }\r
-\r
-       @Override\r
-       public boolean hasValue(int accId) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof OptionalAccessor == false) \r
-                               throw new WireException("Not an option");\r
-                       OptionalAccessor ma = (OptionalAccessor) acc;\r
-                       return ma.hasValue();\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }       \r
-       }\r
-\r
-       @Override\r
-       public int getTag(int accId) throws WireException {\r
-               try {\r
-                       Accessor acc = getAccessor(accId).accessor;\r
-                       if (acc instanceof UnionAccessor == false) \r
-                               throw new WireException("Not an union");\r
-                       UnionAccessor ma = (UnionAccessor) acc;\r
-                       return ma.getTag();\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }       \r
-       }\r
-       \r
-       @Override\r
-       public int addAll(int accId, int index, MutableVariant array) throws WireException {\r
-               try {\r
-                       ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;\r
-                       Datatype type = array.type();\r
-                       if (type instanceof ArrayType == false) {\r
-                               throw new WireException("addAll() array expepected, got "+array.type());\r
-                       }\r
-                       \r
-                       Binding componentBinding = ( (ArrayBinding) array.getBinding() ).getComponentBinding();\r
-                       ArrayBinding binding = new ObjectArrayBinding((ArrayType) type, componentBinding);\r
-                       Object values[] = (Object[]) array.getValue(binding);\r
-                       if (index==-1) {\r
-                               int size = acc.size();\r
-                               acc.addAll(binding, values);\r
-                               return size;\r
-                       } else {\r
-                               acc.addAll(index, binding, values);\r
-                               return index;\r
-                       }\r
-                                               \r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               } catch (AdaptException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public int add(int accId, int index, MutableVariant value) throws WireException {\r
-               try {\r
-                       ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;\r
-\r
-                       if (index==-1) {\r
-                               int size = acc.size();\r
-                               acc.add(value.getBinding(), value.getValue());\r
-                               return size;\r
-                       } else {\r
-                               acc.add(index, value.getBinding(), value.getValue());\r
-                               return index;\r
-                       }\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getArrayElement(int accId, int index) throws WireException {\r
-               try {\r
-                       AccRecord ar = getAccessor(accId);\r
-                       ArrayAccessor acc = (ArrayAccessor) ar.accessor;\r
-                       Binding valueBinding = ((ArrayBinding)ar.binding).getComponentBinding();\r
-                       Object value = acc.get(index, valueBinding );\r
-                       MutableVariant v = new MutableVariant(valueBinding, value);\r
-                       return v;\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-       \r
-       @Override\r
-       public MutableVariant getMapValue(int accId, MutableVariant key) throws WireException {\r
-               try {\r
-                       AccRecord ar = getAccessor(accId);\r
-                       MapAccessor acc = (MapAccessor) ar.accessor;\r
-                       MapBinding mapBinding = (MapBinding)ar.binding;\r
-                       Object value = acc.get(key.getBinding(), key.getValue(), mapBinding.getValueBinding());\r
-                       if (value == null) return new MutableVariant();\r
-                       return new MutableVariant(mapBinding.getValueBinding(), value);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getMapValues(int accId) throws WireException {\r
-               try {\r
-                       AccRecord ar = getAccessor(accId);\r
-                       MapAccessor acc = (MapAccessor) ar.accessor;\r
-                       MapBinding mapBinding = (MapBinding)ar.binding;\r
-                       Binding valueBinding = mapBinding.getValueBinding();\r
-                       Object[] values = acc.getValues( valueBinding );\r
-                       ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(valueBinding.type()), valueBinding);\r
-                       return new MutableVariant(arrayBinding, values);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-\r
-       @Override\r
-       public MutableVariant getMapKeys(int accId) throws WireException {\r
-               try {\r
-                       AccRecord ar = getAccessor(accId);\r
-                       MapAccessor acc = (MapAccessor) ar.accessor;\r
-                       MapBinding mapBinding = (MapBinding)ar.binding;\r
-                       Binding keyBinding = mapBinding.getKeyBinding();\r
-                       Object[] keys = acc.getKeys( keyBinding );\r
-                       ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(keyBinding.type()), keyBinding);\r
-                       return new MutableVariant(arrayBinding, keys);\r
-               } catch (AccessorException e) {\r
-                       throw new WireException( e );\r
-               }\r
-       }\r
-       \r
-}\r
-\r
+/*******************************************************************************
+ * Copyright (c) 2010- Association for Decentralized Information Management in
+ * Industry THTH ry.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *    VTT Technical Research Centre of Finland - initial API and implementation
+ *******************************************************************************/
+package org.simantics.databoard.accessor.wire;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.Methods;
+import org.simantics.databoard.accessor.Accessor;
+import org.simantics.databoard.accessor.Accessor.Listener;
+import org.simantics.databoard.accessor.ArrayAccessor;
+import org.simantics.databoard.accessor.MapAccessor;
+import org.simantics.databoard.accessor.OptionalAccessor;
+import org.simantics.databoard.accessor.RecordAccessor;
+import org.simantics.databoard.accessor.UnionAccessor;
+import org.simantics.databoard.accessor.error.AccessorConstructionException;
+import org.simantics.databoard.accessor.error.AccessorException;
+import org.simantics.databoard.accessor.event.Event;
+import org.simantics.databoard.accessor.impl.AccessorParams;
+import org.simantics.databoard.accessor.interestset.InterestSet;
+import org.simantics.databoard.accessor.reference.ChildReference;
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.databoard.annotations.Optional;
+import org.simantics.databoard.binding.ArrayBinding;
+import org.simantics.databoard.binding.Binding;
+import org.simantics.databoard.binding.MapBinding;
+import org.simantics.databoard.binding.RecordBinding;
+import org.simantics.databoard.binding.UnionBinding;
+import org.simantics.databoard.binding.error.BindingConstructionException;
+import org.simantics.databoard.binding.impl.ObjectArrayBinding;
+import org.simantics.databoard.binding.mutable.MutableVariant;
+import org.simantics.databoard.method.MethodInterface;
+import org.simantics.databoard.method.MethodInterface.Method;
+import org.simantics.databoard.method.MethodNotSupportedException;
+import org.simantics.databoard.method.MethodTypeBinding;
+import org.simantics.databoard.method.TcpConnection;
+import org.simantics.databoard.method.TcpConnection.ConnectionListener;
+import org.simantics.databoard.type.ArrayType;
+import org.simantics.databoard.type.Component;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.type.MapType;
+import org.simantics.databoard.type.UnionType;
+import org.simantics.databoard.util.BijectionMap;
+
+/**
+ * WireServer exposes an accessor over TCP/IP socket. 
+ *
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public class WireServer implements IWireServer {
+       
+       Accessor accessor;
+       Map<TcpConnection, ClientRecord> clients = Collections.synchronizedMap( new HashMap<TcpConnection, ClientRecord>() );
+       MethodInterface mi;
+       AccessorParams params = AccessorParams.DEFAULT;
+       
+       public WireServer(Accessor accessor) {
+               this.accessor = accessor;
+               try {
+                       this.mi = Methods.bindInterface(IWireServer.class, this);
+               } catch (BindingConstructionException e) {
+                       throw new RuntimeException(e);
+               }
+       }
+       
+       public MethodInterface getMethodInterface() {
+               return mi;
+       }
+       
+       static class ClientRecord {
+               TcpConnection connection;
+               MethodInterface clientMethodInterface;
+               Method onEventsMethod;
+               IWireClient clientMethods;
+               int accIdCounter = 0;
+               int listIdCounter = 0;          
+               BijectionMap<Integer, AccRecord> accessorTable = new BijectionMap<Integer, AccRecord>();
+               Map<Integer, LisRecord> listenerTable = new HashMap<Integer, LisRecord>();
+               
+               public void dispose() {                 
+               }               
+       }
+       
+       static class AccRecord {
+               int accId;
+               Accessor accessor;
+               ChildReference reference;
+               Datatype type;
+               Binding binding;
+       }       
+       
+       static class LisRecord {
+               int lisId;
+               int accId;
+               InterestSet is;
+               Listener listener;
+       }
+       
+       static class OnEventsRequest {
+               public @Optional Integer arg1;
+               public @Optional Event[] arg2;
+       }
+           
+       /**
+        * Get or create new client handler associated with current thread.
+        * The result value depends on the current thread.  
+        * 
+        * @return client handler
+        * @throws WireException
+        */
+       public ClientRecord getClient() throws WireException {
+               TcpConnection connection = TcpConnection.getCurrentConnection();
+               if (connection == null) throw new WireException("Internal Error. This method must be invoked in ConnectionThread");
+               try {
+               synchronized(clients) {
+                       ClientRecord handler = clients.get( connection );
+                       if (handler == null) {
+
+                               RecordBinding requestBinding = (RecordBinding) Bindings.getBindingUnchecked( OnEventsRequest.class );
+                               Binding responseBinding = Bindings.INTEGER;
+                               UnionType ut = new UnionType();
+                               ut.components = new Component[0];
+                               UnionBinding errorBinding = (UnionBinding) params.bindingScheme.getBinding( ut );
+                               MethodTypeBinding onEventsBinding = new MethodTypeBinding("onEvents", requestBinding, responseBinding, errorBinding);
+                               
+                               try {
+                                       handler = new ClientRecord();
+                                       handler.clientMethodInterface = connection.getRemoteMethodInterface();
+                                       handler.onEventsMethod = handler.clientMethodInterface.getMethod( onEventsBinding );
+                                       handler.connection = connection;
+                                       handler.clientMethods = Methods.createProxy(IWireClient.class, handler.connection.getRemoteMethodInterface());
+                               } catch (MethodNotSupportedException e) {
+                                       throw new WireException(e);
+                               }
+                               
+                               connection.addConnectionListener( new ConnectionListener() {
+                                       @Override
+                                       public void onError(Exception error) {
+                                               TcpConnection connection = TcpConnection.getCurrentConnection();
+                                               ClientRecord handler = clients.remove(connection);
+                                               if (handler != null) handler.dispose();
+                                       }
+                                       @Override
+                                       public void onClosed() {
+                                               TcpConnection connection = TcpConnection.getCurrentConnection();
+                                               ClientRecord handler = clients.remove(connection);
+                                               if (handler != null) handler.dispose();
+                                       }});
+                               clients.put(connection, handler);
+                       }
+                       return handler;
+               }
+               } catch (BindingConstructionException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       AccRecord getAccessor(int accId) throws WireException {
+               ClientRecord client = getClient();
+               AccRecord acc = client.accessorTable.getRight(accId);
+               if (acc==null) throw new WireException("Invalid accessor id");
+               return acc;
+       }
+       
+       LisRecord getListener(int accId, int lisId) throws WireException {
+               ClientRecord client = getClient();
+               LisRecord lis = client.listenerTable.get(lisId);
+               return lis;
+       }
+       
+       // Assumption is that each method is invoked in a connection thread.
+       // Therefore object synchronization is unnecessary.
+       
+       @Override
+       public AccessorInfo openAccessor(ChildReference ref) throws WireException {
+               try {
+                       ClientRecord client = getClient();
+                       AccRecord acc = new AccRecord();
+                       acc.accessor = accessor.getComponent(ref);
+                       acc.accId = client.accIdCounter++;
+                       acc.reference = ref;
+                       acc.type = accessor.type();
+                       acc.binding = params.bindingScheme.getBinding(acc.type);
+                       client.accessorTable.map(acc.accId, acc);
+                       AccessorInfo ai = new AccessorInfo();
+                       ai.accessorId = acc.accId;
+                       ai.type = acc.type;
+                       return ai;
+               } catch (AccessorConstructionException e) {
+                       throw new WireException( e );
+               } catch (BindingConstructionException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public int closeAccessors(Integer[] accIds) throws WireException {
+               ClientRecord client = getClient();
+               for (Integer accId : accIds) {
+                       client.accessorTable.removeWithLeft(accId);
+               }
+               return 0;
+       }
+       @Override
+       public MutableVariant getValue(int accId) throws WireException {
+               try {
+                       AccRecord acc = getAccessor(accId);
+                       Object value = acc.accessor.getValue(acc.binding);
+                       return new MutableVariant(acc.binding, value);
+               } catch (AccessorException e) {
+                       throw new WireException(e);
+               }
+       }
+
+       @Override
+       public ApplyResult apply(int accId, Event[] changeSet, boolean rollback) {
+               ApplyResult result = new ApplyResult();
+               result.rollbackLog = rollback ? new LinkedList<Event>() : null;         
+               try {
+                       AccRecord acc = getAccessor(accId);
+                       List<Event> changeSetList = new ArrayList<Event>( changeSet.length );
+                       for (Event e : changeSet) changeSetList.add( e );
+                       acc.accessor.apply(changeSetList, result.rollbackLog);
+               } catch (WireException e) {
+                       result.error = e;
+               } catch (AccessorException e) {
+                       result.error = new WireException(e);
+               }               
+               return result;
+       }
+
+       @Override
+       public int addListener(int accId, InterestSet interestSet,
+                       ChildReference path) throws WireException {
+               try {
+                       
+                       ClientRecord client = getClient();
+                       AccRecord acc = getAccessor(accId);
+                       final LisRecord lis = new LisRecord();
+                       lis.accId = accId;
+                       lis.is = interestSet;
+                       lis.lisId = client.listIdCounter++;
+                       lis.listener = new Listener() {
+                               @Override
+                               public void onEvents(Collection<Event> events) {
+                                       try {
+                                               // Push event to client
+                                               ClientRecord client = getClient();
+                                               Event[] eventArray = events.toArray( new Event[events.size()] );
+
+//                                             client.clientMethods.onEvents(lis.lisId, eventArray);
+
+                                               // Invoke async.
+                                               OnEventsRequest req = new OnEventsRequest();
+                                               req.arg1 = lis.lisId;
+                                               req.arg2 = eventArray;
+                                               client.onEventsMethod.invoke( req );
+                                       } catch (WireException e) {
+                                       }
+                               }};
+                       acc.accessor.addListener(lis.listener, interestSet, path, null /*TODO Dispatch thread*/);
+                       client.listenerTable.put(lis.lisId, lis);
+                       return lis.lisId;
+               } catch (AccessorException e) {
+                       throw new WireException(e.getClass().getName()+": "+e.getMessage());
+               }               
+       }
+
+       @Override
+       public int removeListener(int lisId) throws WireException {
+               try {
+                       ClientRecord client = getClient();              
+                       LisRecord lis = client.listenerTable.remove(lisId);
+                       AccRecord acc = getAccessor(lis.accId);
+                       acc.accessor.removeListener(lis.listener);
+                       return 0;
+               } catch (AccessorException e) {
+                       throw new WireException( e ); 
+               }               
+       }
+
+       @Override
+       public int size(int accId) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof ArrayAccessor) return ((ArrayAccessor)acc).size();
+                       if (acc instanceof RecordAccessor) return ((RecordAccessor)acc).count();
+                       if (acc instanceof MapAccessor) return ((MapAccessor)acc).size();
+                       if (acc instanceof UnionAccessor) return ((UnionAccessor)acc).count();
+                       throw new WireException("Cannot get size for "+acc.getClass().getName());
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public int clear(int accId) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof ArrayAccessor) {
+                               ArrayAccessor aa = ((ArrayAccessor)acc); 
+                               aa.remove(0, aa.size());
+                               return 0;
+                       }
+                       if (acc instanceof MapAccessor) {
+                               ((MapAccessor)acc).clear();
+                               return 0;
+                       }                       
+                       throw new WireException("Cannot clear "+acc.getClass().getName());
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+/*
+       @Override
+       public void add(int accId, int index, Variant[] values) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof ArrayAccessor) {
+                               ArrayAccessor aa = (ArrayAccessor) acc;
+                               if (values.length==0) return;
+                               
+                               Binding b = values[0].getBinding();
+                               boolean sameBinding = true;
+                               for (Variant v : values) sameBinding &= v.getBinding()==b;
+                               
+                               if (sameBinding) {
+                                       Object[] objs = new Object[ values.length ];
+                                       for (int i=0; i<values.length; i++) {
+                                               objs[i] = values[i].getValue();
+                                       }
+                                       aa.add(index, b, objs);                                 
+                               } else {
+                                       // Add entry one by one
+                                       for (Variant v : values) {
+                                               aa.add(index, v.getClass(), v.getValue());
+                                               index++;
+                                       }
+                               }
+                               
+                       } else          
+                       throw new WireException("Cannot add to "+acc.getClass().getName());
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+*/
+
+       @Override
+       public boolean containsKey(int accId, MutableVariant key) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       return ma.containsKey(key.getBinding(), key.getValue());
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public boolean containsValue(int accId, MutableVariant value) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       return ma.containsValue(value.getBinding(), value.getValue());
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public MutableVariant getFirstKey(int accId) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );
+                       Object key = ma.getFirstKey( keyBinding );
+                       return new MutableVariant(keyBinding, key);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               } catch (BindingConstructionException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public MutableVariant getLastKey(int accId) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       Binding keyBinding = params.bindingScheme.getBinding( ( (MapType) acc.type() ).keyType );
+                       Object key = ma.getLastKey( keyBinding );
+                       return new MutableVariant(keyBinding, key);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               } catch (BindingConstructionException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public MutableVariant getLowerKey(int accId, MutableVariant key) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       Binding keyBinding = key.getBinding();
+                       Object lowerKey = ma.getLowerKey(keyBinding, key.getValue());
+                       if (lowerKey==null) return new MutableVariant(Bindings.VOID, null);
+                       return new MutableVariant(keyBinding, lowerKey);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }       
+       }
+
+       @Override
+       public MutableVariant getFloorKey(int accId, MutableVariant key) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       Binding keyBinding = key.getBinding();
+                       Object floorKey = ma.getFloorKey(keyBinding, key.getValue());
+                       if (floorKey==null) return new MutableVariant(Bindings.VOID, null);;                    
+                       return new MutableVariant(keyBinding, floorKey);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }       
+       }
+
+       @Override
+       public MutableVariant getCeilingKey(int accId, MutableVariant key) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       Binding keyBinding = key.getBinding();
+                       Object ceilingKey = ma.getCeilingKey(keyBinding, key.getValue());
+                       if (ceilingKey==null) return new MutableVariant(Bindings.VOID, null);;                  
+                       return new MutableVariant(keyBinding, ceilingKey);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }       
+       }
+
+       @Override
+       public MutableVariant getHigherKey(int accId, MutableVariant key) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof MapAccessor == false) 
+                               throw new WireException("Not a map");
+                       MapAccessor ma = (MapAccessor) acc;
+                       Binding keyBinding = key.getBinding();
+                       Object higherKey = ma.getHigherKey(keyBinding, key.getValue());
+                       if (higherKey==null) return new MutableVariant(Bindings.VOID, null);;                   
+                       return new MutableVariant(keyBinding, higherKey);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }       
+       }
+
+       @Override
+       public boolean hasValue(int accId) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof OptionalAccessor == false) 
+                               throw new WireException("Not an option");
+                       OptionalAccessor ma = (OptionalAccessor) acc;
+                       return ma.hasValue();
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }       
+       }
+
+       @Override
+       public int getTag(int accId) throws WireException {
+               try {
+                       Accessor acc = getAccessor(accId).accessor;
+                       if (acc instanceof UnionAccessor == false) 
+                               throw new WireException("Not an union");
+                       UnionAccessor ma = (UnionAccessor) acc;
+                       return ma.getTag();
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }       
+       }
+       
+       @Override
+       public int addAll(int accId, int index, MutableVariant array) throws WireException {
+               try {
+                       ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;
+                       Datatype type = array.type();
+                       if (type instanceof ArrayType == false) {
+                               throw new WireException("addAll() array expepected, got "+array.type());
+                       }
+                       
+                       Binding componentBinding = ( (ArrayBinding) array.getBinding() ).getComponentBinding();
+                       ArrayBinding binding = new ObjectArrayBinding((ArrayType) type, componentBinding);
+                       Object values[] = (Object[]) array.getValue(binding);
+                       if (index==-1) {
+                               int size = acc.size();
+                               acc.addAll(binding, values);
+                               return size;
+                       } else {
+                               acc.addAll(index, binding, values);
+                               return index;
+                       }
+                                               
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               } catch (AdaptException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public int add(int accId, int index, MutableVariant value) throws WireException {
+               try {
+                       ArrayAccessor acc = (ArrayAccessor) getAccessor(accId).accessor;
+
+                       if (index==-1) {
+                               int size = acc.size();
+                               acc.add(value.getBinding(), value.getValue());
+                               return size;
+                       } else {
+                               acc.add(index, value.getBinding(), value.getValue());
+                               return index;
+                       }
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public MutableVariant getArrayElement(int accId, int index) throws WireException {
+               try {
+                       AccRecord ar = getAccessor(accId);
+                       ArrayAccessor acc = (ArrayAccessor) ar.accessor;
+                       Binding valueBinding = ((ArrayBinding)ar.binding).getComponentBinding();
+                       Object value = acc.get(index, valueBinding );
+                       MutableVariant v = new MutableVariant(valueBinding, value);
+                       return v;
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+       
+       @Override
+       public MutableVariant getMapValue(int accId, MutableVariant key) throws WireException {
+               try {
+                       AccRecord ar = getAccessor(accId);
+                       MapAccessor acc = (MapAccessor) ar.accessor;
+                       MapBinding mapBinding = (MapBinding)ar.binding;
+                       Object value = acc.get(key.getBinding(), key.getValue(), mapBinding.getValueBinding());
+                       if (value == null) return new MutableVariant();
+                       return new MutableVariant(mapBinding.getValueBinding(), value);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public MutableVariant getMapValues(int accId) throws WireException {
+               try {
+                       AccRecord ar = getAccessor(accId);
+                       MapAccessor acc = (MapAccessor) ar.accessor;
+                       MapBinding mapBinding = (MapBinding)ar.binding;
+                       Binding valueBinding = mapBinding.getValueBinding();
+                       Object[] values = acc.getValues( valueBinding );
+                       ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(valueBinding.type()), valueBinding);
+                       return new MutableVariant(arrayBinding, values);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+
+       @Override
+       public MutableVariant getMapKeys(int accId) throws WireException {
+               try {
+                       AccRecord ar = getAccessor(accId);
+                       MapAccessor acc = (MapAccessor) ar.accessor;
+                       MapBinding mapBinding = (MapBinding)ar.binding;
+                       Binding keyBinding = mapBinding.getKeyBinding();
+                       Object[] keys = acc.getKeys( keyBinding );
+                       ArrayBinding arrayBinding = new ObjectArrayBinding(new ArrayType(keyBinding.type()), keyBinding);
+                       return new MutableVariant(arrayBinding, keys);
+               } catch (AccessorException e) {
+                       throw new WireException( e );
+               }
+       }
+       
+}
+