/* * Copyright (c) 2011 PonySDK * Owners: * Luciano Broussal <luciano.broussal AT gmail.com> * Mathieu Barbier <mathieu.barbier AT gmail.com> * Nicolas Ciaravola <nicolas.ciaravola.pro AT gmail.com> * * WebSite: * http://code.google.com/p/pony-sdk/ * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.ponysdk.core.ui.basic; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import javax.json.JsonObject; import com.ponysdk.core.model.ClientToServerModel; import com.ponysdk.core.model.HandlerModel; import com.ponysdk.core.model.ServerToClientModel; import com.ponysdk.core.model.WidgetType; import com.ponysdk.core.server.application.UIContext; import com.ponysdk.core.server.stm.Txn; import com.ponysdk.core.ui.basic.event.PTerminalEvent; import com.ponysdk.core.ui.model.ServerBinaryModel; import com.ponysdk.core.writer.ModelWriter; import com.ponysdk.core.writer.ModelWriterCallback; /** * The superclass for all PonySDK objects. */ public abstract class PObject { protected final int ID = UIContext.get().nextID(); protected PWindow window; protected PFrame frame; protected Set<InitializeListener> initializeListeners; protected Set<DestroyListener> destroyListeners; protected Object data; protected LinkedHashMap<Integer, Runnable> stackedInstructions; private String nativeBindingFunction; private PTerminalEvent.Handler terminalHandler; protected boolean initialized = false; protected boolean destroy = false; protected final AtomicInteger atomicKey = new AtomicInteger(ServerToClientModel.DESTROY.getValue()); PObject() { } protected abstract WidgetType getWidgetType(); protected void attach(final PFrame frame) { this.frame = frame; } protected boolean attach(final PWindow window) { if (this.window == null && window != null) { this.window = window; init(); return true; } else if (this.window != window) { throw new IllegalAccessError( "Widget already attached to an other window, current window : #" + this.window + ", new window : #" + window); } return false; } void init() { if (initialized) return; if (window.isOpened()) { applyInit(); } else { window.addOpenHandler(window -> applyInit()); } } protected void applyInit() { final ModelWriter writer = Txn.getWriter(); writer.beginObject(); if (window != PWindow.getMain()) writer.write(ServerToClientModel.WINDOW_ID, window.getID()); if (frame != null) writer.write(ServerToClientModel.FRAME_ID, frame.getID()); writer.write(ServerToClientModel.TYPE_CREATE, ID); writer.write(ServerToClientModel.WIDGET_TYPE, getWidgetType().getValue()); enrichOnInit(writer); writer.endObject(); UIContext.get().registerObject(this); init0(); if (stackedInstructions != null) stackedInstructions.values().forEach(Runnable::run); if (initializeListeners != null) initializeListeners.forEach(listener -> listener.onInitialize(this)); initialized = true; } protected void enrichOnInit(final ModelWriter writer) { } protected void init0() { } public PWindow getWindow() { return window; } public PFrame getFrame() { return frame; } public final int getID() { return ID; } /** * Bind to a Terminal function, usefull to link the objectID and the widget * reference * <p> * <h2>Example :</h2> * <p> * * <pre> * --- Java --- * * bindTerminalFunction("myFunction") * </pre> * <p> * * <pre> * --- JavaScript --- * * myFunction(id, object) { * .... * .... * } * </pre> */ public void bindTerminalFunction(final String functionName) { if (nativeBindingFunction != null) throw new IllegalAccessError("Object already bind to native function: " + nativeBindingFunction); nativeBindingFunction = functionName; saveUpdate(writer -> writer.write(ServerToClientModel.BIND, functionName)); } public void sendToNative(final JsonObject data) { if (destroy) return; if (nativeBindingFunction == null) throw new IllegalAccessError("Object not bind to a native function"); saveUpdate(writer -> writer.write(ServerToClientModel.NATIVE, data)); } public void setTerminalHandler(final PTerminalEvent.Handler terminalHandler) { this.terminalHandler = terminalHandler; } /** * JSON received from the Terminal using pony.sendDataToServer(objectID, JSON) */ public void onClientData(final JsonObject event) { if (destroy) return; if (terminalHandler != null) { final String nativeKey = ClientToServerModel.NATIVE.toStringValue(); if (event.containsKey(nativeKey)) { terminalHandler.onTerminalEvent(new PTerminalEvent(this, event.getJsonObject(nativeKey))); } } } protected LinkedHashMap<Integer, Runnable> safeStackedInstructions() { if (stackedInstructions == null) stackedInstructions = new LinkedHashMap<>(); return stackedInstructions; } protected void saveUpdate(final ModelWriterCallback callback) { saveUpdate(atomicKey.incrementAndGet(), callback); } protected void saveUpdate(final ServerToClientModel serverToClientModel, final Object value) { saveUpdate(serverToClientModel.getValue(), writer -> writer.write(serverToClientModel, value)); } private void saveUpdate(final int atomicKey, final ModelWriterCallback callback) { if (destroy) return; if (initialized) writeUpdate(callback); else safeStackedInstructions().put(atomicKey, () -> writeUpdate(callback)); } void writeUpdate(final ModelWriterCallback callback) { if (destroy) return; final ModelWriter writer = Txn.getWriter(); writer.beginObject(); if (!PWindow.isMain(window)) writer.write(ServerToClientModel.WINDOW_ID, window.getID()); if (frame != null) writer.write(ServerToClientModel.FRAME_ID, frame.getID()); writer.write(ServerToClientModel.TYPE_UPDATE, ID); callback.doWrite(writer); writer.endObject(); } protected void saveAdd(final int objectID, final int parentObjectID) { saveAdd(objectID, parentObjectID, (ServerBinaryModel) null); } protected void saveAdd(final int objectID, final int parentObjectID, final ServerBinaryModel... binaryModels) { if (destroy) return; final ModelWriterCallback callback = writer -> { writer.write(ServerToClientModel.TYPE_ADD, objectID); writer.write(ServerToClientModel.PARENT_OBJECT_ID, parentObjectID); if (binaryModels != null) { for (final ServerBinaryModel binaryModel : binaryModels) { if (binaryModel != null) writer.write(binaryModel.getKey(), binaryModel.getValue()); } } }; if (initialized) writeAdd(callback); else safeStackedInstructions().put(atomicKey.incrementAndGet(), () -> writeAdd(callback)); } private void writeAdd(final ModelWriterCallback callback) { if (destroy) return; final ModelWriter writer = Txn.getWriter(); writer.beginObject(); if (!PWindow.isMain(window)) writer.write(ServerToClientModel.WINDOW_ID, window.getID()); if (frame != null) writer.write(ServerToClientModel.FRAME_ID, frame.getID()); callback.doWrite(writer); writer.endObject(); } protected void saveAddHandler(final HandlerModel type) { if (destroy) return; final ModelWriterCallback callback = writer -> writer.write(ServerToClientModel.HANDLER_TYPE, type.getValue()); if (initialized) writeAddHandler(callback); else safeStackedInstructions().put(atomicKey.incrementAndGet(), () -> writeAddHandler(callback)); } void writeAddHandler(final ModelWriterCallback callback) { if (destroy) return; final ModelWriter writer = Txn.getWriter(); writer.beginObject(); if (!PWindow.isMain(window)) writer.write(ServerToClientModel.WINDOW_ID, window.getID()); if (frame != null) writer.write(ServerToClientModel.FRAME_ID, frame.getID()); writer.write(ServerToClientModel.TYPE_ADD_HANDLER, ID); callback.doWrite(writer); writer.endObject(); } protected void saveRemoveHandler(final HandlerModel type) { if (destroy) return; final ModelWriterCallback callback = writer -> { writer.write(ServerToClientModel.TYPE_REMOVE_HANDLER, ID); writer.write(ServerToClientModel.HANDLER_TYPE, type.getValue()); }; if (initialized) writeRemoveHandler(callback); else safeStackedInstructions().put(atomicKey.incrementAndGet(), () -> writeRemoveHandler(callback)); } private void writeRemoveHandler(final ModelWriterCallback callback) { if (destroy) return; final ModelWriter writer = Txn.getWriter(); writer.beginObject(); if (!PWindow.isMain(window)) writer.write(ServerToClientModel.WINDOW_ID, window.getID()); if (frame != null) writer.write(ServerToClientModel.FRAME_ID, frame.getID()); callback.doWrite(writer); writer.endObject(); } void saveRemove(final int objectID, final int parentObjectID) { if (destroy) return; final ModelWriterCallback callback = writer -> { writer.write(ServerToClientModel.TYPE_REMOVE, objectID); writer.write(ServerToClientModel.PARENT_OBJECT_ID, parentObjectID); }; if (initialized) writeRemove(callback); else safeStackedInstructions().put(atomicKey.incrementAndGet(), () -> writeRemove(callback)); } private void writeRemove(final ModelWriterCallback callback) { if (destroy) return; final ModelWriter writer = Txn.getWriter(); writer.beginObject(); if (!PWindow.isMain(window)) writer.write(ServerToClientModel.WINDOW_ID, window.getID()); if (frame != null) writer.write(ServerToClientModel.FRAME_ID, frame.getID()); callback.doWrite(writer); writer.endObject(); } public void addInitializeListener(final InitializeListener listener) { if (this.initializeListeners == null) initializeListeners = new LinkedHashSet<>(); this.initializeListeners.add(listener); } public void addDestroyListener(final DestroyListener listener) { if (this.destroyListeners == null) destroyListeners = new LinkedHashSet<>(); this.destroyListeners.add(listener); } public void onDestroy() { destroy = true; terminalHandler = null; initializeListeners = null; if (this.destroyListeners != null) this.destroyListeners.forEach(listener -> listener.onDestroy(this)); this.destroyListeners = null; window = null; } public void setData(final Object data) { this.data = data; } @Override public int hashCode() { return ID; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final PObject other = (PObject) obj; return ID == other.ID; } @Override public String toString() { return getClass().getSimpleName() + "#" + ID; } @FunctionalInterface public interface InitializeListener { void onInitialize(PObject object); } @FunctionalInterface public interface DestroyListener { void onDestroy(PObject object); } public boolean isInitialized() { return initialized; } }