001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.plugins; 003 004 import static org.openstreetmap.josm.tools.I18n.tr; 005 006 import java.io.File; 007 import java.io.FileInputStream; 008 import java.io.FilenameFilter; 009 import java.io.IOException; 010 import java.util.ArrayList; 011 import java.util.Collection; 012 import java.util.HashMap; 013 import java.util.List; 014 import java.util.Map; 015 016 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 017 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 018 import org.openstreetmap.josm.io.OsmTransferException; 019 import org.openstreetmap.josm.tools.ImageProvider; 020 import org.openstreetmap.josm.tools.Utils; 021 import org.xml.sax.SAXException; 022 023 /** 024 * This is an asynchronous task for reading plugin information from the files 025 * in the local plugin repositories. 026 * 027 * It scans the files in the local plugins repository (see {@link Preferences#getPluginsDirectory()} 028 * and extracts plugin information from three kind of files: 029 * <ul> 030 * <li>.jar-files, assuming that they represent plugin jars</li> 031 * <li>.jar.new-files, assuming that these are downloaded but not yet installed plugins</li> 032 * <li>cached lists of available plugins, downloaded for instance from 033 * <a href="http://josm.openstreetmap.de/plugins">http://josm.openstreetmap.de/plugins</a></li> 034 * </ul> 035 * 036 */ 037 public class ReadLocalPluginInformationTask extends PleaseWaitRunnable { 038 private Map<String, PluginInformation> availablePlugins; 039 private boolean canceled; 040 041 public ReadLocalPluginInformationTask() { 042 super(tr("Reading local plugin information.."), false); 043 availablePlugins = new HashMap<String, PluginInformation>(); 044 } 045 046 public ReadLocalPluginInformationTask(ProgressMonitor monitor) { 047 super(tr("Reading local plugin information.."),monitor, false); 048 availablePlugins = new HashMap<String, PluginInformation>(); 049 } 050 051 @Override 052 protected void cancel() { 053 canceled = true; 054 } 055 056 @Override 057 protected void finish() {} 058 059 protected void processJarFile(File f, String pluginName) throws PluginException{ 060 PluginInformation info = new PluginInformation( 061 f, 062 pluginName 063 ); 064 if (!availablePlugins.containsKey(info.getName())) { 065 info.localversion = info.version; 066 info.localmainversion = info.mainversion; 067 availablePlugins.put(info.getName(), info); 068 } else { 069 PluginInformation current = availablePlugins.get(info.getName()); 070 current.localversion = info.version; 071 current.localmainversion = info.mainversion; 072 if (info.icon != null) { 073 current.icon = info.icon; 074 } 075 current.early = info.early; 076 current.className = info.className; 077 current.libraries = info.libraries; 078 current.stage = info.stage; 079 current.requires = info.requires; 080 } 081 } 082 083 protected void scanSiteCacheFiles(ProgressMonitor monitor, File pluginsDirectory) { 084 File[] siteCacheFiles = pluginsDirectory.listFiles( 085 new FilenameFilter() { 086 public boolean accept(File dir, String name) { 087 return name.matches("^([0-9]+-)?site.*\\.txt$"); 088 } 089 } 090 ); 091 if (siteCacheFiles == null || siteCacheFiles.length == 0) 092 return; 093 monitor.subTask(tr("Processing plugin site cache files...")); 094 monitor.setTicksCount(siteCacheFiles.length); 095 for (File f: siteCacheFiles) { 096 String fname = f.getName(); 097 monitor.setCustomText(tr("Processing file ''{0}''", fname)); 098 try { 099 processLocalPluginInformationFile(f); 100 } catch(PluginListParseException e) { 101 System.err.println(tr("Warning: Failed to scan file ''{0}'' for plugin information. Skipping.", fname)); 102 e.printStackTrace(); 103 } 104 monitor.worked(1); 105 } 106 } 107 108 protected void scanIconCacheFiles(ProgressMonitor monitor, File pluginsDirectory) { 109 File[] siteCacheFiles = pluginsDirectory.listFiles( 110 new FilenameFilter() { 111 public boolean accept(File dir, String name) { 112 return name.matches("^([0-9]+-)?site.*plugin-icons\\.zip$"); 113 } 114 } 115 ); 116 if (siteCacheFiles == null || siteCacheFiles.length == 0) 117 return; 118 monitor.subTask(tr("Processing plugin site cache icon files...")); 119 monitor.setTicksCount(siteCacheFiles.length); 120 for (File f: siteCacheFiles) { 121 String fname = f.getName(); 122 monitor.setCustomText(tr("Processing file ''{0}''", fname)); 123 for (PluginInformation pi : availablePlugins.values()) { 124 if (pi.icon == null && pi.iconPath != null) { 125 pi.icon = new ImageProvider(pi.name+".jar/"+pi.iconPath) 126 .setArchive(f) 127 .setMaxWidth(24) 128 .setMaxHeight(24) 129 .setOptional(true).get(); 130 } 131 } 132 monitor.worked(1); 133 } 134 } 135 136 protected void scanPluginFiles(ProgressMonitor monitor, File pluginsDirectory) { 137 File[] pluginFiles = pluginsDirectory.listFiles( 138 new FilenameFilter() { 139 public boolean accept(File dir, String name) { 140 return name.endsWith(".jar") || name.endsWith(".jar.new"); 141 } 142 } 143 ); 144 if (pluginFiles == null || pluginFiles.length == 0) 145 return; 146 monitor.subTask(tr("Processing plugin files...")); 147 monitor.setTicksCount(pluginFiles.length); 148 for (File f: pluginFiles) { 149 String fname = f.getName(); 150 monitor.setCustomText(tr("Processing file ''{0}''", fname)); 151 try { 152 if (fname.endsWith(".jar")) { 153 String pluginName = fname.substring(0, fname.length() - 4); 154 processJarFile(f, pluginName); 155 } else if (fname.endsWith(".jar.new")) { 156 String pluginName = fname.substring(0, fname.length() - 8); 157 processJarFile(f, pluginName); 158 } 159 } catch(PluginException e){ 160 System.err.println(tr("Warning: Failed to scan file ''{0}'' for plugin information. Skipping.", fname)); 161 e.printStackTrace(); 162 } 163 monitor.worked(1); 164 } 165 } 166 167 protected void scanLocalPluginRepository(ProgressMonitor monitor, File pluginsDirectory) { 168 if (pluginsDirectory == null) return; 169 try { 170 monitor.beginTask(""); 171 scanSiteCacheFiles(monitor, pluginsDirectory); 172 scanIconCacheFiles(monitor, pluginsDirectory); 173 scanPluginFiles(monitor, pluginsDirectory); 174 } finally { 175 monitor.setCustomText(""); 176 monitor.finishTask(); 177 } 178 } 179 180 protected void processLocalPluginInformationFile(File file) throws PluginListParseException{ 181 FileInputStream fin = null; 182 try { 183 fin = new FileInputStream(file); 184 List<PluginInformation> pis = new PluginListParser().parse(fin); 185 for (PluginInformation pi : pis) { 186 // we always keep plugin information from a plugin site because it 187 // includes information not available in the plugin jars Manifest, i.e. 188 // the download link or localized descriptions 189 // 190 availablePlugins.put(pi.name, pi); 191 } 192 } catch(IOException e) { 193 throw new PluginListParseException(e); 194 } finally { 195 Utils.close(fin); 196 } 197 } 198 199 protected void analyseInProcessPlugins() { 200 for (PluginProxy proxy : PluginHandler.pluginList) { 201 PluginInformation info = proxy.getPluginInformation(); 202 if (canceled)return; 203 if (!availablePlugins.containsKey(info.name)) { 204 availablePlugins.put(info.name, info); 205 } else { 206 availablePlugins.get(info.name).localversion = info.localversion; 207 } 208 } 209 } 210 211 protected void filterOldPlugins() { 212 for (PluginHandler.DeprecatedPlugin p : PluginHandler.DEPRECATED_PLUGINS) { 213 if (canceled)return; 214 if (availablePlugins.containsKey(p.name)) { 215 availablePlugins.remove(p.name); 216 } 217 } 218 } 219 220 @Override 221 protected void realRun() throws SAXException, IOException, OsmTransferException { 222 Collection<String> pluginLocations = PluginInformation.getPluginLocations(); 223 getProgressMonitor().setTicksCount(pluginLocations.size() + 2); 224 if (canceled) return; 225 for (String location : pluginLocations) { 226 scanLocalPluginRepository( 227 getProgressMonitor().createSubTaskMonitor(1, false), 228 new File(location) 229 ); 230 getProgressMonitor().worked(1); 231 if (canceled)return; 232 } 233 analyseInProcessPlugins(); 234 getProgressMonitor().worked(1); 235 if (canceled)return; 236 filterOldPlugins(); 237 getProgressMonitor().worked(1); 238 } 239 240 /** 241 * Replies information about available plugins detected by this task. 242 * 243 * @return information about available plugins detected by this task. 244 */ 245 public List<PluginInformation> getAvailablePlugins() { 246 return new ArrayList<PluginInformation>(availablePlugins.values()); 247 } 248 249 /** 250 * Replies true if the task was canceled by the user 251 * 252 * @return true if the task was canceled by the user 253 */ 254 public boolean isCanceled() { 255 return canceled; 256 } 257 }