All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.mindoo.domino.jna.NotesViewEntryData Maven / Gradle / Ivy
Go to download
Java project to access the HCL Domino C API using Java Native Access (JNA)
package com.mindoo.domino.jna;
import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.mindoo.domino.jna.constants.ReadMask;
import com.mindoo.domino.jna.gc.NotesGC;
import com.mindoo.domino.jna.internal.NotesConstants;
import com.mindoo.domino.jna.internal.TypedItemAccess;
import com.mindoo.domino.jna.utils.EmptyIterator;
import com.mindoo.domino.jna.utils.LMBCSString;
import com.mindoo.domino.jna.utils.NotesStringUtils;
/**
* Data object that contains all data read from collection entries
*
* @author Karsten Lehmann
*/
public class NotesViewEntryData extends TypedItemAccess implements IViewEntryData {
private NotesCollection m_parentCollection;
private int[] m_pos;
private String m_posStr;
private Integer m_noteId;
private String m_unid;
private long[] m_unidAsLongs;
private Integer m_noteClass;
private Integer m_siblingCount;
private Integer m_childCount;
private Integer m_descendantCount;
private Boolean m_isAnyUnread;
private Integer m_indentLevels;
private Integer m_ftScore;
private Boolean m_isUnread;
private Object[] m_columnValues;
private int[] m_columnValueSizes;
private Map m_summaryData;
private SoftReference> m_convertedDataRef;
private String m_singleColumnLookupName;
private Boolean m_preferNotesTimeDates;
/**
* Creates a new instance
*
* @param parentCollection parent notes collection
*/
public NotesViewEntryData(NotesCollection parentCollection) {
m_parentCollection = parentCollection;
}
class CacheableViewEntryData implements Serializable {
private static final long serialVersionUID = -6919729244434994355L;
private int[] m_pos;
private String m_posStr;
private Integer m_noteId;
private String m_unid;
private long[] m_unidAsLongs;
private Integer m_noteClass;
private Integer m_siblingCount;
private Integer m_childCount;
private Integer m_descendantCount;
private Boolean m_isAnyUnread;
private Integer m_indentLevels;
private Integer m_ftScore;
private Boolean m_isUnread;
private Object[] m_columnValues;
private int[] m_columnValueSizes;
private Map m_summaryData;
private SoftReference> m_convertedDataRef;
private String m_singleColumnLookupName;
}
/**
* Method to read the cacheable and serializable data from this object
*
* @return data
*/
CacheableViewEntryData getCacheableData() {
CacheableViewEntryData data = new CacheableViewEntryData();
data.m_pos = m_pos;
data.m_posStr = m_posStr;
data.m_noteId = m_noteId;
data.m_unid = m_unid;
data.m_unidAsLongs = m_unidAsLongs;
data.m_noteClass = m_noteClass;
data.m_siblingCount = m_siblingCount;
data.m_childCount = m_childCount;
data.m_descendantCount = m_descendantCount;
data.m_isAnyUnread = m_isAnyUnread;
data.m_indentLevels = m_indentLevels;
data.m_ftScore = m_ftScore;
data.m_isUnread = m_isUnread;
data.m_columnValues = m_columnValues;
data.m_columnValueSizes = m_columnValueSizes;
data.m_summaryData = m_summaryData;
data.m_convertedDataRef = m_convertedDataRef;
data.m_singleColumnLookupName = m_singleColumnLookupName;
return data;
}
/**
* Method to update the internal state from a cache entry
*
* @param data cache entry data
*/
void updateFromCache(CacheableViewEntryData data) {
if (m_noteId.intValue()!=data.m_noteId.intValue())
throw new IllegalArgumentException("Note ids do not match: "+m_noteId+" != "+data.m_noteId);
m_pos = data.m_pos;
m_posStr = data.m_posStr;
m_noteId = data.m_noteId;
m_unid = data.m_unid;
m_unidAsLongs = data.m_unidAsLongs;
m_noteClass = data.m_noteClass;
m_siblingCount = data.m_siblingCount;
m_childCount = data.m_childCount;
m_descendantCount = data.m_descendantCount;
m_isAnyUnread = data.m_isAnyUnread;
m_indentLevels = data.m_indentLevels;
m_ftScore = data.m_ftScore;
m_isUnread = data.m_isUnread;
m_columnValues = data.m_columnValues;
m_columnValueSizes = data.m_columnValueSizes;
m_summaryData = data.m_summaryData;
m_convertedDataRef = data.m_convertedDataRef;
m_singleColumnLookupName = data.m_singleColumnLookupName;
}
/**
* Returns the parent collection
*
* @return parent collection
*/
public NotesCollection getParent() {
return m_parentCollection;
}
/**
* Method to check whether an entry is a conflict document. Can only returns a true value
* if {@link ReadMask#SUMMARYVALUES} or {@link ReadMask#SUMMARY} is used for the lookup.
*
* @return true if conflict
*/
public boolean isConflict() {
//C API documentation regarding conflict flags in views
//VIEW_TABLE_FLAG_CONFLICT - Replication conflicts will be flagged. If TRUE, the '$Conflict' item must be SECOND-TO-LAST in the list of summary items for this view.
if (m_columnValues!=null) {
if (!m_parentCollection.isConflict()) {
return false;
}
else if (m_parentCollection.isHierarchical()) {
return m_columnValues.length>=2 && m_columnValues[m_columnValues.length-2] != null;
}
else {
//special case for views which have "show response hierarchy" = false:
//here the response column value is missing
return m_columnValues.length>=1 && m_columnValues[m_columnValues.length-1] != null;
}
}
else if (m_summaryData!=null) {
return m_summaryData.get("$Conflict") != null;
}
return false;
}
/**
* Method to check whether an entry is a conflict document. Can only returns a true value
* if {@link ReadMask#SUMMARYVALUES} or {@link ReadMask#SUMMARY} is used for the lookup.
*
* @return true if response
*/
public boolean isResponse() {
//C API documentation regarding response flags in views
//VIEW_TABLE_FLAG_FLATINDEX - Do not index hierarchically If FALSE, the '$REF' item must be LAST in the list of summary items for this view.
if (m_columnValues!=null) {
if (m_parentCollection.isHierarchical()) {
return m_columnValues.length>=1 && m_columnValues[m_columnValues.length-1] != null;
}
else {
//fallback to isConflict as this is the only info we have
return isConflict();
}
}
else if (m_summaryData!=null) {
return m_summaryData.get("$Ref") != null;
}
return false;
}
/**
* Returns the entry position in the view as an int array. Only returns a non-null value if
* {@link ReadMask#INDEXPOSITION} is used for the lookup.
*
* @return position or null
*/
@Override
public int[] getPosition() {
return m_pos;
}
@Override
public int getLevel() {
return m_pos!=null ? (m_pos.length-1) : -1;
}
/**
* Returns the entry position in the view as a string (e.g. 1.2.3). Only returns a non-null value if
* {@link ReadMask#INDEXPOSITION} is used for the lookup.
*
* @return position string or empty string
*/
@Override
public String getPositionStr() {
if (m_posStr==null) {
if (m_pos==null || m_pos.length==0) {
m_posStr = "";
}
else {
StringBuilder sb = new StringBuilder();
for (int i=0; i0)
sb.append(".");
sb.append(m_pos[i]);
}
m_posStr = sb.toString();
}
}
return m_posStr;
}
/**
* Sets the position
*
* @param pos new position
*/
public void setPosition(int[] pos) {
m_pos = pos;
}
/**
* Returns the note id of the entry. Only returns a value if {@link ReadMask#NOTEID} is used for the lookup
*
* @return note id or 0
*/
@Override
public int getNoteId() {
return m_noteId!=null ? m_noteId.intValue() : 0;
}
/**
* Returns the note id of the entry in hex format. Only returns a value if {@link ReadMask#NOTEID} is used for the lookup
*
* @return note id as hex string or null
*/
public String getNoteIdAsHex() {
return m_noteId!=null ? Integer.toString(m_noteId.intValue(), 16) : null;
}
/**
* Sets the note id
*
* @param noteId note id
*/
public void setNoteId(int noteId) {
m_noteId = Integer.valueOf(noteId);
}
/**
* Method to check whether the entry is a document. Only returns a value if {@link ReadMask#NOTEID}
* is used for the lookup
*
* @return true if document
*/
@Override
public boolean isDocument() {
return !isCategory() && !isTotal();
}
/**
* Method to check whether the entry is a category. Only returns a value if {@link ReadMask#NOTEID}
* is used for the lookup
*
* @return true if category
*/
@Override
public boolean isCategory() {
if (m_noteId!=null) {
return (m_noteId.intValue() & NotesConstants.NOTEID_CATEGORY) == NotesConstants.NOTEID_CATEGORY;
}
return false;
}
/**
* Method to check whether the entry is a total value. Only returns a value if {@link ReadMask#NOTEID}
* is used for the lookup
*
* @return true if total
*/
@Override
public boolean isTotal() {
if (m_noteId!=null) {
return (m_noteId.intValue() & NotesConstants.NOTEID_CATEGORY_TOTAL) == NotesConstants.NOTEID_CATEGORY_TOTAL;
}
return false;
}
/**
* Returns the UNID of the entry. Only returns a value if {@link ReadMask#NOTEUNID}
* is used for the lookup
*
* @return UNID or null
*/
@Override
public String getUNID() {
if (m_unid==null) {
if (m_unidAsLongs!=null) {
m_unid = NotesStringUtils.toUNID(m_unidAsLongs[0], m_unidAsLongs[1]);
}
}
return m_unid;
}
/**
* Sets the UNID
*
* @param unid UNID
*/
public void setUNID(String unid) {
m_unid = unid;
}
/**
* Sets the UNID as a long array
*
* @param unidAsLongs long array with file / note timedates
*/
public void setUNID(long[] unidAsLongs) {
m_unidAsLongs = unidAsLongs;
}
/**
* Returns the entry's note class. Only returns a value if {@link ReadMask#NOTECLASS}
* is used for the lookup
*
* @return class
*/
public int getNoteClass() {
return m_noteClass!=null ? m_noteClass.intValue() : 0;
}
/**
* Sets the note class
*
* @param noteClass note class
*/
public void setNoteClass(int noteClass) {
m_noteClass = Integer.valueOf(noteClass);
}
/**
* Returns the sibling count. Only returns a value if {@link ReadMask#INDEXSIBLINGS}
* is used for the lookup
*
* @return count or 0
*/
@Override
public int getSiblingCount() {
return m_siblingCount!=null ? m_siblingCount.intValue() : 0;
}
/**
* Sets the sibling count
*
* @param siblingCount count
*/
public void setSiblingCount(int siblingCount) {
m_siblingCount = Integer.valueOf(siblingCount);
}
/**
* Returns the sibling count. Only returns a value if {@link ReadMask#INDEXCHILDREN}
* is used for the lookup
*
* @return count or 0
*/
@Override
public int getChildCount() {
return m_childCount!=null ? m_childCount.intValue() : 0;
}
/**
* Sets the child count
*
* @param childCount count
*/
public void setChildCount(int childCount) {
m_childCount = Integer.valueOf(childCount);
}
/**
* Returns the descendant count. Only returns a value if {@link ReadMask#INDEXDESCENDANTS}
* is used for the lookup
*
* @return count or 0
*/
@Override
public int getDescendantCount() {
return m_descendantCount!=null ? m_descendantCount.intValue() : 0;
}
/**
* Sets the descendant count
*
* @param descendantCount count
*/
public void setDescendantCount(int descendantCount) {
m_descendantCount = Integer.valueOf(descendantCount);
}
/**
* Returns true if the entry is unread. Only returns a value if {@link ReadMask#INDEXUNREAD}
* is used for the lookup
*
* @return true if unread
*/
public boolean isUnread() {
return m_isUnread!=null ? m_isUnread.booleanValue() : false;
}
/**
* Sets the unread flag
*
* @param isUnread flag
*/
public void setUnread(boolean isUnread) {
m_isUnread = Boolean.valueOf(isUnread);
}
/**
* For category entries where the category contains a "\" character, this method returns the
* index of the category entry (e.g. 1 for "level2" in the string "level1\level2").
*
* Only returns a value if {@link ReadMask#INDENTLEVELS} is used for the lookup.
*
* @return levels or 0
*/
@Override
public int getIndentLevels() {
return m_indentLevels!=null ? m_indentLevels.intValue() : 0;
}
/**
* Sets the indent levels
*
* @param indentLevels levels
*/
public void setIndentLevels(int indentLevels) {
m_indentLevels = Integer.valueOf(indentLevels);
}
/**
* Returns the fulltext search score. Only returns a value if {@link ReadMask#SCORE}
* is used for the lookup
*
* @return score or 0
*/
public int getFTScore() {
return m_ftScore!=null ? m_ftScore.intValue() : 0;
}
/**
* Sets the fulltext search score
*
* @param ftScore score
*/
public void setFTScore(int ftScore) {
m_ftScore = Integer.valueOf(ftScore);
}
/**
* Returns a flag whether the entry or any descendents are unread. Only returns a value if {@link ReadMask#INDEXANYUNREAD}
* is used for the lookup
*
* @return true if any unread
*/
public boolean isAnyUnread() {
return m_isAnyUnread!=null ? m_isAnyUnread.booleanValue() : false;
}
/**
* Sets the any unread flag
*
* @param isAnyUnread flag
*/
public void setAnyUnread(boolean isAnyUnread) {
m_isAnyUnread = Boolean.valueOf(isAnyUnread);
}
/**
* Sets the collection entry column values.
*
* @param itemValues new values
*/
public void setColumnValues(Object[] itemValues) {
m_columnValues = itemValues;
}
public Object[] getColumnValues() {
return m_columnValues;
}
/**
* Returns an iterator of all available columns for which we can read column values
* (e.g. does not return static column names).
* Convenience function that simply calls {@link NotesCollection#getColumnNames()} on the parent collection
*
* @return programmatic column names converted to lowercase
*/
public Iterator getColumnNames() {
if ((m_parentCollection.getNoteId() & NotesConstants.NOTE_ID_SPECIAL) == NotesConstants.NOTE_ID_SPECIAL) {
//special collection (e.g. design collection) where we cannot read the column names from the design element
if (m_summaryData!=null) {
//if we have used ReadMask.SUMMARY to read the data, we can take the summary map keys
return m_summaryData.keySet().iterator();
}
else {
return new EmptyIterator();
}
}
else {
return m_parentCollection.getColumnNames();
}
}
/**
* Returns the number of columns for which we can read column data (e.g. does not count columns
* with static values)
* Convenience function that simply calls {@link NotesCollection#getNumberOfColumns()} on the parent collection
*
* @return number of columns
*/
public int getNumberOfColumnsWithValues() {
return m_parentCollection.getNumberOfColumns();
}
/**
* Converts the column values to a map. If you are only interested in specific columns,
* you get way better performance calling {@link #get(String)} for those columns
* directly, because we lazily convert text/text list column data from LMBCS format to Java String format.
* Calling this method converts all string columns at once.
*
* @return map with programmatic column names as keys
*/
public Map getColumnDataAsMap() {
Map data = m_convertedDataRef==null ? null : m_convertedDataRef.get();
if (data==null) {
data = new TreeMap(String.CASE_INSENSITIVE_ORDER);
Iterator colNames = getColumnNames();
while (colNames.hasNext()) {
String currColName = colNames.next();
Object currColValue = get(currColName);
if (isPreferNotesTimeDates()) {
if (currColValue instanceof Calendar) {
currColValue = new NotesTimeDate((Calendar) currColValue);
}
else if (currColValue instanceof Date) {
currColValue = new NotesTimeDate((Date) currColValue);
}
}
data.put(currColName, currColValue);
}
m_convertedDataRef = new SoftReference>(data);
}
return data;
}
/**
* Returns a list of reader that are allowed to see this view entry.
* This data is only retrieved when {@link ReadMask#SUMMARY} and
* {@link ReadMask#RETURN_READERSLIST} are both used to read the
* collection data.
*
* @return readers or null if not read
*/
public List getReadersList() {
Object readersList = get("$C1$");
if (readersList instanceof List) {
return (List) readersList;
}
else if (readersList instanceof String) {
return Arrays.asList((String) readersList);
}
else {
return null;
}
}
/**
* Method to check whether the the view entry contains a non-null value for a programmatic column
* name
*
* @param columnName column name
* @return true if exists
*/
public boolean has(String columnName) {
if (m_summaryData!=null) {
return m_summaryData.containsKey(columnName);
}
else if (m_columnValues!=null) {
int colIdx = m_parentCollection.getColumnValuesIndex(columnName);
if (colIdx==-1) {
return false;
}
else {
return m_columnValues[colIdx] != null;
}
}
else {
return false;
}
}
/**
* Sets whether methods like {@link #get(String)} should return {@link NotesTimeDate}
* instead of {@link Calendar}.
*
* @param b true to prefer NotesTimeDate (false by default)
*/
public void setPreferNotesTimeDates(boolean b) {
m_preferNotesTimeDates = b;
}
/**
* Returns whether methods like {@link #get(String)} should return {@link NotesTimeDate}
* instead of {@link Calendar}.
*
* @return true to prefer NotesTimeDate
*/
public boolean isPreferNotesTimeDates() {
if (m_preferNotesTimeDates==null) {
return NotesGC.isPreferNotesTimeDate();
}
return m_preferNotesTimeDates;
}
/**
* Returns a column value. Only returns data of either {@link ReadMask#SUMMARY} or {@link ReadMask#SUMMARYVALUES}
* was used to read the collection data
*
* The following data types are returned for the different column data types:
*
* {@link NotesItem#TYPE_TEXT} - {@link String}
* {@link NotesItem#TYPE_TEXT_LIST} - {@link List} of {@link String}
* {@link NotesItem#TYPE_NUMBER} - {@link Double}
* {@link NotesItem#TYPE_NUMBER_RANGE} - {@link List} with {@link Double} values for number lists or double[] values for number ranges (not sure if Notes views really supports them)
* {@link NotesItem#TYPE_TIME} - {@link Calendar} or {@link NotesTimeDate} if {@link #setPreferNotesTimeDates(boolean)} has been called
* {@link NotesItem#TYPE_TIME_RANGE} - {@link List} with {@link Calendar} values for datetime lists or Calendar[] values for datetime ranges {@link NotesTimeDate} if {@link #setPreferNotesTimeDates(boolean)} has been called
*
*
* @param columnNameOrTitle programatic column name or column title
* @return column value or null
*/
@Override
public Object get(String columnNameOrTitle) {
if (isPreferNotesTimeDates()) {
return get(columnNameOrTitle, false);
}
else {
return get(columnNameOrTitle, true);
}
}
/**
* Returns a column value. Only returns data of either {@link ReadMask#SUMMARY} or {@link ReadMask#SUMMARYVALUES}
* was used to read the collection data
*
* The following data types are returned for the different column data types:
*
* {@link NotesItem#TYPE_TEXT} - {@link String}
* {@link NotesItem#TYPE_TEXT_LIST} - {@link List} of {@link String}
* {@link NotesItem#TYPE_NUMBER} - {@link Double}
* {@link NotesItem#TYPE_NUMBER_RANGE} - {@link List} with {@link Double} values for number lists or double[] values for number ranges (not sure if Notes views really supports them)
* {@link NotesItem#TYPE_TIME} - {@link Calendar} or {@link NotesTimeDate} if {@link #setPreferNotesTimeDates(boolean)} has been called
* {@link NotesItem#TYPE_TIME_RANGE} - {@link List} with {@link Calendar} values for datetime lists or Calendar[] values for datetime ranges {@link NotesTimeDate} if {@link #setPreferNotesTimeDates(boolean)} has been called
*
*
* @param columnNameOrTitle programatic column name or column title
* @param convertNotesTimeDateToCalendar true to convert {@link NotesTimeDate} values to {@link Calendar}
* @return column value or null
*/
private Object get(String columnNameOrTitle, boolean convertNotesTimeDateToCalendar) {
Object val = null;
if (m_summaryData!=null) {
if (m_summaryData.containsKey(columnNameOrTitle)) {
val = m_summaryData.get(columnNameOrTitle);
}
else {
//try to find the programmatic column name if columnNameOrTitle contains the column title
int colIdx = m_parentCollection.getColumnValuesIndex(columnNameOrTitle);
if (colIdx!=-1 && colIdx!=65535) {
String progColName = m_parentCollection.getColumnName(colIdx);
if (progColName!=null) {
val = m_summaryData.get(progColName);
}
}
}
}
else if (m_columnValues!=null) {
int colIdx = m_parentCollection.getColumnValuesIndex(columnNameOrTitle);
if (colIdx!=-1 && colIdx!=65535) {
if (colIdx < m_columnValues.length) {
val = m_columnValues[colIdx];
}
else {
val = null;
}
}
}
if (val instanceof List) {
List valAsList = (List) val;
for (int i=0; i summaryData) {
m_summaryData = summaryData;
}
/**
* If this view entry data was received by an optimized lookup that read only one column, you
* can use this method to get the programmatic name of the collection column. The method
* is mainly used for collection data caching purposes.
*
* @return column name or null
*/
public String getSingleColumnLookupName() {
return m_singleColumnLookupName;
}
/**
* If this view entry data was received by an optimized lookup that read only one column,
* this method is used to set the programmatic name of that column.
*
* @param colName column name or null
*/
public void setSingleColumnLookupName(String colName) {
m_singleColumnLookupName = colName;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
if (m_noteId!=null) {
sb.append(",noteid="+m_noteId);
}
String unid = getUNID();
if (unid!=null) {
sb.append(",unid="+unid);
}
String posStr = getPositionStr();
if (posStr!=null && posStr.length()>0) {
sb.append(",pos="+posStr);
}
if (m_noteClass!=null) {
sb.append(",class="+m_noteClass.intValue());
}
if (isDocument()) {
sb.append(",type=document");
}
else if (isCategory()) {
sb.append(",type=category");
}
else if (isTotal()) {
sb.append(",type=total");
}
if (m_indentLevels!=null) {
sb.append(",indentlevel="+m_indentLevels.intValue());
}
if (m_childCount!=null) {
sb.append(",childcount="+m_childCount.intValue());
}
if (m_descendantCount!=null) {
sb.append(",descendantcount="+m_descendantCount.intValue());
}
if (m_siblingCount!=null) {
sb.append(",siblingcount="+m_siblingCount.intValue());
}
if (m_summaryData!=null) {
sb.append(",summary="+m_summaryData.toString());
}
if (m_columnValues!=null) {
sb.append(",columns=[");
for (int i=0; i0)
sb.append(",");
sb.append(colValueToString(m_columnValues[i]));
}
sb.append("]");
}
if (m_ftScore!=null) {
sb.append(",score="+m_ftScore.intValue());
}
if (m_isUnread!=null) {
sb.append(",unread="+m_isUnread.booleanValue());
}
if (m_isAnyUnread!=null) {
sb.append(",anyunread="+m_isAnyUnread.booleanValue());
}
if (sb.length()>0) {
//remove first ","
sb.delete(0, 1);
}
sb.insert(0, "ViewEntry[");
sb.append("]");
return sb.toString();
}
/**
* Converts a column value to a string, used for debugging values
*
* @param val column value
* @return value as string
*/
private String colValueToString(Object val) {
if (val==null)
return "null";
if (val instanceof Calendar) {
return ((Calendar)val).getTime().toString();
}
else {
return val.toString();
}
}
}