1 /* GConfBasedPreferences.java -- GConf based Preferences implementation
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package gnu.java.util.prefs;
40 import gnu.java.util.prefs.gconf.GConfNativePeer;
42 import java.security.Permission;
44 import java.util.Iterator;
45 import java.util.List;
46 import java.util.prefs.AbstractPreferences;
47 import java.util.prefs.BackingStoreException;
50 * This is a GConf based preference implementation which writes the preferences
51 * as GConf key-value pairs. System Root is defined to be the
52 * <code>"/system"</code> directory of GConf for the current user, while User
53 * Root is <code>"/apps/java"</code>. These defaults can be modified by
54 * defining two system properties:<br />
60 * gnu.java.util.prefs.gconf.user_root
65 * and System Root:<br />
69 * gnu.java.util.prefs.gconf.system_root
74 * @author Mario Torre <neugens@limasoftware.net>
76 public class GConfBasedPreferences
77 extends AbstractPreferences
79 /** Get access to Runtime permission */
80 private static final Permission PERMISSION
81 = new RuntimePermission("preferences");
83 /** CGonf client backend */
84 private static GConfNativePeer backend = new GConfNativePeer();
86 /** Default user root path */
87 private static final String DEFAULT_USER_ROOT = "/apps/classpath";
89 /** Default system root path */
90 private static final String DEFAULT_SYSTEM_ROOT = "/system";
92 /** current node full path */
93 private String node = "";
95 /** True if this is a preference node in the user tree, false otherwise. */
96 private final boolean isUser;
99 * Creates a preference root user node.
101 public GConfBasedPreferences()
107 * Creates a preference root node. When <code>isUser</code> is true it will
108 * be user node otherwise it will be a system node.
110 public GConfBasedPreferences(boolean isUser)
112 this(null, "", isUser);
116 * Creates a new preference node given a parent node and a name, which has to
117 * be relative to its parent. When <code>isUser</code> is true it will be user
118 * node otherwise it will be a system node.
120 * @param parent The parent node of this newly created node.
121 * @param name A name relative to the parent node.
122 * @param isUser Set to <code>true</code> initializes this node to be
123 * a user node, <code>false</code> initialize it to be a system node.
125 public GConfBasedPreferences(AbstractPreferences parent, String name,
129 this.isUser = isUser;
131 // stores the fully qualified name of this node
132 String absolutePath = this.absolutePath();
133 if (absolutePath != null && absolutePath.endsWith("/"))
135 absolutePath = absolutePath.substring(0, absolutePath.length() - 1);
138 // strip invalid characters
139 // please, note that all names are unescaped into the native peer
140 int index = absolutePath.lastIndexOf('/');
143 absolutePath = absolutePath.substring(0, index + 1);
144 absolutePath = absolutePath + GConfNativePeer.escapeString(name);
147 this.node = this.getRealRoot(isUser) + absolutePath;
149 boolean nodeExist = backend.nodeExist(this.node);
151 this.newNode = !nodeExist;
155 * Returns a child node with the given name.
156 * If the child node does not exists, it will be created.
158 * @param name The name of the requested node.
159 * @return A new reference to the node, creating the node if it is necessary.
161 protected AbstractPreferences childSpi(String name)
163 // we don't check anything here, if the node is a new node this will be
164 // detected in the constructor, so we simply return a new reference to
165 // the requested node.
167 GConfBasedPreferences preferenceNode
168 = new GConfBasedPreferences(this, name, this.isUser);
170 // register the node for to GConf so that it can listen
171 // events outside the scope of the application
172 backend.startWatchingNode(this.node);
174 return preferenceNode;
178 * Returns an array of names of the children of this preference node.
179 * If the current node does not have children, the returned array will be
180 * of <code>size</code> 0 (that is, not <code>null</code>).
182 * @return A <code>String</code> array of names of children of the current
184 * @throws BackingStoreException if this operation cannot be completed.
186 protected String[] childrenNamesSpi() throws BackingStoreException
188 List nodeList = backend.getChildrenNodes(this.node);
189 String[] nodes = new String[nodeList.size()];
190 nodeList.toArray(nodes);
196 * Suggest a flush to the backend. Actually, this is only a suggestion as
197 * GConf handles this for us asynchronously. More over, both sync and flush
198 * have the same meaning in this class, so calling sync has exactly the same
202 * @throws BackingStoreException if this operation cannot be completed.
204 public void flush() throws BackingStoreException
206 backend.suggestSync();
213 * @throws BackingStoreException if this operation cannot be completed.
215 protected void flushSpi() throws BackingStoreException
221 * Returns all of the key in this preference node.
222 * If the current node does not have preferences, the returned array will be
225 * @return A <code>String</code> array of keys stored under the current
227 * @throws BackingStoreException if this operation cannot be completed.
229 protected String[] keysSpi() throws BackingStoreException
231 List keyList = backend.getKeys(this.node);
232 String[] keys = new String[keyList.size()];
233 keyList.toArray(keys);
239 * Does a recursive postorder traversal of the preference tree, starting from
240 * the given directory invalidating every preference found in the node.
242 * @param directory The name of the starting directory (node)
244 private void postorderRemove(String directory)
248 // gets the listing of directories in this node
249 List dirs = backend.getChildrenNodes(directory);
251 if (dirs.size() != 0)
253 String currentDir = null;
255 for (Iterator itr = dirs.iterator(); itr.hasNext();)
257 currentDir = (String) itr.next();
259 // recursive search inside this directory
260 postorderRemove(currentDir);
264 // remove all the keys associated to this directory
265 List entries = backend.getKeys(directory);
267 if (entries.size() != 0)
271 for (Iterator keys = entries.iterator(); keys.hasNext();)
273 key = (String) keys.next();
278 catch (BackingStoreException ex)
285 * Stores the given key-value pair into this preference node.
287 * @param key The key of this preference.
288 * @param value The value of this preference.
290 protected void putSpi(String key, String value)
292 backend.setString(this.getGConfKey(key), value);
296 * Removes this preference node, including all its children.
297 * Also removes the preferences associated.
299 protected void removeNodeSpi() throws BackingStoreException
301 this.postorderRemove(this.node);
306 * Removes the given key from this preference node.
307 * If the key does not exist, no operation is performed.
309 * @param key The key to remove.
311 protected void removeSpi(String key)
313 backend.unset(this.getGConfKey(key));
317 * Suggest a sync to the backend. Actually, this is only a suggestion as GConf
318 * handles this for us asynchronously. More over, both sync and flush have the
319 * same meaning in this class, so calling flush has exactly the same effect.
322 * @throws BackingStoreException if this operation cannot be completed due to
323 * a failure in the backing store, or inability to communicate with
326 public void sync() throws BackingStoreException
335 * @throws BackingStoreException if this operation cannot be completed due to
336 * a failure in the backing store, or inability to communicate with
339 protected void syncSpi() throws BackingStoreException
345 * Returns the value of the given key.
346 * If the keys does not have a value, or there is an error in the backing
347 * store, <code>null</code> is returned instead.
349 * @param key The key to retrieve.
350 * @return The value associated with the given key.
352 protected String getSpi(String key)
354 return backend.getKey(this.getGConfKey(key));
358 * Returns <code>true</code> if this preference node is a user node,
359 * <code>false</code> if is a system preference node.
361 * @return <code>true</code> if this preference node is a user node,
362 * <code>false</code> if is a system preference node.
364 public boolean isUserNode()
374 * Builds a GConf key string suitable for operations on the backend.
376 * @param key The key to convert into a valid GConf key.
377 * @return A valid Gconf key.
379 private String getGConfKey(String key)
381 String nodeName = "";
384 // please, note that all names are unescaped into the native peer
385 key = GConfNativePeer.escapeString(key);
387 if (this.node.endsWith("/"))
389 nodeName = this.node + key;
393 nodeName = this.node + "/" + key;
400 * Builds the root node to use for this preference.
402 * @param isUser Defines if this node is a user (<code>true</code>) or system
403 * (<code>false</code>) node.
404 * @return The real root of this preference tree.
406 private String getRealRoot(boolean isUser)
408 // not sure about this, we should have already these permissions...
409 SecurityManager security = System.getSecurityManager();
411 if (security != null)
413 security.checkPermission(PERMISSION);
420 root = System.getProperty("gnu.java.util.prefs.gconf.user_root",
425 root = System.getProperty("gnu.java.util.prefs.gconf.system_root",
426 DEFAULT_SYSTEM_ROOT);