View Javadoc

1   /* ===================================================================
2    * Copyright 2002-06 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    * This software is distributed on an "AS IS"
6    * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
7    * See the License for the specific language governing rights and
8    * limitations under the License.
9    * =================================================================== */
10  package com.sri.emo.controller.nodefilter;
11  
12  import com.jcorporate.expresso.core.controller.*;
13  import com.jcorporate.expresso.core.db.DBException;
14  import com.jcorporate.expresso.core.dbobj.MultiDBObject;
15  import com.jcorporate.expresso.core.dbobj.ValidValue;
16  import com.jcorporate.expresso.core.security.SuperUser;
17  import com.jcorporate.expresso.services.dbobj.RowGroupPerms;
18  import com.jcorporate.expresso.services.dbobj.UserGroup;
19  import com.sri.common.controller.AbstractDBController;
20  import com.sri.common.util.QueryUtils;
21  import com.sri.emo.annotations.NodeTag;
22  import com.sri.emo.controller.NodeAction;
23  import com.sri.emo.dbobj.Attribute;
24  import com.sri.emo.dbobj.Node;
25  import com.sri.emo.dbobj.NodeType;
26  
27  import java.util.*;
28  
29  
30  /***
31   * Provides a session storage wrapper around the filter criteria.
32   *
33   * @author Michael Rimov
34   * @author Larry Hamel
35   */
36  public class NodeFilter implements FilterKeyConstants {
37  
38  //    private Logger log = Logger.getLogger(NodeFilter.class);
39      private ExpressoRequest request;
40      private Map filters = null;
41      public static final String IS_TAG_RADIO = "isTag";
42      public static final String IS_LAST_EDITOR_RADIO = "isLastEd";
43      private static final String TAG_OR_LAST_EDITOR_TEXT = "TAG_OR_LAST_ED_TEXT";
44  //    private Logger getLogger() {
45  //        return log;
46  //    }
47  
48      /***
49       * Constructs a filter for the given request
50       */
51      public NodeFilter(ExpressoRequest request) {
52          this.request = request;
53      }
54  
55      /***
56       * @return true if filtering criteria exists
57       */
58      public boolean isFiltered() throws ControllerException, DBException {
59          Map filterMap = getCriteria();
60          return (filterMap != null && !filterMap.isEmpty()) || getFilterAttribute() != null;
61      }
62  
63      /***
64       * Retrieve the criteria stored in the session.  This object
65       * <em>always</em> generates a new copy of the criteria
66       *
67       * @return map of criteria, never null
68       */
69      private Map getCriteria() throws ControllerException {
70  //        if (filters != null) return new HashMap(filters);
71          Map result = null;
72          if (filters != null) {
73              result = filters;
74          } else {
75  
76              result = (Map) request.getSession().getPersistentAttribute(FILTER_KEY);
77              if (result == null) {
78                  filters = result = new HashMap();
79              }
80          }
81  
82          return result;
83      }
84  
85      /***
86       * Sets the new search criteria
87       *
88       * @param newCriteria
89       */
90      private void setCriteria(final Map newCriteria) throws ControllerException {
91          assert newCriteria != null;
92          request.getSession().setPersistentAttribute(FILTER_KEY, newCriteria);
93          filters = null; // clear cache
94      }
95  
96  
97      /***
98       * attribute filtering involves some field of an attribute
99       * associated with a given node
100      *
101      * @return attribute that will filter, or null if no filtering is needed
102      */
103     public Attribute getFilterAttribute() throws DBException {
104         Attribute attrib = new Attribute();
105         AbstractDBController.populateDBObject(attrib, request);
106 
107         // attrib will always have at LEAST fields NODE_ID and NODE_TYPE set.
108         // any others? their presence would indicate that a join in necessary.
109         // note: to determine if any other settings are here,
110         // first, clear the always-set fields in order to see if others exist
111         // with isEmpty()
112         String rememberType = attrib.getParentNodeType();
113         String id = attrib.getParentNodeId();
114         attrib.setParentNodeId((String) null);
115         attrib.setParentNodeType((String) null);
116 
117         if (attrib.isEmpty()) {
118             return null;
119         }
120 
121         attrib.setParentNodeType(rememberType);
122         attrib.setParentNodeId(id);
123 
124         return attrib;
125     }
126 
127     /***
128      * add filtering for last editor
129      */
130     public void addLasteditorFilter(Node samplenode) throws ControllerException, DBException {
131         String lastEd = (String) getCriteria().get(FILTER_LAST_EDITOR);
132         if (lastEd != null && lastEd.length() > 0) {
133             lastEd = lastEd.trim();
134             samplenode.setNodeOwner("%" + lastEd + "%"); // todo change to join for display name test too
135         }
136     }
137 
138     public boolean isFilteringWords() throws ControllerException {
139         return getCriteria().get(FILTER_WORDS) != null;
140     }
141 
142     /***
143      * add any filters to the multiDBObject supplied.  may add nothing, depending on criteria
144      *
145      * @param multi      a multiobject with already-added sample node object
146      * @param samplenode
147      */
148     public void addFiltersToMulti(MultiDBObject multi, Node samplenode) throws ControllerException, DBException {
149         StringBuffer where = new StringBuffer();
150 
151         // word filter
152         if (isFilteringWords()) {
153             String words = (String) getCriteria().get(FILTER_WORDS);
154             words = words.trim();
155             String[] wordarr = words.split("//W"); // whitespace
156 
157             String[] fieldsToSearch = new String[]{Node.NODE_ANNOTATION, Node.NODE_COMMENT, Node.NODE_TITLE};
158 
159             where.append(QueryUtils.keywordMatchClause(wordarr, fieldsToSearch));
160         }
161 
162         if (isFilteringTags()) {
163             multi.addDBObj(NodeTag.class.getName(), NodeTag.TAGS_TABLE);
164             multi.setForeignKey(NodeTag.TAGS_TABLE, NodeTag.TAG_PARENT_NODE,
165                     Node.NODE_TABLE, Node.NODE_ID);
166             multi.setFieldsToRetrieveToNone(NodeTag.TAGS_TABLE);
167 
168             addAnd(where);
169 
170             String tags = (String) getCriteria().get(FILTER_TAGS);
171             tags = tags.trim();
172             String[] wordarr = tags.split("//W"); // whitespace
173             where.append(QueryUtils.keywordMatchClause(wordarr, NodeTag.TAG_VALUE));
174         }
175 
176         if (isFilteringGroups()) {
177             multi.addDBObj(RowGroupPerms.class.getName(), RowGroupPerms.GROUP_PERMISSIONS_TABLE_NAME);
178             multi.setFieldsToRetrieveToNone(RowGroupPerms.GROUP_PERMISSIONS_TABLE_NAME);
179 
180             // row group perms has a field for the name of the target table
181             // this setfield doesn't work for custom where
182 //            multi.setField(RowGroupPerms.GROUP_PERMISSIONS_TABLE_NAME, RowGroupPerms.TABLE_NAME, Node.NODE_TABLE);
183             addAnd(where);
184 
185             where.append(
186                     QueryUtils.fieldEqualsValueClause(
187                             RowGroupPerms.TABLE_NAME, Node.NODE_TABLE, true, false));
188 
189             // different types--int vs. string; cannot be set as foreign key using easy multi method
190 //            joinObject.setForeignKey(RowGroupPerms.TABLE_NAME, RowGroupPerms.ROW_KEY,
191 //                    Node.NODE_TABLE, Node.NODE_ID);
192             addAnd(where);
193 
194             // manually convert types; careful to convert and compare strings rather than convert to numbers
195             where.append(RowGroupPerms.ROW_KEY);
196             where.append(" = ");
197             where.append("CAST( ");
198             where.append(Node.NODE_TABLE);
199             where.append(".");
200             where.append(Node.NODE_ID);
201             where.append(" AS CHAR ) ");
202 
203             addAnd(where);
204 
205             String grp = (String) getCriteria().get(FILTER_GROUP);
206             where.append(
207                     QueryUtils.fieldEqualsValueClause(
208                             RowGroupPerms.GROUP, grp, true, false));
209         }
210 
211         // ok, did we end up with a custom clause?
212         if (where.length() > 0) {
213             // add samplenode filters
214             addAnd(where);
215 
216             // we must add back any where clauses present before our customization
217             String prestatement = multi.getSQLSelectStatement();
218             int whereStart = prestatement.indexOf("WHERE");
219             if (whereStart >= 0) {
220                 where.append(prestatement.substring(whereStart + "WHERE".length()));
221             }
222 //            where.append(
223 //                    QueryUtils.fieldEqualsValueClause(
224 //                            Node.NODE_TYPE, samplenode.getNodeType(), true, false));
225 //
226 //            if ( samplenode.getNodeOwner().length() > 0) {
227 //                addAnd(where);
228 //
229 //                where.append(
230 //                        QueryUtils.fieldEqualsValueClause(
231 //                                Node.NODE_OWNER, samplenode.getNodeOwner(), true, false));
232 //            }
233 
234             multi.setCustomWhereClause(where.toString(), true);
235         }
236     }
237 
238     private void addAnd(StringBuffer where) {
239         if (where.length() > 0) {
240             where.append(" AND ");
241         }
242     }
243 
244     private boolean isFilteringGroups() throws ControllerException {
245         return getCriteria().get(FILTER_GROUP) != null;
246     }
247 
248     private boolean isFilteringTags() throws ControllerException {
249         return getCriteria().get(FILTER_TAGS) != null;
250     }
251 
252     public MultiDBObject getMulti(Node samplenode) throws DBException, ControllerException {
253         MultiDBObject multi = new MultiDBObject();
254         multi.setDataContext(request.getDataContext());
255         multi.setSelectDistinct(true);
256         multi.addDBObj(samplenode, Node.NODE_TABLE);
257 
258         Attribute attrib = getFilterAttribute();
259         if (attrib != null) {
260             // we must do a join to filter by attribute
261             // reset fields cleared for isEmpty() test of existance of criteria
262             attrib.setField(Attribute.NODE_ID, samplenode.getNodeId());
263             attrib.setField(Attribute.NODE_TYPE, samplenode.getNodeType());
264 
265             multi.addDBObj(attrib, Attribute.TABLE_NAME);
266             multi.setForeignKey(
267                     Attribute.TABLE_NAME, Attribute.NODE_ID,
268                     Node.NODE_TABLE, Node.NODE_ID);
269 
270             multi.setFieldsToRetrieveToNone(Attribute.TABLE_NAME);
271         }
272 
273         // lastEditor is the owner field of the Node
274         addLasteditorFilter(samplenode);
275 
276         //
277         // set up Multi for other criteria
278         //
279 
280         addFiltersToMulti(multi, samplenode);
281 
282 
283         return multi;
284     }
285 
286     public void addInputs(ExpressoResponse response)
287             throws DBException, ControllerException {
288 
289         //Groups
290         Input groupInput = new Input();
291         groupInput.setName(FILTER_GROUP);
292         groupInput.setValidValues(getGroupValidValues());
293         if (isFilteringGroups()) {
294             groupInput.setDefaultValue((String) getCriteria().get(FILTER_GROUP));
295         }
296         response.add(groupInput);
297 
298 
299         Input tagOrLastEditorRadio = new Input(TAG_OR_LAST_EDITOR_RADIO);
300         tagOrLastEditorRadio.setType(Input.ATTRIBUTE_RADIO_VERTICAL);
301         tagOrLastEditorRadio.addValidValue(IS_TAG_RADIO, "Tag");
302         tagOrLastEditorRadio.addValidValue(IS_LAST_EDITOR_RADIO, "Last Editor");
303         tagOrLastEditorRadio.setDefaultValue(IS_TAG_RADIO);
304         if (isFilteringLastEditor()) {
305             tagOrLastEditorRadio.setDefaultValue(IS_LAST_EDITOR_RADIO);
306         }
307         response.add(tagOrLastEditorRadio);
308 
309 
310         Input words = new Input(FILTER_WORDS);
311         words.setType(Input.ATTRIBUTE_TEXTLINE);
312         if (isFilteringWords()) {
313             words.setDefaultValue((String) getCriteria().get(FILTER_WORDS));
314         }
315         response.add(words);
316 
317         Input tagOrLastEd = new Input(TAG_OR_LAST_EDITOR_TEXT);
318         tagOrLastEd.setType(Input.ATTRIBUTE_TEXTLINE);
319         if (isFilteringTags()) {
320             tagOrLastEd.setDefaultValue((String) getCriteria().get(FILTER_TAGS));
321         } else if (isFilteringLastEditor()) {
322             tagOrLastEd.setDefaultValue((String) getCriteria().get(FILTER_LAST_EDITOR));
323         }
324         response.add(tagOrLastEd);
325 
326         String nodeTypeName = request.getParameter(NodeType.NODE_TYPE_NAME);
327         Transition submit = new Transition("Filter", "Filter", NodeAction.class, DoFilterNodes.STATE_NAME);
328         submit.addParam(Node.NODE_TYPE, request.getParameter(Node.NODE_TYPE));
329         if ( nodeTypeName != null ) {
330             submit.addParam(NodeType.NODE_TYPE_NAME, nodeTypeName);
331         }
332         Attribute attrib = getFilterAttribute();
333         if (attrib != null) {
334             List list = attrib.getMetaData().getFieldNamesList();
335             for (Iterator iterator = list.iterator(); iterator.hasNext();) {
336                 String name = (String) iterator.next();
337                 if (!attrib.getMetaData().isVirtual(name) && !attrib.isFieldNull(name)) {
338                     submit.addParam(name, attrib.getField(name));
339                 }
340             }
341         }
342         NodeAction.checkEmbedded(request, submit);
343 
344         response.add(submit);
345     }
346 
347     private List getGroupValidValues() throws DBException {
348         UserGroup groups = new UserGroup(SuperUser.INSTANCE);
349         List groupValidValues = groups.getValues();
350         ListIterator listIterator = new ArrayList(groupValidValues).listIterator();
351 
352         // remove unwanted items
353         while (listIterator.hasNext()) {
354             ValidValue validValue = (ValidValue) listIterator.next();
355             if (UserGroup.UNKNOWN_USERS_GROUP.equals(validValue.getKey())) {
356                 groupValidValues.remove(validValue);
357             }
358             if (UserGroup.NOT_REG_USERS_GROUP.equals(validValue.getKey())) {
359                 groupValidValues.remove(validValue);
360             }
361             if (UserGroup.ADMIN_GROUP.equals(validValue.getKey())) {
362                 groupValidValues.remove(validValue);
363             }
364             if (UserGroup.DEMO_GROUP.equals(validValue.getKey())) {
365                 groupValidValues.remove(validValue);
366             }
367         }
368         groupValidValues.add(0, new ValidValue("", "Any"));
369         return groupValidValues;
370     }
371 
372     private boolean isFilteringLastEditor() throws ControllerException {
373         return getCriteria().get(FILTER_LAST_EDITOR) != null;
374     }
375 
376     /***
377      * get criteria from request and replace in session
378      */
379     public void saveNewCriteria() throws ControllerException {
380 
381         decodeTagOrLastEditor();
382 
383         Map map = new HashMap();
384         for (int i = 0; i < ALL_FILTER_KEYS.length; i++) {
385             String key = ALL_FILTER_KEYS[i];
386             String val = request.getParameter(key);
387             if (val != null) {
388                 val = val.trim();
389                 if (val.length() > 0) map.put(key, val);
390             }
391         }
392 
393         setCriteria(map);
394     }
395 
396     private void decodeTagOrLastEditor() throws ControllerException {
397         String tagEditorText = request.getParameter(TAG_OR_LAST_EDITOR_TEXT);
398         if (tagEditorText != null && tagEditorText.length() > 0) {
399             // decode radio
400             String radio = request.getParameter(TAG_OR_LAST_EDITOR_RADIO);
401             boolean isTag = true;
402             if (IS_LAST_EDITOR_RADIO.equals(radio)) {
403                 isTag = false;
404             }
405 
406             if (isTag) {
407                 request.setParameter(FILTER_TAGS, tagEditorText);
408             } else {
409                 request.setParameter(FILTER_LAST_EDITOR, tagEditorText);
410             }
411         }
412     }
413 }