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.withObject(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 // routing tables are only changed on the layout thread 166 LayoutBlock created = layoutBlock; 167 jmri.util.ThreadingUtil.runOnLayout(created::initializeLayoutBlockRouting); 168 return doPost(layoutBlock, data, name, type, request); 169 } 170 171 @Override 172 public void doDelete(String type, String name, JsonNode data, JsonRequest request) throws JsonException { 173 LayoutBlock layoutBlock = InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(name); 174 if (layoutBlock == null) { 175 throw new JsonException(HttpServletResponse.SC_NOT_FOUND, Bundle.getMessage(request.locale, JsonException.ERROR_NOT_FOUND, type, name), request.id); 176 } 177 List<String> listeners = layoutBlock.getListenerRefs(); 178 if (!listeners.isEmpty() && !acceptForceDeleteToken(type, name, data.path(JSON.FORCE_DELETE).asText())) { 179 ArrayNode conflicts = mapper.createArrayNode(); 180 listeners.forEach(conflicts::add); 181 throwDeleteConflictException(type, name, conflicts, request); 182 } else { 183 InstanceManager.getDefault(LayoutBlockManager.class).deregister(layoutBlock); 184 } 185 } 186 187 @Override 188 public JsonNode doGetList(String type, JsonNode data, JsonRequest request) throws JsonException { 189 return doGetList(InstanceManager.getDefault(LayoutBlockManager.class), LAYOUTBLOCK, data, request); 190 } 191 192 @Override 193 public JsonNode doSchema(String type, boolean server, JsonRequest request) throws JsonException { 194 switch (type) { 195 case LAYOUTBLOCK: 196 case LAYOUTBLOCKS: 197 return doSchema(type, 198 server, 199 "jmri/server/json/layoutblock/layoutBlock-server.json", 200 "jmri/server/json/layoutblock/layoutBlock-client.json", 201 request.id); 202 default: 203 throw new JsonException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, Bundle.getMessage(request.locale, JsonException.ERROR_UNKNOWN_TYPE, type), request.id); 204 } 205 } 206 207 @Override 208 public LayoutBlock getNamedBean(String type, String name, JsonNode data, JsonRequest request) throws JsonException { 209 try { 210 if (!data.isEmpty() && !data.isNull()) { 211 if (JSON.PUT.equals(request.method)) { 212 doPut(type, name, data, request); 213 } else if (JSON.POST.equals(request.method)) { 214 doPost(type, name, data, request); 215 } 216 } 217 return InstanceManager.getDefault(LayoutBlockManager.class).getBySystemName(name); 218 } catch (IllegalArgumentException ex) { 219 throw new JsonException(HttpServletResponse.SC_BAD_REQUEST, Bundle.getMessage(request.locale, "ErrorInvalidSystemName", name, type), request.id); 220 } 221 } 222}