/** * Return filename from a "title" string, ala selection in matchingComboBox. * * @param title The title for the entry. * @return The filename for the RosterEntry matching title, or null if no such RosterEntry exists. */ public String fileFromTitle(String title) { RosterEntry r = entryFromTitle(title); if (r != null) { return r.getFileName(); } return null; }
/** Rebuild the Roster index and store it. */ public void reindex() { Roster roster = new Roster(); for (String fileName : Roster.getAllFileNames()) { // Read file try { Element loco = (new LocoFile()) .rootFromName(LocoFile.getFileLocation() + fileName) .getChild("locomotive"); if (loco != null) { RosterEntry re = new RosterEntry(loco); re.setFileName(fileName); roster.addEntry(re); } } catch (JDOMException | IOException ex) { log.error("Exception while loading loco XML file: {} execption: {}", fileName, ex); } } this.makeBackupFile(this.getRosterIndexPath()); try { roster.writeFile(this.getRosterIndexPath()); } catch (IOException ex) { log.error("Exception while writing the new roster file, may not be complete: {}", ex); } this.reloadRosterFile(); log.info("Roster rebuilt, stored in {}", this.getRosterIndexPath()); }
public void actionPerformed(ActionEvent e) { // obtain a HardcopyWriter to do this Roster r = Roster.instance(); String title = "DecoderPro Roster"; String rosterGroup = r.getDefaultRosterGroup(); // rosterGroup may legitimately be null // but getProperty returns null if the property cannot be found, so // we test that the property exists before attempting to get its value if (Beans.hasProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP)) { rosterGroup = (String) Beans.getProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP); } if (rosterGroup == null) { title = title + " All Entries"; } else { title = title + " Group " + rosterGroup + " Entires"; } HardcopyWriter writer = null; try { writer = new HardcopyWriter(mFrame, title, 10, .5, .5, .5, .5, isPreview); } catch (HardcopyWriter.PrintCanceledException ex) { log.debug("Print cancelled"); return; } // add the image ImageIcon icon = new ImageIcon(FileUtil.findURL("resources/decoderpro.gif", FileUtil.Location.INSTALLED)); // we use an ImageIcon because it's guaranteed to have been loaded when ctor is complete writer.write(icon.getImage(), new JLabel(icon)); // Add a number of blank lines, so that the roster entry starts below the decoderpro logo int height = icon.getImage().getHeight(null); int blanks = (height - writer.getLineAscent()) / writer.getLineHeight(); try { for (int i = 0; i < blanks; i++) { String s = "\n"; writer.write(s, 0, s.length()); } } catch (IOException ex) { log.warn("error during printing: " + ex); } // Loop through the Roster, printing as needed List<RosterEntry> l = r.matchingList(null, null, null, null, null, null, null); // take all log.debug("Roster list size: " + l.size()); for (RosterEntry re : l) { if (rosterGroup != null) { if (re.getAttribute(Roster.getRosterGroupProperty(rosterGroup)) != null && re.getAttribute(Roster.getRosterGroupProperty(rosterGroup)).equals("yes")) { re.printEntry(writer); } } else { re.printEntry(writer); } } // and force completion of the printing writer.close(); }
/** * Return RosterEntry from a "id" string. * * @param id The id for the RosterEntry. * @return The matching RosterEntry or null */ public RosterEntry getEntryForId(String id) { for (RosterEntry re : _list) { if (re.getId().equals(id)) { return re; } } return null; }
/** * Return RosterEntry from a "title" string, ala selection in matchingComboBox. * * @param title The title for the RosterEntry. * @return The matching RosterEntry or null */ public RosterEntry entryFromTitle(String title) { for (RosterEntry re : _list) { if (re.titleString().equals(title)) { return re; } } return null; }
public Set<String> getAllAttributeKeys() { // slow but effective algorithm Set<String> result = new TreeSet<>(); java.util.Iterator<RosterEntry> i = _list.iterator(); while (i.hasNext()) { RosterEntry r = i.next(); result.addAll(r.getAttributes()); } return result; }
public List<RosterEntry> getEntriesWithAttributeKey(String key) { // slow but effective algorithm ArrayList<RosterEntry> result = new ArrayList<>(); java.util.Iterator<RosterEntry> i = _list.iterator(); while (i.hasNext()) { RosterEntry r = i.next(); if (r.getAttribute(key) != null) { result.add(r); } } return result; }
/** * Remove a RosterEntry object from the in-memory Roster. This does not delete the file for the * RosterEntry! * * @param e Entry to remove */ public void removeEntry(RosterEntry e) { log.debug("Remove entry {}", e); _list.remove(e); e.removePropertyChangeListener(this); setDirty(true); firePropertyChange(REMOVE, e, null); }
/** * Add a RosterEntry object to the in-memory Roster. * * @param e Entry to add */ public void addEntry(RosterEntry e) { if (log.isDebugEnabled()) { log.debug("Add entry " + e); } int i = _list.size() - 1; // Last valid index while (i >= 0) { if (e.getId().compareToIgnoreCase(_list.get(i).getId()) > 0) { break; // I can never remember whether I want break or continue here } i--; } _list.add(i + 1, e); e.addPropertyChangeListener(this); this.addRosterGroups(e.getGroups(this)); setDirty(true); firePropertyChange(ADD, null, e); }
public int getGroupIndex(String group, RosterEntry re) { List<RosterEntry> l = matchingList(null, null, null, null, null, null, null); int num = 0; for (RosterEntry r : l) { if (group != null) { if ((r.getAttribute(getRosterGroupProperty(group)) != null) && r.getAttribute(getRosterGroupProperty(group)).equals("yes")) { // NOI18N if (r == re) { return num; } num++; } } else { if (re == r) { return num; } num++; } } return -1; }
/** * Get the Nth RosterEntry in the group * * @param group The group being queried. * @param i The index within the group of the requested entry. * @return The specified entry in the group or null if i is larger than the group, or the group * does not exist. */ public RosterEntry getGroupEntry(String group, int i) { List<RosterEntry> l = matchingList(null, null, null, null, null, null, null); int num = 0; for (RosterEntry r : l) { if (group != null) { if ((r.getAttribute(getRosterGroupProperty(group)) != null) && r.getAttribute(getRosterGroupProperty(group)).equals("yes")) { // NOI18N if (num == i) { return r; } num++; } } else { if (num == i) { return r; } num++; } } return null; }
/** * Check if an entry is consistent with specific properties. * * <p>A null String argument always matches. Strings are used for convenience in GUI building. * * @param r the roster entry being checked * @param roadName road name of entry or null for any road name * @param roadNumber road number of entry of null for any number * @param dccAddress address of entry or null for any address * @param mfg manufacturer of entry or null for any manufacturer * @param decoderModel decoder model of entry or null for any model * @param decoderFamily decoder family of entry or null for any family * @param id id of entry or null for any id * @param group group entry is member of or null for any group * @return True if the entry matches */ public boolean checkEntry( RosterEntry r, String roadName, String roadNumber, String dccAddress, String mfg, String decoderModel, String decoderFamily, String id, String group) { if (id != null && !id.equals(r.getId())) { return false; } if (roadName != null && !roadName.equals(r.getRoadName())) { return false; } if (roadNumber != null && !roadNumber.equals(r.getRoadNumber())) { return false; } if (dccAddress != null && !dccAddress.equals(r.getDccAddress())) { return false; } if (mfg != null && !mfg.equals(r.getMfg())) { return false; } if (decoderModel != null && !decoderModel.equals(r.getDecoderModel())) { return false; } if (decoderFamily != null && !decoderFamily.equals(r.getDecoderFamily())) { return false; } if (group != null && !Roster.ALLENTRIES.equals(group) && (r.getAttribute(Roster.getRosterGroupProperty(group)) == null || !r.getAttribute(Roster.getRosterGroupProperty(group)).equals("yes"))) { // NOI18N return false; } return true; }
@Override public void actionPerformed(ActionEvent event) { Roster roster = Roster.instance(); String rosterGroup = Roster.instance().getDefaultRosterGroup(); RosterEntry[] entries; // rosterGroup may legitimately be null // but getProperty returns null if the property cannot be found, so // we test that the property exists before attempting to get its value if (Beans.hasProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP)) { rosterGroup = (String) Beans.getProperty(wi, RosterGroupSelector.SELECTED_ROSTER_GROUP); log.debug("selectedRosterGroup was {}", rosterGroup); } if (Beans.hasProperty(wi, "selectedRosterEntries")) { entries = (RosterEntry[]) Beans.getProperty(wi, "selectedRosterEntries"); if (entries != null) { log.debug("selectedRosterEntries found {} entries", entries.length); } else { log.debug("selectedRosterEntries left entries null"); } } else { entries = selectRosterEntry(rosterGroup); if (entries != null) { log.debug("selectRosterEntry(rosterGroup) found {} entries", entries.length); } else { log.debug("selectRosterEntry(rosterGroup) left entries null"); } } if (entries == null) { return; } // get parent object if there is one // Component parent = null; // if ( event.getSource() instanceof Component) parent = (Component)event.getSource(); // find the file for the selected entry for (RosterEntry re : entries) { String filename = roster.fileFromTitle(re.titleString()); String fullFilename = LocoFile.getFileLocation() + filename; log.debug("resolves to [{}], [{}]", filename, fullFilename); // prompt for one last chance log.debug("rosterGroup now {}", rosterGroup); if (rosterGroup == null) { if (!userOK(re.titleString(), filename, fullFilename)) { return; } // delete it from roster roster.removeEntry(re); } else { String group = Roster.getRosterGroupProperty(rosterGroup); log.debug("removing {} group from entry", group); re.deleteAttribute(group); re.updateFile(); } Roster.writeRosterFile(); // backup the file & delete it if (rosterGroup == null) { try { // ensure preferences will be found FileUtil.createDirectory(LocoFile.getFileLocation()); // move original file to backup LocoFile df = new LocoFile(); // need a dummy object to do this operation in next line df.makeBackupFile(LocoFile.getFileLocation() + filename); } catch (Exception ex) { log.error("error during locomotive file output: " + ex); } } } }
public Builder player(Map<String, String> values) { this.players.add(RosterEntry.of(values)); return this; }
/** * Read the contents of a roster XML file into this object. * * <p>Note that this does not clear any existing entries. * * @param name filename of roster file */ void readFile(String name) throws org.jdom2.JDOMException, java.io.IOException { // roster exists? if (!(new File(name)).exists()) { log.debug( "no roster file found; this is normal if you haven't put decoders in your roster yet"); return; } // find root Element root = rootFromName(name); if (root == null) { log.error("Roster file exists, but could not be read; roster not available"); return; } // if (log.isDebugEnabled()) XmlFile.dumpElement(root); // decode type, invoke proper processing routine if a decoder file if (root.getChild("roster") != null) { // NOI18N List<Element> l = root.getChild("roster").getChildren("locomotive"); // NOI18N if (log.isDebugEnabled()) { log.debug("readFile sees " + l.size() + " children"); } l.stream() .forEach( (e) -> { addEntry(new RosterEntry(e)); }); // Scan the object to check the Comment and Decoder Comment fields for // any <?p?> processor directives and change them to back \n characters for (int i = 0; i < numEntries(); i++) { // Get a RosterEntry object for this index RosterEntry r = _list.get(i); // Extract the Comment field and create a new string for output String tempComment = r.getComment(); String xmlComment = ""; // transfer tempComment to xmlComment one character at a time, except // when <?p?> is found. In that case, insert a \n and skip over those // characters in tempComment. for (int k = 0; k < tempComment.length(); k++) { if (tempComment.startsWith("<?p?>", k)) { // NOI18N xmlComment = xmlComment + "\n"; // NOI18N k = k + 4; } else { xmlComment = xmlComment + tempComment.substring(k, k + 1); } } r.setComment(xmlComment); // Now do the same thing for the decoderComment field String tempDecoderComment = r.getDecoderComment(); String xmlDecoderComment = ""; for (int k = 0; k < tempDecoderComment.length(); k++) { if (tempDecoderComment.startsWith("<?p?>", k)) { // NOI18N xmlDecoderComment = xmlDecoderComment + "\n"; // NOI18N k = k + 4; } else { xmlDecoderComment = xmlDecoderComment + tempDecoderComment.substring(k, k + 1); } } r.setDecoderComment(xmlDecoderComment); } } else { log.error("Unrecognized roster file contents in file: " + name); } if (root.getChild("rosterGroup") != null) { // NOI18N List<Element> groups = root.getChild("rosterGroup").getChildren("group"); // NOI18N groups .stream() .forEach( (group) -> { addRosterGroup(group.getText()); }); } }
/** * Write the entire roster to a file object. This does not do backup; that has to be done * separately. See writeRosterFile() for a public function that finds the default location, does a * backup and then calls this. * * @param file an op */ void writeFile(File file) throws java.io.IOException { // create root element Element root = new Element("roster-config"); // NOI18N root.setAttribute( "noNamespaceSchemaLocation", // NOI18N "http://jmri.org/xml/schema/roster" + schemaVersion + ".xsd", // NOI18N org.jdom2.Namespace.getNamespace( "xsi", // NOI18N "http://www.w3.org/2001/XMLSchema-instance")); // NOI18N Document doc = newDocument(root); // add XSLT processing instruction // <?xml-stylesheet type="text/xsl" href="XSLT/roster.xsl"?> java.util.Map<String, String> m = new java.util.HashMap<>(); m.put("type", "text/xsl"); // NOI18N m.put("href", xsltLocation + "roster2array.xsl"); // NOI18N ProcessingInstruction p = new ProcessingInstruction("xml-stylesheet", m); // NOI18N doc.addContent(0, p); String newLocoString = SymbolicProgBundle.getMessage("LabelNewDecoder"); // Check the Comment and Decoder Comment fields for line breaks and // convert them to a processor directive for storage in XML // Note: this is also done in the LocoFile.java class to do // the same thing in the indidvidual locomotive roster files // Note: these changes have to be undone after writing the file // since the memory version of the roster is being changed to the // file version for writing for (int i = 0; i < numEntries(); i++) { // Extract the RosterEntry at this index and inspect the Comment and // Decoder Comment fields to change any \n characters to <?p?> processor // directives so they can be stored in the xml file and converted // back when the file is read. RosterEntry r = _list.get(i); if (!r.getId().equals(newLocoString)) { String tempComment = r.getComment(); String xmlComment = ""; // transfer tempComment to xmlComment one character at a time, except // when \n is found. In that case, insert <?p?> for (int k = 0; k < tempComment.length(); k++) { if (tempComment.startsWith("\n", k)) { // NOI18N xmlComment = xmlComment + "<?p?>"; // NOI18N } else { xmlComment = xmlComment + tempComment.substring(k, k + 1); } } r.setComment(xmlComment); // Now do the same thing for the decoderComment field String tempDecoderComment = r.getDecoderComment(); String xmlDecoderComment = ""; for (int k = 0; k < tempDecoderComment.length(); k++) { if (tempDecoderComment.startsWith("\n", k)) { // NOI18N xmlDecoderComment = xmlDecoderComment + "<?p?>"; // NOI18N } else { xmlDecoderComment = xmlDecoderComment + tempDecoderComment.substring(k, k + 1); } } r.setDecoderComment(xmlDecoderComment); } else { log.debug("skip unsaved roster entry with default name " + r.getId()); } } // All Comments and Decoder Comment line feeds have been changed to processor directives // add top-level elements Element values = new Element("roster"); // NOI18N root.addContent(values); // add entries for (int i = 0; i < numEntries(); i++) { if (!_list.get(i).getId().equals(newLocoString)) { values.addContent(_list.get(i).store()); } else { log.debug("skip unsaved roster entry with default name " + _list.get(i).getId()); } } if (!this.rosterGroups.isEmpty()) { Element rosterGroup = new Element("rosterGroup"); // NOI18N rosterGroups .keySet() .stream() .forEach( (name) -> { Element group = new Element("group"); // NOI18N if (!name.equals(Roster.ALLENTRIES)) { group.addContent(name); rosterGroup.addContent(group); } }); root.addContent(rosterGroup); } writeXML(file, doc); // Now that the roster has been rewritten in file form we need to // restore the RosterEntry object to its normal \n state for the // Comment and Decoder comment fields, otherwise it can cause problems in // other parts of the program (e.g. in copying a roster) for (int i = 0; i < numEntries(); i++) { RosterEntry r = _list.get(i); if (!r.getId().equals(newLocoString)) { String xmlComment = r.getComment(); String tempComment = ""; for (int k = 0; k < xmlComment.length(); k++) { if (xmlComment.startsWith("<?p?>", k)) { // NOI18N tempComment = tempComment + "\n"; // NOI18N k = k + 4; } else { tempComment = tempComment + xmlComment.substring(k, k + 1); } } r.setComment(tempComment); String xmlDecoderComment = r.getDecoderComment(); String tempDecoderComment = ""; // NOI18N for (int k = 0; k < xmlDecoderComment.length(); k++) { if (xmlDecoderComment.startsWith("<?p?>", k)) { // NOI18N tempDecoderComment = tempDecoderComment + "\n"; // NOI18N k = k + 4; } else { tempDecoderComment = tempDecoderComment + xmlDecoderComment.substring(k, k + 1); } } r.setDecoderComment(tempDecoderComment); } else { log.debug("skip unsaved roster entry with default name " + r.getId()); } } // done - roster now stored, so can't be dirty setDirty(false); firePropertyChange(SAVED, false, true); }