use mam reference instead of timestamp

This commit is contained in:
Daniel Gultsch 2017-05-07 21:05:35 +02:00
parent 185dac6953
commit 91db2023d3
8 changed files with 155 additions and 75 deletions

View File

@ -30,6 +30,7 @@ import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.xmpp.chatstate.ChatState; import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.mam.MamReference;
public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation> { public class Conversation extends AbstractEntity implements Blockable, Comparable<Conversation> {
@ -349,12 +350,16 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
return this.mFirstMamReference; return this.mFirstMamReference;
} }
public void setLastClearHistory(long time) { public void setLastClearHistory(long time,String reference) {
setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY,String.valueOf(time)); if (reference != null) {
setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, String.valueOf(time) + ":" + reference);
} else {
setAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, String.valueOf(time));
}
} }
public long getLastClearHistory() { public MamReference getLastClearHistory() {
return getLongAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY, 0); return MamReference.fromAttribute(getAttribute(ATTRIBUTE_LAST_CLEAR_HISTORY));
} }
public List<Jid> getAcceptedCryptoTargets() { public List<Jid> getAcceptedCryptoTargets() {
@ -468,11 +473,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
if (this.messages.size() == 0) { if (this.messages.size() == 0) {
Message message = new Message(this, "", Message.ENCRYPTION_NONE); Message message = new Message(this, "", Message.ENCRYPTION_NONE);
message.setType(Message.TYPE_STATUS); message.setType(Message.TYPE_STATUS);
message.setTime(Math.max(getCreated(),getLastClearHistory())); message.setTime(Math.max(getCreated(),getLastClearHistory().getTimestamp()));
return message; return message;
} else { } else {
Message message = this.messages.get(this.messages.size() - 1); return this.messages.get(this.messages.size() - 1);
return message;
} }
} }
@ -819,19 +823,19 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
} }
} }
public long getLastMessageTransmitted() { public MamReference getLastMessageTransmitted() {
final long last_clear = getLastClearHistory(); final MamReference lastClear = getLastClearHistory();
long last_received = 0; MamReference lastReceived = new MamReference(0);
synchronized (this.messages) { synchronized (this.messages) {
for(int i = this.messages.size() - 1; i >= 0; --i) { for(int i = this.messages.size() - 1; i >= 0; --i) {
Message message = this.messages.get(i); Message message = this.messages.get(i);
if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon()) { if (message.getStatus() == Message.STATUS_RECEIVED || message.isCarbon()) {
last_received = message.getTimeSent(); lastReceived = new MamReference(message.getTimeSent(),message.getServerMsgId());
break; break;
} }
} }
} }
return Math.max(last_clear,last_received); return MamReference.max(lastClear,lastReceived);
} }
public void setMutedTill(long value) { public void setMutedTill(long value) {

View File

@ -246,7 +246,9 @@ public class IqGenerator extends AbstractGenerator {
} else if (mam.getWith()!=null) { } else if (mam.getWith()!=null) {
data.put("with", mam.getWith().toString()); data.put("with", mam.getWith().toString());
} }
data.put("start", getTimestamp(mam.getStart())); if (mam.getStart() != 0) {
data.put("start", getTimestamp(mam.getStart()));
}
data.put("end", getTimestamp(mam.getEnd())); data.put("end", getTimestamp(mam.getEnd()));
data.submit(); data.submit();
query.addChild(data); query.addChild(data);

View File

@ -52,6 +52,7 @@ import eu.siacs.conversations.entities.ServiceDiscoveryResult;
import eu.siacs.conversations.utils.MimeUtils; import eu.siacs.conversations.utils.MimeUtils;
import eu.siacs.conversations.xmpp.jid.InvalidJidException; import eu.siacs.conversations.xmpp.jid.InvalidJidException;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.mam.MamReference;
public class DatabaseBackend extends SQLiteOpenHelper { public class DatabaseBackend extends SQLiteOpenHelper {
@ -830,7 +831,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return db.delete(Message.TABLENAME,where,whereArgs) > 0; return db.delete(Message.TABLENAME,where,whereArgs) > 0;
} }
public Pair<Long, String> getLastMessageReceived(Account account) { public MamReference getLastMessageReceived(Account account) {
Cursor cursor = null; Cursor cursor = null;
try { try {
SQLiteDatabase db = this.getReadableDatabase(); SQLiteDatabase db = this.getReadableDatabase();
@ -841,7 +842,7 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return null; return null;
} else { } else {
cursor.moveToFirst(); cursor.moveToFirst();
return new Pair<>(cursor.getLong(0), cursor.getString(1)); return new MamReference(cursor.getLong(0), cursor.getString(1));
} }
} catch (Exception e) { } catch (Exception e) {
return null; return null;
@ -866,23 +867,23 @@ public class DatabaseBackend extends SQLiteOpenHelper {
return time; return time;
} }
public Pair<Long,String> getLastClearDate(Account account) { public MamReference getLastClearDate(Account account) {
SQLiteDatabase db = this.getReadableDatabase(); SQLiteDatabase db = this.getReadableDatabase();
String[] columns = {Conversation.ATTRIBUTES}; String[] columns = {Conversation.ATTRIBUTES};
String selection = Conversation.ACCOUNT + "=?"; String selection = Conversation.ACCOUNT + "=?";
String[] args = {account.getUuid()}; String[] args = {account.getUuid()};
Cursor cursor = db.query(Conversation.TABLENAME,columns,selection,args,null,null,null); Cursor cursor = db.query(Conversation.TABLENAME,columns,selection,args,null,null,null);
long maxClearDate = 0; MamReference maxClearDate = new MamReference(0);
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
try { try {
final JSONObject jsonObject = new JSONObject(cursor.getString(0)); final JSONObject o = new JSONObject(cursor.getString(0));
maxClearDate = Math.max(maxClearDate, jsonObject.getLong(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY)); maxClearDate = MamReference.max(maxClearDate, MamReference.fromAttribute(o.getString(Conversation.ATTRIBUTE_LAST_CLEAR_HISTORY)));
} catch (Exception e) { } catch (Exception e) {
//ignored //ignored
} }
} }
cursor.close(); cursor.close();
return new Pair<>(maxClearDate,null); return maxClearDate;
} }
private Cursor getCursorForSession(Account account, AxolotlAddress contact) { private Cursor getCursorForSession(Account account, AxolotlAddress contact) {

View File

@ -19,6 +19,7 @@ import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded;
import eu.siacs.conversations.xmpp.OnIqPacketReceived; import eu.siacs.conversations.xmpp.OnIqPacketReceived;
import eu.siacs.conversations.xmpp.jid.Jid; import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.mam.MamReference;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
@ -46,34 +47,26 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
} }
} }
} }
final Pair<Long,String> lastMessageReceived = mXmppConnectionService.databaseBackend.getLastMessageReceived(account); MamReference mamReference = MamReference.max(
final Pair<Long,String> lastClearDate = mXmppConnectionService.databaseBackend.getLastClearDate(account); mXmppConnectionService.databaseBackend.getLastMessageReceived(account),
long startCatchup; mXmppConnectionService.databaseBackend.getLastClearDate(account)
final String reference; );
if (lastMessageReceived != null && lastMessageReceived.first >= lastClearDate.first) { mamReference = MamReference.max(mamReference,mXmppConnectionService.getAutomaticMessageDeletionDate());
startCatchup = lastMessageReceived.first;
reference = lastMessageReceived.second;
} else {
startCatchup = lastClearDate.first;
reference = null;
}
startCatchup = Math.max(startCatchup,mXmppConnectionService.getAutomaticMessageDeletionDate());
long endCatchup = account.getXmppConnection().getLastSessionEstablished(); long endCatchup = account.getXmppConnection().getLastSessionEstablished();
final Query query; final Query query;
if (startCatchup == 0) { if (mamReference.getTimestamp() == 0) {
return; return;
} else if (endCatchup - startCatchup >= Config.MAM_MAX_CATCHUP) { } else if (endCatchup - mamReference.getTimestamp() >= Config.MAM_MAX_CATCHUP) {
startCatchup = endCatchup - Config.MAM_MAX_CATCHUP; long startCatchup = endCatchup - Config.MAM_MAX_CATCHUP;
List<Conversation> conversations = mXmppConnectionService.getConversations(); List<Conversation> conversations = mXmppConnectionService.getConversations();
for (Conversation conversation : conversations) { for (Conversation conversation : conversations) {
if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted()) { if (conversation.getMode() == Conversation.MODE_SINGLE && conversation.getAccount() == account && startCatchup > conversation.getLastMessageTransmitted().getTimestamp()) {
this.query(conversation,startCatchup,true); this.query(conversation,startCatchup,true);
} }
} }
query = new Query(account, startCatchup, endCatchup); query = new Query(account, new MamReference(startCatchup), endCatchup);
} else { } else {
query = new Query(account, startCatchup, endCatchup); query = new Query(account, mamReference, endCatchup);
query.reference = reference;
} }
synchronized (this.queries) { synchronized (this.queries) {
this.queries.add(query); this.queries.add(query);
@ -82,9 +75,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
} }
public void catchupMUC(final Conversation conversation) { public void catchupMUC(final Conversation conversation) {
if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) { if (conversation.getLastMessageTransmitted().getTimestamp() < 0 && conversation.countMessages() == 0) {
query(conversation, query(conversation,
0, new MamReference(0),
System.currentTimeMillis(), System.currentTimeMillis(),
true); true);
} else { } else {
@ -96,9 +89,9 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
} }
public Query query(final Conversation conversation) { public Query query(final Conversation conversation) {
if (conversation.getLastMessageTransmitted() < 0 && conversation.countMessages() == 0) { if (conversation.getLastMessageTransmitted().getTimestamp() < 0 && conversation.countMessages() == 0) {
return query(conversation, return query(conversation,
0, new MamReference(0),
System.currentTimeMillis(), System.currentTimeMillis(),
false); false);
} else { } else {
@ -113,23 +106,27 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
return this.query(conversation,conversation.getLastMessageTransmitted(),end, allowCatchup); return this.query(conversation,conversation.getLastMessageTransmitted(),end, allowCatchup);
} }
public Query query(Conversation conversation, long start, long end, boolean allowCatchup) { public Query query(Conversation conversation, MamReference start, long end, boolean allowCatchup) {
synchronized (this.queries) { synchronized (this.queries) {
final Query query; final Query query;
final long startActual = Math.max(start,mXmppConnectionService.getAutomaticMessageDeletionDate()); final MamReference startActual = MamReference.max(start,mXmppConnectionService.getAutomaticMessageDeletionDate());
if (start==0) { if (start.getTimestamp() == 0) {
query = new Query(conversation, startActual, end, false); query = new Query(conversation, startActual, end, false);
query.reference = conversation.getFirstMamReference(); query.reference = conversation.getFirstMamReference();
} else { } else {
long maxCatchup = Math.max(startActual,System.currentTimeMillis() - Config.MAM_MAX_CATCHUP); if (allowCatchup) {
if (maxCatchup > startActual) { MamReference maxCatchup = MamReference.max(startActual, System.currentTimeMillis() - Config.MAM_MAX_CATCHUP);
Query reverseCatchup = new Query(conversation,startActual,maxCatchup,false); if (maxCatchup.greaterThan(startActual)) {
this.queries.add(reverseCatchup); Query reverseCatchup = new Query(conversation, startActual, maxCatchup.getTimestamp(), false);
this.execute(reverseCatchup); this.queries.add(reverseCatchup);
this.execute(reverseCatchup);
}
query = new Query(conversation, maxCatchup, end, allowCatchup);
} else {
query = new Query(conversation, startActual, end, false);
} }
query = new Query(conversation, maxCatchup, end, allowCatchup);
} }
if (start > end) { if (start.greaterThan(end)) {
return null; return null;
} }
this.queries.add(query); this.queries.add(query);
@ -305,27 +302,26 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
private boolean catchup = true; private boolean catchup = true;
public Query(Conversation conversation, long start, long end) { public Query(Conversation conversation, MamReference start, long end, boolean catchup) {
this(conversation.getAccount(), start, end); this(conversation.getAccount(),catchup ? start : start.timeOnly(),end);
this.conversation = conversation; this.conversation = conversation;
}
public Query(Conversation conversation, long start, long end, boolean catchup) {
this(conversation,start,end);
this.pagingOrder = catchup ? PagingOrder.NORMAL : PagingOrder.REVERSE; this.pagingOrder = catchup ? PagingOrder.NORMAL : PagingOrder.REVERSE;
this.catchup = catchup; this.catchup = catchup;
} }
public Query(Account account, long start, long end) { public Query(Account account, MamReference start, long end) {
this.account = account; this.account = account;
this.start = start; if (start.getReference() != null) {
this.reference = start.getReference();
} else {
this.start = start.getTimestamp();
}
this.end = end; this.end = end;
this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32); this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32);
} }
private Query page(String reference) { private Query page(String reference) {
Query query = new Query(this.account,this.start,this.end); Query query = new Query(this.account,new MamReference(this.start,reference),this.end);
query.reference = reference;
query.conversation = conversation; query.conversation = conversation;
query.totalCount = totalCount; query.totalCount = totalCount;
query.actualCount = actualCount; query.actualCount = actualCount;
@ -445,8 +441,10 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
builder.append(getWith().toString()); builder.append(getWith().toString());
} }
} }
builder.append(", start="); if (this.start != 0) {
builder.append(AbstractGenerator.getTimestamp(this.start)); builder.append(", start=");
builder.append(AbstractGenerator.getTimestamp(this.start));
}
builder.append(", end="); builder.append(", end=");
builder.append(AbstractGenerator.getTimestamp(this.end)); builder.append(AbstractGenerator.getTimestamp(this.end));
builder.append(", order="+pagingOrder.toString()); builder.append(", order="+pagingOrder.toString());

View File

@ -135,6 +135,7 @@ import eu.siacs.conversations.xmpp.jid.Jid;
import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager;
import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived; import eu.siacs.conversations.xmpp.jingle.OnJinglePacketReceived;
import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket;
import eu.siacs.conversations.xmpp.mam.MamReference;
import eu.siacs.conversations.xmpp.pep.Avatar; import eu.siacs.conversations.xmpp.pep.Avatar;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
@ -1644,10 +1645,10 @@ public class XmppConnectionService extends Service {
callback.onMoreMessagesLoaded(messages.size(), conversation); callback.onMoreMessagesLoaded(messages.size(), conversation);
} else if (conversation.hasMessagesLeftOnServer() } else if (conversation.hasMessagesLeftOnServer()
&& account.isOnlineAndConnected() && account.isOnlineAndConnected()
&& conversation.getLastClearHistory() == 0) { && conversation.getLastClearHistory().getTimestamp() == 0) {
if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam()) if ((conversation.getMode() == Conversation.MODE_SINGLE && account.getXmppConnection().getFeatures().mam())
|| (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) { || (conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().mamSupport())) {
MessageArchiveService.Query query = getMessageArchiveService().query(conversation, 0, timestamp, false); MessageArchiveService.Query query = getMessageArchiveService().query(conversation, new MamReference(0), timestamp, false);
if (query != null) { if (query != null) {
query.setCallback(callback); query.setCallback(callback);
callback.informUser(R.string.fetching_history_from_server); callback.informUser(R.string.fetching_history_from_server);
@ -2253,7 +2254,7 @@ public class XmppConnectionService extends Service {
x.addChild("history").setAttribute("maxchars", "0"); x.addChild("history").setAttribute("maxchars", "0");
} else { } else {
// Fallback to muc history // Fallback to muc history
x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted())); x.addChild("history").setAttribute("since", PresenceGenerator.getTimestamp(conversation.getLastMessageTransmitted().getTimestamp()));
} }
sendPresencePacket(account, packet); sendPresencePacket(account, packet);
if (onConferenceJoined != null) { if (onConferenceJoined != null) {
@ -3664,15 +3665,19 @@ public class XmppConnectionService extends Service {
} }
public void clearConversationHistory(final Conversation conversation) { public void clearConversationHistory(final Conversation conversation) {
long clearDate; final long clearDate;
final String reference;
if (conversation.countMessages() > 0) { if (conversation.countMessages() > 0) {
clearDate = conversation.getLatestMessage().getTimeSent() + 1000; Message latestMessage = conversation.getLatestMessage();
clearDate = latestMessage.getTimeSent() + 1000;
reference = latestMessage.getServerMsgId();
} else { } else {
clearDate = System.currentTimeMillis(); clearDate = System.currentTimeMillis();
reference = null;
} }
conversation.clearMessages(); conversation.clearMessages();
conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam conversation.setHasMessagesLeftOnServer(false); //avoid messages getting loaded through mam
conversation.setLastClearHistory(clearDate); conversation.setLastClearHistory(clearDate,reference);
Runnable runnable = new Runnable() { Runnable runnable = new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@ -1350,7 +1350,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
private boolean showLoadMoreMessages(final Conversation c) { private boolean showLoadMoreMessages(final Conversation c) {
final boolean mam = hasMamSupport(c); final boolean mam = hasMamSupport(c);
final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService(); final MessageArchiveService service = activity.xmppConnectionService.getMessageArchiveService();
return mam && (c.getLastClearHistory() != 0 || (c.countMessages() == 0 && c.messagesLoaded.get() && c.hasMessagesLeftOnServer() && !service.queryInProgress(c))); return mam && (c.getLastClearHistory().getTimestamp() != 0 || (c.countMessages() == 0 && c.messagesLoaded.get() && c.hasMessagesLeftOnServer() && !service.queryInProgress(c)));
} }
private boolean hasMamSupport(final Conversation c) { private boolean hasMamSupport(final Conversation c) {

View File

@ -65,6 +65,7 @@ import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.GeoHelper; import eu.siacs.conversations.utils.GeoHelper;
import eu.siacs.conversations.utils.Patterns; import eu.siacs.conversations.utils.Patterns;
import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.utils.UIHelper;
import eu.siacs.conversations.xmpp.mam.MamReference;
public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler { public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextView.CopyHandler {
@ -561,16 +562,16 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
} }
private void loadMoreMessages(Conversation conversation) { private void loadMoreMessages(Conversation conversation) {
conversation.setLastClearHistory(0); conversation.setLastClearHistory(0,null);
activity.xmppConnectionService.updateConversation(conversation); activity.xmppConnectionService.updateConversation(conversation);
conversation.setHasMessagesLeftOnServer(true); conversation.setHasMessagesLeftOnServer(true);
conversation.setFirstMamReference(null); conversation.setFirstMamReference(null);
long timestamp = conversation.getLastMessageTransmitted(); long timestamp = conversation.getLastMessageTransmitted().getTimestamp();
if (timestamp == 0) { if (timestamp == 0) {
timestamp = System.currentTimeMillis(); timestamp = System.currentTimeMillis();
} }
conversation.messagesLoaded.set(true); conversation.messagesLoaded.set(true);
MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(conversation, 0, timestamp, false); MessageArchiveService.Query query = activity.xmppConnectionService.getMessageArchiveService().query(conversation, new MamReference(0), timestamp, false);
if (query != null) { if (query != null) {
Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show(); Toast.makeText(activity, R.string.fetching_history_from_server, Toast.LENGTH_LONG).show();
} else { } else {

View File

@ -0,0 +1,69 @@
package eu.siacs.conversations.xmpp.mam;
public class MamReference {
private final long timestamp;
private final String reference;
public MamReference(long timestamp) {
this.timestamp = timestamp;
this.reference = null;
}
public MamReference(long timestamp, String reference) {
this.timestamp = timestamp;
this.reference = reference;
}
public long getTimestamp() {
return timestamp;
}
public String getReference() {
return reference;
}
public boolean greaterThan(MamReference b) {
return timestamp > b.getTimestamp();
}
public boolean greaterThan(long b) {
return timestamp > b;
}
public static MamReference max(MamReference a, MamReference b) {
if (a != null && b != null) {
return a.timestamp > b.timestamp ? a : b;
} else if (a != null) {
return a;
} else {
return b;
}
}
public static MamReference max(MamReference a, long b) {
return max(a,new MamReference(b));
}
public static MamReference fromAttribute(String attr) {
if (attr == null) {
return new MamReference(0);
} else {
String[] attrs = attr.split(":");
try {
long timestamp = Long.parseLong(attrs[0]);
if (attrs.length >= 2) {
return new MamReference(timestamp,attrs[1]);
} else {
return new MamReference(timestamp);
}
} catch (Exception e) {
return new MamReference(0);
}
}
}
public MamReference timeOnly() {
return reference == null ? this : new MamReference(timestamp);
}
}