001package jmri.jmrit.display.layoutEditor.configurexml; 002 003import java.awt.Color; 004import java.awt.geom.Point2D; 005import java.text.DecimalFormat; 006import java.util.HashMap; 007import java.util.List; 008import java.util.Map; 009import jmri.jmrit.display.layoutEditor.*; 010import jmri.util.ColorUtil; 011import org.jdom2.Attribute; 012import org.jdom2.DataConversionException; 013import org.jdom2.Element; 014 015/** 016 * This module handles configuration for display.TrackSegment objects for a 017 * LayoutEditor. 018 * 019 * @author Bob Jacobsen Copyright (c) 2020 020 * @author David Duchamp Copyright (c) 2007 021 * @author George Warner Copyright (c) 2017-2019 022 */ 023public class TrackSegmentViewXml extends LayoutTrackViewXml { 024 025 public TrackSegmentViewXml() { 026 } 027 028 /** 029 * Default implementation for storing the contents of a TrackSegment 030 * 031 * @param o Object to store, of type TrackSegment 032 * @return Element containing the complete info 033 */ 034 @Override 035 public Element store(Object o) { 036 037 TrackSegmentView view = (TrackSegmentView) o; 038 TrackSegment trk = view.getTrackSegment(); 039 040 Element element = new Element("tracksegment"); // NOI18N 041 042 // include attributes 043 element.setAttribute("ident", trk.getId()); 044 if (!trk.getBlockName().isEmpty()) { 045 element.setAttribute("blockname", trk.getBlockName()); 046 } 047 048 element.setAttribute("connect1name", trk.getConnect1Name()); 049 element.setAttribute("type1", "" + htpMap.outputFromEnum(trk.getType1()) ); 050 element.setAttribute("connect2name", trk.getConnect2Name()); 051 element.setAttribute("type2", "" + htpMap.outputFromEnum(trk.getType2()) ); 052 053 element.setAttribute("dashed", (view.isDashed() ? "yes" : "no")); 054 element.setAttribute("mainline", (trk.isMainline() ? "yes" : "no")); 055 element.setAttribute("hidden", (view.isHidden() ? "yes" : "no")); 056 057 if (view.isArc()) { 058 element.setAttribute("arc", "yes"); 059 element.setAttribute("flip", (view.isFlip() ? "yes" : "no")); 060 element.setAttribute("circle", (view.isCircle() ? "yes" : "no")); 061 if (view.isCircle()) { 062 element.setAttribute("angle", "" + (view.getAngle())); 063 element.setAttribute("hideConLines", (view.hideConstructionLines() ? "yes" : "no")); 064 } 065 } 066 067 if (view.isBezier()) { 068 element.setAttribute("bezier", "yes"); 069 element.setAttribute("hideConLines", "" + (view.hideConstructionLines() ? "yes" : "no")); 070 // add control points 071 Element elementControlpoints = new Element("controlpoints"); 072 for (int i = 0; i < view.getNumberOfBezierControlPoints(); i++) { 073 Element elementControlpoint = new Element("controlpoint"); 074 075 elementControlpoint.setAttribute("index", "" + i); 076 077 Point2D pt = view.getBezierControlPoint(i); 078 // correctly working code copied from PositionablePointVewXml: 079 DecimalFormat twoDecFormat = new DecimalFormat("#.##"); 080 elementControlpoint.setAttribute("x", ""+Float.valueOf(twoDecFormat.format(pt.getX()))); 081 elementControlpoint.setAttribute("y", ""+Float.valueOf(twoDecFormat.format(pt.getY()))); 082 // String.format follows the Locale of JMRI, resulting in ',' as decimal separator = invalid xml 083 elementControlpoints.addContent(elementControlpoint); 084 } 085 element.addContent(elementControlpoints); 086 } 087 088 // store decorations 089 Map<String, String> decorations = view.getDecorations(); 090 if (decorations.size() > 0) { 091 Element decorationsElement = new Element("decorations"); 092 for (Map.Entry<String, String> entry : decorations.entrySet()) { 093 String name = entry.getKey(); 094 if (!name.equals("arrow") && !name.equals("bridge") 095 && !name.equals("bumper") && !name.equals("tunnel")) { 096 Element decorationElement = new Element("decoration"); 097 decorationElement.setAttribute("name", name); 098 String value = entry.getValue(); 099 if (!value.isEmpty()) { 100 decorationElement.setAttribute("value", value); 101 } 102 decorationsElement.addContent(decorationElement); 103 } 104 } 105 element.addContent(decorationsElement); 106 107 if (view.getArrowStyle() > 0) { 108 Element decorationElement = new Element("arrow"); 109 decorationElement.setAttribute("style", Integer.toString(view.getArrowStyle())); 110 if (view.isArrowEndStart() && view.isArrowEndStop()) { 111 decorationElement.setAttribute("end", "both"); 112 } else if (view.isArrowEndStop()) { 113 decorationElement.setAttribute("end", "stop"); 114 } else { 115 decorationElement.setAttribute("end", "start"); 116 } 117 if (view.isArrowDirIn() && view.isArrowDirOut()) { 118 decorationElement.setAttribute("direction", "both"); 119 } else if (view.isArrowDirOut()) { 120 decorationElement.setAttribute("direction", "out"); 121 } else { 122 decorationElement.setAttribute("direction", "in"); 123 } 124 decorationElement.setAttribute("color", ColorUtil.colorToHexString(view.getArrowColor())); 125 decorationElement.setAttribute("linewidth", Integer.toString(view.getArrowLineWidth())); 126 decorationElement.setAttribute("length", Integer.toString(view.getArrowLength())); 127 decorationElement.setAttribute("gap", Integer.toString(view.getArrowGap())); 128 decorationsElement.addContent(decorationElement); 129 } 130 if (view.isBridgeSideLeft() || view.isBridgeSideRight()) { 131 Element decorationElement = new Element("bridge"); 132 if (view.isBridgeSideLeft() && view.isBridgeSideRight()) { 133 decorationElement.setAttribute("side", "both"); 134 } else if (view.isBridgeSideLeft()) { 135 decorationElement.setAttribute("side", "left"); 136 } else { 137 decorationElement.setAttribute("side", "right"); 138 } 139 if (view.isBridgeHasEntry() && view.isBridgeHasExit()) { 140 decorationElement.setAttribute("end", "both"); 141 } else if (view.isBridgeHasEntry()) { 142 decorationElement.setAttribute("end", "entry"); 143 } else if (view.isBridgeHasExit()) { 144 decorationElement.setAttribute("end", "exit"); 145 } 146 decorationElement.setAttribute("color", ColorUtil.colorToHexString(view.getBridgeColor())); 147 decorationElement.setAttribute("linewidth", Integer.toString(view.getBridgeLineWidth())); 148 decorationElement.setAttribute("approachwidth", Integer.toString(view.getBridgeApproachWidth())); 149 decorationElement.setAttribute("deckwidth", Integer.toString(view.getBridgeDeckWidth())); 150 decorationsElement.addContent(decorationElement); 151 } 152 if (view.isBumperEndStart() || view.isBumperEndStop()) { 153 Element decorationElement = new Element("bumper"); 154 if (view.isBumperEndStart() && view.isBumperEndStop()) { 155 decorationElement.setAttribute("end", "both"); 156 } else if (view.isBumperEndStop()) { 157 decorationElement.setAttribute("end", "stop"); 158 } else { 159 decorationElement.setAttribute("end", "start"); 160 } 161 decorationElement.setAttribute("color", ColorUtil.colorToHexString(view.getBumperColor())); 162 decorationElement.setAttribute("linewidth", Integer.toString(view.getBumperLineWidth())); 163 decorationElement.setAttribute("length", Integer.toString(view.getBumperLength())); 164 if (view.isBumperFlipped()) { 165 decorationElement.setAttribute("flip", "true"); 166 } 167 decorationsElement.addContent(decorationElement); 168 } 169 170 if (view.isTunnelSideLeft() || view.isTunnelSideRight()) { 171 Element decorationElement = new Element("tunnel"); 172 if (view.isTunnelSideLeft() && view.isTunnelSideRight()) { 173 decorationElement.setAttribute("side", "both"); 174 } else if (view.isTunnelSideLeft()) { 175 decorationElement.setAttribute("side", "left"); 176 } else { 177 decorationElement.setAttribute("side", "right"); 178 } 179 if (view.isTunnelHasEntry() && view.isTunnelHasExit()) { 180 decorationElement.setAttribute("end", "both"); 181 } else if (view.isTunnelHasEntry()) { 182 decorationElement.setAttribute("end", "entry"); 183 } else if (view.isTunnelHasExit()) { 184 decorationElement.setAttribute("end", "exit"); 185 } 186 decorationElement.setAttribute("color", ColorUtil.colorToHexString(view.getTunnelColor())); 187 decorationElement.setAttribute("linewidth", Integer.toString(view.getTunnelLineWidth())); 188 decorationElement.setAttribute("entrancewidth", Integer.toString(view.getTunnelEntranceWidth())); 189 decorationElement.setAttribute("floorwidth", Integer.toString(view.getTunnelFloorWidth())); 190 decorationsElement.addContent(decorationElement); 191 } 192 } 193 194 //element.setAttribute("class", getClass().getName()); 195 log.debug("storing old fixed class name for TrackSegment"); 196 element.setAttribute("class", "jmri.jmrit.display.layoutEditor.configurexml.TrackSegmentXml"); 197 198 storeLogixNG_Data(view, element); 199 200 return element; 201 } // store 202 203 @Override 204 public boolean load(Element shared, Element perNode) { 205 log.error("Invalid method called"); 206 return false; 207 } 208 209 /** 210 * Load, starting with the track segment element, then all all attributes 211 * 212 * @param element Top level Element to unpack. 213 * @param o LayoutEditor as an Object 214 */ 215 @Override 216 public void load(Element element, Object o) { 217 // create the objects 218 LayoutEditor p = (LayoutEditor) o; 219 220 // get attributes 221 String name = element.getAttribute("ident").getValue(); 222 223 boolean dash = getAttributeBooleanValue(element, "dashed", true); 224 boolean main = getAttributeBooleanValue(element, "mainline", true); 225 boolean hide = getAttributeBooleanValue(element, "hidden", false); 226 227 String con1Name = element.getAttribute("connect1name").getValue(); 228 String con2Name = element.getAttribute("connect2name").getValue(); 229 230 231 HitPointType type1 = htpMap.inputFromAttribute(element.getAttribute("type1")); 232 233 HitPointType type2 = htpMap.inputFromAttribute(element.getAttribute("type2")); 234 235 // create the new TrackSegment and view 236 TrackSegment lt = new TrackSegment(name, 237 con1Name, type1, con2Name, type2, 238 main, p); 239 TrackSegmentView lv = new TrackSegmentView(lt, p); 240 lv.setHidden(hide); 241 242 lv.setDashed(dash); 243 lv.setArc( getAttributeBooleanValue(element, "arc", false) ); 244 245 if (lv.isArc()) { 246 lv.setFlip( getAttributeBooleanValue(element, "flip", false) ); 247 lv.setCircle( getAttributeBooleanValue(element, "circle", false) ); 248 if (lv.isCircle()) { 249 lv.setAngle( getAttributeDoubleValue(element, "angle", 0.0) ); 250 } 251 } 252 253 if ( getAttributeBooleanValue(element, "bezier", false)) { 254 // load control points 255 Element controlpointsElement = element.getChild("controlpoints"); 256 if (controlpointsElement != null) { 257 List<Element> elementList = controlpointsElement.getChildren("controlpoint"); 258 if (elementList != null) { 259 if (elementList.size() >= 2) { 260 for (Element value : elementList) { 261 double x = 0.0; 262 double y = 0.0; 263 int index = 0; 264 try { 265 index = (value.getAttribute("index")).getIntValue(); 266 x = (value.getAttribute("x")).getFloatValue(); 267 y = (value.getAttribute("y")).getFloatValue(); 268 } catch (DataConversionException e) { 269 log.error("failed to convert controlpoint coordinates or index attributes"); 270 } 271 lv.setBezierControlPoint(new Point2D.Double(x, y), index); 272 } 273 } else { 274 log.error("Track segment Bezier two controlpoint elements not found. (found {})", elementList.size()); 275 } 276 } else { 277 log.error("Track segment Bezier controlpoint elements not found."); 278 } 279 } else { 280 log.error("Track segment Bezier controlpoints element not found."); 281 } 282 // NOTE: do this LAST (so reCenter won't be called yet) 283 lv.setBezier(true); 284 } 285 286 if ( getAttributeBooleanValue(element, "hideConLines", false) ) { 287 lv.hideConstructionLines(TrackSegmentView.HIDECON); 288 } 289 // load decorations 290 Element decorationsElement = element.getChild("decorations"); 291 if (decorationsElement != null) { 292 List<Element> decorationElementList = decorationsElement.getChildren(); 293 if (decorationElementList != null) { 294 Map<String, String> decorations = new HashMap<>(); 295 for (Element decorationElement : decorationElementList) { 296 String decorationName = decorationElement.getName(); 297 if (decorationName.equals("arrow")) { 298 Attribute a = decorationElement.getAttribute("style"); 299 if (a != null) { 300 try { 301 lv.setArrowStyle(a.getIntValue()); 302 } catch (DataConversionException e) { 303 } 304 } 305 // assume both ends 306 lv.setArrowEndStart(true); 307 lv.setArrowEndStop(true); 308 a = decorationElement.getAttribute("end"); 309 if (a != null) { 310 String value = a.getValue(); 311 if (value.equals("start")) { 312 lv.setArrowEndStop(false); 313 } else if (value.equals("stop")) { 314 lv.setArrowEndStart(false); 315 } 316 } 317 // assume both directions 318 lv.setArrowDirIn(true); 319 lv.setArrowDirOut(true); 320 a = decorationElement.getAttribute("direction"); 321 if (a != null) { 322 String value = a.getValue(); 323 if (value.equals("in")) { 324 lv.setArrowDirOut(false); 325 } else if (value.equals("out")) { 326 lv.setArrowDirIn(false); 327 } 328 } 329 a = decorationElement.getAttribute("color"); 330 if (a != null) { 331 try { 332 lv.setArrowColor(ColorUtil.stringToColor(a.getValue())); 333 } catch (IllegalArgumentException e) { 334 lv.setArrowColor(Color.BLACK); 335 log.error("Invalid color {}; using black", a.getValue()); 336 } 337 } 338 a = decorationElement.getAttribute("linewidth"); 339 if (a != null) { 340 try { 341 lv.setArrowLineWidth(a.getIntValue()); 342 } catch (DataConversionException e) { 343 } 344 } 345 a = decorationElement.getAttribute("length"); 346 if (a != null) { 347 try { 348 lv.setArrowLength(a.getIntValue()); 349 } catch (DataConversionException e) { 350 } 351 } 352 a = decorationElement.getAttribute("gap"); 353 if (a != null) { 354 try { 355 lv.setArrowGap(a.getIntValue()); 356 } catch (DataConversionException e) { 357 } 358 } 359 } else if (decorationName.equals("bridge")) { 360 // assume both sides 361 lv.setBridgeSideLeft(true); 362 lv.setBridgeSideRight(true); 363 Attribute a = decorationElement.getAttribute("side"); 364 if (a != null) { 365 String value = a.getValue(); 366 if (value.equals("right")) { 367 lv.setBridgeSideLeft(false); 368 } else if (value.equals("left")) { 369 lv.setBridgeSideRight(false); 370 } 371 } 372 // assume neither end 373 lv.setBridgeHasEntry(false); 374 lv.setBridgeHasExit(false); 375 a = decorationElement.getAttribute("end"); 376 if (a != null) { 377 String value = a.getValue(); 378 if (value.equals("both")) { 379 lv.setBridgeHasEntry(true); 380 lv.setBridgeHasExit(true); 381 } else if (value.equals("entry")) { 382 lv.setBridgeHasEntry(true); 383 lv.setBridgeHasExit(false); 384 } else if (value.equals("exit")) { 385 lv.setBridgeHasEntry(false); 386 lv.setBridgeHasExit(true); 387 } 388 } 389 390 a = decorationElement.getAttribute("color"); 391 if (a != null) { 392 try { 393 lv.setBridgeColor(ColorUtil.stringToColor(a.getValue())); 394 } catch (IllegalArgumentException e) { 395 lv.setBridgeColor(Color.BLACK); 396 log.error("Invalid color {}; using black", a.getValue()); 397 } 398 } 399 400 a = decorationElement.getAttribute("linewidth"); 401 if (a != null) { 402 try { 403 lv.setBridgeLineWidth(a.getIntValue()); 404 } catch (DataConversionException e) { 405 } 406 } 407 408 a = decorationElement.getAttribute("approachwidth"); 409 if (a != null) { 410 try { 411 lv.setBridgeApproachWidth(a.getIntValue()); 412 } catch (DataConversionException e) { 413 } 414 } 415 416 a = decorationElement.getAttribute("deckwidth"); 417 if (a != null) { 418 try { 419 lv.setBridgeDeckWidth(a.getIntValue()); 420 } catch (DataConversionException e) { 421 } 422 } 423 } else if (decorationName.equals("bumper")) { 424 // assume both ends 425 lv.setBumperEndStart(true); 426 lv.setBumperEndStop(true); 427 Attribute a = decorationElement.getAttribute("end"); 428 if (a != null) { 429 String value = a.getValue(); 430 if (value.equals("start")) { 431 lv.setBumperEndStop(false); 432 } else if (value.equals("stop")) { 433 lv.setBumperEndStart(false); 434 } 435 } 436 437 a = decorationElement.getAttribute("color"); 438 if (a != null) { 439 try { 440 lv.setBumperColor(ColorUtil.stringToColor(a.getValue())); 441 } catch (IllegalArgumentException e) { 442 lv.setBumperColor(Color.BLACK); 443 log.error("Invalid color {}; using black", a.getValue()); 444 } 445 } 446 447 a = decorationElement.getAttribute("linewidth"); 448 if (a != null) { 449 try { 450 lv.setBumperLineWidth(a.getIntValue()); 451 } catch (DataConversionException e) { 452 } 453 } 454 455 a = decorationElement.getAttribute("length"); 456 if (a != null) { 457 try { 458 lv.setBumperLength(a.getIntValue()); 459 } catch (DataConversionException e) { 460 } 461 } 462 463 a = decorationElement.getAttribute("flip"); 464 if (a != null) { 465 try { 466 lv.setBumperFlipped(a.getBooleanValue()); 467 } catch (DataConversionException e) { 468 } 469 } 470 } else if (decorationName.equals("tunnel")) { 471 // assume both sides 472 lv.setTunnelSideLeft(true); 473 lv.setTunnelSideRight(true); 474 Attribute a = decorationElement.getAttribute("side"); 475 if (a != null) { 476 String value = a.getValue(); 477 if (value.equals("right")) { 478 lv.setTunnelSideLeft(false); 479 } else if (value.equals("left")) { 480 lv.setTunnelSideRight(false); 481 } 482 } 483 // assume neither end 484 lv.setTunnelHasEntry(false); 485 lv.setTunnelHasExit(false); 486 a = decorationElement.getAttribute("end"); 487 if (a != null) { 488 String value = a.getValue(); 489 if (value.equals("both")) { 490 lv.setTunnelHasEntry(true); 491 lv.setTunnelHasExit(true); 492 } else if (value.equals("entry")) { 493 lv.setTunnelHasEntry(true); 494 lv.setTunnelHasExit(false); 495 } else if (value.equals("exit")) { 496 lv.setTunnelHasEntry(false); 497 lv.setTunnelHasExit(true); 498 } 499 } 500 501 a = decorationElement.getAttribute("color"); 502 if (a != null) { 503 try { 504 lv.setTunnelColor(ColorUtil.stringToColor(a.getValue())); 505 } catch (IllegalArgumentException e) { 506 lv.setTunnelColor(Color.BLACK); 507 log.error("Invalid color {}; using black", a.getValue()); 508 } 509 } 510 511 a = decorationElement.getAttribute("linewidth"); 512 if (a != null) { 513 try { 514 lv.setTunnelLineWidth(a.getIntValue()); 515 } catch (DataConversionException e) { 516 } 517 } 518 519 a = decorationElement.getAttribute("entrancewidth"); 520 if (a != null) { 521 try { 522 lv.setTunnelEntranceWidth(a.getIntValue()); 523 } catch (DataConversionException e) { 524 } 525 } 526 527 a = decorationElement.getAttribute("floorwidth"); 528 if (a != null) { 529 try { 530 lv.setTunnelFloorWidth(a.getIntValue()); 531 } catch (DataConversionException e) { 532 } 533 } 534 } else { 535 try { 536 String eName = decorationElement.getAttribute("name").getValue(); 537 Attribute a = decorationElement.getAttribute("value"); 538 String eValue = (a != null) ? a.getValue() : ""; 539 decorations.put(eName, eValue); 540 } catch (NullPointerException e) { // considered normal if the attribute is not present 541 } 542 } 543 } 544 lv.setDecorations(decorations); 545 } 546 } 547 548 // get remaining attribute 549 Attribute a = element.getAttribute("blockname"); 550 if (a != null) { 551 lt.tLayoutBlockName = a.getValue(); 552 } 553 554 loadLogixNG_Data(lv, element); 555 556 p.addLayoutTrack(lt, lv); 557 } 558 559 static final EnumIO<HitPointType> htpMap = new EnumIoNamesNumbers<>(HitPointType.class); 560 561 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(TrackSegmentViewXml.class); 562}