001package jmri.server.json.layoutblock;
002
003import static jmri.server.json.JSON.NAME;
004import static jmri.server.json.JSON.PUT;
005import static jmri.server.json.layoutblock.JsonLayoutBlock.LAYOUTBLOCK;
006import static jmri.server.json.layoutblock.JsonLayoutBlock.LAYOUTBLOCKS;
007
008import com.fasterxml.jackson.databind.JsonNode;
009import java.beans.PropertyChangeEvent;
010import java.beans.PropertyChangeListener;
011import java.io.IOException;
012import java.util.HashMap;
013import java.util.HashSet;
014import jmri.InstanceManager;
015import jmri.JmriException;
016import jmri.jmrit.display.layoutEditor.LayoutBlock;
017import jmri.jmrit.display.layoutEditor.LayoutBlockManager;
018import jmri.server.json.JSON;
019import jmri.server.json.JsonConnection;
020import jmri.server.json.JsonException;
021import jmri.server.json.JsonRequest;
022import jmri.server.json.JsonSocketService;
023import org.slf4j.Logger;
024import org.slf4j.LoggerFactory;
025
026/**
027 *
028 * @author mstevetodd Copyright (C) 2018
029 * @author Randall Wood
030 */
031public class JsonLayoutBlockSocketService extends JsonSocketService<JsonLayoutBlockHttpService> {
032
033    private final HashMap<LayoutBlock, LayoutBlockListener> layoutBlockListeners = new HashMap<>();
034    private final LayoutBlocksListener layoutBlocksListener = new LayoutBlocksListener();
035    private static final Logger log = LoggerFactory.getLogger(JsonLayoutBlockSocketService.class);
036
037    public JsonLayoutBlockSocketService(JsonConnection connection) {
038        super(connection, new JsonLayoutBlockHttpService(connection.getObjectMapper()));
039    }
040
041    @Override
042    public void onMessage(String type, JsonNode data, JsonRequest request) throws IOException, JmriException, JsonException {
043        String name = data.path(NAME).asText();
044        LayoutBlock layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(name);
045        if (!request.method.equals(PUT) && layoutBlock != null && !layoutBlock.getSystemName().equals(name)) {
046            name = layoutBlock.getSystemName();
047        }
048        switch (request.method) {
049            case JSON.DELETE:
050                service.doDelete(type, name, data, request);
051                break;
052            case JSON.POST:
053                connection.sendMessage(service.doPost(type, name, data, request), request.id);
054                break;
055            case JSON.PUT:
056                connection.sendMessage(service.doPut(type, name, data, request), request.id);
057                break;
058            default:
059            case JSON.GET:
060                connection.sendMessage(service.doGet(type, name, data, request), request.id);
061                break;
062        }
063        layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(name);
064        if (layoutBlock != null && !layoutBlockListeners.containsKey(layoutBlock)) {
065                LayoutBlockListener listener = new LayoutBlockListener(layoutBlock);
066                layoutBlock.addPropertyChangeListener(listener);
067                layoutBlockListeners.put(layoutBlock, listener);
068        }
069    }
070
071    @Override
072    public void onList(String type, JsonNode data, JsonRequest request) throws IOException, JmriException, JsonException {
073        connection.sendMessage(service.doGetList(type, data, request), request.id);
074        log.debug("adding LayoutBlocksListener");
075        InstanceManager.getDefault(LayoutBlockManager.class).addPropertyChangeListener(layoutBlocksListener); //add parent listener
076    }
077
078    @Override
079    public void onClose() {
080        layoutBlockListeners.values().stream().forEach(layoutblock ->
081            layoutblock.layoutBlock.removePropertyChangeListener(layoutblock));
082        layoutBlockListeners.clear();
083    }
084
085    private class LayoutBlockListener implements PropertyChangeListener {
086
087        protected final LayoutBlock layoutBlock;
088
089        public LayoutBlockListener(LayoutBlock layoutblock) {
090            layoutBlock = layoutblock;
091        }
092
093        @Override
094        public void propertyChange(PropertyChangeEvent e) {
095            if (e.getPropertyName().equals("redraw")) {
096                log.debug("{} property '{}' changed from '{}' to '{}'", layoutBlock.getUserName(),
097                        e.getPropertyName(), e.getOldValue(), e.getNewValue());
098                try {
099                    try {
100                        connection.sendMessage(service.doGet(LAYOUTBLOCK, layoutBlock.getSystemName(), connection.getObjectMapper().createObjectNode(), new JsonRequest(getLocale(), getVersion(), JSON.GET, 0)), 0);
101                    } catch (JsonException ex) {
102                        connection.sendMessage(ex.getJsonMessage(), 0);
103                    }
104                } catch (IOException ex) {
105                    // if we get an error, de-register
106                    layoutBlock.removePropertyChangeListener(this);
107                    layoutBlockListeners.remove(layoutBlock);
108                }
109            }
110        }
111    }
112
113    private class LayoutBlocksListener implements PropertyChangeListener {
114        @Override
115        public void propertyChange(PropertyChangeEvent evt) {
116            log.debug("in LayoutBlocksListener for '{}' ('{}' => '{}')", evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
117
118            try {
119                try {
120                    // send the new list
121                    connection.sendMessage(service.doGetList(LAYOUTBLOCKS, service.getObjectMapper().createObjectNode(), new JsonRequest(getLocale(), getVersion(), JSON.GET, 0)), 0);
122                    //child added or removed, reset listeners
123                    if (evt.getPropertyName().equals("length")) { // NOI18N
124                        removeListenersFromRemovedBeans();
125                    }
126                } catch (JsonException ex) {
127                    log.warn("json error sending LayoutBlocks: {}", ex.getJsonMessage());
128                    connection.sendMessage(ex.getJsonMessage(), 0);
129                }
130            } catch (IOException ex) {
131                // if we get an error, de-register
132                log.debug("deregistering layoutBlocksListener due to IOException");
133                InstanceManager.getDefault(LayoutBlockManager.class).removePropertyChangeListener(layoutBlocksListener);
134            }
135        }
136
137        private void removeListenersFromRemovedBeans() {
138            for (LayoutBlock layoutBlock : new HashSet<>(layoutBlockListeners.keySet())) {
139                if (InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(layoutBlock.getSystemName()) == null) {
140                    layoutBlockListeners.remove(layoutBlock);
141                }
142            }
143        }
144    }
145
146}