source: josm/trunk/src/org/openstreetmap/josm/data/Preferences.java@ 3194

Last change on this file since 3194 was 3194, checked in by jttt, 14 years ago

Fix #4875 Display settings->Colors->Set all to default segfaults

  • Property svn:eol-style set to native
File size: 26.5 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.data;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.io.BufferedReader;
8import java.io.File;
9import java.io.FileInputStream;
10import java.io.FileOutputStream;
11import java.io.IOException;
12import java.io.InputStreamReader;
13import java.io.OutputStreamWriter;
14import java.io.PrintWriter;
15import java.nio.channels.FileChannel;
16import java.util.ArrayList;
17import java.util.Arrays;
18import java.util.Collection;
19import java.util.Collections;
20import java.util.LinkedList;
21import java.util.List;
22import java.util.Map;
23import java.util.Properties;
24import java.util.SortedMap;
25import java.util.TreeMap;
26import java.util.Map.Entry;
27import java.util.concurrent.CopyOnWriteArrayList;
28import java.util.regex.Matcher;
29import java.util.regex.Pattern;
30
31import javax.swing.JOptionPane;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.tools.ColorHelper;
35
36/**
37 * This class holds all preferences for JOSM.
38 *
39 * Other classes can register their beloved properties here. All properties will be
40 * saved upon set-access.
41 *
42 * @author imi
43 */
44public class Preferences {
45 //static private final Logger logger = Logger.getLogger(Preferences.class.getName());
46
47 /**
48 * Internal storage for the preference directory.
49 * Do not access this variable directly!
50 * @see #getPreferencesDirFile()
51 */
52 private File preferencesDirFile = null;
53
54 public interface PreferenceChangeEvent{
55 String getKey();
56 String getOldValue();
57 String getNewValue();
58 }
59
60 public interface PreferenceChangedListener {
61 void preferenceChanged(PreferenceChangeEvent e);
62 }
63
64 private static class DefaultPreferenceChangeEvent implements PreferenceChangeEvent {
65 private final String key;
66 private final String oldValue;
67 private final String newValue;
68
69 public DefaultPreferenceChangeEvent(String key, String oldValue, String newValue) {
70 this.key = key;
71 this.oldValue = oldValue;
72 this.newValue = newValue;
73 }
74
75 public String getKey() {
76 return key;
77 }
78 public String getOldValue() {
79 return oldValue;
80 }
81 public String getNewValue() {
82 return newValue;
83 }
84 }
85
86 /**
87 * Class holding one bookmarkentry.
88 * @author imi
89 */
90 public static class Bookmark implements Comparable<Bookmark> {
91 private String name;
92 private Bounds area;
93
94 public Bookmark() {
95 area = null;
96 name = null;
97 }
98
99 public Bookmark(Bounds area) {
100 this.area = area;
101 }
102
103 @Override public String toString() {
104 return name;
105 }
106
107 public int compareTo(Bookmark b) {
108 return name.toLowerCase().compareTo(b.name.toLowerCase());
109 }
110
111 public Bounds getArea() {
112 return area;
113 }
114
115 public String getName() {
116 return name;
117 }
118
119 public void setName(String name) {
120 this.name = name;
121 }
122
123 public void setArea(Bounds area) {
124 this.area = area;
125 }
126 }
127
128 public interface ColorKey {
129 String getColorName();
130 String getSpecialName();
131 Color getDefault();
132 }
133
134 private final CopyOnWriteArrayList<PreferenceChangedListener> listeners = new CopyOnWriteArrayList<PreferenceChangedListener>();
135
136 public void addPreferenceChangeListener(PreferenceChangedListener listener) {
137 if (listener != null) {
138 listeners.addIfAbsent(listener);
139 }
140 }
141
142 public void removePreferenceChangeListener(PreferenceChangedListener listener) {
143 listeners.remove(listener);
144 }
145
146 protected void firePrefrenceChanged(String key, String oldValue, String newValue) {
147 PreferenceChangeEvent evt = new DefaultPreferenceChangeEvent(key, oldValue, newValue);
148 for (PreferenceChangedListener l : listeners) {
149 l.preferenceChanged(evt);
150 }
151 }
152
153 /**
154 * Map the property name to the property object.
155 */
156 protected final SortedMap<String, String> properties = new TreeMap<String, String>();
157 protected final SortedMap<String, String> defaults = new TreeMap<String, String>();
158
159 /**
160 * Override some values on read. This is intended to be used for technology previews
161 * where we want to temporarily modify things without changing the user's preferences
162 * file.
163 */
164 protected static final SortedMap<String, String> override = new TreeMap<String, String>();
165 static {
166 //override.put("osm-server.version", "0.5");
167 //override.put("osm-server.additional-versions", "");
168 //override.put("osm-server.url", "http://openstreetmap.gryph.de/api");
169 //override.put("plugins", null);
170 }
171
172 /**
173 * Return the location of the user defined preferences file
174 */
175 public String getPreferencesDir() {
176 final String path = getPreferencesDirFile().getPath();
177 if (path.endsWith(File.separator))
178 return path;
179 return path + File.separator;
180 }
181
182 public File getPreferencesDirFile() {
183 if (preferencesDirFile != null)
184 return preferencesDirFile;
185 String path;
186 path = System.getProperty("josm.home");
187 if (path != null) {
188 preferencesDirFile = new File(path);
189 } else {
190 path = System.getenv("APPDATA");
191 if (path != null) {
192 preferencesDirFile = new File(path, "JOSM");
193 } else {
194 preferencesDirFile = new File(System.getProperty("user.home"), ".josm");
195 }
196 }
197 return preferencesDirFile;
198 }
199
200 public File getPreferenceFile() {
201 return new File(getPreferencesDirFile(), "preferences");
202 }
203
204 public File getPluginsDirectory() {
205 return new File(getPreferencesDirFile(), "plugins");
206 }
207
208 /**
209 * @return A list of all existing directories where resources could be stored.
210 */
211 public Collection<String> getAllPossiblePreferenceDirs() {
212 LinkedList<String> locations = new LinkedList<String>();
213 locations.add(Main.pref.getPreferencesDir());
214 String s;
215 if ((s = System.getenv("JOSM_RESOURCES")) != null) {
216 if (!s.endsWith(File.separator)) {
217 s = s + File.separator;
218 }
219 locations.add(s);
220 }
221 if ((s = System.getProperty("josm.resources")) != null) {
222 if (!s.endsWith(File.separator)) {
223 s = s + File.separator;
224 }
225 locations.add(s);
226 }
227 String appdata = System.getenv("APPDATA");
228 if (System.getenv("ALLUSERSPROFILE") != null && appdata != null
229 && appdata.lastIndexOf(File.separator) != -1) {
230 appdata = appdata.substring(appdata.lastIndexOf(File.separator));
231 locations.add(new File(new File(System.getenv("ALLUSERSPROFILE"),
232 appdata), "JOSM").getPath());
233 }
234 locations.add("/usr/local/share/josm/");
235 locations.add("/usr/local/lib/josm/");
236 locations.add("/usr/share/josm/");
237 locations.add("/usr/lib/josm/");
238 return locations;
239 }
240
241 synchronized public boolean hasKey(final String key) {
242 return override.containsKey(key) ? override.get(key) != null : properties.containsKey(key);
243 }
244
245 synchronized public String get(final String key) {
246 putDefault(key, null);
247 if (override.containsKey(key))
248 return override.get(key);
249 if (!properties.containsKey(key))
250 return "";
251 return properties.get(key);
252 }
253
254 synchronized public String get(final String key, final String def) {
255 putDefault(key, def);
256 if (override.containsKey(key))
257 return override.get(key);
258 final String prop = properties.get(key);
259 if (prop == null || prop.equals(""))
260 return def;
261 return prop;
262 }
263
264 synchronized public Map<String, String> getAllPrefix(final String prefix) {
265 final Map<String,String> all = new TreeMap<String,String>();
266 for (final Entry<String,String> e : properties.entrySet())
267 if (e.getKey().startsWith(prefix)) {
268 all.put(e.getKey(), e.getValue());
269 }
270 for (final Entry<String,String> e : override.entrySet())
271 if (e.getKey().startsWith(prefix))
272 if (e.getValue() == null) {
273 all.remove(e.getKey());
274 } else {
275 all.put(e.getKey(), e.getValue());
276 }
277 return all;
278 }
279
280 synchronized public TreeMap<String, String> getAllColors() {
281 final TreeMap<String,String> all = new TreeMap<String,String>();
282 for (final Entry<String,String> e : defaults.entrySet())
283 if (e.getKey().startsWith("color.") && e.getValue() != null) {
284 all.put(e.getKey().substring(6), e.getValue());
285 }
286 for (final Entry<String,String> e : properties.entrySet())
287 if (e.getKey().startsWith("color.")) {
288 all.put(e.getKey().substring(6), e.getValue());
289 }
290 for (final Entry<String,String> e : override.entrySet())
291 if (e.getKey().startsWith("color."))
292 if (e.getValue() == null) {
293 all.remove(e.getKey().substring(6));
294 } else {
295 all.put(e.getKey().substring(6), e.getValue());
296 }
297 return all;
298 }
299
300 synchronized public Map<String, String> getDefaults() {
301 return defaults;
302 }
303
304 synchronized public void putDefault(final String key, final String def) {
305 if(!defaults.containsKey(key) || defaults.get(key) == null) {
306 defaults.put(key, def);
307 } else if(def != null && !defaults.get(key).equals(def)) {
308 System.out.println("Defaults for " + key + " differ: " + def + " != " + defaults.get(key));
309 }
310 }
311
312 synchronized public boolean getBoolean(final String key) {
313 putDefault(key, null);
314 if (override.containsKey(key))
315 return override.get(key) == null ? false : Boolean.parseBoolean(override.get(key));
316 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : false;
317 }
318
319 synchronized public boolean getBoolean(final String key, final boolean def) {
320 putDefault(key, Boolean.toString(def));
321 if (override.containsKey(key))
322 return override.get(key) == null ? def : Boolean.parseBoolean(override.get(key));
323 return properties.containsKey(key) ? Boolean.parseBoolean(properties.get(key)) : def;
324 }
325
326 synchronized public boolean put(final String key, String value) {
327 String oldvalue = properties.get(key);
328 if(value != null && value.length() == 0) {
329 value = null;
330 }
331 if(!((oldvalue == null && (value == null || value.equals(defaults.get(key))))
332 || (value != null && oldvalue != null && oldvalue.equals(value))))
333 {
334 if (value == null) {
335 properties.remove(key);
336 } else {
337 properties.put(key, value);
338 }
339 try {
340 save();
341 } catch(IOException e){
342 System.out.println(tr("Warning: failed to persist preferences to ''{0}''", getPreferenceFile().getAbsoluteFile()));
343 }
344 firePrefrenceChanged(key, oldvalue, value);
345 return true;
346 }
347 return false;
348 }
349
350 synchronized public boolean put(final String key, final boolean value) {
351 return put(key, Boolean.toString(value));
352 }
353
354 synchronized public boolean putInteger(final String key, final Integer value) {
355 return put(key, Integer.toString(value));
356 }
357
358 synchronized public boolean putDouble(final String key, final Double value) {
359 return put(key, Double.toString(value));
360 }
361
362 synchronized public boolean putLong(final String key, final Long value) {
363 return put(key, Long.toString(value));
364 }
365
366 /**
367 * Called after every put. In case of a problem, do nothing but output the error
368 * in log.
369 */
370 public void save() throws IOException {
371 /* currently unused, but may help to fix configuration issues in future */
372 putInteger("josm.version", Version.getInstance().getVersion());
373
374 updateSystemProperties();
375 File prefFile = new File(getPreferencesDirFile(), "preferences");
376
377 // Backup old preferences if there are old preferences
378 if(prefFile.exists()) {
379 copyFile(prefFile, new File(prefFile + "_backup"));
380 }
381
382 final PrintWriter out = new PrintWriter(new OutputStreamWriter(
383 new FileOutputStream(prefFile + "_tmp"), "utf-8"), false);
384 for (final Entry<String, String> e : properties.entrySet()) {
385 String s = defaults.get(e.getKey());
386 /* don't save default values */
387 if(s == null || !s.equals(e.getValue())) {
388 out.println(e.getKey() + "=" + e.getValue());
389 }
390 }
391 out.close();
392
393 File tmpFile = new File(prefFile + "_tmp");
394 copyFile(tmpFile, prefFile);
395 tmpFile.delete();
396 }
397
398 /**
399 * Simple file copy function that will overwrite the target file
400 * Taken from http://www.rgagnon.com/javadetails/java-0064.html (CC-NC-BY-SA)
401 * @param in
402 * @param out
403 * @throws IOException
404 */
405 public static void copyFile(File in, File out) throws IOException {
406 FileChannel inChannel = new FileInputStream(in).getChannel();
407 FileChannel outChannel = new FileOutputStream(out).getChannel();
408 try {
409 inChannel.transferTo(0, inChannel.size(),
410 outChannel);
411 }
412 catch (IOException e) {
413 throw e;
414 }
415 finally {
416 if (inChannel != null) {
417 inChannel.close();
418 }
419 if (outChannel != null) {
420 outChannel.close();
421 }
422 }
423 }
424
425 public void load() throws IOException {
426 properties.clear();
427 final BufferedReader in = new BufferedReader(new InputStreamReader(
428 new FileInputStream(getPreferencesDir()+"preferences"), "utf-8"));
429 int lineNumber = 0;
430 ArrayList<Integer> errLines = new ArrayList<Integer>();
431 for (String line = in.readLine(); line != null; line = in.readLine(), lineNumber++) {
432 final int i = line.indexOf('=');
433 if (i == -1 || i == 0) {
434 errLines.add(lineNumber);
435 continue;
436 }
437 properties.put(line.substring(0,i), line.substring(i+1));
438 }
439 if (!errLines.isEmpty())
440 throw new IOException(tr("Malformed config file at lines {0}", errLines));
441 updateSystemProperties();
442 }
443
444 public void init(boolean reset){
445 // get the preferences.
446 File prefDir = getPreferencesDirFile();
447 if (prefDir.exists()) {
448 if(!prefDir.isDirectory()) {
449 System.err.println(tr("Warning: Failed to initialize preferences. Preference directory ''{0}'' is not a directory.", prefDir.getAbsoluteFile()));
450 JOptionPane.showMessageDialog(
451 Main.parent,
452 tr("<html>Failed to initialize preferences.<br>Preference directory ''{0}'' is not a directory.</html>", prefDir.getAbsoluteFile()),
453 tr("Error"),
454 JOptionPane.ERROR_MESSAGE
455 );
456 return;
457 }
458 } else {
459 if (! prefDir.mkdirs()) {
460 System.err.println(tr("Warning: Failed to initialize preferences. Failed to create missing preference directory: {0}", prefDir.getAbsoluteFile()));
461 JOptionPane.showMessageDialog(
462 Main.parent,
463 tr("<html>Failed to initialize preferences.<br>Failed to create missing preference directory: {0}</html>",prefDir.getAbsoluteFile()),
464 tr("Error"),
465 JOptionPane.ERROR_MESSAGE
466 );
467 return;
468 }
469 }
470
471 File preferenceFile = getPreferenceFile();
472 try {
473 if (!preferenceFile.exists()) {
474 System.out.println(tr("Warning: Missing preference file ''{0}''. Creating a default preference file.", preferenceFile.getAbsoluteFile()));
475 resetToDefault();
476 save();
477 } else if (reset) {
478 System.out.println(tr("Warning: Replacing existing preference file ''{0}'' with default preference file.", preferenceFile.getAbsoluteFile()));
479 resetToDefault();
480 save();
481 }
482 } catch(IOException e) {
483 e.printStackTrace();
484 JOptionPane.showMessageDialog(
485 Main.parent,
486 tr("<html>Failed to initialize preferences.<br>Failed to reset preference file to default: {0}</html>",getPreferenceFile().getAbsoluteFile()),
487 tr("Error"),
488 JOptionPane.ERROR_MESSAGE
489 );
490 return;
491 }
492 try {
493 load();
494 } catch (IOException e) {
495 e.printStackTrace();
496 File backupFile = new File(prefDir,"preferences.bak");
497 JOptionPane.showMessageDialog(
498 Main.parent,
499 tr("<html>Preferences file had errors.<br> Making backup of old one to <br>{0}<br> and creating a new default preference file.</html>", backupFile.getAbsoluteFile()),
500 tr("Error"),
501 JOptionPane.ERROR_MESSAGE
502 );
503 preferenceFile.renameTo(backupFile);
504 try {
505 resetToDefault();
506 save();
507 } catch(IOException e1) {
508 e1.printStackTrace();
509 System.err.println(tr("Warning: Failed to initialize preferences.Failed to reset preference file to default: {0}", getPreferenceFile()));
510 }
511 }
512 }
513
514 public final void resetToDefault(){
515 properties.clear();
516 }
517
518 public File getBookmarksFile() {
519 return new File(getPreferencesDir(),"bookmarks");
520 }
521
522 public Collection<Bookmark> loadBookmarks() throws IOException {
523 File bookmarkFile = getBookmarksFile();
524 if (!bookmarkFile.exists()) {
525 bookmarkFile.createNewFile();
526 }
527 BufferedReader in = new BufferedReader(new InputStreamReader(
528 new FileInputStream(bookmarkFile), "utf-8"));
529
530 LinkedList<Bookmark> bookmarks = new LinkedList<Bookmark>();
531 for (String line = in.readLine(); line != null; line = in.readLine()) {
532 // FIXME: legacy code using ',' sign, should be \u001e only
533 Matcher m = Pattern.compile("^(.+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)[,\u001e](-?\\d+.\\d+)$").matcher(line);
534 if (!m.matches() || m.groupCount() != 5) {
535 System.err.println(tr("Error: Unexpected line ''{0}'' in bookmark file ''{1}''",line, bookmarkFile.toString()));
536 continue;
537 }
538 Bookmark b = new Bookmark();
539 b.setName(m.group(1));
540 double[] values= new double[4];
541 for (int i = 0; i < 4; ++i) {
542 try {
543 values[i] = Double.parseDouble(m.group(i+2));
544 } catch(NumberFormatException e) {
545 System.err.println(tr("Error: Illegal double value ''{0}'' on line ''{1}'' in bookmark file ''{2}''",m.group(i+2),line, bookmarkFile.toString()));
546 continue;
547 }
548 }
549 b.setArea(new Bounds(values));
550 bookmarks.add(b);
551 }
552 in.close();
553 Collections.sort(bookmarks);
554 return bookmarks;
555 }
556
557 public void saveBookmarks(Collection<Bookmark> bookmarks) throws IOException {
558 File bookmarkFile = new File(Main.pref.getPreferencesDir()+"bookmarks");
559 if (!bookmarkFile.exists()) {
560 bookmarkFile.createNewFile();
561 }
562 PrintWriter out = new PrintWriter(new OutputStreamWriter(
563 new FileOutputStream(bookmarkFile), "utf-8"));
564 for (Bookmark b : bookmarks) {
565 out.print(b.getName()+ "\u001e");
566 Bounds area = b.getArea();
567 out.print(area.getMin().lat() +"\u001e");
568 out.print(area.getMin().lon() +"\u001e");
569 out.print(area.getMax().lat() +"\u001e");
570 out.print(area.getMax().lon());
571 out.println();
572 }
573 out.close();
574 }
575
576 /**
577 * Convenience method for accessing colour preferences.
578 *
579 * @param colName name of the colour
580 * @param def default value
581 * @return a Color object for the configured colour, or the default value if none configured.
582 */
583 synchronized public Color getColor(String colName, Color def) {
584 return getColor(colName, null, def);
585 }
586
587 public Color getColor(ColorKey key) {
588 return getColor(key.getColorName(), key.getSpecialName(), key.getDefault());
589 }
590
591 /**
592 * Convenience method for accessing colour preferences.
593 *
594 * @param colName name of the colour
595 * @param specName name of the special colour settings
596 * @param def default value
597 * @return a Color object for the configured colour, or the default value if none configured.
598 */
599 synchronized public Color getColor(String colName, String specName, Color def) {
600 putDefault("color."+colName, ColorHelper.color2html(def));
601 String colStr = specName != null ? get("color."+specName) : "";
602 if(colStr.equals("")) {
603 colStr = get("color."+colName);
604 }
605 return colStr.equals("") ? def : ColorHelper.html2color(colStr);
606 }
607
608 synchronized public Color getDefaultColor(String colName) {
609 String colStr = defaults.get("color."+colName);
610 return colStr == null || "".equals(colStr) ? null : ColorHelper.html2color(colStr);
611 }
612
613 synchronized public boolean putColor(String colName, Color val) {
614 return put("color."+colName, val != null ? ColorHelper.color2html(val) : null);
615 }
616
617 synchronized public int getInteger(String key, int def) {
618 putDefault(key, Integer.toString(def));
619 String v = get(key);
620 if(null == v)
621 return def;
622
623 try {
624 return Integer.parseInt(v);
625 } catch(NumberFormatException e) {
626 // fall out
627 }
628 return def;
629 }
630
631 synchronized public long getLong(String key, long def) {
632 putDefault(key, Long.toString(def));
633 String v = get(key);
634 if(null == v)
635 return def;
636
637 try {
638 return Long.parseLong(v);
639 } catch(NumberFormatException e) {
640 // fall out
641 }
642 return def;
643 }
644
645 synchronized public double getDouble(String key, double def) {
646 putDefault(key, Double.toString(def));
647 String v = get(key);
648 if(null == v)
649 return def;
650
651 try {
652 return Double.parseDouble(v);
653 } catch(NumberFormatException e) {
654 // fall out
655 }
656 return def;
657 }
658
659 synchronized public double getDouble(String key, String def) {
660 putDefault(key, def);
661 String v = get(key);
662 if(v != null && v.length() != 0) {
663 try { return Double.parseDouble(v); } catch(NumberFormatException e) {}
664 }
665 try { return Double.parseDouble(def); } catch(NumberFormatException e) {}
666 return 0.0;
667 }
668
669 synchronized public String getCollectionAsString(final String key) {
670 String s = get(key);
671 if(s != null && s.length() != 0) {
672 s = s.replaceAll("\u001e",",");
673 }
674 return s;
675 }
676
677 public boolean isCollection(String key, boolean def) {
678 String s = get(key);
679 if (s != null && s.length() != 0)
680 return s.indexOf("\u001e") >= 0 || s.indexOf("§§§") >= 0;
681 else
682 return def;
683 }
684
685 synchronized public Collection<String> getCollection(String key, Collection<String> def) {
686 String s = get(key);
687 if(def != null)
688 {
689 String d = null;
690 for(String a : def)
691 {
692 if(d != null) {
693 d += "\u001e" + a;
694 } else {
695 d = a;
696 }
697 }
698 putDefault(key, d);
699 }
700 if(s != null && s.length() != 0)
701 {
702 if(s.indexOf("\u001e") < 0) /* FIXME: legacy code, remove later */
703 {
704 String r =s;
705 if(r.indexOf("§§§") > 0) {
706 r = r.replaceAll("§§§","\u001e");
707 } else {
708 r = r.replace(';','\u001e');
709 }
710 if(!r.equals(s)) /* save the converted string */
711 {
712 put(key,r);
713 s = r;
714 }
715 }
716 return Arrays.asList(s.split("\u001e"));
717 }
718 return def;
719 }
720 synchronized public void removeFromCollection(String key, String value) {
721 List<String> a = new ArrayList<String>(getCollection(key, null));
722 a.remove(value);
723 putCollection(key, a);
724 }
725 synchronized public boolean putCollection(String key, Collection<String> val) {
726 String s = null;
727 if(val != null)
728 {
729 for(String a : val)
730 {
731 if(s != null) {
732 s += "\u001e" + a;
733 } else {
734 s = a;
735 }
736 }
737 }
738 return put(key, s);
739 }
740
741 /**
742 * Updates system properties with the current values in the preferences.
743 *
744 */
745 public void updateSystemProperties() {
746 Properties sysProp = System.getProperties();
747 sysProp.put("http.agent", Version.getInstance().getAgentString());
748 System.setProperties(sysProp);
749 }
750
751 /**
752 * The default plugin site
753 */
754 private final static String[] DEFAULT_PLUGIN_SITE = {"http://josm.openstreetmap.de/plugin"};
755
756 /**
757 * Replies the collection of plugin site URLs from where plugin lists can be downloaded
758 *
759 * @return
760 */
761 public Collection<String> getPluginSites() {
762 return getCollection("pluginmanager.sites", Arrays.asList(DEFAULT_PLUGIN_SITE));
763 }
764
765 /**
766 * Sets the collection of plugin site URLs.
767 *
768 * @param sites the site URLs
769 */
770 public void setPluginSites(Collection<String> sites) {
771 putCollection("pluginmanager.sites", sites);
772 }
773
774}
Note: See TracBrowser for help on using the repository browser.