/*
* � Copyright IBM Corp. 2010
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.ibm.xsp.extlib.component.picker.data;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
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 com.ibm.commons.Platform;
import com.ibm.commons.util.StringUtil;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.complex.ValueBindingObjectImpl;
import com.ibm.xsp.context.FacesContextEx;
import com.ibm.xsp.model.domino.DominoUtils;
import com.ibm.xsp.model.domino.wrapped.DominoViewEntry;
/**
* Abstract data provider for a picker that reads the data from a Domino view.
* <p>
* This class does not preclude how the
* </p>
*/
public abstract class AbstractDominoViewPickerData extends ValueBindingObjectImpl implements IPickerData {
public static final String SEARCH_STARTFROM = "startFrom"; // default //$NON-NLS-1$
public static final String SEARCH_MATCH = "match"; //$NON-NLS-1$
public static final String SEARCH_FTSEARCH = "ftSearch"; //$NON-NLS-1$
public AbstractDominoViewPickerData() {
}
protected abstract EntryMetaData createEntryMetaData(IPickerOptions options) throws NotesException;
public String getSearchType() {
return null;
}
// ====================================================================
// Data access implementation
// ====================================================================
public abstract static class EntryMetaData {
private View view;
private IPickerOptions options;
protected EntryMetaData(IPickerOptions options) throws NotesException {
this.options = options;
this.view = openView();
}
public View getView() {
return view;
}
public IPickerOptions getOptions() {
return options;
}
protected int findSortColumnIndex(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;
}
protected int findColumnIndex(Vector<ViewColumn> vc, 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;
}
protected abstract View openView() throws NotesException;
protected abstract Entry createEntry(ViewEntry ve) throws NotesException;
}
public abstract static class Entry implements IPickerEntry {
private EntryMetaData metaData;
private Object value;
private Object label;
private Object[] attributes;
@SuppressWarnings("unchecked") // $NON-NLS-1$
protected Entry(EntryMetaData metaData, 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);
}
public EntryMetaData getMetaData() {
return metaData;
}
public Object getValue() {
return value;
}
public Object getLabel() {
return label;
}
public int getAttributeCount() {
return 0;
}
public String getAttributeName(int index) {
return null;
}
public Object getAttributeValue(int index) {
return attributes[index];
}
protected abstract Object readValue(ViewEntry ve, Vector<Object> columnValues) throws NotesException;
protected abstract Object readLabel(ViewEntry ve, Vector<Object> columnValues) throws NotesException;
protected abstract Object[] readAttributes(ViewEntry ve, Vector<Object> columnValues) throws NotesException;
}
public static class Result implements IPickerResult {
private List<IPickerEntry> entries;
private int count;
protected Result(List<IPickerEntry> entries, int count) {
this.entries = entries;
this.count = count;
}
public List<IPickerEntry> getEntries() {
return entries;
}
public int getTotalCount() {
return count;
}
}
public boolean hasCapability(int capability) {
if(capability==CAPABILITY_MULTIPLESOURCES) {
return false;
}
return true;
}
public IPickerResult readEntries(IPickerOptions options) {
try {
EntryMetaData meta = createEntryMetaData(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;
if (!StringUtil.isEmpty(key)) {
vc = view.getAllEntriesByKey(key);
} else {
// key is empty, default to return all, but check option
String optionName = "xsp.extlib.dominoViewPicker.searchMatch.emptyKey.all"; //$NON-NLS-1$
String optionValue = FacesContextEx.getCurrentInstance().getProperty(optionName);
if( "false".equals(optionValue) ){ //$NON-NLS-1$
// when explicitly set to "false" then revert to the old pre-901v14 behavior,
// match to empty key, returns none. [Change for SPR#MKEE9ZKDJR, and for
// GitPR#14,PartD3: https://github.com/OpenNTF/XPagesExtensionLibrary/pull/14 ]
vc = view.getAllEntriesByKey(key);
}else{ // default
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 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;
ve = DominoUtils.getViewEntryByKeyWithOptions(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();
}
}
protected void applyFTSearch(IPickerOptions options, View view, String key) throws NotesException {
if(StringUtil.isNotEmpty(key)) {
view.FTSearch(key);
}
}
public List<IPickerEntry> loadEntries(Object[] ids, String[] attributeNames) {
try {
EntryMetaData meta = createEntryMetaData(null);
View view = meta.getView();
view.setAutoUpdate(false);
try {
// TODO: use the options here?
ArrayList<IPickerEntry> entries = new ArrayList<IPickerEntry>(ids.length);
for(int i=0; i<ids.length; i++) {
ViewEntry ve = view.getEntryByKey(ids[i],true);
if(ve!=null) {
Entry e = meta.createEntry(ve);
entries.add(e);
} else {
entries.add(null);
}
//ve.recycle();
}
return entries;
} finally {
// Recycle the view?
}
} catch(NotesException ex) {
throw new FacesExceptionEx(ex,"Error while loading entry"); // $NLX-AbstractDominoViewPickerData.Errorwhileloadingentry-1$
}
}
}