package edu.uw.cs.cse461.SNet; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import edu.uw.cs.cse461.DB461.DB461SQLite; import edu.uw.cs.cse461.Net.DDNS.DDNSFullName; import edu.uw.cs.cse461.util.Log; /** * Abstracts the underlying sqlite database into a set of easy-to-use methods. *
* ONLY THE THREAD THAT OPENS AN SQLITE4JAVA DATABASE CAN OPERATE ON IT. * This means you should not cache a PhotoManager. Instead, create one * one entry to your code, use it while that thread executes, and then discard * it before returning. *
* The db has two tables: *
*
* @author zahorjan
*
*/
public class SNetDB461 extends DB461SQLite {
private static final String TAG="SNetDB461";
private final String mDBFilename;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Community table and record types
private static String[] communityFields[] = { {"name", "TEXT"},
{"generation", "INTEGER"},
{"myhash", "INTEGER"},
{"chosenhash", "INTEGER"},
{"isfriend", "BOOLEAN"}
};
public class CommunityTable extends DB461Table
* Fields:
*
* Fields:
*
* This is not intended to supplant write()! It's here only because you should
* make sure the root and the local host are both members every time you start the
* app (since the DB might not exist - e.g., the first run).
* @param user
*/
public void registerMember(DDNSFullName member) throws DB461Exception {
CommunityRecord r = COMMUNITYTABLE.readOne(member.toString());
if ( r != null ) return;
r = COMMUNITYTABLE.createRecord();
r.name = member;
r.generation = -1;
r.myPhotoHash = 0; // 0 is used to indicate no photo. What are the odds some photo will hash to 0?
r.chosenPhotoHash = 0;
COMMUNITYTABLE.write(r);
}
/**
* Checks db for consistency violations and tries to fix them.
* Consistency requirements:
*
*
* @author zahorjan
*
*/
public class CommunityRecord extends Record implements Comparable
*
* @author zahorjan
*
*/
public class PhotoRecord extends Record {
public int hash;
public int refCount;
public File file;
@Override
protected PhotoRecord initialize(DBRecordIterator it) throws DB461Exception {
try {
hash = it.getInt(0);
refCount = it.getInt(1);
String fullPath = it.getString(2);
if ( fullPath.equals("null")) file = null;
else file = new File(fullPath);
} catch (Exception e) {
throw new DB461Exception( "photo record initialization caught exception: " + e.getMessage() );
}
return this;
}
@Override
public String toString() {
final String SEP = " ";
StringBuilder sb = new StringBuilder();
sb.append("[")
.append(SEP).append("Hash: ").append(hash)
.append(SEP).append("RefCount: ").append(refCount)
.append(SEP).append("Filename: ").append(file)
.append("]");
return sb.toString();
}
}
// Photo table and record definitions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
// Photo class
/**
* Utility class that wraps a photo file and provides handy functionality.
* @author zahorjan
*
*/
static public class Photo {
private int mHash; // the hash value of the file's contents
private File mPhotoFile; // handle to the file
/**
* Wraps a photo file. If the source file isn't in our own gallery, creates a
* copy there.
* @param photoFile
* @throws IOException
* @throws FileNotFoundException
*/
public Photo(File photoFile) throws IOException, FileNotFoundException {
mPhotoFile = photoFile;
mHash = _computeHash(mPhotoFile);
}
/**
* You might sometimes know the hash and not want to recompute it just to create a Photo object.
* @param hash
* @param file
*/
public Photo(int hash, File file) {
mHash = hash;
mPhotoFile = new File(file.getAbsolutePath());
}
public int hash() { return mHash; }
public File file() { return mPhotoFile; }
/**
* Set hash based on contents in file named by the Photo object.
* @param photo
*/
private static int _computeHash(File photoFile) throws IOException, FileNotFoundException {
final int BUFLEN = 4096;
byte buffer[] = new byte[BUFLEN];
int read;
FileInputStream in = new FileInputStream(photoFile.getAbsolutePath());
try {
MessageDigest md = MessageDigest.getInstance("MD5");
while ( (read = in.read(buffer)) >= 0 ) {
if ( read > 0 ) {
md.update(buffer, 0, read);
}
else Log.w(TAG, "_setHash read 0 bytes");
}
return ByteBuffer.wrap(md.digest()).getInt();
} catch (NoSuchAlgorithmException e) {
System.err.println("_computeHash: no such algorithm: MD5");
}
finally {
if ( in != null ) in.close();
}
return 0;
}
@Override
public String toString() {
return "[Hash: " + String.format("%0x", hash()) + " File: " + mPhotoFile + " Length: " + mPhotoFile.length() + "]";
}
}
// Photo class
//------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------
/**
* If the database doesn't already exist, creates it and creates the two tables it uses.
* (No content is added to the tables.) Otherwise, opens the existing database.
* @throws DB461Exception
*/
public SNetDB461(String dbFilename) throws DB461Exception {
super(dbFilename);
mDBFilename = dbFilename;
Log.d(TAG, "dbFilename = " + dbFilename);
PHOTOTABLE = new PhotoTable();
addTable(PHOTOTABLE);
COMMUNITYTABLE = new CommunityTable();
addTable(COMMUNITYTABLE);
try {
openOrCreateDatabase();
} catch (Exception e) {
String msg = "Couldn't open or create db: " + e.getMessage();
Log.e(TAG, msg);
throw new DB461Exception(msg);
}
}
public String dbFilename() {
return mDBFilename;
}
/**
* Because of Java restrictions on nested classes, this record has be created by an SNetDB461 object.
* @return
*/
public CommunityRecord createCommunityRecord() {
return new CommunityRecord();
}
/**
* Because of Java restrictions on nested classes, this record has be created by an SNetDB461 object.
* @return
*/
public PhotoRecord createPhotoRecord() {
return new PhotoRecord();
}
/**
* When you're done using an SNetDB461, it's good practice to call discard().
*/
public void discard() {
close();
}
/**
* Called only to make sure that the a member is registered. If s/he already is,
* has no effect. Otherwise, creates a record for the user initialized to
* indicate no information is available.
*
*
*/
public void checkAndFixDB(File galleryDir) throws DB461Exception {
HashMap