Path: blob/master/src/java.prefs/macosx/classes/java/util/prefs/MacOSXPreferences.java
41159 views
/*1* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.prefs;2627import java.util.Objects;2829class MacOSXPreferences extends AbstractPreferences {30// fixme need security checks?3132// CF preferences file name for Java nodes with short names33// This value is also in MacOSXPreferencesFile.c34private static final String defaultAppName = "com.apple.java.util.prefs";3536// true if this node is a child of userRoot or is userRoot37private final boolean isUser;3839// CF's storage location for this node and its keys40private final MacOSXPreferencesFile file;4142// absolutePath() + "/"43private final String path;4445// User root and system root nodes46private static volatile MacOSXPreferences userRoot;47private static volatile MacOSXPreferences systemRoot;484950// Returns user root node, creating it if necessary.51// Called by MacOSXPreferencesFactory52static Preferences getUserRoot() {53MacOSXPreferences root = userRoot;54if (root == null) {55synchronized (MacOSXPreferences.class) {56root = userRoot;57if (root == null) {58userRoot = root = new MacOSXPreferences(true);59}60}61}62return root;63}646566// Returns system root node, creating it if necessary.67// Called by MacOSXPreferencesFactory68static Preferences getSystemRoot() {69MacOSXPreferences root = systemRoot;70if (root == null) {71synchronized (MacOSXPreferences.class) {72root = systemRoot;73if (root == null) {74systemRoot = root = new MacOSXPreferences(false);75}76}77}78return root;79}808182// Create a new root node. Called by getUserRoot() and getSystemRoot()83// Synchronization is provided by the caller.84private MacOSXPreferences(boolean newIsUser) {85this(null, "", false, true, newIsUser);86}878889// Create a new non-root node with the given parent.90// Called by childSpi().91private MacOSXPreferences(MacOSXPreferences parent, String name) {92this(parent, name, false, false, false);93}9495private MacOSXPreferences(MacOSXPreferences parent, String name,96boolean isNew)97{98this(parent, name, isNew, false, false);99}100101private MacOSXPreferences(MacOSXPreferences parent, String name,102boolean isNew, boolean isRoot, boolean isUser)103{104super(parent, name);105if (isRoot)106this.isUser = isUser;107else108this.isUser = isUserNode();109path = isRoot ? absolutePath() : absolutePath() + "/";110file = cfFileForNode(this.isUser);111if (isNew)112newNode = isNew;113else114newNode = file.addNode(path);115}116117// Create and return the MacOSXPreferencesFile for this node.118// Does not write anything to the file.119private MacOSXPreferencesFile cfFileForNode(boolean isUser)120{121String name = path;122// /one/two/three/four/five/123// The fourth slash is the end of the first three components.124// If there is no fourth slash, the name has fewer than 3 components125int pos = -1;126for (int i = 0; i < 4; i++) {127pos = name.indexOf('/', pos+1);128if (pos == -1) break;129}130131if (pos == -1) {132// fewer than three components - use default name133name = defaultAppName;134} else {135// truncate to three components, no leading or trailing '/'136// replace '/' with '.' to make filesystem happy137// convert to all lowercase to survive on HFS+138name = name.substring(1, pos);139name = name.replace('/', '.');140name = name.toLowerCase();141}142143return MacOSXPreferencesFile.getFile(name, isUser);144}145146147// AbstractPreferences implementation148@Override149protected void putSpi(String key, String value)150{151file.addKeyToNode(path, key, value);152}153154// AbstractPreferences implementation155@Override156protected String getSpi(String key)157{158return file.getKeyFromNode(path, key);159}160161// AbstractPreferences implementation162@Override163protected void removeSpi(String key)164{165Objects.requireNonNull(key, "Specified key cannot be null");166file.removeKeyFromNode(path, key);167}168169170// AbstractPreferences implementation171@Override172protected void removeNodeSpi()173throws BackingStoreException174{175// Disallow flush or sync between these two operations176// (they may be manipulating two different files)177synchronized(MacOSXPreferencesFile.class) {178((MacOSXPreferences)parent()).removeChild(name());179file.removeNode(path);180}181}182183// Erase knowledge about a child of this node. Called by removeNodeSpi.184private void removeChild(String child)185{186file.removeChildFromNode(path, child);187}188189190// AbstractPreferences implementation191@Override192protected String[] childrenNamesSpi()193throws BackingStoreException194{195String[] result = file.getChildrenForNode(path);196if (result == null) throw new BackingStoreException("Couldn't get list of children for node '" + path + "'");197return result;198}199200// AbstractPreferences implementation201@Override202protected String[] keysSpi()203throws BackingStoreException204{205String[] result = file.getKeysForNode(path);206if (result == null) throw new BackingStoreException("Couldn't get list of keys for node '" + path + "'");207return result;208}209210// AbstractPreferences implementation211@Override212protected AbstractPreferences childSpi(String name)213{214// Add to parent's child list here and disallow sync215// because parent and child might be in different files.216synchronized(MacOSXPreferencesFile.class) {217boolean isNew = file.addChildToNode(path, name);218return new MacOSXPreferences(this, name, isNew);219}220}221222// AbstractPreferences override223@Override224public void flush()225throws BackingStoreException226{227// Flush should *not* check for removal, unlike sync, but should228// prevent simultaneous removal.229synchronized(lock) {230if (isUser) {231if (!MacOSXPreferencesFile.flushUser()) {232throw new BackingStoreException("Synchronization failed for node '" + path + "'");233}234} else {235if (!MacOSXPreferencesFile.flushWorld()) {236throw new BackingStoreException("Synchronization failed for node '" + path + "'");237}238}239}240}241242// AbstractPreferences implementation243@Override244protected void flushSpi()245throws BackingStoreException246{247// nothing here - overridden flush() doesn't call this248}249250// AbstractPreferences override251@Override252public void sync()253throws BackingStoreException254{255synchronized(lock) {256if (isRemoved())257throw new IllegalStateException("Node has been removed");258// fixme! overkill259if (isUser) {260if (!MacOSXPreferencesFile.syncUser()) {261throw new BackingStoreException("Synchronization failed for node '" + path + "'");262}263} else {264if (!MacOSXPreferencesFile.syncWorld()) {265throw new BackingStoreException("Synchronization failed for node '" + path + "'");266}267}268}269}270271// AbstractPreferences implementation272@Override273protected void syncSpi()274throws BackingStoreException275{276// nothing here - overridden sync() doesn't call this277}278}279280281282