001package jmri.server.json.layoutblock;
002
003import static jmri.server.json.JSON.DATA;
004import static jmri.server.json.JSON.STATE;
005import static jmri.server.json.JSON.USERNAME;
006import static jmri.server.json.layoutblock.JsonLayoutBlock.BLOCK_COLOR;
007import static jmri.server.json.layoutblock.JsonLayoutBlock.EXTRA_COLOR;
008import static jmri.server.json.layoutblock.JsonLayoutBlock.LAYOUTBLOCK;
009import static jmri.server.json.layoutblock.JsonLayoutBlock.LAYOUTBLOCKS;
010import static jmri.server.json.layoutblock.JsonLayoutBlock.OCCUPANCY_SENSOR;
011import static jmri.server.json.layoutblock.JsonLayoutBlock.OCCUPIED_COLOR;
012import static jmri.server.json.layoutblock.JsonLayoutBlock.OCCUPIED_SENSE;
013import static jmri.server.json.layoutblock.JsonLayoutBlock.TRACK_COLOR;
014import static jmri.server.json.layoutblock.JsonLayoutBlock.USE_EXTRA_COLOR;
015import static jmri.server.json.block.JsonBlock.BLOCK;
016import static jmri.server.json.memory.JsonMemory.MEMORY;
017import static jmri.server.json.sensor.JsonSensor.SENSOR;
018
019import com.fasterxml.jackson.databind.JsonNode;
020import com.fasterxml.jackson.databind.ObjectMapper;
021import com.fasterxml.jackson.databind.node.ArrayNode;
022import com.fasterxml.jackson.databind.node.ObjectNode;
023import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
024
025import java.awt.Color;
026import java.util.List;
027
028import javax.servlet.http.HttpServletResponse;
029
030import jmri.InstanceManager;
031import jmri.Memory;
032import jmri.MemoryManager;
033import jmri.Sensor;
034import jmri.SensorManager;
035import jmri.jmrit.display.layoutEditor.LayoutBlock;
036import jmri.jmrit.display.layoutEditor.LayoutBlockManager;
037import jmri.server.json.JSON;
038import jmri.server.json.JsonException;
039import jmri.server.json.JsonNonProvidedNamedBeanHttpService;
040import jmri.server.json.JsonRequest;
041import jmri.util.ColorUtil;
042
043/**
044 *
045 * @author mstevetodd Copyright (C) 2018
046 * @author Randall Wood
047 */
048public class JsonLayoutBlockHttpService extends JsonNonProvidedNamedBeanHttpService<LayoutBlock> {
049
050    public JsonLayoutBlockHttpService(ObjectMapper mapper) {
051        super(mapper);
052    }
053
054    @Override
055    @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "check here for null is complexity for a situation that should not be possible")
056    public JsonNode doGet(String type, String name, JsonNode data, JsonRequest request) throws JsonException {
057        return doGet(InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(name), name, type, request);
058    }
059
060    @Override
061    protected ObjectNode doGet(LayoutBlock layoutBlock, String name, String type, JsonRequest request) throws JsonException {
062        ObjectNode root = super.getNamedBean(layoutBlock, name, type, request); // throws JsonException if layoutBlock == null
063        ObjectNode data = root.with(DATA);
064        data.put(STATE, layoutBlock.getState());
065        data.put(USE_EXTRA_COLOR, layoutBlock.getUseExtraColor());
066        data.put(BLOCK_COLOR, ColorUtil.colorToColorName(layoutBlock.getBlockColor()));
067        data.put(TRACK_COLOR, ColorUtil.colorToColorName(layoutBlock.getBlockTrackColor()));
068        data.put(OCCUPIED_COLOR, ColorUtil.colorToColorName(layoutBlock.getBlockOccupiedColor()));
069        data.put(EXTRA_COLOR, ColorUtil.colorToColorName(layoutBlock.getBlockExtraColor()));
070        if (layoutBlock.getOccupancySensor() != null) {
071            data.put(OCCUPANCY_SENSOR, layoutBlock.getOccupancySensor().getSystemName());
072        } else {
073            data.putNull(OCCUPANCY_SENSOR);
074        }
075        if (layoutBlock.getMemory() != null) {
076            data.put(MEMORY, layoutBlock.getMemory().getSystemName());
077        } else {
078            data.putNull(MEMORY);
079        }
080        if (layoutBlock.getBlock() != null) {
081            data.put(BLOCK, layoutBlock.getBlock().getSystemName());
082        } else {
083            data.putNull(BLOCK);
084        }
085        data.put(OCCUPIED_SENSE, layoutBlock.getOccupiedSense());
086        return root;
087    }
088
089    @Override
090    public JsonNode doPost(String type, String name, JsonNode data, JsonRequest request) throws JsonException {
091        return doPost(InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(name), data, name, type, request);
092    }
093
094    public JsonNode doPost(LayoutBlock layoutBlock, JsonNode data, String name, String type, JsonRequest request) throws JsonException {
095        postNamedBean(layoutBlock, data, name, type, request); // throws JsonException if layoutBlock == null
096        setBlockTrackColor(layoutBlock, TRACK_COLOR, type, data, request);
097        setBlockTrackColor(layoutBlock, OCCUPIED_COLOR, type, data, request);
098        setBlockTrackColor(layoutBlock, EXTRA_COLOR, type, data, request);
099        String string = "";
100        if (!data.path(MEMORY).isMissingNode()) {
101            string = !data.path(MEMORY).isNull() ? data.path(MEMORY).asText() : null;
102            if (string != null) {
103                Memory memory = InstanceManager.getDefault(MemoryManager.class).getBySystemName(string);
104                if (memory == null) {
105                    throw new JsonException(HttpServletResponse.SC_NOT_FOUND, Bundle.getMessage(request.locale, JsonException.ERROR_NOT_FOUND, MEMORY, string), request.id);
106                }
107                layoutBlock.setMemoryName(memory.getUserName());
108            } else {
109                layoutBlock.setMemoryName(null);
110            }
111        }
112        if (!data.path(OCCUPANCY_SENSOR).isMissingNode()) {
113            string = !data.path(OCCUPANCY_SENSOR).isNull() ? data.path(OCCUPANCY_SENSOR).asText() : null;
114            if (string != null) {
115                Sensor sensor = InstanceManager.getDefault(SensorManager.class).getBySystemName(string);
116                if (sensor == null) {
117                    throw new JsonException(HttpServletResponse.SC_NOT_FOUND, Bundle.getMessage(request.locale, JsonException.ERROR_NOT_FOUND, SENSOR, string), request.id);
118                }
119                layoutBlock.setOccupancySensorName(sensor.getUserName());
120            } else {
121                layoutBlock.setOccupancySensorName(null);
122            }
123        }
124        layoutBlock.setUseExtraColor(data.path(USE_EXTRA_COLOR).asBoolean(layoutBlock.getUseExtraColor()));
125        return doGet(layoutBlock, name, type, request);
126    }
127
128    private void setBlockTrackColor(LayoutBlock layoutBlock, String key, String type, JsonNode data, JsonRequest request) throws JsonException {
129        String value = "";
130        try {
131            value = data.path(key).asText();
132            Color color = (!data.path(key).isMissingNode() ? ColorUtil.stringToColor(value) : null);
133            if (color != null) {
134                layoutBlock.setBlockTrackColor(color);
135            }
136        } catch (IllegalArgumentException ex) {
137            throw new JsonException(HttpServletResponse.SC_BAD_REQUEST, Bundle.getMessage(request.locale, JsonException.ERROR_BAD_PROPERTY_VALUE, value, key, type), request.id);
138        }
139    }
140
141    @Override
142    public JsonNode doPut(String type, String name, JsonNode data, JsonRequest request) throws JsonException {
143        LayoutBlock layoutBlock = null;
144        String systemName = name;
145        // an empty name should be null to create a LayoutBlock with a generated systemName
146        // the name would come across as an empty string even if null in the JSON payload
147        // because JsonNode.asText() returns an empty String when JsonNode.isNull() == true
148        if (systemName.isEmpty()) {
149            systemName = null;
150        }
151        if (data.path(USERNAME).isMissingNode() || data.path(USERNAME).isNull()) {
152            layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).createNewLayoutBlock();
153        } else {
154            String userName = data.path(USERNAME).asText();
155            if (!userName.isEmpty()) {
156                layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).createNewLayoutBlock(systemName, userName);
157            } else {
158                throw new JsonException(HttpServletResponse.SC_BAD_REQUEST, Bundle.getMessage(request.locale, "ErrorEmptyAttribute", USERNAME, type), request.id);
159            }
160        }
161        if (layoutBlock == null) {
162            throw new JsonException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, Bundle.getMessage(request.locale, "ErrorInternal", type), request.id);
163        }
164        layoutBlock.initializeLayoutBlock();
165        layoutBlock.initializeLayoutBlockRouting();
166        return doPost(layoutBlock, data, name, type, request);
167    }
168
169    @Override
170    public void doDelete(String type, String name, JsonNode data, JsonRequest request) throws JsonException {
171        LayoutBlock layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(name);
172        if (layoutBlock == null) {
173            throw new JsonException(HttpServletResponse.SC_NOT_FOUND, Bundle.getMessage(request.locale, JsonException.ERROR_NOT_FOUND, type, name), request.id);
174        }
175        List<String> listeners = layoutBlock.getListenerRefs();
176        if (!listeners.isEmpty() && !acceptForceDeleteToken(type, name, data.path(JSON.FORCE_DELETE).asText())) {
177            ArrayNode conflicts = mapper.createArrayNode();
178            listeners.forEach(conflicts::add);
179            throwDeleteConflictException(type, name, conflicts, request);
180        } else {
181            InstanceManager.getDefault(LayoutBlockManager.class).deregister(layoutBlock);
182        }
183    }
184
185    @Override
186    public JsonNode doGetList(String type, JsonNode data, JsonRequest request) throws JsonException {
187        return doGetList(InstanceManager.getDefault(LayoutBlockManager.class), LAYOUTBLOCK, data, request);
188    }
189
190    @Override
191    public JsonNode doSchema(String type, boolean server, JsonRequest request) throws JsonException {
192        switch (type) {
193            case LAYOUTBLOCK:
194            case LAYOUTBLOCKS:
195                return doSchema(type,
196                        server,
197                        "jmri/server/json/layoutblock/layoutBlock-server.json",
198                        "jmri/server/json/layoutblock/layoutBlock-client.json",
199                        request.id);
200            default:
201                throw new JsonException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, Bundle.getMessage(request.locale, JsonException.ERROR_UNKNOWN_TYPE, type), request.id);
202        }
203    }
204
205    @Override
206    public LayoutBlock getNamedBean(String type, String name, JsonNode data, JsonRequest request) throws JsonException {
207        try {
208            if (!data.isEmpty() && !data.isNull()) {
209                if (JSON.PUT.equals(request.method)) {
210                    doPut(type, name, data, request);
211                } else if (JSON.POST.equals(request.method)) {
212                    doPost(type, name, data, request);
213                }
214            }
215            return InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(name);
216        } catch (IllegalArgumentException ex) {
217            throw new JsonException(HttpServletResponse.SC_BAD_REQUEST, Bundle.getMessage(request.locale, "ErrorInvalidSystemName", name, type), request.id);
218        }
219    }
220}