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

Last change on this file since 3878 was 3848, checked in by bastiK, 13 years ago

Experimental mapcss support. All *.java files in the gui/mappaint/mapcss/parser folder are generated from the javacc source file MapCSSParser.jj in the same folder. The generated code sums up to 2700 lines, there is no further build dependency.

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