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}