// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.plugins.opendata.core.modules; import static org.openstreetmap.josm.tools.I18n.tr; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.gui.PleaseWaitRunnable; import org.openstreetmap.josm.gui.progress.ProgressMonitor; import org.openstreetmap.josm.io.OsmTransferException; import org.openstreetmap.josm.tools.ImageProvider; import org.xml.sax.SAXException; /** * This is an asynchronous task for reading module information from the files * in the local module repositories. * * It scans the files in the local modules repository (see {@see Preferences#getModulesDirectory()} * and extracts module information from three kind of files: * <ul> * <li>.jar-files, assuming that they represent module jars</li> * <li>.jar.new-files, assuming that these are downloaded but not yet installed modules</li> * <li>cached lists of available modules, downloaded for instance from * <a href="http://svn.openstreetmap.org/applications/editors/josm/plugins/opendata/modules.txt">OSM SVN</a></li> * </ul> */ public class ReadLocalModuleInformationTask extends PleaseWaitRunnable { private Map<String, ModuleInformation> availableModules; private boolean canceled; public ReadLocalModuleInformationTask() { super(tr("Reading local module information.."), false); availableModules = new HashMap<>(); } public ReadLocalModuleInformationTask(ProgressMonitor monitor) { super(tr("Reading local module information.."), monitor, false); availableModules = new HashMap<>(); } @Override protected void cancel() { canceled = true; } @Override protected void finish() {} protected void processJarFile(File f, String moduleName) throws ModuleException { ModuleInformation info = new ModuleInformation( f, moduleName ); if (!availableModules.containsKey(info.getName())) { info.localversion = info.version; availableModules.put(info.getName(), info); } else { ModuleInformation current = availableModules.get(info.getName()); current.localversion = info.version; if (info.icon != null) { current.icon = info.icon; } current.className = info.className; current.libraries = info.libraries; } } protected void scanSiteCacheFiles(ProgressMonitor monitor, File modulesDirectory) { File[] siteCacheFiles = modulesDirectory.listFiles( new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.matches("^([0-9]+-)?site.*\\.txt$"); } } ); if (siteCacheFiles == null || siteCacheFiles.length == 0) return; monitor.subTask(tr("Processing module site cache files...")); monitor.setTicksCount(siteCacheFiles.length); for (File f: siteCacheFiles) { String fname = f.getName(); monitor.setCustomText(tr("Processing file ''{0}''", fname)); try { processLocalModuleInformationFile(f); } catch (ModuleListParseException e) { Main.warn(tr("Warning: Failed to scan file ''{0}'' for module information. Skipping.", fname)); e.printStackTrace(); } monitor.worked(1); } } protected void scanIconCacheFiles(ProgressMonitor monitor, File modulesDirectory) { File[] siteCacheFiles = modulesDirectory.listFiles( new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.matches("^([0-9]+-)?site.*modules-icons\\.zip$"); } } ); if (siteCacheFiles == null || siteCacheFiles.length == 0) return; monitor.subTask(tr("Processing module site cache icon files...")); monitor.setTicksCount(siteCacheFiles.length); for (File f: siteCacheFiles) { String fname = f.getName(); monitor.setCustomText(tr("Processing file ''{0}''", fname)); for (ModuleInformation pi : availableModules.values()) { if (pi.icon == null && pi.iconPath != null) { pi.icon = new ImageProvider(pi.name+".jar/"+pi.iconPath) .setArchive(f) .setMaxWidth(24) .setMaxHeight(24) .setOptional(true).get(); } } monitor.worked(1); } } protected void scanModuleFiles(ProgressMonitor monitor, File modulesDirectory) { File[] moduleFiles = modulesDirectory.listFiles( new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar") || name.endsWith(".jar.new"); } } ); if (moduleFiles == null || moduleFiles.length == 0) return; monitor.subTask(tr("Processing module files...")); monitor.setTicksCount(moduleFiles.length); for (File f: moduleFiles) { String fname = f.getName(); monitor.setCustomText(tr("Processing file ''{0}''", fname)); try { if (fname.endsWith(".jar")) { String moduleName = fname.substring(0, fname.length() - 4); processJarFile(f, moduleName); } else if (fname.endsWith(".jar.new")) { String moduleName = fname.substring(0, fname.length() - 8); processJarFile(f, moduleName); } } catch (ModuleException e) { Main.warn(tr("Warning: Failed to scan file ''{0}'' for module information. Skipping.", fname)); e.printStackTrace(); } monitor.worked(1); } } protected void scanLocalModuleRepository(ProgressMonitor monitor, File modulesDirectory) { if (modulesDirectory == null) return; try { monitor.beginTask(""); scanSiteCacheFiles(monitor, modulesDirectory); scanIconCacheFiles(monitor, modulesDirectory); scanModuleFiles(monitor, modulesDirectory); } finally { monitor.setCustomText(""); monitor.finishTask(); } } protected void processLocalModuleInformationFile(File file) throws ModuleListParseException { try (FileInputStream fin = new FileInputStream(file)) { List<ModuleInformation> pis = new ModuleListParser().parse(fin); for (ModuleInformation pi : pis) { // we always keep module information from a module site because it // includes information not available in the module jars Manifest, i.e. // the download link or localized descriptions // availableModules.put(pi.name, pi); } } catch (IOException e) { throw new ModuleListParseException(e); } } protected void analyseInProcessModules() { for (Module module : ModuleHandler.moduleList) { ModuleInformation info = module.getModuleInformation(); if (canceled) return; if (!availableModules.containsKey(info.name)) { availableModules.put(info.name, info); } else { availableModules.get(info.name).localversion = info.localversion; } } } @Override protected void realRun() throws SAXException, IOException, OsmTransferException { Collection<String> moduleLocations = ModuleInformation.getModuleLocations(); getProgressMonitor().setTicksCount(moduleLocations.size() + 2); if (canceled) return; for (String location : moduleLocations) { scanLocalModuleRepository( getProgressMonitor().createSubTaskMonitor(1, false), new File(location) ); getProgressMonitor().worked(1); if (canceled) return; } analyseInProcessModules(); getProgressMonitor().worked(1); if (canceled) return; getProgressMonitor().worked(1); } /** * Replies information about available modules detected by this task. * * @return information about available modules detected by this task. */ public List<ModuleInformation> getAvailableModules() { return new ArrayList<>(availableModules.values()); } /** * Replies true if the task was canceled by the user * * @return true if the task was canceled by the user */ public boolean isCanceled() { return canceled; } }