// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import org.openstreetmap.josm.Main;
/**
* Use this class if you want to cache and store a single file that gets updated regularly.
* Unless you flush() it will be kept in memory. If you want to cache a lot of data and/or files,
* use CacheFiles
* @author xeen
*
*/
public abstract class CacheCustomContent {
/**
* Common intervals
*/
final static public int INTERVAL_ALWAYS = -1;
final static public int INTERVAL_HOURLY = 60*60;
final static public int INTERVAL_DAILY = INTERVAL_HOURLY * 24;
final static public int INTERVAL_WEEKLY = INTERVAL_DAILY * 7;
final static public int INTERVAL_MONTHLY = INTERVAL_WEEKLY * 4;
final static public int INTERVAL_NEVER = Integer.MAX_VALUE;
/**
* Where the data will be stored
*/
private byte[] data = null;
/**
* The ident that identifies the stored file. Includes file-ending.
*/
final private String ident;
/**
* The (file-)path where the data will be stored
*/
final private File path;
/**
* How often to update the cached version
*/
final private int updateInterval;
/**
* This function will be executed when an update is required. It has to be implemented by the
* inheriting class and should use a worker if it has a long wall time as the function is
* executed in the current thread.
* @return the data to cache
*/
protected abstract byte[] updateData();
/**
* This function serves as a comfort hook to perform additional checks if the cache is valid
* @return True if the cached copy is still valid
*/
protected boolean isCacheValid() {
return true;
}
/**
* Initializes the class. Note that all read data will be stored in memory until it is flushed
* by flushData().
* @param ident
* @param updateInterval
*/
public CacheCustomContent(String ident, int updateInterval) {
this.ident = ident;
this.updateInterval = updateInterval;
this.path = new File(Main.pref.getPreferencesDir(), ident);
}
/**
* Updates data if required
* @return Returns the data
*/
public byte[] updateIfRequired() {
if(Main.pref.getInteger("cache." + ident, 0) + updateInterval < new Date().getTime()/1000
|| !isCacheValid())
return updateForce();
return getData();
}
/**
* Updates data if required
* @return Returns the data as string
*/
public String updateIfRequiredString() {
if(Main.pref.getInteger("cache." + ident, 0) + updateInterval < new Date().getTime()/1000
|| !isCacheValid())
return updateForceString();
return getDataString();
}
/**
* Executes an update regardless of updateInterval
* @return Returns the data
*/
public byte[] updateForce() {
this.data = updateData();
saveToDisk();
Main.pref.putInteger("cache." + ident, (int)(new Date().getTime()/1000));
return data;
}
/**
* Executes an update regardless of updateInterval
* @return Returns the data as String
*/
public String updateForceString() {
updateForce();
try {
return new String(data,"utf-8");
} catch(UnsupportedEncodingException e){
e.printStackTrace();
return "";
}
}
/**
* Returns the data without performing any updates
* @return the data
*/
public byte[] getData() {
if(data == null) {
loadFromDisk();
}
return data;
}
/**
* Returns the data without performing any updates
* @return the data as String
*/
public String getDataString() {
try {
return new String(getData(), "utf-8");
} catch(UnsupportedEncodingException e){
e.printStackTrace();
return "";
}
}
/**
* Tries to load the data using the given ident from disk. If this fails, data will be updated
*/
private void loadFromDisk() {
try {
BufferedInputStream input = new BufferedInputStream(new FileInputStream(path));
this.data = new byte[input.available()];
input.read(this.data);
input.close();
} catch(IOException e) {
this.data = updateForce();
}
}
/**
* Stores the data to disk
*/
private void saveToDisk() {
try {
BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(path));
output.write(this.data);
output.flush();
output.close();
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* Flushes the data from memory. Class automatically reloads it from disk or updateData() if
* required
*/
public void flushData() {
data = null;
}
}