001// License: GPL. For details, see Readme.txt file. 002package org.openstreetmap.gui.jmapviewer; 003 004import java.awt.Desktop; 005import java.awt.image.BufferedImage; 006import java.io.IOException; 007import java.net.URI; 008import java.net.URISyntaxException; 009import java.net.URL; 010import java.text.MessageFormat; 011import java.util.Map; 012import java.util.Objects; 013import java.util.TreeMap; 014import java.util.logging.Level; 015import java.util.logging.Logger; 016 017import javax.imageio.ImageIO; 018 019/** 020 * Feature adapter allows to override JMapViewer behaviours from a client application such as JOSM. 021 */ 022public final class FeatureAdapter { 023 024 private static BrowserAdapter browserAdapter = new DefaultBrowserAdapter(); 025 private static ImageAdapter imageAdapter = new DefaultImageAdapter(); 026 private static TranslationAdapter translationAdapter = new DefaultTranslationAdapter(); 027 private static LoggingAdapter loggingAdapter = new DefaultLoggingAdapter(); 028 private static SettingsAdapter settingsAdapter = new DefaultSettingsAdapter(); 029 030 private FeatureAdapter() { 031 // private constructor for utility classes 032 } 033 034 public interface BrowserAdapter { 035 void openLink(String url); 036 } 037 038 public interface TranslationAdapter { 039 String tr(String text, Object... objects); 040 // TODO: more i18n functions 041 } 042 043 public interface LoggingAdapter { 044 Logger getLogger(String name); 045 } 046 047 public interface ImageAdapter { 048 BufferedImage read(URL input, boolean readMetadata, boolean enforceTransparency) throws IOException; 049 } 050 051 /** 052 * Basic settings system allowing to store/retrieve String key/value pairs. 053 */ 054 public interface SettingsAdapter { 055 /** 056 * Get settings value for a certain key and provide a default value. 057 * @param key the identifier for the setting 058 * @param def the default value. For each call of get() with a given key, the 059 * default value must be the same. {@code def} may be null. 060 * @return the corresponding value if the property has been set before, {@code def} otherwise 061 */ 062 String get(String key, String def); 063 064 /** 065 * Set a value for a certain setting. 066 * @param key the unique identifier for the setting 067 * @param value the value of the setting. Can be null or "" which both removes the key-value entry. 068 * @return {@code true}, if something has changed (i.e. value is different than before) 069 */ 070 boolean put(String key, String value); 071 } 072 073 public static void registerBrowserAdapter(BrowserAdapter browserAdapter) { 074 FeatureAdapter.browserAdapter = Objects.requireNonNull(browserAdapter); 075 } 076 077 public static void registerImageAdapter(ImageAdapter imageAdapter) { 078 FeatureAdapter.imageAdapter = Objects.requireNonNull(imageAdapter); 079 } 080 081 public static void registerTranslationAdapter(TranslationAdapter translationAdapter) { 082 FeatureAdapter.translationAdapter = Objects.requireNonNull(translationAdapter); 083 } 084 085 public static void registerLoggingAdapter(LoggingAdapter loggingAdapter) { 086 FeatureAdapter.loggingAdapter = Objects.requireNonNull(loggingAdapter); 087 } 088 089 /** 090 * Registers settings adapter. 091 * @param settingsAdapter settings adapter, must not be null 092 * @throws NullPointerException if settingsAdapter is null 093 */ 094 public static void registerSettingsAdapter(SettingsAdapter settingsAdapter) { 095 FeatureAdapter.settingsAdapter = Objects.requireNonNull(settingsAdapter); 096 } 097 098 public static void openLink(String url) { 099 browserAdapter.openLink(url); 100 } 101 102 public static BufferedImage readImage(URL url) throws IOException { 103 return imageAdapter.read(url, false, false); 104 } 105 106 public static String tr(String text, Object... objects) { 107 return translationAdapter.tr(text, objects); 108 } 109 110 public static Logger getLogger(String name) { 111 return loggingAdapter.getLogger(name); 112 } 113 114 public static Logger getLogger(Class<?> klass) { 115 return loggingAdapter.getLogger(klass.getSimpleName()); 116 } 117 118 /** 119 * Get settings value for a certain key and provide a default value. 120 * @param key the identifier for the setting 121 * @param def the default value. For each call of get() with a given key, the 122 * default value must be the same. {@code def} may be null. 123 * @return the corresponding value if the property has been set before, {@code def} otherwise 124 */ 125 public static String getSetting(String key, String def) { 126 return settingsAdapter.get(key, def); 127 } 128 129 /** 130 * Get settings value for a certain key and provide a default value. 131 * @param key the identifier for the setting 132 * @param def the default value. For each call of get() with a given key, the 133 * default value must be the same. {@code def} may be null. 134 * @return the corresponding value if the property has been set before, {@code def} otherwise 135 */ 136 public static int getIntSetting(String key, int def) { 137 return Integer.parseInt(settingsAdapter.get(key, Integer.toString(def))); 138 } 139 140 /** 141 * Set a value for a certain setting. 142 * @param key the unique identifier for the setting 143 * @param value the value of the setting. Can be null or "" which both removes the key-value entry. 144 * @return {@code true}, if something has changed (i.e. value is different than before) 145 */ 146 public static boolean putSetting(String key, String value) { 147 return settingsAdapter.put(key, value); 148 } 149 150 public static class DefaultBrowserAdapter implements BrowserAdapter { 151 @Override 152 public void openLink(String url) { 153 if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { 154 try { 155 Desktop.getDesktop().browse(new URI(url)); 156 } catch (IOException e) { 157 e.printStackTrace(); 158 } catch (URISyntaxException e) { 159 e.printStackTrace(); 160 } 161 } else { 162 getLogger(FeatureAdapter.class).log(Level.SEVERE, tr("Opening link not supported on current platform (''{0}'')", url)); 163 } 164 } 165 } 166 167 public static class DefaultImageAdapter implements ImageAdapter { 168 @Override 169 public BufferedImage read(URL input, boolean readMetadata, boolean enforceTransparency) throws IOException { 170 return ImageIO.read(input); 171 } 172 } 173 174 public static class DefaultTranslationAdapter implements TranslationAdapter { 175 @Override 176 public String tr(String text, Object... objects) { 177 return MessageFormat.format(text, objects); 178 } 179 } 180 181 public static class DefaultLoggingAdapter implements LoggingAdapter { 182 @Override 183 public Logger getLogger(String name) { 184 return Logger.getLogger(name); 185 } 186 } 187 188 /** 189 * Default settings adapter keeping settings in memory only. 190 */ 191 public static class DefaultSettingsAdapter implements SettingsAdapter { 192 private final Map<String, String> settings = new TreeMap<>(); 193 194 @Override 195 public String get(String key, String def) { 196 return settings.getOrDefault(key, def); 197 } 198 199 @Override 200 public boolean put(String key, String value) { 201 return !Objects.equals(value == null || value.isEmpty() ? settings.remove(key) : settings.put(key, value), value); 202 } 203 } 204}