package org.openntf.domino.big.impl; import static org.openntf.domino.big.NoteCoordinate.Utils.getLongFromReplid; import static org.openntf.domino.big.NoteCoordinate.Utils.getLongsFromUnid; import static org.openntf.domino.big.NoteCoordinate.Utils.getReplidFromLong; import static org.openntf.domino.big.NoteCoordinate.Utils.getUnidFromLongs; import static org.openntf.domino.big.NoteCoordinate.Utils.insertByteArray; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.Map; import javolution.util.FastMap; import org.openntf.domino.Database; import org.openntf.domino.DbDirectory; import org.openntf.domino.Document; import org.openntf.domino.NoteCollection; import org.openntf.domino.Session; import org.openntf.domino.View; import org.openntf.domino.types.Null; import org.openntf.domino.utils.Factory; import org.openntf.domino.utils.Factory.SessionType; import com.google.common.primitives.Longs; /* * NTF This class stores information on where to find a note in a simple 3-long address * These can be easily converted to replicaids and UNIDs to find the exact document across * an entire enterprise */ public class NoteCoordinate implements org.openntf.domino.big.NoteCoordinate { private static ThreadLocal<byte[]> extreadbuffer_ = new ThreadLocal<byte[]>() { @Override protected byte[] initialValue() { return new byte[24]; } }; long db; long x; long y; transient Boolean isView_; transient private Map<String, Object> propertyCache; transient private Database database_; transient private Document document_; //TODO NTF we should probably have a factory that creates these instead of instantiating them directly public NoteCoordinate(final long db, final long x, final long y) { this.db = db; this.x = x; this.y = y; } public NoteCoordinate(final byte[] bytes) { if (bytes.length >= 24) { this.db = Longs.fromBytes(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]); this.x = Longs.fromBytes(bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); this.y = Longs.fromBytes(bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]); } else { throw new IllegalArgumentException("Can't construct new NoteCoordinate from byte length of " + bytes.length); } } public NoteCoordinate(final CharSequence metaversalid) { try { this.db = getLongFromReplid(metaversalid.subSequence(0, 16)); long[] unids = getLongsFromUnid(metaversalid.subSequence(16, 48)); this.x = unids[0]; this.y = unids[1]; } catch (Throwable t) { System.err.println("Unable to create a NoteCoordinate from character sequence: " + metaversalid); throw new RuntimeException(t); } } public NoteCoordinate(final CharSequence replicaid, final CharSequence unid) { this.db = getLongFromReplid(replicaid); long[] unids = getLongsFromUnid(unid); this.x = unids[0]; this.y = unids[1]; } public NoteCoordinate(final Document doc) { this(doc.getAncestorDatabase().getReplicaID(), doc.getUniversalID()); } public NoteCoordinate(final View view) { this(view.getAncestorDatabase().getReplicaID(), view.getUniversalID()); isView_ = true; } public NoteCoordinate(final NoteCollection notecoll, final String nid) { this(notecoll.getAncestorDatabase().getReplicaID(), notecoll.getUNID(nid)); } @Override public String getReplicaId() { return getReplidFromLong(db); } @Override public Long getReplicaLong() { return db; } protected long getDbid() { return db; } @Override public String getUNID() { return getUnidFromLongs(x, y); } protected Database getDatabase(final String server) { String replid = getReplidFromLong(db); Session session = Factory.getSession(SessionType.CURRENT); DbDirectory dir = session.getDbDirectory(server); return dir.openDatabaseByReplicaID(replid); } @Override public Document getDocument() { return getDocument(""); } @Override public Document getDocument(final String serverName) { return getDatabase(serverName).getDocumentByUNID(getUNID(), true); } public View getView() { Database database = getDatabase(""); Document doc = getDocument(""); return database.getView(doc); } private Map<String, Object> getPropertyCache() { if (propertyCache == null) { propertyCache = new FastMap<String, Object>(); } return propertyCache; } @Override public Object get(final String key) { Object result = getPropertyCache().get(key); if (result == null) { result = getDocument().get(key); if (result == null) { getPropertyCache().put(key, Null.INSTANCE); return null; } else { getPropertyCache().put(key, result); return result; } } else if (result == Null.INSTANCE) { return null; } else { return result; } } @Override public boolean isView() { if (isView_ == null) { Document doc = getDocument(); if (!doc.isNewNote()) { try { isView_ = doc.hasItem("$Index"); } catch (Exception e) { isView_ = false; // System.err.println("Exception thrown while checking isView for a document: " + e.getMessage() + " on notecoordinate " // + toString()); } } else { isView_ = false; } } return isView_; } @Override public void readExternal(final ObjectInput arg0) throws IOException, ClassNotFoundException { byte[] bytes = extreadbuffer_.get(); arg0.read(bytes); this.db = Longs.fromBytes(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]); this.x = Longs.fromBytes(bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]); this.y = Longs.fromBytes(bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23]); } @Override public void writeExternal(final ObjectOutput arg0) throws IOException { arg0.write(toByteArray()); } @Override public byte[] toByteArray() { byte[] result = new byte[24]; insertByteArray(db, result, 0); insertByteArray(x, result, 8); insertByteArray(y, result, 16); return result; } @Override public int insertToByteArray(final byte[] bytes, final int pos) { insertByteArray(db, bytes, pos + 0); insertByteArray(x, bytes, pos + 8); insertByteArray(y, bytes, pos + 16); return pos + 24; } @Override public String toString() { return getReplicaId() + getUNID(); } @Override public int compareTo(final org.openntf.domino.big.NoteCoordinate arg0) { NoteCoordinate local = (NoteCoordinate) arg0; if (this.db > local.db) return 1; if (this.db < local.db) return -1; if (this.y > local.y) return 1; if (this.y < local.y) return -1; if (this.x > local.x) return 1; if (this.x < local.x) return -1; return 0; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (db ^ (db >>> 32)); result = prime * result + (int) (x ^ (x >>> 32)); result = prime * result + (int) (y ^ (y >>> 32)); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (!getClass().equals(obj.getClass())) return false; NoteCoordinate other = (NoteCoordinate) obj; if (db != other.db) return false; if (x != other.x) return false; if (y != other.y) return false; return true; } }