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