package com.scrivenvar.preferences;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.*;
import java.util.prefs.AbstractPreferences;
import java.util.prefs.BackingStoreException;
public class FilePreferences extends AbstractPreferences {
private final Map<String, String> mRoot = new TreeMap<>();
private final Map<String, FilePreferences> mChildren = new TreeMap<>();
private boolean mRemoved;
private final Object mMutex = new Object();
public FilePreferences( final AbstractPreferences parent,
final String name ) {
super( parent, name );
try {
sync();
} catch( final BackingStoreException ex ) {
error( ex );
}
}
@Override
protected void putSpi( final String key, final String value ) {
mRoot.put( key, value );
try {
flush();
} catch( final BackingStoreException ex ) {
error( ex );
}
}
@Override
protected String getSpi( final String key ) {
return mRoot.get( key );
}
@Override
protected void removeSpi( final String key ) {
mRoot.remove( key );
try {
flush();
} catch( final BackingStoreException ex ) {
error( ex );
}
}
@Override
protected void removeNodeSpi() throws BackingStoreException {
mRemoved = true;
flush();
}
@Override
protected String[] keysSpi() {
return mRoot.keySet().toArray( new String[ 0 ] );
}
@Override
protected String[] childrenNamesSpi() {
return mChildren.keySet().toArray( new String[ 0 ] );
}
@Override
protected FilePreferences childSpi( final String name ) {
FilePreferences child = mChildren.get( name );
if( child == null || child.isRemoved() ) {
child = new FilePreferences( this, name );
mChildren.put( name, child );
}
return child;
}
@Override
protected void syncSpi() {
if( isRemoved() ) {
return;
}
final File file = FilePreferencesFactory.getPreferencesFile();
if( !file.exists() ) {
return;
}
synchronized( mMutex ) {
final Properties p = new Properties();
try {
p.load( new FileInputStream( file ) );
final String path = getPath();
final Enumeration<?> pnen = p.propertyNames();
while( pnen.hasMoreElements() ) {
final String propKey = (String) pnen.nextElement();
if( propKey.startsWith( path ) ) {
final String subKey = propKey.substring( path.length() );
if( subKey.indexOf( '.' ) == -1 ) {
mRoot.put( subKey, p.getProperty( propKey ) );
}
}
}
} catch( final Exception ex ) {
error( new BackingStoreException( ex ) );
}
}
}
private String getPath() {
final FilePreferences parent = (FilePreferences) parent();
return parent == null ? "" : parent.getPath() + name() + '.';
}
@Override
protected void flushSpi() {
final File file = FilePreferencesFactory.getPreferencesFile();
synchronized( mMutex ) {
final Properties p = new Properties();
try {
final String path = getPath();
if( file.exists() ) {
p.load( new FileInputStream( file ) );
final List<String> toRemove = new ArrayList<>();
final Enumeration<?> pnen = p.propertyNames();
while( pnen.hasMoreElements() ) {
String propKey = (String) pnen.nextElement();
if( propKey.startsWith( path ) ) {
final String subKey = propKey.substring( path.length() );
if( subKey.indexOf( '.' ) == -1 ) {
toRemove.add( propKey );
}
}
}
for( final String propKey : toRemove ) {
p.remove( propKey );
}
}
if( !mRemoved ) {
for( final String s : mRoot.keySet() ) {
p.setProperty( path + s, mRoot.get( s ) );
}
}
p.store( new FileOutputStream( file ), "FilePreferences" );
} catch( final Exception ex ) {
error( new BackingStoreException( ex ) );
}
}
}
private void error( final BackingStoreException ex ) {
throw new RuntimeException( ex );
}
}