package simulator.interfaces.html; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import models.algebra.Constant; import models.algebra.Expression; import models.algebra.Term; import models.dataConstraintModel.JsonTerm; import models.dataConstraintModel.ListTerm; import models.dataConstraintModel.MapTerm; import models.dataConstraintModel.ResourcePath; import models.dataFlowModel.DataTransferChannel; import simulator.Event; import simulator.Resource; import simulator.ResourceIdentifier; import simulator.Simulator; import simulator.SystemState; import simulator.interfaces.INativeReceiver; public class HtmlPresenter implements INativeReceiver{ public final String screenUpdateChannelName = "ScreenUpdate"; public final String setVisibleChannelName = "SetVisible"; public final String setTextChannelName = "SetText"; public final String mouseEventChannelName = "MouseEvent"; public final String textEventChannelName = "TextEvent"; public final String changeScreenChannelName = "ChangeScreen"; protected Simulator simulator; protected DataTransferChannel screenUpdateChannel; protected DataTransferChannel setVisibleChannel; protected DataTransferChannel mouseEventChannel; protected DataTransferChannel textEventChannel; protected DataTransferChannel setTextChannel; protected DataTransferChannel changeScreenChannel; protected Map<DataTransferChannel, Map<String, Resource>> channelAndResourcesForReceiving = new HashMap<>(); protected Map<String, HtmlElement> elements; protected IWebSocketMessageSender ws; public HtmlPresenter(Simulator simulator, IWebSocketMessageSender ws, Map<String, HtmlElement> elems) { this.simulator = simulator; this.ws = ws; screenUpdateChannel = (DataTransferChannel) simulator.getModel().getChannel(screenUpdateChannelName); setVisibleChannel = (DataTransferChannel) simulator.getModel().getChannel(setVisibleChannelName); mouseEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(mouseEventChannelName); setTextChannel = (DataTransferChannel) simulator.getModel().getChannel(setTextChannelName); textEventChannel = (DataTransferChannel) simulator.getModel().getInputChannel(textEventChannelName); changeScreenChannel = (DataTransferChannel) simulator.getModel().getInputChannel(changeScreenChannelName); simulator.addNativeReceiver(this, screenUpdateChannel); elements = elems; initScreen(); } private void initScreen() { Expression initScExp = simulator.getCurState().getResource("screen").getState().getValue(); if (initScExp instanceof JsonTerm) { JsonTerm sendData = new JsonTerm(); sendData.addMember("\"method\"", new Constant("\"updateHtml\"")); JsonTerm sendElements = new JsonTerm(); ListTerm addElements = new ListTerm(); ListTerm delElements = new ListTerm(); JsonTerm nextSc = (JsonTerm) initScExp; Expression newWidgets = nextSc.get("widgets"); if (newWidgets instanceof MapTerm) { Set<String> newWidSet = new HashSet<>(((MapTerm) newWidgets).keySet()); if (!newWidSet.isEmpty()) { addNewWidgets(newWidSet, simulator.getCurState().getResource("screen"), newWidgets, addElements); } } sendElements.addMember("\"add\"", addElements); sendElements.addMember("\"delete\"", delElements); sendData.addMember("\"data\"", sendElements); ws.send(sendData.toString()); } } @Override public void onReceiveFromModel(Event event, SystemState nextSystemState) { //画面遷移するときに呼ばれそう Expression message = event.getMessage(); if (message instanceof Term && ((Term) message).getChildren().size() >= 2) { Expression curScExp = ((Term) message).getChild(0); Expression nextScExp = ((Term) message).getChild(1); if (curScExp instanceof JsonTerm && nextScExp instanceof JsonTerm) { JsonTerm sendData = new JsonTerm(); sendData.addMember("\"method\"", new Constant("\"updateHtml\"")); JsonTerm sendElements = new JsonTerm(); ListTerm addElements = new ListTerm(); ListTerm delElements = new ListTerm(); JsonTerm curSc = (JsonTerm) curScExp; JsonTerm nextSc = (JsonTerm) nextScExp; Expression oldWidgets = curSc.get("widgets"); Expression newWidgets = nextSc.get("widgets"); if (oldWidgets instanceof MapTerm && newWidgets instanceof MapTerm) { Set<String> oldWidSet = new HashSet<>(((MapTerm) oldWidgets).keySet()); Set<String> newWidSet = new HashSet<>(((MapTerm) newWidgets).keySet()); oldWidSet.removeAll(((MapTerm) newWidgets).keySet()); newWidSet.removeAll(((MapTerm)oldWidgets).keySet()); if(!oldWidSet.isEmpty()) { deleteOldWidgets(oldWidSet, delElements); } if (!newWidSet.isEmpty()) { addNewWidgets(newWidSet, nextSystemState, event.getInputResource().getResourceIdentifier(), newWidgets, addElements); } } sendElements.addMember("\"add\"", addElements); sendElements.addMember("\"delete\"", delElements); sendData.addMember("\"data\"", sendElements); ws.send(sendData.toString()); } } } private void addNewWidgets(Set<String> newWidSet, Resource screenResource, Expression newWidgets, ListTerm addElements) { Resource widgetsResource = screenResource.getChildrenMap().get("widgets"); for(String newWid: newWidSet) { Expression value = ((MapTerm) newWidgets).get(newWid); if(value instanceof JsonTerm) { JsonTerm widget = (JsonTerm) value; Resource widgetResource = widgetsResource.getChildrenMap().get(newWid); Expression type = widget.get("type"); HtmlScreenSender screenSender = new HtmlScreenSender(simulator, changeScreenChannel, changeScreenChannel.getOutputResources().iterator().next(), screenResource); switch(type.toString()) { case "\"button\"": addButton(widget, newWid, addElements, widgetResource, screenSender); break; case "\"label\"": addLabel(widget, newWid, addElements, widgetResource); break; case "\"textInput\"": addTextInput(widget, newWid, addElements, widgetResource); break; } } } } private void addNewWidgets(Set<String> newWidSet, SystemState nextSystemState, ResourceIdentifier ri, Expression newWidgets, ListTerm addElements) { Resource screenResource = nextSystemState.getResource(ri); addNewWidgets(newWidSet, screenResource, newWidgets, addElements); } private void addButton(JsonTerm widget, String wid, ListTerm addElements, Resource widgetResource, HtmlScreenSender screenSender) { String text = widget.get("text").toString(); HtmlElement button = new HtmlElement("button", wid, text); Set<String> keySet = widget.keySet(); button.setStyle("\"position\"", "\"absolute\""); if(keySet.contains("x")) { button.setStyle("\"left\"", "\"" + widget.get("x") + "px\""); } if(keySet.contains("y")) { button.setStyle("\"top\"", "\"" + widget.get("y") + "px\""); } if(keySet.contains("width")) { button.setStyle("\"width\"", "\"" + widget.get("width") + "px\""); } if(keySet.contains("height")) { button.setStyle("\"height\"", "\"" + widget.get("height") + "px\""); } //ボタンが押されたり離されたりした時を検知 ResourcePath resPath = mouseEventChannel.getOutputResources().iterator().next(); button.setRestEventListener(new HtmlElementMouseSender(simulator, mouseEventChannel, resPath, widgetResource, screenSender)); //ボタンが透明になったことを通知 HtmlElementVisibilityReceiver nativeVisibilityReceiver = new HtmlElementVisibilityReceiver(button, ws); simulator.addNativeReceiver(nativeVisibilityReceiver, setVisibleChannel, widgetResource); Map<String, Resource> resources = channelAndResourcesForReceiving.get(setVisibleChannel); if (resources == null) { resources = new HashMap<>(); channelAndResourcesForReceiving.put(setVisibleChannel, resources); } resources.put(wid, widgetResource); //ボタンの文字が変わったことを通知 HtmlElementTextReceiver nativeTextReceiver = new HtmlElementTextReceiver(button, ws); simulator.addNativeReceiver(nativeTextReceiver, setTextChannel, widgetResource); resources = channelAndResourcesForReceiving.get(setTextChannel); if (resources == null) { resources = new HashMap<>(); channelAndResourcesForReceiving.put(setTextChannel, resources); } resources.put(wid, widgetResource); elements.put(wid, button); addElements.append(button.toMap()); } private void addLabel(JsonTerm widget, String wid, ListTerm addElements, Resource widgetResource) { String text = widget.get("text").toString(); HtmlElement label = new HtmlElement("label", wid, text); Set<String> keySet = widget.keySet(); label.setStyle("\"position\"", "\"absolute\""); if(keySet.contains("x")) { label.setStyle("\"left\"", "\"" + widget.get("x") + "px\""); } if(keySet.contains("y")) { label.setStyle("\"top\"", "\"" + widget.get("y") + "px\""); } if(keySet.contains("width")) { label.setStyle("\"width\"", "\"" + widget.get("width") + "px\""); } if(keySet.contains("height")) { label.setStyle("\"height\"", "\"" + widget.get("height") + "px\""); } //ボタンが透明になったことを通知 HtmlElementVisibilityReceiver nativeVisibilityReceiver = new HtmlElementVisibilityReceiver(label, ws); simulator.addNativeReceiver(nativeVisibilityReceiver, setVisibleChannel, widgetResource); Map<String, Resource> resources = channelAndResourcesForReceiving.get(setVisibleChannel); if (resources == null) { resources = new HashMap<>(); channelAndResourcesForReceiving.put(setVisibleChannel, resources); } resources.put(wid, widgetResource); //ボタンの文字が変わったことを通知 HtmlElementTextReceiver nativeTextReceiver = new HtmlElementTextReceiver(label, ws); simulator.addNativeReceiver(nativeTextReceiver, setTextChannel, widgetResource); resources = channelAndResourcesForReceiving.get(setTextChannel); if (resources == null) { resources = new HashMap<>(); channelAndResourcesForReceiving.put(setTextChannel, resources); } resources.put(wid, widgetResource); elements.put(wid, label); addElements.append(label.toMap()); } private void addTextInput(JsonTerm widget, String wid, ListTerm addElements, Resource widgetResource) { String text = widget.get("text").toString(); HtmlElement textInput = new HtmlElement("textInput", wid, text); Set<String> keySet = widget.keySet(); textInput.setStyle("\"position\"", "\"absolute\""); if(keySet.contains("x")) { textInput.setStyle("\"left\"", "\"" + widget.get("x") + "px\""); } if(keySet.contains("y")) { textInput.setStyle("\"top\"", "\"" + widget.get("y") + "px\""); } if(keySet.contains("width")) { textInput.setStyle("\"width\"", "\"" + widget.get("width") + "px\""); } if(keySet.contains("height")) { textInput.setStyle("\"height\"", "\"" + widget.get("height") + "px\""); } //文字の変更を検知 ResourcePath resPath = textEventChannel.getOutputResources().iterator().next(); textInput.setRestEventListener(new HtmlElementTextSender(simulator, textEventChannel, resPath, widgetResource)); //ボタンが透明になったことを通知 HtmlElementVisibilityReceiver nativeVisibilityReceiver = new HtmlElementVisibilityReceiver(textInput, ws); simulator.addNativeReceiver(nativeVisibilityReceiver, setVisibleChannel, widgetResource); Map<String, Resource> resources = channelAndResourcesForReceiving.get(setVisibleChannel); if (resources == null) { resources = new HashMap<>(); channelAndResourcesForReceiving.put(setVisibleChannel, resources); } resources.put(wid, widgetResource); //ボタンの文字が変わったことを通知 HtmlElementTextReceiver nativeTextReceiver = new HtmlElementTextReceiver(textInput, ws); simulator.addNativeReceiver(nativeTextReceiver, setTextChannel, widgetResource); resources = channelAndResourcesForReceiving.get(setTextChannel); if (resources == null) { resources = new HashMap<>(); channelAndResourcesForReceiving.put(setTextChannel, resources); } resources.put(wid, widgetResource); elements.put(wid, textInput); addElements.append(textInput.toMap()); } private void deleteOldWidgets(Set<String> oldWidSet, ListTerm delElements) { for (String oldWid : oldWidSet) { elements.remove(oldWid); //TODO // del widgets delElements.append(new Constant(oldWid)); } for (DataTransferChannel channel : channelAndResourcesForReceiving.keySet()) { Map<String, Resource> widToResource = channelAndResourcesForReceiving.get(channel); for (String oldWid: oldWidSet) { Resource resource = widToResource.remove(oldWid); if (resource != null) { simulator.removeNativeReceiver(channel, resource); } } } } }