// messageBoard.java
// -------------------------------------
// (C) by Michael Peter Christen; mc@yacy.net
// first published on http://www.anomic.de
// Frankfurt, Germany, 2004
// last major change: 28.06.2004
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package net.yacy.data;
import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import net.yacy.cora.document.encoding.UTF8;
import net.yacy.cora.order.Base64Order;
import net.yacy.cora.order.NaturalOrder;
import net.yacy.cora.util.ConcurrentLog;
import net.yacy.cora.util.SpaceExceededException;
import net.yacy.kelondro.blob.MapHeap;
public class MessageBoard {
private static final int categoryLength = 12;
private static final String dateFormat = "yyyyMMddHHmmss";
private static final SimpleDateFormat SimpleFormatter = new SimpleDateFormat(dateFormat, Locale.US);
static {
SimpleFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
}
private MapHeap database = null;
private int sn = 0;
public MessageBoard(final File path) throws IOException {
new File(path.getParent()).mkdir();
if (database == null) {
//database = new MapView(BLOBTree.toHeap(path, true, true, categoryLength + dateFormat.length() + 2, recordSize, '_', NaturalOrder.naturalOrder, pathNew), 500, '_');
database = new MapHeap(path, categoryLength + dateFormat.length() + 2, NaturalOrder.naturalOrder, 1024 * 64, 500, '_');
}
sn = 0;
}
public int size() {
return database.size();
}
public synchronized void close() {
database.close();
}
static String dateString() {
synchronized (SimpleFormatter) {
return SimpleFormatter.format(new Date());
}
}
String snString() {
String s = Integer.toString(sn);
if (s.length() == 1) s = "0" + s;
sn++;
if (sn > 99) sn = 0;
return s;
}
public entry newEntry(final String category,
final String authorName, final String authorHash,
final String recName, final String recHash,
final String subject, final byte[] message) {
return new entry(category, authorName, authorHash, recName, recHash, subject, message);
}
public class entry {
String key; // composed by category and date
Map<String, String> record; // contains author, target hash, subject and message
public entry(final String category,
String authorName, String authorHash,
String recName, String recHash,
String subject, final byte[] message) {
record = new HashMap<String, String>();
key = category;
if (key.length() > categoryLength) key = key.substring(0, categoryLength);
while (key.length() < categoryLength) key += "_";
key += dateString() + snString();
record.put("author", ((authorName != null) && (!authorName.isEmpty())) ? authorName : "anonymous");
record.put("recipient", ((recName != null) && (!recName.isEmpty())) ? recName : "anonymous");
record.put("ahash", (authorHash != null) ? authorHash : "");
record.put("rhash", (recHash != null) ? recHash : "");
record.put("subject", (subject != null) ? subject : "");
record.put("message", (message == null) ? "" : Base64Order.enhancedCoder.encode(message));
record.put("read", "false");
}
entry(final String key, final Map<String, String> record) {
this.key = key;
this.record = record;
}
public Date date() {
try {
String c = key.substring(categoryLength);
c = c.substring(0, c.length() - 2);
synchronized (SimpleFormatter) {
return SimpleFormatter.parse(c);
}
} catch (final ParseException e) {
return new Date();
}
}
public String category() {
String c = key.substring(0, categoryLength);
while (c.endsWith("_")) c = c.substring(0, c.length() - 1);
return c;
}
public String author() {
final String a = record.get("author");
if (a == null) return "anonymous";
return a;
}
public String recipient() {
final String a = record.get("recipient");
if (a == null) return "anonymous";
return a;
}
public String authorHash() {
final String a = record.get("ahash");
return a;
}
public String recipientHash() {
final String a = record.get("rhash");
return a;
}
public String subject() {
final String s = record.get("subject");
if (s == null) return "";
return s;
}
public byte[] message() {
final String m = record.get("message");
if (m == null) return new byte[0];
record.put("read", "true");
return Base64Order.enhancedCoder.decode(m);
}
public boolean read() {
final String r = record.get("read");
if (r == null) return false;
if (r.equals("false")) return false;
return true;
}
}
public String write(final entry message) {
// writes a message and returns key
try {
database.insert(UTF8.getBytes(message.key), message.record);
return message.key;
} catch (final Exception e) {
ConcurrentLog.logException(e);
return null;
}
}
public entry read(final String key) {
Map<String, String> record;
try {
record = database.get(UTF8.getBytes(key));
} catch (final IOException e) {
ConcurrentLog.logException(e);
return null;
} catch (final SpaceExceededException e) {
ConcurrentLog.logException(e);
return null;
}
return new entry(key, record);
}
public void remove(final String key) {
try {
database.delete(UTF8.getBytes(key));
} catch (final IOException e) {
}
}
public Iterator<String> keys(final String category, final boolean up) throws IOException {
//return database.keys();
return new catIter(category, up);
}
public class catIter implements Iterator<String> {
private Iterator<byte[]> allIter = null;
private String nextKey = null;
private String category = "";
public catIter(final String category, final boolean up) throws IOException {
this.allIter = database.keys(up, false);
this.category = category;
findNext();
}
public void findNext() {
while (allIter.hasNext()) {
nextKey = UTF8.String(allIter.next());
if (this.category == null || nextKey.startsWith(this.category)) return;
}
nextKey = null;
}
@Override
public boolean hasNext() {
return nextKey != null;
}
@Override
public String next() {
final String next = nextKey;
findNext();
return next;
}
@Override
public void remove() {
}
}
}