/*
* � 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.domino.services.rest.das.view;
import static com.ibm.domino.services.rest.RestServiceConstants.ITEM_FORM;
import static com.ibm.domino.services.rest.RestServiceConstants.SORT_ORDER_DESCENDING;
import java.util.Vector;
import lotus.domino.Database;
import lotus.domino.DateTime;
import lotus.domino.Document;
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.util.StringUtil;
import com.ibm.domino.services.Loggers;
import com.ibm.domino.services.ResponseCode;
import com.ibm.domino.services.ServiceException;
import com.ibm.xsp.model.domino.wrapped.DominoViewEntry;
/**
* Domino View Service.
*/
public class RestViewNavigatorFactory {
/**
* Factory that creates a RestViewNavigator based on the view parameters.
*/
public static RestViewNavigator createNavigator(View view, ViewParameters parameters) throws ServiceException {
// NOI navigators
try {
String ftSearch = parameters.getSearch();
if(StringUtil.isNotEmpty(ftSearch)) {
return new FullTextNavigator(view,parameters);
}
// Key filtered navigator
Object keys = parameters.getKeys();
if(keys!=null) {
return new SearchKeyNavigator(view,parameters);
}
// On the parent id
String parentId = parameters.getParentId();
if(parentId!=null) {
return new ParentNavigator(view,parameters);
}
// On a category
String cat = parameters.getCategoryFilter();
if(cat!=null) {
return new CategoryNavigator(view,parameters);
}
// On a startkey
Object startKeys = parameters.getStartKeys();
if(startKeys != null) {
return new StartKeyNavigator(view,parameters);
}
// Regular navigator...
return new GenericNavigator(view,parameters);
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
public static RestViewNavigator createNavigatorForDesign(View view, ViewParameters parameters) throws ServiceException {
// NOI navigators
try {
return new NOINavigatorForDesign(view,parameters);
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
/**
* Abstract Backend classes navigator.
*/
public static abstract class NOINavigator extends RestViewNavigator {
View view;
Vector<ViewColumn> columns;
ViewEntry entry;
Vector<Object> columnValues;
protected NOINavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
this.view = view;
this.columns = (Vector<ViewColumn>)view.getColumns();
}
protected void resortView() {
// resort the view if sort criteria are specified
String sortColumn = getParameters().getSortColumn();
if (StringUtil.isNotEmpty(sortColumn)) {
try {
String sortOrder = getParameters().getSortOrder();
boolean ascending = !StringUtil.equals(sortOrder, SORT_ORDER_DESCENDING);
view.resortView(sortColumn, ascending);
} catch(NotesException ex) {
// Swallow the error if sort doesn't work
}
}
}
// Global methods
@Override
public int getTopLevelEntryCount() throws ServiceException {
try {
return view.getEntryCount();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
// Set the current entry
public boolean initEntry(ViewEntry e) throws NotesException {
if(entry!=null) {
entry.recycle();
}
columnValues = null;
entry = e;
if(e!=null) {
e.setPreferJavaDates(true);
return true;
}
return false;
}
// System columns
@Override
public String getUniversalId() throws ServiceException {
try {
return entry.getUniversalID();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public String getNoteId() throws ServiceException {
try {
return entry.getNoteID();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public String getPosition() throws ServiceException {
try {
return entry.getPosition('.');
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public boolean getRead() throws ServiceException {
try {
return entry.getRead();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public int getSiblings() throws ServiceException {
try {
return entry.getSiblingCount();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public int getDescendants() throws ServiceException {
try {
return entry.getDescendantCount();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public int getChildren() throws ServiceException {
try {
return entry.getChildCount();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public int getIndent() throws ServiceException {
try {
return entry.getIndentLevel();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public boolean isDocument() throws ServiceException {
try {
return entry.isDocument();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public boolean isCategory() throws ServiceException {
try {
return entry.isCategory();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public boolean isResponse() throws ServiceException {
try{
Object op = entry.getParent();
if(null != op && op instanceof ViewEntry){
ViewEntry parent = (ViewEntry)op;
return parent.isDocument();
}
return false;
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
// Column values
@Override
public int getColumnCount() throws ServiceException {
return columns.size();
}
@Override
public String getColumnName(int index) throws ServiceException {
try {
ViewColumn c = columns.get(index);
return c.getItemName();
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public Object getColumnValue(int index) throws ServiceException {
try {
if(index>=0) {
if(columnValues==null) {
columnValues = entry.getColumnValues();
}
ViewColumn c = columns.get(index);
int idx = c.getColumnValuesIndex();
if(idx!=DominoViewEntry.VC_NOT_PRESENT) {
//Looks like the vector is smaller when the entry is a category
if(idx<columnValues.size()) {
return columnValues.get(idx);
}
}
}
return null; // No values...
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public Object getColumnValue(String name) throws ServiceException {
return getColumnValue(findColumnPosition(name));
}
// Added by Tim Tripcony
// Hoping to add support for Form Formulas later on
@Override
public String getForm() throws ServiceException {
String result = "";
if (isDocument()) {
try {
Document document = entry.getDocument();
if (document != null) {
result = document.getItemValueString(ITEM_FORM);
}
} catch (NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
return result;
}
public int findColumnPosition(String name) throws ServiceException {
try {
int count = columns.size();
// Look for a programmatic name first
for(int i=0; i<count; i++) {
ViewColumn c = columns.get(i);
String s = c.getItemName();
if(StringUtil.equalsIgnoreCase(s, name)) {
return i;
}
}
// Then default to the title
for(int i=0; i<count; i++) {
ViewColumn c = columns.get(i);
String s = c.getTitle();
if(StringUtil.equalsIgnoreCase(s, name)) {
return i;
}
}
// Nothing found!
return Integer.MIN_VALUE;
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public String getItemName(String columnName) throws ServiceException {
try {
int pos = findColumnPosition(columnName);
if(pos>=0) {
ViewColumn col = columns.get(pos);
if(!col.isFormula()) {
// Hope that the item name is correct here...
return col.getItemName();
}
}
return null;
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public ViewEntry getViewEntry() {
return entry;
}
}
public static class NOINavigatorForDesign extends NOINavigator {
protected NOINavigatorForDesign(View view, ViewParameters parameters) throws NotesException {
super(view, parameters);
}
// This method is valid for this navigator
//public abstract String getItemName(String columnName) throws ServiceException;
@Override
public int getTopLevelEntryCount() throws ServiceException {throw new IllegalStateException();}
@Override
public boolean first(int start, int count) throws ServiceException {throw new IllegalStateException();}
@Override
public boolean next() throws ServiceException {throw new IllegalStateException();}
@Override
public String getUniversalId() throws ServiceException {throw new IllegalStateException();}
@Override
public String getNoteId() throws ServiceException {throw new IllegalStateException();}
@Override
public String getPosition() throws ServiceException {throw new IllegalStateException();}
@Override
public boolean getRead() throws ServiceException {throw new IllegalStateException();}
@Override
public int getSiblings() throws ServiceException {throw new IllegalStateException();}
@Override
public int getDescendants() throws ServiceException {throw new IllegalStateException();}
@Override
public int getChildren() throws ServiceException {throw new IllegalStateException();}
@Override
public int getIndent() throws ServiceException {throw new IllegalStateException();}
@Override
public int getColumnCount() throws ServiceException {throw new IllegalStateException();}
@Override
public String getColumnName(int index) throws ServiceException {throw new IllegalStateException();}
@Override
public Object getColumnValue(int index) throws ServiceException {throw new IllegalStateException();}
@Override
public Object getColumnValue(String name) throws ServiceException {throw new IllegalStateException();}
@Override
public boolean isDocument() throws ServiceException {throw new IllegalStateException();}
}
/**
* Abstract Backend classes navigator based on a ViewEntryCollection.
*/
public static abstract class ViewCollectionNavigator extends NOINavigator {
ViewEntryCollection collection;
protected ViewCollectionNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
}
@Override
public void recycle() {
try {
if(collection != null) {
collection.recycle();
collection = null;
}
} catch(NotesException ex) {
if( Loggers.SERVICES_LOGGER.isTraceDebugEnabled() ){
Loggers.SERVICES_LOGGER.info("Exception thrown on recycle.", ex); // $NON-NLS-1$
}
}
super.recycle();
}
boolean bFirst = false;
@Override
public boolean first(int start, int count) throws ServiceException {
if (bFirst)
return bFirst;
try {
collection = createCollection();
if(!initEntry(collection.getFirstEntry())) {
return false;
}
if(start>0) {
for(int i=0; i<start; i++) {
if(!initEntry(collection.getNextEntry())) {
return false;
}
}
}
bFirst = true;
return true;
} catch(NotesException ex) {
throw new ServiceException(ex, ResponseCode.BAD_REQUEST, ex.getLocalizedMessage());
}
}
@Override
public boolean next() throws ServiceException {
try {
if(!initEntry(collection.getNextEntry())) {
return false;
}
return true;
} catch(NotesException ex) {
throw new ServiceException(ex, ResponseCode.BAD_REQUEST, ex.getLocalizedMessage());
}
}
@Override
public int getTopLevelEntryCount() throws ServiceException {
if (collection != null) {
try {
return collection.getCount();
} catch(NotesException ex) {
throw new ServiceException(ex, ResponseCode.BAD_REQUEST, ex.getLocalizedMessage());
}
} else {
return super.getTopLevelEntryCount();
}
}
protected abstract ViewEntryCollection createCollection() throws NotesException, ServiceException;
}
/**
* Backend classes navigator filtering on a FT search.
*/
public static class FullTextNavigator extends ViewCollectionNavigator {
public FullTextNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
}
@Override
protected ViewEntryCollection createCollection() throws NotesException, ServiceException {
Database db = view.getParent();
if (!db.isFTIndexed()) {
throw new ServiceException(null, "Database is not full text indexed."); // $NON-NLS-1$
}
String search = getParameters().getSearch();
int maxDocs = getParameters().getSearchMaxDocs();
String sortColumn = getParameters().getSortColumn();
// if 'sortColumn' is specified then use FTSearchSorted
// FTSearchSorted call will fail if the DB is not full text indexed
if (StringUtil.isNotEmpty(sortColumn)) {
try {
String sortOrder = getParameters().getSortOrder();
boolean ascending = !StringUtil.equals(sortOrder, SORT_ORDER_DESCENDING);
if (maxDocs < 0)
maxDocs = 0;
boolean exact = getParameters().isKeysExactMatch();
boolean variants = false, fuzzy = false;
view.FTSearchSorted(search, maxDocs, sortColumn, ascending, exact, variants, fuzzy);
} catch(NotesException ex) {
throw new ServiceException(ex, ResponseCode.BAD_REQUEST, ex.getLocalizedMessage());
}
return view.getAllEntries();
}
if(maxDocs<=0) {
view.FTSearch(search);
} else {
view.FTSearch(search,maxDocs);
}
return view.getAllEntries();
}
}
/**
* Backend classes navigator filtering on a search key.
*/
public static class SearchKeyNavigator extends ViewCollectionNavigator {
protected SearchKeyNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
resortView();
}
@Override
protected ViewEntryCollection createCollection() throws NotesException {
boolean exact = getParameters().isKeysExactMatch();
Object keys = getParameters().getKeys();
if(keys instanceof Vector<?>) {
Vector v = (Vector)keys;
for (int i = 0; i < v.size(); i++) {
if (v.get(i) instanceof java.util.Date) {
DateTime notesDate = view.getParent().getParent().createDateTime((java.util.Date)v.get(i));
v.remove(i);
v.add(i, notesDate);
}
}
return view.getAllEntriesByKey((Vector<?>)keys,exact);
} else if (keys instanceof java.util.Date) {
DateTime notesDate = view.getParent().getParent().createDateTime((java.util.Date)keys);
return view.getAllEntriesByKey(notesDate,exact);
} else {
return view.getAllEntriesByKey(keys,exact);
}
}
}
/**
* Abstract Backend classes navigator based on the ViewNavigator.
*/
public static abstract class ViewNavigatorNavigator extends NOINavigator {
ViewNavigator navigator;
protected ViewNavigatorNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
}
@Override
public void recycle() {
if(navigator!=null) {
try {
navigator.recycle();
navigator = null;
} catch(NotesException ex) {}
}
super.recycle();
}
boolean bFirst = false;
@Override
public boolean first(int start, int count) throws ServiceException {
if (bFirst)
return bFirst;
try {
navigator = createNavigator();
int maxLevels = getParameters().getExpandLevel();
if(maxLevels!=Integer.MAX_VALUE) {
navigator.setMaxLevel(maxLevels);
}
if(!navigator.gotoFirst()) {
return false;
}
navigator.setBufferMaxEntries(count);
if(start>0) {
int skipped = navigator.skip(start);
if(skipped<start) {
return false;
}
}
bFirst = initEntry(navigator.getCurrent());
return bFirst;
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public boolean next() throws ServiceException {
try {
return initEntry(navigator.getNext());
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
}
@Override
public int getTopLevelEntryCount() throws ServiceException {
if (view!=null) {
try {
if(view.isCategorized()) {
return navigator.getCount();
}
else {
return view.getEntryCount();
}
} catch(NotesException ex) {
throw new ServiceException(ex,""); // $NON-NLS-1$
}
} else {
return super.getTopLevelEntryCount();
}
}
protected abstract ViewNavigator createNavigator() throws NotesException;
}
/**
* Backend classes generic navigator.
*/
public static class GenericNavigator extends ViewNavigatorNavigator {
public GenericNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
resortView();
}
@Override
protected ViewNavigator createNavigator() throws NotesException {
return view.createViewNav();
}
}
/**
* Backend classes navigator starting from a particular key.
*/
public static class StartKeyNavigator extends ViewNavigatorNavigator {
public StartKeyNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
resortView();
}
@Override
protected ViewNavigator createNavigator() throws NotesException {
Object startKeys = getParameters().getStartKeys();
ViewEntry ve = view.getEntryByKey(startKeys);
if(ve!=null) {
return view.createViewNavFrom(ve);
}
return view.createViewNav();
}
@Override
public int getTopLevelEntryCount() throws ServiceException {
if (navigator!=null) {
return navigator.getCount();
} else {
return super.getTopLevelEntryCount();
}
}
}
/**
* Backend classes navigator filtering on a category.
*/
public static class CategoryNavigator extends ViewNavigatorNavigator {
public CategoryNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
}
@Override
protected ViewNavigator createNavigator() throws NotesException {
String cat = getParameters().getCategoryFilter();
return view.createViewNavFromCategory(cat);
}
@Override
public int getTopLevelEntryCount() throws ServiceException {
if (navigator!=null) {
return navigator.getCount();
} else {
return super.getTopLevelEntryCount();
}
}
}
/**
* Backend classes navigator filtering on a parent id.
*/
public static class ParentNavigator extends ViewNavigatorNavigator {
public ParentNavigator(View view, ViewParameters parameters) throws NotesException {
super(view,parameters);
}
@Override
protected ViewNavigator createNavigator() throws NotesException {
String parentId = getParameters().getParentId();
Document doc = null;
if (parentId.length() == 32)
doc = view.getParent().getDocumentByUNID(parentId);
else
doc = view.getParent().getDocumentByID(parentId);
return view.createViewNavFromDescendants(doc);
}
@Override
public int getTopLevelEntryCount() throws ServiceException {
if (navigator!=null) {
return navigator.getCount();
} else {
return super.getTopLevelEntryCount();
}
}
}
}