001package jmri.jmrit.decoderdefn; 002 003import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 004import java.awt.event.ActionEvent; 005import java.io.File; 006import java.io.FileNotFoundException; 007import java.io.FileOutputStream; 008import java.io.IOException; 009import java.io.InputStream; 010import java.io.OutputStream; 011import java.net.URI; 012import java.net.URL; 013import javax.swing.Icon; 014import javax.swing.JPanel; 015import jmri.jmrit.XmlFile; 016import jmri.util.FileUtil; 017import jmri.util.swing.JmriAbstractAction; 018import jmri.util.swing.WindowInterface; 019import jmri.util.swing.JmriJOptionPane; 020 021import org.jdom2.Element; 022 023/** 024 * Install decoder definition from URL 025 * 026 * @author Bob Jacobsen Copyright (C) 2008 027 * @see jmri.jmrit.XmlFile 028 */ 029public class InstallDecoderURLAction extends JmriAbstractAction { 030 031 public InstallDecoderURLAction(String s, WindowInterface wi) { 032 super(s, wi); 033 } 034 035 public InstallDecoderURLAction(String s, Icon i, WindowInterface wi) { 036 super(s, i, wi); 037 } 038 039 public InstallDecoderURLAction(String s) { 040 super(s); 041 } 042 043 public InstallDecoderURLAction(String s, JPanel who) { 044 super(s); 045 } 046 047 JPanel _who; 048 049 URL pickURL(JPanel who) { 050 // show input dialog 051 String urlname = JmriJOptionPane.showInputDialog(who, Bundle.getMessage("InputURL"),""); 052 if ( urlname == null || urlname.isBlank() ){ 053 JmriJOptionPane.showMessageDialog(who, Bundle.getMessage("NoURL")); 054 return null; 055 } 056 try { 057 return new URI(urlname).toURL(); 058 } catch (java.net.MalformedURLException | java.net.URISyntaxException e) { 059 JmriJOptionPane.showMessageDialog(who, Bundle.getMessage("MalformedURL")); 060 } 061 return null; 062 } 063 064 @Override 065 public void actionPerformed(ActionEvent e) { 066 067 // get the input URL 068 URL url = pickURL(_who); 069 if (url == null) { 070 return; 071 } 072 073 if (checkFile(url, _who)) { 074 // OK, do the actual copy 075 copyAndInstall(url, _who); 076 } 077 } 078 079 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings(value="SLF4J_SIGN_ONLY_FORMAT", 080 justification="Specific log message format") 081 void copyAndInstall(URL from, JPanel who) { 082 log.debug("[{}]", from.getFile()); 083 084 // get output name 085 File temp = new File(from.getFile()); 086 087 log.debug("File [{}]", temp.toString()); 088 089 // ensure directories exist 090 FileUtil.createDirectory(FileUtil.getUserFilesPath() + "decoders"); 091 092 // output file 093 File toFile = new File(FileUtil.getUserFilesPath() + "decoders" + File.separator + temp.getName()); 094 log.debug("file [{}]", toFile.toString()); 095 096 // first do the copy, but not if source and output files are the same 097 if (!temp.toString().equals(toFile.toString())) { 098 if (!copyfile(from, toFile, _who)) { 099 return; 100 } 101 } else { 102 // write a log entry 103 log.info("Source and destination files identical - file not copied"); 104 log.info(" source file: {}", temp.toString()); 105 log.info(" destination: {}", toFile.toString()); 106 } 107 108 // and rebuild index 109 DecoderIndexFile.forceCreationOfNewIndex(); 110 111 // Done OK 112 JmriJOptionPane.showMessageDialog(who, Bundle.getMessage("CompleteOK")); 113 } 114 115 @SuppressFBWarnings(value = "OBL_UNSATISFIED_OBLIGATION", justification = "Looks like false positive") 116 boolean copyfile(URL from, File toFile, JPanel who) { 117 InputStream in = null; 118 OutputStream out = null; 119 try { 120 in = from.openConnection().getInputStream(); 121 122 // open for overwrite 123 out = new FileOutputStream(toFile); 124 125 byte[] buf = new byte[1024]; 126 int len; 127 while ((len = in.read(buf)) > 0) { 128 out.write(buf, 0, len); 129 } 130 // done - finally cleans up 131 } catch (FileNotFoundException ex) { 132 log.debug("unexpected", ex); 133 JmriJOptionPane.showMessageDialog(who, Bundle.getMessage("CopyError1")); 134 return false; 135 } catch (IOException e) { 136 log.debug("IO Exception ", e); 137 JmriJOptionPane.showMessageDialog(who, Bundle.getMessage("CopyError2")); 138 return false; 139 } finally { 140 try { 141 if (in != null) { 142 in.close(); 143 } 144 } catch (IOException e1) { 145 log.error("exception closing in stream", e1); 146 } 147 try { 148 if (out != null) { 149 out.close(); 150 } 151 } catch (IOException e2) { 152 log.error("exception closing out stream", e2); 153 } 154 } 155 156 return true; 157 } 158 159 boolean checkFile(URL url, JPanel who) { 160 // read the definition to check it (later should be outside this thread?) 161 try { 162 Element root = readFile(url); 163 if (log.isDebugEnabled()) { 164 log.debug("parsing complete"); 165 } 166 167 // check to see if there's a decoder element 168 if (root.getChild("decoder") == null) { 169 JmriJOptionPane.showMessageDialog(who, Bundle.getMessage("WrongContent")); 170 return false; 171 } 172 return true; 173 174 } catch (java.io.IOException | org.jdom2.JDOMException ex) { 175 log.debug("Exception checking file", ex); 176 JmriJOptionPane.showMessageDialog(who, Bundle.getMessage("ParseError")); 177 return false; 178 } 179 } 180 181 /** 182 * Read and verify an XML file. 183 * 184 * @param url the URL of the file 185 * @return the root element in the file 186 * @throws org.jdom2.JDOMException if the file cannot be parsed 187 * @throws java.io.IOException if the file cannot be read 188 */ 189 Element readFile(URL url) throws org.jdom2.JDOMException, java.io.IOException { 190 XmlFile xf = new XmlFile() { 191 }; // odd syntax is due to XmlFile being abstract 192 193 return xf.rootFromURL(url); 194 195 } 196 197 // never invoked, because we overrode actionPerformed above 198 @Override 199 public jmri.util.swing.JmriPanel makePanel() { 200 throw new IllegalArgumentException("Should not be invoked"); 201 } 202 203 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(InstallDecoderURLAction.class); 204 205}