001package jmri.jmrit.operations.trains; 002 003import java.io.File; 004import java.text.SimpleDateFormat; 005 006import org.jdom2.*; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010import jmri.*; 011import jmri.jmrit.operations.OperationsManager; 012import jmri.jmrit.operations.OperationsXml; 013import jmri.jmrit.operations.automation.AutomationManager; 014import jmri.jmrit.operations.setup.Setup; 015import jmri.jmrit.operations.trains.manualtrainbuilder.TrainManualBuildManager; 016import jmri.jmrit.operations.trains.schedules.TrainScheduleManager; 017import jmri.util.FileUtil; 018 019/** 020 * Loads and stores trains using xml files. Also stores various train parameters 021 * managed by the TrainManager. 022 * 023 * @author Daniel Boudreau Copyright (C) 2008, 2010, 2015 024 */ 025public class TrainManagerXml extends OperationsXml implements InstanceManagerAutoDefault, InstanceManagerAutoInitialize { 026 027 private boolean fileLoaded = false; 028 private String operationsFileName = "OperationsTrainRoster.xml";// NOI18N 029 030 private static final String BUILD_REPORT_FILE_NAME = Bundle.getMessage("train") + " ("; 031 private static final String MANIFEST_FILE_NAME = Bundle.getMessage("train") + " ("; 032 private static final String SWITCH_LIST_FILE_NAME = Bundle.getMessage("location") + " ("; 033 private static final String BACKUP_BUILD_REPORT_FILE_NAME = Bundle.getMessage("Report") + " " + Bundle.getMessage("train") + " ("; 034 private static final String FILE_TYPE_TXT = ").txt"; // NOI18N 035 private static final String FILE_TYPE_CSV = ").csv"; // NOI18N 036 037 // the directories under operations 038 protected static final String BUILD_STATUS = "buildstatus"; // NOI18N 039 protected static final String MANIFESTS = "manifests"; // NOI18N 040 protected static final String SWITCH_LISTS = "switchLists"; // NOI18N 041 public static final String CSV_MANIFESTS = "csvManifests"; // NOI18N 042 public static final String CSV_SWITCH_LISTS = "csvSwitchLists"; // NOI18N 043 protected static final String JSON_MANIFESTS = "jsonManifests"; // NOI18N 044 protected static final String MANIFESTS_BACKUPS = "manifestsBackups"; // NOI18N 045 protected static final String SWITCH_LISTS_BACKUPS = "switchListsBackups"; // NOI18N 046 protected static final String BUILD_STATUS_BACKUPS = "buildStatusBackups"; // NOI18N 047 048 public TrainManagerXml() { 049 } 050 051 @Override 052 public void writeFile(String name) throws java.io.FileNotFoundException, java.io.IOException { 053 log.debug("writeFile {}", name); 054 // This is taken in large part from "Java and XML" page 368 055 File file = findFile(name); 056 if (file == null) { 057 file = new File(name); 058 } 059 // create root element 060 Element root = new Element("operations-config"); // NOI18N 061 Document doc = newDocument(root, dtdLocation + "operations-trains.dtd"); // NOI18N 062 063 // add XSLT processing instruction 064 java.util.Map<String, String> m = new java.util.HashMap<>(); 065 m.put("type", "text/xsl"); // NOI18N 066 m.put("href", xsltLocation + "operations-trains.xsl"); // NOI18N 067 ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); // NOI18N 068 doc.addContent(0, p); 069 070 InstanceManager.getDefault(TrainManager.class).store(root); 071 InstanceManager.getDefault(TrainManualBuildManager.class).store(root); 072 InstanceManager.getDefault(TrainScheduleManager.class).store(root); 073 InstanceManager.getDefault(AutomationManager.class).store(root); 074 075 writeXML(file, doc); 076 077 // done - train file now stored, so can't be dirty 078 setDirty(false); 079 } 080 081 /** 082 * Read the contents of a roster XML file into this object. Note that this 083 * does not clear any existing entries. 084 */ 085 @Override 086 public void readFile(String name) throws org.jdom2.JDOMException, java.io.IOException { 087 088 // suppress rootFromName(name) warning message by checking to see if file exists 089 if (findFile(name) == null) { 090 log.debug("{} file could not be found", name); 091 fileLoaded = true; // set flag, could be the first time 092 return; 093 } 094 // find root 095 Element root = rootFromName(name); 096 if (root == null) { 097 log.debug("{} file could not be read", name); 098 return; 099 } 100 101 if (!root.getName().equals("operations-config")) { 102 log.warn("OperationsPro train file corrupted"); 103 return; 104 } 105 106 InstanceManager.getDefault(TrainManager.class).load(root); 107 InstanceManager.getDefault(TrainManualBuildManager.class).load(root); 108 InstanceManager.getDefault(TrainScheduleManager.class).load(root); 109 110 fileLoaded = true; // set flag trains are loaded 111 InstanceManager.getDefault(AutomationManager.class).load(root); 112 113 log.debug("Trains have been loaded!"); 114 115 for (Train train : InstanceManager.getDefault(TrainManager.class).getTrainsByIdList()) { 116 if (train.isBuilding()) { 117 log.warn("Reseting train ({}), was building when saved", train.getName()); 118 train.reset(); 119 } 120 } 121 122 setDirty(false); // clear dirty flag 123 124 // loading complete run startup scripts 125 InstanceManager.getDefault(TrainManager.class).runStartUpScripts(); 126 InstanceManager.getDefault(AutomationManager.class).runStartupAutomation(); 127 } 128 129 public boolean isTrainFileLoaded() { 130 return fileLoaded; 131 } 132 133 /** 134 * Store the train's build report 135 * 136 * @param name Full path name for train build report 137 * @return Build report File. 138 */ 139 public File createTrainBuildReportFile(String name) { 140 return createFile(defaultBuildReportFileName(name)); // don't backup 141 } 142 143 public File getTrainBuildReportFile(String name) { 144 File file = new File(defaultBuildReportFileName(name)); 145 return file; 146 } 147 148 public String defaultBuildReportFileName(String name) { 149 return OperationsXml.getFileLocation() 150 + OperationsXml.getOperationsDirectoryName() 151 + File.separator 152 + BUILD_STATUS 153 + File.separator 154 + BUILD_REPORT_FILE_NAME 155 + name 156 + FILE_TYPE_TXT; // NOI18N 157 } 158 159 /** 160 * Creates the train's manifest file. 161 * 162 * @param name Full path name for manifest file. 163 * @return Manifest File. 164 */ 165 public File createTrainManifestFile(String name) { 166 savePreviousManifestFile(name); 167 return createFile(getDefaultManifestFileName(name)); // don't backup 168 } 169 170 public File getTrainManifestFile(String name) { 171 File file = new File(getDefaultManifestFileName(name)); 172 return file; 173 } 174 175 public String getDefaultManifestFileName(String name) { 176 return OperationsXml.getFileLocation() 177 + OperationsXml.getOperationsDirectoryName() 178 + File.separator 179 + MANIFESTS 180 + File.separator 181 + MANIFEST_FILE_NAME 182 + name 183 + FILE_TYPE_TXT;// NOI18N 184 } 185 186 public String getBackupManifestFileName(String name, String lastModified) { 187 return getBackupManifestDirectoryName() 188 + name 189 + File.separator 190 + MANIFEST_FILE_NAME 191 + name 192 + ") " 193 + lastModified 194 + ".txt";// NOI18N 195 } 196 197 public String getBackupManifestDirectoryName() { 198 return OperationsXml.getFileLocation() 199 + OperationsXml.getOperationsDirectoryName() 200 + File.separator 201 + MANIFESTS_BACKUPS 202 + File.separator; 203 } 204 205 public String getBackupManifestDirectoryName(String name) { 206 return getBackupManifestDirectoryName() + name + File.separator; 207 } 208 209 public String getBackupSwitchListFileName(String name, String lastModified) { 210 return getBackupSwitchListDirectoryName() 211 + name 212 + File.separator 213 + SWITCH_LIST_FILE_NAME 214 + name 215 + ") " 216 + lastModified 217 + ".txt";// NOI18N 218 } 219 220 public String getBackupSwitchListDirectoryName() { 221 return OperationsXml.getFileLocation() 222 + OperationsXml.getOperationsDirectoryName() 223 + File.separator 224 + SWITCH_LISTS_BACKUPS 225 + File.separator; 226 } 227 228 public String getBackupSwitchListDirectoryName(String name) { 229 return getBackupSwitchListDirectoryName() + name + File.separator; 230 } 231 232 public String getBackupBuildStatusFileName(String name, String lastModified) { 233 return getBackupBuildStatusDirectoryName() 234 + name 235 + File.separator 236 + BACKUP_BUILD_REPORT_FILE_NAME 237 + name 238 + ") " 239 + lastModified 240 + ".txt";// NOI18N 241 } 242 243 public String getBackupBuildStatusDirectoryName() { 244 return OperationsXml.getFileLocation() 245 + OperationsXml.getOperationsDirectoryName() 246 + File.separator 247 + BUILD_STATUS_BACKUPS 248 + File.separator; 249 } 250 251 public String getBackupBuildStatusDirectoryName(String name) { 252 return getBackupBuildStatusDirectoryName() + name + File.separator; 253 } 254 255 /** 256 * Store the CSV train manifest 257 * 258 * @param name Full path name to CSV train manifest file. 259 * @return Train CSV manifest File. 260 */ 261 public File createTrainCsvManifestFile(String name) { 262 return createFile(getDefaultCsvManifestFileName(name)); // don't backup 263 } 264 265 public File getTrainCsvManifestFile(String name) { 266 File file = new File(getDefaultCsvManifestFileName(name)); 267 return file; 268 } 269 270 public String getDefaultCsvManifestFileName(String name) { 271 return getDefaultCsvManifestDirectory() + MANIFEST_FILE_NAME + name + FILE_TYPE_CSV; 272 } 273 274 private String getDefaultCsvManifestDirectory() { 275 return OperationsXml.getFileLocation() 276 + OperationsXml.getOperationsDirectoryName() 277 + File.separator 278 + CSV_MANIFESTS 279 + File.separator; 280 } 281 282 public void createDefaultCsvManifestDirectory() { 283 FileUtil.createDirectory(getDefaultCsvManifestDirectory()); 284 } 285 286 /** 287 * Store the Json manifest for a train 288 * 289 * @param name file name 290 * @param ext file extension to use 291 * @return Json manifest File 292 */ 293 public File createManifestFile(String name, String ext) { 294 return createFile(getDefaultManifestFileName(name, ext)); // don't backup 295 } 296 297 public File getManifestFile(String name, String ext) { 298 return new File(getDefaultManifestFileName(name, ext)); 299 } 300 301 private String getDefaultManifestFileName(String name, String ext) { 302 return InstanceManager.getDefault(OperationsManager.class).getPath(JSON_MANIFESTS) + File.separator + "train-" + name + "." + ext; // NOI18N 303 } 304 305 /** 306 * Store the switch list for a location 307 * 308 * @param name The location's name, to become file name. 309 * @return Switch list File. 310 */ 311 public File createSwitchListFile(String name) { 312 savePreviousSwitchListFile(name); 313 return createFile(getDefaultSwitchListName(name)); // don't backup 314 } 315 316 public File getSwitchListFile(String name) { 317 File file = new File(getDefaultSwitchListName(name)); 318 return file; 319 } 320 321 public String getDefaultSwitchListName(String name) { 322 return OperationsXml.getFileLocation() 323 + OperationsXml.getOperationsDirectoryName() 324 + File.separator 325 + SWITCH_LISTS 326 + File.separator 327 + SWITCH_LIST_FILE_NAME 328 + name 329 + FILE_TYPE_TXT; // NOI18N 330 } 331 332 /** 333 * Store the CSV switch list for a location 334 * 335 * @param name Location's name, to become file name. 336 * @return CSV switch list File. 337 */ 338 public File createCsvSwitchListFile(String name) { 339 return createFile(getDefaultCsvSwitchListFileName(name), true); // create backup 340 } 341 342 public File getCsvSwitchListFile(String name) { 343 File file = new File(getDefaultCsvSwitchListFileName(name)); 344 return file; 345 } 346 347 public String getDefaultCsvSwitchListFileName(String name) { 348 return getDefaultCsvSwitchListDirectoryName() + SWITCH_LIST_FILE_NAME + name + FILE_TYPE_CSV; 349 } 350 351 public String getDefaultCsvSwitchListDirectoryName() { 352 return OperationsXml.getFileLocation() 353 + OperationsXml.getOperationsDirectoryName() 354 + File.separator 355 + CSV_SWITCH_LISTS 356 + File.separator; 357 } 358 359 public void createDefaultCsvSwitchListDirectory() { 360 FileUtil.createDirectory(getDefaultCsvSwitchListDirectoryName()); 361 } 362 363 @Override 364 public void setOperationsFileName(String name) { 365 operationsFileName = name; 366 } 367 368 @Override 369 public String getOperationsFileName() { 370 return operationsFileName; 371 } 372 373 /** 374 * Save previous manifest file in a separate directory called 375 * manifestBackups. Each train manifest is saved in a unique directory using 376 * the train's name. 377 */ 378 private void savePreviousManifestFile(String name) { 379 if (Setup.isSaveTrainManifestsEnabled()) { 380 // create the manifest backup directory 381 createDirectory(getBackupManifestDirectoryName()); 382 // now create unique backup directory for each train manifest 383 createDirectory(getBackupManifestDirectoryName(name)); 384 // get old manifest file 385 File file = findFile(getDefaultManifestFileName(name)); 386 if (file == null) { 387 log.debug("No ({}) manifest file to backup", name); 388 } else if (file.canWrite()) { 389 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 390 String backupName = getBackupManifestFileName(name, lastModified); // NOI18N 391 if (file.renameTo(new File(backupName))) { 392 log.debug("created new manifest backup file {}", backupName); 393 } else { 394 log.error("could not create manifest backup file {}", backupName); 395 } 396 } 397 } 398 } 399 400 /** 401 * Save previous switch list file in a separate directory called 402 * switchListBackups. Each switch list is saved in a unique directory using 403 * the location's name. 404 */ 405 private void savePreviousSwitchListFile(String name) { 406 if (Setup.isSaveTrainManifestsEnabled()) { 407 // create the switch list backup directory 408 createDirectory(getBackupSwitchListDirectoryName()); 409 // now create unique backup directory for location 410 createDirectory(getBackupSwitchListDirectoryName(name)); 411 // get old switch list file 412 File file = findFile(getDefaultSwitchListName(name)); 413 if (file == null) { 414 log.debug("No ({}) switch list file to backup", name); 415 } else if (file.canRead()) { 416 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 417 String backupName = getBackupSwitchListFileName(name, lastModified); // NOI18N 418 File backupCopy = new File(backupName); 419 try { 420 FileUtil.copy(file, backupCopy); 421 log.debug("created new switch list backup file {}", backupName); 422 } catch (Exception e) { 423 log.error("could not create switch list backup file {}", backupName); 424 } 425 } 426 } 427 } 428 429 /** 430 * Save previous train build status file in a separate directory called 431 * BuildStatusBackups. Each build status is saved in a unique directory using 432 * the train's name. 433 * @param name train's name 434 */ 435 public void savePreviousBuildStatusFile(String name) { 436 if (Setup.isSaveTrainManifestsEnabled()) { 437 // create the build status backup directory 438 createDirectory(getBackupBuildStatusDirectoryName()); 439 // now create unique backup directory for each train 440 createDirectory(getBackupBuildStatusDirectoryName(name)); 441 // get old build status file for this train 442 File file = findFile(defaultBuildReportFileName(name)); 443 if (file == null) { 444 log.debug("No ({}) train build status file to backup", name); 445 } else if (file.canRead()) { 446 String lastModified = new SimpleDateFormat("yyyyMMdd-HHmmssSSS").format(file.lastModified()); // NOI18N 447 String backupName = getBackupBuildStatusFileName(name, lastModified); // NOI18N 448 File backupCopy = new File(backupName); 449 try { 450 FileUtil.copy(file, backupCopy); 451 log.debug("created new train build status backup file {}", backupName); 452 } catch (Exception e) { 453 log.error("could not create train build status backup file {}", backupName); 454 } 455 } 456 } 457 } 458 459 public void dispose() { 460 } 461 462 private static final Logger log = LoggerFactory.getLogger(TrainManagerXml.class); 463 464 @Override 465 public void initialize() { 466 load(); 467 } 468 469}