/*******************************************************************************
* Imixs Workflow Technology
* Copyright (C) 2003, 2008 Imixs Software Solutions GmbH,
* http://www.imixs.com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You can receive a copy of the GNU General Public
* License at http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Imixs Software Solutions GmbH - initial API and implementation
* Ralph Soika
*
*******************************************************************************/
package org.imixs.marty.workflow;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.enterprise.event.Observes;
import javax.faces.event.AjaxBehaviorEvent;
import javax.inject.Inject;
import javax.inject.Named;
import javax.naming.NamingException;
import org.imixs.marty.util.WorkitemHelper;
import org.imixs.workflow.ItemCollection;
import org.imixs.workflow.ItemCollectionComparator;
import org.imixs.workflow.engine.WorkflowService;
import org.imixs.workflow.engine.lucene.LuceneSearchService;
import org.imixs.workflow.exceptions.AccessDeniedException;
import org.imixs.workflow.exceptions.QueryException;
/**
* The WorkitemLinkController provides suggest-box behavior based on the JSF 2.0
* ajax capability to add WorkItem references to the current WorkItem.
*
* All WorkItem references will be stored in the property 'txtworkitemref'
* Note: @RequestScoped did not work because the ajax request will reset the
* result during submit
*
*
*
* @author rsoika
* @version 1.0
*/
@Named("workitemLinkController")
@SessionScoped
public class WorkitemLinkController implements Serializable {
public static final String LINK_PROPERTY = "txtworkitemref";
public static final int MAX_SEARCH_RESULT=999;
public static Logger logger = Logger.getLogger(WorkitemLinkController.class.getName());
@Inject
protected WorkflowController workflowController;
@EJB
protected WorkflowService workflowService;
private static final long serialVersionUID = 1L;
private List<ItemCollection> searchResult = null;
private Map<String, List<ItemCollection>> externalReferences = null;
private Map<String, List<ItemCollection>> references = null;
private String input = null;
private int minimumChars = 3; // minimum input required for a suggestion
public WorkitemLinkController() {
super();
searchResult = new ArrayList<ItemCollection>();
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
/**
* minimum input required for a suggestion Default is 3
*
* @return
*/
public int getMinimumChars() {
return minimumChars;
}
public void setMinimumChars(int minimumChars) {
this.minimumChars = minimumChars;
}
/**
* This method reset the search and input state.
*/
public void reset() {
searchResult = new ArrayList<ItemCollection>();
input = "";
logger.fine("workitemLinkController reset");
}
/**
* This ajax event method reset the search and input state.
*
* @param event
*/
public void reset(AjaxBehaviorEvent event) {
reset();
}
/**
* Starts a lucene search to provide searchResult for suggest list
*
* @param filter
* - search filter
* @param minchars
* - the minimum length for the filter string
*/
public void search(String filter, int minchars) {
if (input == null || input.isEmpty() || input.length() < minchars)
return;
logger.fine("LinkController SearchOption=" + filter);
searchResult = new ArrayList<ItemCollection>();
try {
String sSearchTerm = "";
// search only type workitem and workitemsarchive
sSearchTerm += "((type:workitem) OR (type:workitemarchive)) AND ";
if (filter != null && !"".equals(filter)) {
String sNewFilter = filter;
sNewFilter = sNewFilter.replace(".", "?");
sSearchTerm += "(" + sNewFilter + ") AND ";
}
if (!"".equals(input)) {
// escape input..
input = LuceneSearchService.escapeSearchTerm(input);
sSearchTerm += " (*" + input.toLowerCase() + "*)";
}
searchResult = workflowService.getDocumentService().find(sSearchTerm, MAX_SEARCH_RESULT, 0);
// clone result list
for (int i = 0; i < searchResult.size(); i++) {
searchResult.set(i, WorkitemHelper.clone(searchResult.get(i)));
}
} catch (Exception e) {
logger.warning(" lucene error!");
e.printStackTrace();
}
}
/*
* Starts a lucene search to provide searchResult for suggest list
*/
public void search(String filter) {
search(filter, minimumChars);
}
public List<ItemCollection> getSearchResult() {
return searchResult;
}
/**
* This methods adds a new workItem reference
*/
public void add(String aUniqueID, ItemCollection workitem) {
logger.fine("LinkController add workitem reference: " + aUniqueID);
@SuppressWarnings("unchecked")
List<String> refList = workitem.getItemValue(LINK_PROPERTY);
// clear empty entry if set
if (refList.size() == 1 && "".equals(refList.get(0)))
refList.remove(0);
// test if not yet a member of
if (refList.indexOf(aUniqueID) == -1) {
refList.add(aUniqueID);
workitem.replaceItemValue(LINK_PROPERTY, refList);
}
// reset
reset(null);
references = null;
}
/**
* This methods removes a workItem reference
*/
public void remove(String aUniqueID, ItemCollection workitem) {
logger.fine("LinkController remove workitem reference: " + aUniqueID);
@SuppressWarnings("unchecked")
List<String> refList = workitem.getItemValue(LINK_PROPERTY);
// test if a member of
if (refList.indexOf(aUniqueID) > -1) {
refList.remove(aUniqueID);
workitem.replaceItemValue(LINK_PROPERTY, refList);
}
// reset
reset(null);
references = null;
}
/**
* This method returns a list of all ItemCollections referred by the current
* workItem (txtWorkitemRef).
*
* @return - list of ItemCollection
*/
public List<ItemCollection> getReferences() {
return getReferences("");
}
/**
* This method returns a list of ItemCollections referred by the current
* workItem (txtWorkitemRef).
*
* The filter will be applied to the result list. So each WorkItem will be
* tested if it matches the filter expression. The results of this method
* are cached into the references map. This cache is discarded if the
* current workItem changed.
*
* The resultset is sorted by $created
*
* @return - list of ItemCollection with matches the current filter
*/
@SuppressWarnings("unchecked")
public List<ItemCollection> getReferences(String filter) {
List<ItemCollection> filterResult = null;
if (references == null)
references = new HashMap<String, List<ItemCollection>>();
// lazy loading references by filter?
filterResult = references.get(filter);
if (filterResult == null) {
// build a new workitem list for that filter....
filterResult = new ArrayList<ItemCollection>();
if (workflowController.getWorkitem() == null) {
return filterResult;
}
logger.fine("lookup references for: " + filter);
// lookup the references...
List<String> list = workflowController.getWorkitem().getItemValue(LINK_PROPERTY);
// empty list?
if (list.size() == 0 || (list.size() == 1 && "".equals(list.get(0)))) {
references.put(filter, filterResult);
return filterResult;
}
// start query and filter the result
String sQuery = "(";
sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") AND (";
for (String aID : list) {
sQuery += "$uniqueid:\"" + aID + "\" OR ";
}
// cut last ,
sQuery = sQuery.substring(0, sQuery.length() - 3);
sQuery += " )";
List<ItemCollection> workitems;
try {
workitems = workflowService.getDocumentService().find(sQuery, MAX_SEARCH_RESULT, 0);
// sort by modified
Collections.sort(workitems, new ItemCollectionComparator("$created", true));
if (workitems.size() == 0) {
references.put(filter, filterResult);
return filterResult;
}
// now test if filter matches, and clone the workItem
for (ItemCollection itemcol : workitems) {
// test
if (WorkitemHelper.matches(itemcol, filter)) {
filterResult.add(WorkitemHelper.clone(itemcol));
}
}
references.put(filter, filterResult);
} catch (QueryException e) {
logger.warning("loadVersionWorkItemList - invalid query: " + e.getMessage());
}
}
return filterResult;
}
/**
* Returns a list of all workItems holding a reference to the current
* workItem.
*
* @return
* @throws NamingException
*/
public List<ItemCollection> getExternalReferences() {
return getExternalReferences("");
}
/**
* returns a list of all workItems holding a reference to the current
* workItem. If the filter is set the processID will be tested for the
* filter regex
*
* @return
* @param filter
* @throws NamingException
*/
public List<ItemCollection> getExternalReferences(String filter) {
List<ItemCollection> filterResult = null;
if (externalReferences == null)
externalReferences = new HashMap<String, List<ItemCollection>>();
// lazy loading references by filter?
filterResult = externalReferences.get(filter);
if (filterResult == null) {
// build a new workitem list for that filter....
filterResult = new ArrayList<ItemCollection>();
String uniqueid = workflowController.getWorkitem().getItemValueString("$uniqueid");
// return an empty list if still no $uniqueid is defined for the
// current
// workitem
if ("".equals(uniqueid))
return filterResult;
// select all references.....
// String sQuery = "SELECT workitem FROM Entity AS workitem" + "
// JOIN workitem.textItems AS rnr"
// + " WHERE workitem.type IN ('workitem','workitemarchive') " + "
// AND rnr.itemName = '"
// + LINK_PROPERTY + "'" + " AND rnr.itemValue='" + uniqueid + "'" +
// " ORDER BY workitem.created DESC";
String sQuery = "(";
sQuery = " (type:\"workitem\" OR type:\"workitemarchive\") AND (" + LINK_PROPERTY + ":\"" + uniqueid
+ "\")";
List<ItemCollection> workitems = null;
try {
workitems = workflowService.getDocumentService().find(sQuery, MAX_SEARCH_RESULT, 0);
// sort by modified
Collections.sort(workitems, new ItemCollectionComparator("$created", true));
if (workitems.size() == 0) {
externalReferences.put(filter, filterResult);
return filterResult;
}
} catch (Exception e) {
e.printStackTrace();
}
if (filter != null) {
// now test if filter matches, and clone the workItem
for (ItemCollection itemcol : workitems) {
// test
if (WorkitemHelper.matches(itemcol, filter)) {
filterResult.add(WorkitemHelper.clone(itemcol));
}
}
}
externalReferences.put(filter, filterResult);
}
return filterResult;
}
/**
* WorkflowEvent listener
*
* @param workflowEvent
* @throws AccessDeniedException
*/
public void onWorkflowEvent(@Observes WorkflowEvent workflowEvent) throws AccessDeniedException {
// reset suggest list state
reset();
if (workflowEvent == null)
return;
// skip if not a workItem...
if (workflowEvent.getWorkitem() != null
&& !workflowEvent.getWorkitem().getItemValueString("type").startsWith("workitem"))
return;
if (WorkflowEvent.WORKITEM_CHANGED == workflowEvent.getEventType()) {
externalReferences = null;
references = null;
}
if (WorkflowEvent.WORKITEM_AFTER_PROCESS == workflowEvent.getEventType()) {
externalReferences = null;
references = null;
}
}
}