package org.swellrt.beta.model.remote; import org.swellrt.beta.common.SException; import org.swellrt.beta.model.SEvent; import org.swellrt.beta.model.SList; import org.swellrt.beta.model.SNode; import org.swellrt.beta.model.SPrimitive; import org.swellrt.beta.model.SUtils; import org.swellrt.beta.model.js.HasJsProxy; import org.swellrt.beta.model.js.Proxy; import org.swellrt.beta.model.js.SListProxyHandler; import org.waveprotocol.wave.model.adt.ObservableElementList; import jsinterop.annotations.JsOptional; public class SListRemote extends SNodeRemoteContainer implements SList<SNodeRemote>, HasJsProxy, ObservableElementList.Listener<SNodeRemote> { public static SListRemote create(SObjectRemote object, SubstrateId substrateId, ObservableElementList<SNodeRemote, SNodeRemote> list) { return new SListRemote(object, substrateId, list); } private final ObservableElementList<SNodeRemote, SNodeRemote> list; private Proxy proxy; protected SListRemote(SObjectRemote object, SubstrateId substrateId, ObservableElementList<SNodeRemote, SNodeRemote> list) { super(substrateId, object); this.list = list; this.list.addListener(this); } @Override public Object get(int index) throws SException { SNode node = getNode(index); getObject().checkReadable(node); if (node == null) return null; if (node instanceof SPrimitive) return ((SPrimitive) node).get(); return node; } @Override public SNode getNode(int index) throws SException { try { return list.get(index); } catch (IndexOutOfBoundsException e) { throw new SException(SException.DATA_ERROR, e); } catch (Exception e) { throw new SException(SException.DATA_ERROR, e); } } @Override public SList<SNodeRemote> add(SNode value) throws SException { check(); SNodeRemote remoteValue = getObject().transformToRemote(value, this, false); this.list.add(remoteValue); return this; } @Override public SList<SNodeRemote> add(Object value) throws SException { SNode node = SUtils.castToSNode(value); return add(node); } @Override public SList<SNodeRemote> add(SNode value, int index) throws SException { check(); if (index >= 0 && index <= this.list.size()) { SNodeRemote remoteValue = getObject().transformToRemote(value, this, false); this.list.add(index, remoteValue); } else { throw new SException(SException.OUT_OF_BOUNDS_INDEX); } return this; } @Override public SList<SNodeRemote> add(Object value, @JsOptional Object index) throws SException { SNode node = SUtils.castToSNode(value); if (index != null) { return add(node, (int) index); } else { return add(node); } } @Override public SList<SNodeRemote> remove(int index) throws SException { check(); SNodeRemote node = (SNodeRemote) getNode(index); getObject().checkWritable(node); if (node instanceof SNodeRemoteContainer) { SNodeRemoteContainer nrc = (SNodeRemoteContainer) node; nrc.deattach(); } this.list.remove(node); getObject().deleteNode(node); return this; } @Override public void clear() throws SException { this.list.clear(); } @Override public boolean isEmpty() { return this.list.size() == 0; } @Override public int size() { return this.list.size(); } @Override public Object asNative() { if (proxy == null) proxy = new Proxy(this, new SListProxyHandler()); return proxy; } // // Node remote container // @Override protected void clearCache() { // TODO Auto-generated method stub } /** * Perform a sanity check. Raise an exception if this node * can't perform the operation or the container object is * in a bad state. * <p> * Don't use it for read operations to avoid client frameworks * (like angular2) receiving exceptions in templates. */ protected void check() throws SException { if (this.getParent() == null) throw new SException(SException.NOT_ATTACHED_NODE); getObject().check(); } // // Js Proxies // @Override public void setJsProxy(Proxy proxy) { this.proxy = proxy; } @Override public Proxy getJsProxy() { return this.proxy; } // // Event handling // @Override public void onValueAdded(SNodeRemote entry) { try { check(); // Ignore events if state is inconsistent SEvent e = new SEvent(SEvent.ADDED_VALUE, this, ""+list.indexOf(entry), entry); triggerEvent(e); } catch (SException e) { // Swallow it } } @Override public void onValueRemoved(SNodeRemote entry) { try { check(); // Ignore events if state is inconsistent SEvent e = new SEvent(SEvent.REMOVED_VALUE, this, ""+list.indexOf(entry), entry); triggerEvent(e); } catch (SException e) { // Swallow it } } @Override public Iterable<SNodeRemote> values() { return list.getValues(); } @Override public String toString() { return "SMListRemote ["+getSubstrateId()+"]"; } @Override public int indexOf(SNodeRemote node) throws SException { return this.list.indexOf((SNodeRemote) node); } }