001package jmri.web.servlet.panel; 002 003import java.awt.Color; 004import java.util.List; 005 006import javax.annotation.Nonnull; 007import javax.servlet.annotation.WebServlet; 008import javax.servlet.http.HttpServlet; 009import jmri.*; 010import jmri.jmrit.display.Positionable; 011import jmri.jmrit.display.layoutEditor.*; 012import jmri.util.ColorUtil; 013import org.jdom2.*; 014import org.jdom2.output.*; 015import org.openide.util.lookup.ServiceProvider; 016import org.slf4j.*; 017 018/** 019 * Return xml (for specified LayoutPanel) suitable for use by external clients. 020 * <p> 021 * See JMRI Web Server - Panel Servlet Help in help/en/html/web/PanelServlet.shtml for an example description of 022 * the interaction between the Web Servlets, the Web Browser and the JMRI application. 023 * 024 * @author mstevetodd -- based on PanelServlet.java by Randall Wood 025 */ 026@WebServlet(name = "LayoutPanelServlet", 027 urlPatterns = {"/panel/Layout"}) 028@ServiceProvider(service = HttpServlet.class) 029public class LayoutPanelServlet extends AbstractPanelServlet { 030 031 private final static Logger log = LoggerFactory.getLogger(LayoutPanelServlet.class); 032 033 @Override 034 protected String getPanelType() { 035 return "LayoutPanel"; 036 } 037 038 @Override 039 protected String getXmlPanel(String name) { 040 log.debug("Getting {} for {}", getPanelType(), name); 041 LayoutEditor editor = (LayoutEditor) getEditor(name); 042 if (editor == null) { 043 log.warn("Requested LayoutPanel [{}] does not exist.", name); 044 return "ERROR Requested panel [" + name + "] does not exist."; 045 } 046 Element panel = new Element("panel"); 047 048 panel.setAttribute("name", name); 049 panel.setAttribute("paneltype", getPanelType()); 050 panel.setAttribute("height", Integer.toString(editor.gContext.getLayoutHeight())); 051 panel.setAttribute("width", Integer.toString(editor.gContext.getLayoutWidth())); 052 panel.setAttribute("panelheight", Integer.toString(editor.gContext.getLayoutHeight())); 053 panel.setAttribute("panelwidth", Integer.toString(editor.gContext.getLayoutWidth())); 054 panel.setAttribute("showtooltips", (editor.showToolTip()) ? "yes" : "no"); 055 panel.setAttribute("controlling", (editor.allControlling()) ? "yes" : "no"); 056 panel.setAttribute("xscale", Float.toString((float) editor.gContext.getXScale())); 057 panel.setAttribute("yscale", Float.toString((float) editor.gContext.getYScale())); 058 panel.setAttribute("mainlinetrackwidth", Integer.toString(editor.gContext.getMainlineTrackWidth())); 059 panel.setAttribute("sidelinetrackwidth", Integer.toString(editor.gContext.getSidelineTrackWidth())); 060 panel.setAttribute("mainlineblockwidth", Integer.toString(editor.gContext.getMainlineBlockWidth())); 061 panel.setAttribute("sidelineblockwidth", Integer.toString(editor.gContext.getSidelineBlockWidth())); 062 panel.setAttribute("turnoutcircles", (editor.getTurnoutCircles()) ? "yes" : "no"); 063 panel.setAttribute("turnoutcirclesize", Integer.toString(editor.getTurnoutCircleSize())); 064 panel.setAttribute("turnoutdrawunselectedleg", (editor.isTurnoutDrawUnselectedLeg()) ? "yes" : "no"); 065 if (editor.getBackgroundColor() == null) { 066 panel.setAttribute("backgroundcolor", ColorUtil.colorToColorName(Color.lightGray)); 067 } else { 068 panel.setAttribute("backgroundcolor", ColorUtil.colorToColorName(editor.getBackgroundColor())); 069 } 070 panel.setAttribute("defaulttrackcolor", editor.getDefaultTrackColor()); 071 panel.setAttribute("defaultoccupiedtrackcolor", editor.getDefaultOccupiedTrackColor()); 072 panel.setAttribute("defaultalternativetrackcolor", editor.getDefaultAlternativeTrackColor()); 073 panel.setAttribute("defaulttextcolor", editor.getDefaultTextColor()); 074 panel.setAttribute("turnoutcirclecolor", editor.getTurnoutCircleColor()); 075 panel.setAttribute("turnoutcirclethrowncolor", editor.getTurnoutCircleThrownColor()); 076 panel.setAttribute("turnoutfillcontrolcircles", (editor.isTurnoutFillControlCircles()) ? "yes" : "no"); 077 078 //add Layout Track Drawing Options settings 079 LayoutTrackDrawingOptions ltdo = editor.getLayoutTrackDrawingOptions(); 080 panel.setAttribute("mainBallastColor", (ColorUtil.colorToColorName(ltdo.getMainBallastColor()))); 081 panel.setAttribute("mainBallastWidth", (Integer.toString(ltdo.getMainBallastWidth()))); 082 panel.setAttribute("mainBlockLineDashPercentageX10", (Integer.toString(ltdo.getMainBlockLineDashPercentageX10()))); 083 panel.setAttribute("mainBlockLineWidth", (Integer.toString(ltdo.getMainBlockLineWidth()))); 084 panel.setAttribute("mainRailColor", (ColorUtil.colorToColorName(ltdo.getMainRailColor()))); 085 panel.setAttribute("mainRailCount", (Integer.toString(ltdo.getMainRailCount()))); 086 panel.setAttribute("mainRailGap", (Integer.toString(ltdo.getMainRailGap()))); 087 panel.setAttribute("mainRailWidth", (Integer.toString(ltdo.getMainRailWidth()))); 088 panel.setAttribute("mainTieColor", (ColorUtil.colorToColorName(ltdo.getMainTieColor()))); 089 panel.setAttribute("mainTieGap", (Integer.toString(ltdo.getMainTieGap()))); 090 panel.setAttribute("mainTieLength", (Integer.toString(ltdo.getMainTieLength()))); 091 panel.setAttribute("mainTieWidth", (Integer.toString(ltdo.getMainTieWidth()))); 092 panel.setAttribute("sideBallastColor", (ColorUtil.colorToColorName(ltdo.getSideBallastColor()))); 093 panel.setAttribute("sideBallastWidth", (Integer.toString(ltdo.getSideBallastWidth()))); 094 panel.setAttribute("sideBlockLineDashPercentageX10", (Integer.toString(ltdo.getSideBlockLineDashPercentageX10()))); 095 panel.setAttribute("sideBlockLineWidth", (Integer.toString(ltdo.getSideBlockLineWidth()))); 096 panel.setAttribute("sideRailColor", (ColorUtil.colorToColorName(ltdo.getSideRailColor()))); 097 panel.setAttribute("sideRailCount", (Integer.toString(ltdo.getSideRailCount()))); 098 panel.setAttribute("sideRailGap", (Integer.toString(ltdo.getSideRailGap()))); 099 panel.setAttribute("sideRailWidth", (Integer.toString(ltdo.getSideRailWidth()))); 100 panel.setAttribute("sideTieColor", (ColorUtil.colorToColorName(ltdo.getSideTieColor()))); 101 panel.setAttribute("sideTieGap", (Integer.toString(ltdo.getSideTieGap()))); 102 panel.setAttribute("sideTieLength", (Integer.toString(ltdo.getSideTieLength()))); 103 panel.setAttribute("sideTieWidth", (Integer.toString(ltdo.getSideTieWidth()))); 104 105 // include positionable elements 106 List<Positionable> contents = editor.getContents(); 107 log.debug("Number of positionable elements: {}", contents.size()); 108 for (Positionable sub : contents) { 109 if (sub != null) { 110 try { 111 panel.addContent(positionableElement(sub)); 112 } catch (Exception ex) { 113 log.error("Error storing panel positionable element", ex); 114 } 115 } 116 } 117 118 // include LayoutBlocks 119 LayoutBlockManager lbm = InstanceManager.getDefault(LayoutBlockManager.class); 120 java.util.Iterator<LayoutBlock> iter = lbm.getNamedBeanSet().iterator(); 121 SensorManager sm = InstanceManager.sensorManagerInstance(); 122 int num = 0; 123 while (iter.hasNext()) { 124 LayoutBlock b = iter.next(); 125 if (b == null) { 126 log.error("LayoutBlock null during LayoutBlock store"); 127 continue; 128 } 129 if (b.getUseCount() > 0) { 130 // save only those LayoutBlocks that are in use--skip abandoned ones 131 Element elem = new Element("layoutblock").setAttribute("systemName", b.getSystemName()); 132 String uname = b.getUserName(); 133 if (uname != null && !uname.isEmpty()) { 134 elem.setAttribute("username", uname); 135 } 136 // get occupancy sensor from layoutblock if it is valid 137 if (!b.getOccupancySensorName().isEmpty()) { 138 Sensor s = sm.getSensor(b.getOccupancySensorName()); 139 if (s != null) { 140 elem.setAttribute("occupancysensor", s.getSystemName()); //send systemname 141 } 142 //if layoutblock has no occupancy sensor, use one from block, if it is populated 143 } else { 144 if (b.getBlock() != null) { 145 Sensor s = b.getBlock().getSensor(); 146 if (s != null) { 147 elem.setAttribute("occupancysensor", s.getSystemName()); //send systemname 148 } 149 } 150 } 151 152 elem.setAttribute("occupiedsense", Integer.toString(b.getOccupiedSense())); 153 elem.setAttribute("trackcolor", ColorUtil.colorToColorName(b.getBlockTrackColor())); 154 elem.setAttribute("occupiedcolor", ColorUtil.colorToColorName(b.getBlockOccupiedColor())); 155 elem.setAttribute("extracolor", ColorUtil.colorToColorName(b.getBlockExtraColor())); 156 Memory m = b.getMemory(); 157 if (!b.getMemoryName().isEmpty() && (m != null)) { 158 elem.setAttribute("memory", m.getSystemName()); // NOI18N 159 } 160 if (!b.useDefaultMetric()) { 161 elem.addContent(new Element("metric").addContent(Integer.toString(b.getBlockMetric()))); 162 } 163 //add to the panel xml 164 panel.addContent(elem); 165 num++; 166 } 167 } 168 log.debug("Number of layoutblock elements: {}", num); 169 170 // include LayoutTrackViews 171 List<LayoutTrackView> layoutTrackViews = editor.getLayoutTrackViews(); 172 log.debug("Number of LayoutTrack elements: {}", layoutTrackViews.size()); 173 174 // 1st pass send everything but track segment views; 2nd send track segment views 175 for (int pass = 0; pass < 2; pass++) { 176 for (Object sub : layoutTrackViews) { 177 boolean isTSV = sub instanceof TrackSegmentView; 178 if (pass == (isTSV ? 1 : 0)) { 179 try { 180 Element e = jmri.configurexml.ConfigXmlManager.elementFromObject(sub); 181 if (e != null) { 182 replaceUserNames(e); 183 if (sub instanceof LayoutTurntable) { 184 List<Element> raytracks = e.getChildren("raytrack"); 185 for (Element raytrack : raytracks) { 186 replaceUserNameAttribute(raytrack, "turnout", "turnout"); 187 } 188 } 189 panel.addContent(e); 190 } 191 } catch (Exception e) { 192 log.error("Error storing panel LayoutTrack element", e); 193 } 194 } 195 } 196 } 197 198 // include LayoutShapes 199 List<LayoutShape> layoutShapes = editor.getLayoutShapes(); 200 for (Object sub : layoutShapes) { 201 try { 202 Element e = jmri.configurexml.ConfigXmlManager.elementFromObject(sub); 203 if (e != null) { 204 panel.addContent(e); 205 } 206 } catch (Exception e) { 207 log.error("Error storing panel LayoutShape element", e); 208 } 209 } 210 log.debug("Number of LayoutShape elements: {}", layoutShapes.size()); 211 212 //write out formatted document 213 Document doc = new Document(panel); 214 XMLOutputter fmt = new XMLOutputter(); 215 fmt.setFormat(Format.getPrettyFormat() 216 .setLineSeparator(System.getProperty("line.separator")) 217 .setTextMode(Format.TextMode.TRIM)); 218 219 return fmt.outputString(doc); 220 } 221 222 /** 223 * Replace userName value of attrName with systemName for type attrType. 224 * 225 * @param e element to be updated 226 * @param beanType bean type to use for userName lookup 227 * @param attrName attribute name to replace 228 * 229 */ 230 private void replaceUserNameAttribute(@Nonnull Element e, @Nonnull String beanType, @Nonnull String attrName) { 231 Attribute a = e.getAttribute(attrName); 232 if (a == null) { 233 return; 234 } 235 String sn; 236 String un = a.getValue(); 237 238 switch (beanType) { 239 case "turnout": 240 Turnout t = InstanceManager.getDefault(TurnoutManager.class).getTurnout(un); 241 if (t == null) { 242 return; 243 } 244 sn = t.getSystemName(); 245 break; 246 case "layoutBlock": 247 LayoutBlock lb = InstanceManager.getDefault(LayoutBlockManager.class).getLayoutBlock(un); 248 if (lb == null) { 249 return; 250 } 251 sn = lb.getSystemName(); 252 break; 253 default: 254 return; 255 } 256 if (!un.equals(sn)) { 257 a.setValue(sn); 258 log.debug("systemName '{}' replaced userName '{}' for {}", sn, un, attrName); 259 } 260 } 261 262 /** 263 * Replace child element value of attrName with systemName for type attrType. 264 * 265 * @param e element to be updated 266 * @param beanType bean type to use for userName lookup 267 * @param childName child element name whose text will be replaced 268 * 269 */ 270 private void replaceUserNameChild(@Nonnull Element e, @Nonnull String beanType, @Nonnull String childName) { 271 Element c = e.getChild(childName); 272 if (c == null) { 273 return; 274 } 275 String sn; 276 String un = c.getText(); 277 278 switch (beanType) { 279 case "turnout": 280 Turnout t = InstanceManager.getDefault(TurnoutManager.class).getTurnout(un); 281 if (t == null) { 282 return; 283 } 284 sn = t.getSystemName(); 285 break; 286 default: 287 return; 288 } 289 if (!un.equals(sn)) { 290 c.setText(sn); 291 log.debug("systemName '{}' replaced userName '{}' for {}", sn, un, childName); 292 } 293 } 294 295 /** 296 * Update the element replacing username with systemname for known 297 * attributes and children. 298 * 299 * @param e element to be updated 300 */ 301 private void replaceUserNames(@Nonnull Element e) { 302 replaceUserNameAttribute(e, "turnout", "turnoutname"); 303 replaceUserNameAttribute(e, "turnout", "secondturnoutname"); 304 305 // block names for turnouts 306 replaceUserNameAttribute(e, "layoutBlock", "blockname"); 307 replaceUserNameAttribute(e, "layoutBlock", "blockbname"); 308 replaceUserNameAttribute(e, "layoutBlock", "blockcname"); 309 replaceUserNameAttribute(e, "layoutBlock", "blockdname"); 310 311 // block names for level crossings 312 replaceUserNameAttribute(e, "layoutBlock", "blocknameac"); 313 replaceUserNameAttribute(e, "layoutBlock", "blocknamebd"); 314 315 replaceUserNameChild(e, "turnout", "turnout"); 316 replaceUserNameChild(e, "turnout", "turnoutB"); 317 } 318 319 @Override 320 protected String getJsonPanel(String name) { 321 // TODO Auto-generated method stub 322 return "ERROR JSON support not implemented"; 323 } 324 325}