package org.openntf.domino.xsp.helpers;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewColumn;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import lotus.domino.ViewNavigator;
import org.openntf.domino.utils.Factory;
import com.ibm.commons.Platform;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.extlib.component.picker.data.DominoViewValuePickerData;
import com.ibm.xsp.extlib.component.picker.data.EmptyPickerResult;
import com.ibm.xsp.extlib.component.picker.data.IPickerEntry;
import com.ibm.xsp.extlib.component.picker.data.IPickerOptions;
import com.ibm.xsp.extlib.component.picker.data.IPickerResult;
import com.ibm.xsp.extlib.domino.ExtlibDominoLogger;
import com.ibm.xsp.model.domino.DominoUtils;
import com.ibm.xsp.model.domino.wrapped.DominoViewEntry;
/**
* @author withersp
*
* OpenNTF extension to DominoViewValuePickerData, required because DominoUtils.getViewEntryByKeyWithOptions() crashes the server if
* anything other than a lotus.domino.View is passed to it
*/
public class OpenntfViewValuePickerData extends DominoViewValuePickerData {
/**
* Constructor
*/
public OpenntfViewValuePickerData() {
}
/**
* _EntryMetaData class, required because the methods of
* {@link com.ibm.xsp.extlib.component.picker.data.DominoViewPickerData._EntryMetaData} are 'protected', so not accessible from here
*
* This class is an exact duplicate of that class
*/
public class _EntryMetaData extends EntryMetaData {
private int valueIndex;
private int labelIndex;
@SuppressWarnings("unused")
private String[] attributeNames;
@SuppressWarnings("unused")
private int[] attributeIndexes;
/**
* Constructor, passing in options for getting a subset of the lookup values, e.g. start, typeahead key, search key, count etc.
*
* @param options
* IPickerOptions to refine lookup values
* @throws NotesException
*/
@SuppressWarnings("unchecked")
// $NON-NLS-1$
protected _EntryMetaData(final IPickerOptions options) throws NotesException {
super(options);
Vector<ViewColumn> vc = getView().getColumns();
// Look for the key column
if ((valueIndex = findSortColumnIndex(vc)) < 0) {
throw new FacesExceptionEx(null, "Cannot find a value column in the view {0}", getView().getName()); // $NLX-DominoViewPickerData.Cannotfindavaluecolumnintheview0-1$
}
// Look for the label column
String labelName = getLabelColumn();
if (StringUtil.isNotEmpty(labelName)) {
if ((labelIndex = findColumnIndex(vc, labelName)) < 0) {
throw new FacesExceptionEx(null, "Cannot find label column {0}", labelName); // $NLX-DominoViewPickerData.Cannotfindlabelcolumn0-1$
}
} else {
labelIndex = -1;
}
// // Look for the view attributes
// this.attributeNames = attributeNames;
// if(attributeNames!=null) {
// int sz = attributeNames.length;
// this.attributeIndexes = new int[sz];
// for(int i=0; i<sz; i++) {
// if( (attributeIndexes[i] = findColumnIndex(vc, attributeNames[i]))<0) {
// throw new FacesExceptionEx(null,"Cannot find attributes column {0}",attributeNames[i]);
// }
// }
// }
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.xsp.helpers.OpenntfViewValuePickerData.EntryMetaData#createEntry(lotus.domino.ViewEntry)
*/
@Override
protected Entry createEntry(final ViewEntry ve) throws NotesException {
return new _Entry(this, ve);
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.xsp.helpers.OpenntfViewValuePickerData.EntryMetaData#openView()
*/
@Override
protected View openView() throws NotesException {
Database db = DominoUtils.openDatabaseByName(getDatabaseName());
View view = db.getView(getViewName());
String labelName = getLabelColumn();
if (StringUtil.isNotEmpty(labelName)) {
try {
view.resortView(labelName, true);
} catch (NotesException ex) {
// We can't resort the view so we silently fail
// We just report it to the console
if (ExtlibDominoLogger.DOMINO.isWarnEnabled()) {
ExtlibDominoLogger.DOMINO.warnp(this, "openView", ex, //$NON-NLS-1$
StringUtil.format("The view {0} needs the column {1} to be sortable for the value picker to be searchable",
getViewName(), labelName)); // $NLW-DominoViewPickerData_ValuePickerNotSearchable_UnsortableColumn-1$
}
}
}
return view;
}
}
/**
* _Entry class, required because the methods of {@link com.ibm.xsp.extlib.component.picker.data.DominoViewPickerData._Entry} are
* 'protected', so not accessible from here
*
* This class is an exact duplicate of that class
*/
public class _Entry extends Entry {
@SuppressWarnings("unused")
private Object[] attributes;
/**
* Constructor, passing in the EntryMetaData object for the ViewEntry and the ViewEntry itself
*
* @param metaData
* EntryMetaData to access the view, its design and the search options
* @param ve
* ViewEntry being iterated
* @throws NotesException
*/
protected _Entry(final EntryMetaData metaData, final ViewEntry ve) throws NotesException {
super(metaData, ve);
// // And the extra attributes
// if(metaData.attributeIndexes!=null) {
// int ac = metaData.attributeIndexes.length;
// this.attributes = new Object[ac];
// for(int i=0; i<ac; i++) {
// attributes[i] = columnValues.get(metaData.attributeIndexes[i]);
// }
// }
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.xsp.helpers.OpenntfViewValuePickerData.Entry#getMetaData()
*/
@Override
public _EntryMetaData getMetaData() {
return (_EntryMetaData) super.getMetaData();
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.xsp.helpers.OpenntfViewValuePickerData.Entry#readValue(lotus.domino.ViewEntry, java.util.Vector)
*/
@Override
protected Object readValue(final ViewEntry ve, final Vector<Object> columnValues) throws NotesException {
int idx = getMetaData().valueIndex;
return idx >= 0 ? columnValues.get(idx) : null;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.xsp.helpers.OpenntfViewValuePickerData.Entry#readLabel(lotus.domino.ViewEntry, java.util.Vector)
*/
@Override
protected Object readLabel(final ViewEntry ve, final Vector<Object> columnValues) throws NotesException {
int idx = getMetaData().labelIndex;
return idx >= 0 ? columnValues.get(idx) : null;
}
/*
* (non-Javadoc)
*
* @see org.openntf.domino.xsp.helpers.OpenntfViewValuePickerData.Entry#readAttributes(lotus.domino.ViewEntry, java.util.Vector)
*/
@Override
protected Object[] readAttributes(final ViewEntry ve, final Vector<Object> columnValues) throws NotesException {
return null;
}
}
// ====================================================================
// Data access implementation
//
// Static abstract class for EntryMetaData, extended by _EntryMetaData
// ====================================================================
public abstract static class EntryMetaData {
private View view;
private IPickerOptions options;
/**
* Constructor loading in the options
*
* @param options
* IPickerOptions to refine lookup values
* @throws NotesException
*/
protected EntryMetaData(final IPickerOptions options) throws NotesException {
this.options = options;
this.view = openView();
}
/**
* Getter for the view property
*
* @return View underlying view the ViewEntry is for
*/
public View getView() {
return view;
}
/**
* Getter for the options property
*
* @return IPickerOptions containing e.g. start, source, count, key (typeahead key), startKey (search key)
*/
public IPickerOptions getOptions() {
return options;
}
/**
* Gets the first sorted column's index from the View
*
* @param vc
* Vector<ViewColumn> pulled from the design of the View
* @return int first sorted column, starting at 0
* @throws NotesException
*/
protected int findSortColumnIndex(final Vector<ViewColumn> vc) throws NotesException {
int fc = -1;
// Find the first sorted column
int nc = vc.size();
for (int i = 0; i < nc; i++) {
ViewColumn c = vc.get(i);
if (c.isSorted()) {
return i;
}
if (fc < 0 && c.getColumnValuesIndex() != DominoViewEntry.VC_NOT_PRESENT) {
fc = i;
}
}
// Else, return the first column
return fc;
}
/**
* Finds the column index for a specific column name searching on:
* <ul>
* <li>Programmatic name (case insensitive)</li>
* <li>Column title</li>
* </ul>
*
* @param vc
* Vector<ViewColumn> pulled from the design of the View
* @param name
* String programmatic name or column title
* @return int index of relevant column, starting at 0. -1 if not found.
* @throws NotesException
*/
protected int findColumnIndex(final Vector<ViewColumn> vc, final String name) throws NotesException {
int nc = vc.size();
// Look for a programmatic name first
for (int i = 0; i < nc; i++) {
if (StringUtil.equalsIgnoreCase(vc.get(i).getItemName(), name)) {
return i;
}
}
// Then default to the title
for (int i = 0; i < nc; i++) {
if (StringUtil.equalsIgnoreCase(vc.get(i).getTitle(), name)) {
return i;
}
}
return -1;
}
/**
* Opens the underlying View
*
* @return View the ViewEntry is from
* @throws NotesException
*/
protected abstract View openView() throws NotesException;
/**
* Creates a new Entry object from the ViewEntry
*
* @param ve
* ViewEntry being passed
* @return Entry object based on the ViewEntry
* @throws NotesException
*/
protected abstract Entry createEntry(ViewEntry ve) throws NotesException;
}
/**
* Static abstract class based on IPickerEntry interface, further implemented by _Entry
*/
public abstract static class Entry implements IPickerEntry {
private EntryMetaData metaData;
private Object value;
private Object label;
private Object[] attributes;
/**
* Creates a new Entry object
*
* @param metaData
* EntryMetaData to access the view, its design and the search options
* @param ve
* ViewEntry being iterated
* @throws NotesException
*/
@SuppressWarnings("unchecked")
// $NON-NLS-1$
protected Entry(final EntryMetaData metaData, final ViewEntry ve) throws NotesException {
this.metaData = metaData;
// Read the values from the view entry
Vector<Object> columnValues = ve.getColumnValues();
// Read the value
this.value = readValue(ve, columnValues);
// Read the label
this.label = readLabel(ve, columnValues);
// Read the extra attributes
this.attributes = readAttributes(ve, columnValues);
}
/**
* Getter to access the EntryMetaData object loaded in
*
* @return EntryMetaData
*/
public EntryMetaData getMetaData() {
return metaData;
}
/*
* (non-Javadoc)
*
* @see com.ibm.xsp.extlib.component.picker.data.IPickerEntry#getValue()
*/
@Override
public Object getValue() {
return value;
}
/*
* (non-Javadoc)
*
* @see com.ibm.xsp.extlib.component.picker.data.IPickerEntry#getLabel()
*/
@Override
public Object getLabel() {
return label;
}
/*
* (non-Javadoc)
*
* @see com.ibm.xsp.extlib.component.picker.data.IPickerEntry#getAttributeCount()
*/
@Override
public int getAttributeCount() {
return 0;
}
/*
* (non-Javadoc)
*
* @see com.ibm.xsp.extlib.component.picker.data.IPickerEntry#getAttributeName(int)
*/
@Override
public String getAttributeName(final int index) {
return null;
}
/*
* (non-Javadoc)
*
* @see com.ibm.xsp.extlib.component.picker.data.IPickerEntry#getAttributeValue(int)
*/
@Override
public Object getAttributeValue(final int index) {
return attributes[index];
}
/**
* Reads the value to store from the picker
*
* @param ve
* ViewEntry being read
* @param columnValues
* Vector<Object> of column values for the ViewEntry
* @return Object value to be stored
* @throws NotesException
*/
protected abstract Object readValue(ViewEntry ve, Vector<Object> columnValues) throws NotesException;
/**
* Reads the label to display in the picker
*
* @param ve
* ViewEntry being read
* @param columnValues
* Vector<Object> of column values for the ViewEntry
* @return Object label to display
* @throws NotesException
*/
protected abstract Object readLabel(ViewEntry ve, Vector<Object> columnValues) throws NotesException;
/**
* Reads the attributes for the ViewEntry. In _Entry this returns null
*
* @param ve
* ViewEntry being read
* @param columnValues
* Vector<Object> of column values for the ViewEntry
* @return Object[] of attributes
* @throws NotesException
*/
protected abstract Object[] readAttributes(ViewEntry ve, Vector<Object> columnValues) throws NotesException;
}
/**
* Static abstract class based on IPickerResult interface containing the entries to return and the count
*/
public static class Result implements IPickerResult {
private List<IPickerEntry> entries;
private int count;
/**
* Constructor, passing in entries to display and the count
*
* @param entries
* List<IPickerEntry> of options to display
* @param count
* int number of results
*/
protected Result(final List<IPickerEntry> entries, final int count) {
this.entries = entries;
this.count = count;
}
/*
* (non-Javadoc)
*
* @see com.ibm.xsp.extlib.component.picker.data.IPickerResult#getEntries()
*/
@Override
public List<IPickerEntry> getEntries() {
return entries;
}
/*
* (non-Javadoc)
*
* @see com.ibm.xsp.extlib.component.picker.data.IPickerResult#getTotalCount()
*/
@Override
public int getTotalCount() {
return count;
}
}
/*
* (non-Javadoc)
*
* @see
* com.ibm.xsp.extlib.component.picker.data.AbstractDominoViewPickerData#readEntries(com.ibm.xsp.extlib.component.picker.data.IPickerOptions
* )
*/
@Override
public IPickerResult readEntries(final IPickerOptions options) {
try {
EntryMetaData meta = new _EntryMetaData(options);
View view = meta.getView();
view.setAutoUpdate(false);
try {
ArrayList<IPickerEntry> entries = new ArrayList<IPickerEntry>();
int start = options.getStart();
int count = options.getCount();
String key = options.getKey();
String _startKey = options.getStartKey();
if (StringUtil.isNotEmpty(_startKey)) {
key = _startKey;
}
String searchType = getSearchType();
if (StringUtil.isEmpty(searchType)) {
searchType = SEARCH_STARTFROM;
}
if (StringUtil.equals(searchType, SEARCH_MATCH)) {
ViewEntryCollection vc = view.getAllEntriesByKey(key);
ViewEntry ve = start > 0 ? vc.getNthEntry(start) : vc.getFirstEntry();
for (int i = 0; i < count && ve != null; i++) {
entries.add(meta.createEntry(ve));
ve = vc.getNextEntry(ve);
}
int nEntries = vc.getCount();
return new Result(entries, nEntries);
}
if (StringUtil.equals(searchType, SEARCH_FTSEARCH)) {
applyFTSearch(options, view, key);
ViewEntryCollection vc = view.getAllEntries();
ViewEntry ve = start > 0 ? vc.getNthEntry(start) : vc.getFirstEntry();
for (int i = 0; i < count && ve != null; i++) {
entries.add(meta.createEntry(ve));
ve = vc.getNextEntry(ve);
}
int nEntries = vc.getCount();
return new Result(entries, nEntries);
} else {
ViewNavigator nav = view.createViewNav();
try {
ViewEntry ve = null;
if (key != null) {
int searchOptions = DominoUtils.FIND_GREATER_THAN | DominoUtils.FIND_EQUAL | DominoUtils.FIND_PARTIAL
| DominoUtils.FIND_CASE_INSENSITIVE;
// This is the one line that's different
ve = DominoUtils.getViewEntryByKeyWithOptions(Factory.getWrapperFactory().toLotus(view), key, searchOptions);
} else {
ve = nav.getCurrent();
}
if (start > 0) {
if (nav.skip(start) != start) {
// ok not all of them are skipped, stop the
// process
count = 0;
}
}
for (int i = 0; i < count && ve != null; i++) {
entries.add(meta.createEntry(ve));
ve = nav.getNext(ve);
}
int nEntries = -1;
return new Result(entries, nEntries);
} finally {
nav.recycle();
}
}
} finally {
// Recycle the view?
}
} catch (Exception ex) {
Platform.getInstance().log(ex);
// Swallow the exception for the end user and return an empty picker
return new EmptyPickerResult();
}
}
}