jaroslav@761: /** jaroslav@761: * Back 2 Browser Bytecode Translator jaroslav@761: * Copyright (C) 2012 Jaroslav Tulach jaroslav@761: * jaroslav@761: * This program is free software: you can redistribute it and/or modify jaroslav@761: * it under the terms of the GNU General Public License as published by jaroslav@761: * the Free Software Foundation, version 2 of the License. jaroslav@761: * jaroslav@761: * This program is distributed in the hope that it will be useful, jaroslav@761: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@761: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@761: * GNU General Public License for more details. jaroslav@761: * jaroslav@761: * You should have received a copy of the GNU General Public License jaroslav@761: * along with this program. Look for COPYING file in the top folder. jaroslav@761: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@761: */ jaroslav@761: package org.apidesign.bck2brwsr.htmlpage; jaroslav@761: jaroslav@976: import java.lang.reflect.InvocationTargetException; jaroslav@976: import java.lang.reflect.Method; jaroslav@761: import java.util.ArrayList; jaroslav@761: import java.util.Collection; jaroslav@921: import java.util.Iterator; jaroslav@976: import java.util.logging.Level; jaroslav@976: import java.util.logging.Logger; jaroslav@887: import org.apidesign.bck2brwsr.core.JavaScriptOnly; jaroslav@761: jaroslav@761: /** jaroslav@761: * jaroslav@761: * @author Jaroslav Tulach jaroslav@761: */ jaroslav@761: public final class KOList extends ArrayList { jaroslav@761: private final String name; jaroslav@761: private final String[] deps; jaroslav@761: private Knockout model; jaroslav@950: private Runnable onchange; jaroslav@761: jaroslav@761: public KOList(String name, String... deps) { jaroslav@761: this.name = name; jaroslav@761: this.deps = deps; jaroslav@761: } jaroslav@761: jaroslav@761: public void assign(Knockout model) { jaroslav@955: if (this.model != model) { jaroslav@955: this.model = model; jaroslav@955: notifyChange(); jaroslav@955: } jaroslav@761: } jaroslav@950: jaroslav@950: public KOList onChange(Runnable r) { jaroslav@950: if (this.onchange != null) { jaroslav@950: throw new IllegalStateException(); jaroslav@950: } jaroslav@950: this.onchange = r; jaroslav@950: return this; jaroslav@950: } jaroslav@761: jaroslav@761: @Override jaroslav@761: public boolean add(T e) { jaroslav@761: boolean ret = super.add(e); jaroslav@761: notifyChange(); jaroslav@761: return ret; jaroslav@761: } jaroslav@761: jaroslav@761: @Override jaroslav@928: public boolean addAll(Collection c) { jaroslav@928: boolean ret = super.addAll(c); jaroslav@928: notifyChange(); jaroslav@928: return ret; jaroslav@928: } jaroslav@928: jaroslav@928: @Override jaroslav@928: public boolean addAll(int index, Collection c) { jaroslav@928: boolean ret = super.addAll(index, c); jaroslav@928: notifyChange(); jaroslav@928: return ret; jaroslav@928: } jaroslav@928: jaroslav@928: @Override jaroslav@761: public boolean remove(Object o) { jaroslav@761: boolean ret = super.remove(o); jaroslav@761: notifyChange(); jaroslav@761: return ret; jaroslav@761: } jaroslav@761: jaroslav@761: @Override jaroslav@761: public void clear() { jaroslav@761: super.clear(); jaroslav@761: notifyChange(); jaroslav@761: } jaroslav@761: jaroslav@761: @Override jaroslav@761: public boolean removeAll(Collection c) { jaroslav@761: boolean ret = super.removeAll(c); jaroslav@761: notifyChange(); jaroslav@761: return ret; jaroslav@761: } jaroslav@761: jaroslav@761: @Override jaroslav@761: public boolean retainAll(Collection c) { jaroslav@761: boolean ret = super.retainAll(c); jaroslav@761: notifyChange(); jaroslav@761: return ret; jaroslav@761: } jaroslav@761: jaroslav@761: @Override jaroslav@761: public T set(int index, T element) { jaroslav@761: T ret = super.set(index, element); jaroslav@761: notifyChange(); jaroslav@761: return ret; jaroslav@761: } jaroslav@761: jaroslav@761: @Override jaroslav@761: public void add(int index, T element) { jaroslav@761: super.add(index, element); jaroslav@761: notifyChange(); jaroslav@761: } jaroslav@761: jaroslav@761: @Override jaroslav@761: public T remove(int index) { jaroslav@761: T ret = super.remove(index); jaroslav@761: notifyChange(); jaroslav@761: return ret; jaroslav@761: } jaroslav@921: jaroslav@921: @Override jaroslav@921: public String toString() { jaroslav@921: Iterator it = iterator(); jaroslav@921: if (!it.hasNext()) { jaroslav@921: return "[]"; jaroslav@921: } jaroslav@921: String sep = ""; jaroslav@921: StringBuilder sb = new StringBuilder(); jaroslav@921: sb.append('['); jaroslav@921: while (it.hasNext()) { jaroslav@921: T t = it.next(); jaroslav@921: sb.append(sep); jaroslav@921: sb.append(ConvertTypes.toJSON(t)); jaroslav@921: sep = ","; jaroslav@921: } jaroslav@921: sb.append(']'); jaroslav@921: return sb.toString(); jaroslav@921: } jaroslav@887: jaroslav@887: jaroslav@887: @JavaScriptOnly(name = "koArray", value = "function() { return this.toArray___3Ljava_lang_Object_2(); }") jaroslav@887: private static native int koArray(); jaroslav@971: jaroslav@971: public Object koData() { jaroslav@976: Object[] arr = toArray(); jaroslav@976: Method toKO = null; jaroslav@976: for (int i = 0; i < arr.length; i++) { jaroslav@976: if (arr[i] == null) { jaroslav@976: continue; jaroslav@976: } jaroslav@976: if (toKO == null || toKO.getDeclaringClass() != arr[i].getClass()) { jaroslav@976: try { jaroslav@976: toKO = arr[i].getClass().getDeclaredMethod("koData"); jaroslav@976: toKO.setAccessible(true); jaroslav@976: } catch (NoSuchMethodException ex) { jaroslav@976: LOG.log(Level.FINE, "No koData conversion for " + arr[i], ex); jaroslav@976: toKO = null; jaroslav@976: } jaroslav@976: } jaroslav@976: if (toKO != null) { jaroslav@976: try { jaroslav@979: Object prev = arr[i]; jaroslav@976: arr[i] = toKO.invoke(arr[i]); jaroslav@993: LOG.log(Level.FINER, "{0}th element {1} replaced by {2}", new Object[]{i, prev, arr[i]}); jaroslav@976: } catch (IllegalAccessException | InvocationTargetException ex) { jaroslav@976: LOG.log(Level.SEVERE, "Problems invoking koData on " + arr[i], ex); jaroslav@976: } jaroslav@976: } jaroslav@976: } jaroslav@987: return Knockout.toArray(arr); jaroslav@971: } jaroslav@976: private static final Logger LOG = Logger.getLogger(KOList.class.getName()); jaroslav@761: jaroslav@761: private void notifyChange() { jaroslav@761: Knockout m = model; jaroslav@950: if (m != null) { jaroslav@950: m.valueHasMutated(name); jaroslav@950: for (String dependant : deps) { jaroslav@950: m.valueHasMutated(dependant); jaroslav@950: } jaroslav@761: } jaroslav@950: Runnable r = onchange; jaroslav@950: if (r != null) { jaroslav@950: r.run(); jaroslav@761: } jaroslav@761: } jaroslav@930: jaroslav@930: @Override jaroslav@930: public KOList clone() { jaroslav@955: KOList ko = (KOList)super.clone(); jaroslav@955: ko.model = null; jaroslav@955: return ko; jaroslav@930: } jaroslav@761: jaroslav@761: }