/*
* BasicProperties.java
* (FScape)
*
* Copyright (c) 2001-2016 Hanns Holger Rutz. All rights reserved.
*
* This software is published under the GNU General Public License v3+
*
*
* For further information, please contact Hanns Holger Rutz at
* contact@sciss.de
*/
package de.sciss.fscape.prop;
import de.sciss.fscape.Application;
import de.sciss.fscape.util.PrefsUtil;
import de.sciss.io.AudioFile;
import de.sciss.io.AudioFileDescr;
import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.util.NoSuchElementException;
import java.util.Properties;
import java.util.StringTokenizer;
/**
* Abgeleitete Properties-Klasse zur Verwaltung von Presets
* Einem Key-Namen stet als Value eine Properties-Liste gegenueber, die automatisch
* in einen "String" umgewandelt wird (durch Ersetzen der Linefeeds)
*/
public class BasicProperties
extends Properties {
// -------- private variables --------
private File f;
protected static final String header = "Created by FScape; do not edit manually!";
// true, wenn Presets seit dem letzten Speichern geaendert
protected boolean modified = false;
// -------- public methods --------
/**
* Legt das Presets-Objekt an mit entsprechenden Default-Eintraegen
*
* @param owner Inhaber der Preferences (daraus wird der Name abgeleitet)
* @param defProp voreingestellter Preset
* @param typeName such as "presets" or "prefs"
*/
public BasicProperties( Class owner, Properties defProp, String typeName )
{
// default Properties, der Rest wird von oben gemanagt
super( defProp );
String ownName, presetsName;
int i;
ownName = owner.getName();
i = ownName.lastIndexOf( '.' ) + 1; // find last part of the name
presetsName = ownName.substring( i );
try {
// fileName = FileManager.findFolder( 0x70726566 ) + File.separator+ "FScape" + File.separator + typeName + File.separator + presetsName + '.' + typeName;
f = new File( System.getProperty( "user.home" ) + File.separator+ "FScape" + File.separator + typeName + File.separator + presetsName + '.' + typeName );
load();
} catch( IOException e ) {
// nothing
}
}
/**
* Constructs empty Properties object
*/
public BasicProperties( Properties defProp, File f )
{
super( defProp );
this.f = f;
}
public boolean isModified()
{
return modified;
}
/**
* Loads FScape document from disk
*/
public void load()
throws IOException {
synchronized (this) {
final BufferedReader dis = new BufferedReader(new InputStreamReader(new FileInputStream(f)));
String line = "";
do {
line = dis.readLine();
if (!line.startsWith("#")) {
final String message;
if (AudioFile.retrieveType(f) == AudioFileDescr.TYPE_UNKNOWN) {
message = "Unknown file format";
} else {
message = "This is an audio file.\nYou don't open audio files via the File menu!";
}
throw new IOException("This file is not an FScape document.\n" + message);
}
} while (!line.contains(header));
load(dis);
modified = false;
}
}
/**
* Sichert die Presets auf der Festplatte
*
* @param force wenn false, wird nur gespeichert, wenn es Modifikationen gab
*/
public void store( boolean force )
throws IOException
{
File f2, p;
synchronized( this ) {
if( force || modified ) {
if( f.exists() && Application.userPrefs.getBoolean(PrefsUtil.KEY_BACKUP, false)) {
f2 = new File(Application.userPrefs.get(PrefsUtil.KEY_BAKDIR, ""), f.getName() );
copyFile( f, f2 );
}
try {
store( new FileOutputStream( f ), header );
modified = false;
}
catch( IOException e ) {
p = f.getParentFile();
p.mkdirs();
// retry after folder-creation
store( new FileOutputStream( f ), header );
modified = false;
}
// try {
// MRJAdapter.setFileType( f.getAbsoluteFile(), "FScP" ); // Constants.OSTypePrefs );
// } catch( IOException e ) { /* ignored */} // Filetype nicht wichtig
}
}
}
public void store()
throws IOException
{
store( false );
}
private static void copyFile( File source, File target )
throws IOException
{
if( target.exists() ) target.delete();
RandomAccessFile sourceRAF = new RandomAccessFile( source, "r" );
RandomAccessFile targetRAF = new RandomAccessFile( target, "rw" );
byte[] buffer = new byte[ 16768 ];
int len;
long pos = 0;
long totLen = sourceRAF.length();
while( pos < totLen ) {
len = (int) Math.min( 16768, totLen - pos );
sourceRAF.readFully( buffer, 0, len );
targetRAF.write( buffer, 0, len );
pos += len;
}
sourceRAF.close();
targetRAF.close();
}
/**
* Fuellt eine Property mit dem uebergebenen String
*
* @return vorheriger Wert
*/
public Object setProperty( String key, String val )
{
return put( key, val );
}
public Object put( Object key, Object val )
{
synchronized( this ) {
modified = true; // indicate that prefs should be saved on exit
return super.put( key, val );
}
}
// don't know why we need this...
public Object put( String key, String val )
{
synchronized( this ) {
modified = true; // indicate that prefs should be saved on exit
return super.put( key, val );
}
}
// ---------- statische Utility methoden ----------
/**
* Fuellt eine Property mit den uebergebenen Punkt-Koordinaten
*/
public static Object setPointProperty( Properties p, String key, Point pt )
{
return p.put( key, "" + pt.x + "," + pt.y );
}
/**
* Fuellt eine Property mit den uebergebenen Dimensionen
*/
public static Object setDimensionProperty( Properties p, String key, Dimension dim )
{
return p.put( key, "" + dim.width + "," + dim.height );
}
/**
* Fuellt eine Property mit den Koordinaten eines Rechtecks
*/
public static Object setRectangleProperty( Properties p, String key, Rectangle rect )
{
return p.put( key, "" + rect.x + "," + rect.y + "," + rect.width + "," + rect.height );
}
/**
* Besorgt ein Property der Form "x,y" als Point Objekt
*
* @return entsprechenden Punkt oder null bei Fehler
*/
public static Point getPointProperty( Properties p, String key )
{
int x, y;
StringTokenizer valTok;
String val = p.getProperty( key );
try {
if( val == null ) throw new NoSuchElementException();
valTok = new StringTokenizer( val, "," );
x = Integer.parseInt( valTok.nextToken() );
y = Integer.parseInt( valTok.nextToken() );
return new Point( x, y );
} catch( NoSuchElementException e1 ) {
return null;
} catch( NumberFormatException e2 ) {
return null;
}
}
/**
* Besorgt ein Property der Form "breite,hoehe" als Dimension Objekt
*
* @return entsprechende Dimension oder null bei Fehler
*/
public static Dimension getDimensionProperty( Properties p, String key )
{
int width, height;
StringTokenizer valTok;
String val = p.getProperty( key );
try {
if( val == null ) throw new NoSuchElementException();
valTok = new StringTokenizer( val, "," );
width = Integer.parseInt( valTok.nextToken() );
height = Integer.parseInt( valTok.nextToken() );
return new Dimension( width, height );
} catch( NoSuchElementException e1 ) {
return null;
} catch( NumberFormatException e2 ) {
return null;
}
}
/**
* Besorgt ein Property der Form "x,y,breite,hoehe" als Rectangle Objekt
*
* @return entsprechendes Rechteck oder null bei Fehler
*/
public static Rectangle getRectangleProperty( Properties p, String key )
{
int x, y, width, height;
StringTokenizer valTok;
String val = p.getProperty( key );
try {
if( val == null ) throw new NoSuchElementException();
valTok = new StringTokenizer( val, "," );
x = Integer.parseInt( valTok.nextToken() );
y = Integer.parseInt( valTok.nextToken() );
width = Integer.parseInt( valTok.nextToken() );
height = Integer.parseInt( valTok.nextToken() );
return new Rectangle( x, y, width, height );
} catch( NoSuchElementException e1 ) {
return null;
} catch( NumberFormatException e2 ) {
return null;
}
}
}