001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2007
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.faces.beans;
034:
035: import com.flexive.faces.FxJsfUtils;
036: import com.flexive.faces.messages.FxFacesMsgErr;
037: import com.flexive.faces.messages.FxFacesMsgInfo;
038: import com.flexive.shared.CacheAdmin;
039: import com.flexive.shared.EJBLookup;
040: import com.flexive.shared.exceptions.FxApplicationException;
041: import com.flexive.shared.exceptions.FxInvalidQueryNodeException;
042: import com.flexive.shared.exceptions.FxRuntimeException;
043: import com.flexive.shared.search.AdminResultLocations;
044: import com.flexive.shared.search.ResultLocation;
045: import com.flexive.shared.search.ResultViewType;
046: import com.flexive.shared.search.query.*;
047: import static com.flexive.shared.search.query.QueryRootNode.Type.CONTENTSEARCH;
048: import com.flexive.shared.structure.FxAssignment;
049: import com.flexive.shared.structure.FxPropertyAssignment;
050: import com.flexive.shared.structure.FxType;
051: import com.flexive.shared.tree.FxTreeMode;
052: import com.flexive.shared.tree.FxTreeNode;
053: import com.flexive.shared.value.FxLargeNumber;
054: import org.apache.commons.lang.StringUtils;
055: import org.apache.commons.logging.LogFactory;
056: import org.apache.commons.logging.Log;
057:
058: import javax.faces.event.ActionEvent;
059: import java.io.Serializable;
060: import java.util.ArrayList;
061: import java.util.List;
062: import java.util.Scanner;
063:
064: /**
065: * Search query editor beans.
066: *
067: * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
068: * @version $Rev: 224 $
069: */
070: public class QueryEditorBean implements Serializable {
071: private static final Log LOG = LogFactory
072: .getLog(QueryEditorBean.class);
073: private static final long serialVersionUID = -7734399826904382438L;
074:
075: /**
076: * JSF root component containing the query editor
077: */
078: private static final String RESET_COMPONENT_ID = "frm:queryEditor";
079:
080: private List<FxAssignment> properties = null;
081: private QueryRootNode rootNode = null;
082: private long addAssignmentId;
083: private int addAssignmentNodeId = -1;
084: private boolean addNodeLive = false;
085: private int removeNodeId = -1;
086: private String nodeSelection = null;
087:
088: private boolean saveQuery;
089:
090: private SqlQueryBuilder queryBuilder;
091: private ResultLocation location = AdminResultLocations.ADMIN;
092: private String filterTypeName;
093:
094: public QueryEditorBean() {
095: parseRequestParameters();
096: }
097:
098: /**
099: * Parse the request parameters and perform actions as requested.
100: * Works only if the QueryEditorBean remains request-scoped!
101: */
102: private void parseRequestParameters() {
103: try {
104: String action = FxJsfUtils.getParameter("action");
105: if (StringUtils.isBlank(action)) {
106: // no action requested
107: return;
108: }
109: if ("nodeSearch".equals(action)) {
110: // create a new query with the node set as "nodeId"
111: setRootNode(new QueryRootNode(
112: QueryRootNode.Type.CONTENTSEARCH, location));
113: addAssignmentId = FxJsfUtils.getLongParameter("nodeId",
114: FxTreeNode.ROOT_NODE);
115: addNodeLive = FxJsfUtils.getBooleanParameter(
116: "liveMode", false);
117: addTreeNode(null);
118: } else if ("typeSearch".equals(action)) {
119: setRootNode(new QueryRootNode(
120: QueryRootNode.Type.CONTENTSEARCH, location));
121: addAssignmentId = FxJsfUtils.getLongParameter("typeId",
122: -1);
123: if (addAssignmentId != -1) {
124: addTypeQuery(null);
125: }
126: } else if ("assignmentSearch".equals(action)) {
127: setRootNode(new QueryRootNode(
128: QueryRootNode.Type.CONTENTSEARCH, location));
129: addAssignmentId = FxJsfUtils.getLongParameter(
130: "assignmentId", -1);
131: if (addAssignmentId != -1) {
132: addProperty(null);
133: }
134: } else if ("new".equals(action)) {
135: // create a new search query
136: getRootNode().getChildren().clear();
137: getRootNode().setName(null);
138: } else if ("load".equals(action)) {
139: setRootNode(EJBLookup.getSearchEngine().load(
140: AdminResultLocations.ADMIN,
141: FxJsfUtils.getParameter("name")));
142: }
143: FxJsfUtils.resetFaceletsComponent(RESET_COMPONENT_ID);
144: } catch (Exception e) {
145: LOG.error("Failed to parse request parameters: "
146: + e.getMessage(), e);
147: }
148: }
149:
150: /**
151: * Show the query editor, restore last query (TODO).
152: *
153: * @return the final page
154: */
155: public String show() {
156: return "contentQuery";
157: }
158:
159: /**
160: * Execute the current search query.
161: *
162: * @return the outcome
163: */
164: public String executeSearch() {
165: SearchResultBean resultBean = (SearchResultBean) FxJsfUtils
166: .getManagedBean("fxSearchResultBean");
167: SqlQueryBuilder builder = getQueryBuilder();
168: if (StringUtils.isNotBlank(filterTypeName)) {
169: builder.filterType(filterTypeName);
170: }
171: if (rootNode == null || rootNode.getChildren().size() == 0) {
172: new FxFacesMsgErr("QueryEditor.err.emptyQuery")
173: .addToContext();
174: return null;
175: }
176: try {
177: rootNode.buildSqlQuery(builder);
178: } catch (FxRuntimeException e) {
179: if (e.getConverted() instanceof FxInvalidQueryNodeException) {
180: final FxInvalidQueryNodeException queryNodeException = (FxInvalidQueryNodeException) e
181: .getConverted();
182: // add error message for node component
183: final FxFacesMsgErr msg = new FxFacesMsgErr(
184: queryNodeException);
185: msg.setId(String.valueOf(queryNodeException
186: .getTreeNodeId()));
187: msg.addToContext();
188: } else {
189: new FxFacesMsgErr("QueryEditor.err.buildQuery", e)
190: .addToContext();
191: }
192: return show();
193: } catch (Exception e) {
194: new FxFacesMsgErr("QueryEditor.err.buildQuery", e)
195: .addToContext();
196: return show();
197: }
198: if (saveQuery) {
199: if (StringUtils.isBlank(rootNode.getName())) {
200: new FxFacesMsgErr("QueryEditor.err.saveQuery.empty")
201: .addToContext("queryName");
202: return null;
203: }
204: try {
205: getRootNode().setName(rootNode.getName());
206: EJBLookup.getSearchEngine().save(getRootNode());
207: new FxFacesMsgInfo("QueryEditor.nfo.saveQuery",
208: rootNode.getName()).addToContext();
209: } catch (FxApplicationException e) {
210: new FxFacesMsgErr(e).addToContext();
211: }
212: }
213: resultBean.setQueryBuilder(builder);
214: resultBean.setStartRow(0);
215: resultBean.getSessionData().setBriefcaseId(-1); // TODO: open briefcases in own location
216: updateQueryStore();
217: return resultBean.show();
218: }
219:
220: /**
221: * Add the (property) assignment stored in addAssignmentId
222: * to the node identified by addAssignmentNodeId.
223: *
224: * @param event the action event
225: */
226: public void addProperty(ActionEvent event) {
227: final FxPropertyAssignment assignment = (FxPropertyAssignment) CacheAdmin
228: .getEnvironment().getAssignment(addAssignmentId);
229: addQueryNode(new AssignmentValueNode(getRootNode().getNewId(),
230: assignment));
231: }
232:
233: /**
234: * Add the tree node stored in addAssignmentId to the node identified by
235: * addAssignmentNodeId.
236: *
237: * @param event the action event
238: * @throws FxApplicationException on errors
239: */
240: public void addTreeNode(ActionEvent event)
241: throws FxApplicationException {
242: final FxTreeNode treeNode = EJBLookup.getTreeEngine().getNode(
243: FxTreeMode.Edit, addAssignmentId);
244: final TreeValueNode newNode = new TreeValueNode(getRootNode()
245: .getNewId(), treeNode.getId(),
246: addNodeLive ? FxTreeMode.Live : FxTreeMode.Edit,
247: treeNode.getLabel());
248: addQueryNode(newNode);
249: }
250:
251: /**
252: * Add a query node that searches only for types of the given type ID.
253: *
254: * @param event the action event
255: */
256: public void addTypeQuery(ActionEvent event) {
257: final FxType type = CacheAdmin.getEnvironment().getType(
258: addAssignmentId);
259: final AssignmentValueNode node = new AssignmentValueNode(
260: getRootNode().getNewId(), CacheAdmin.getEnvironment()
261: .getAssignment("ROOT/TYPEDEF"));
262: node.setValue(new FxLargeNumber(false, type.getId()));
263: addQueryNode(node);
264: }
265:
266: public void addQueryNode(QueryNode newNode) {
267: QueryRootNode root = getRootNode();
268: QueryNode targetNode;
269: QueryNode addAssignmentNode = null;
270: try {
271: targetNode = addAssignmentNodeId != -1 ? root.findChild(
272: addAssignmentNodeId).getParent() : root;
273: addAssignmentNode = addAssignmentNodeId != -1
274: && addAssignmentNodeId != root.getId() ? root
275: .findChild(addAssignmentNodeId) : null;
276: } catch (FxRuntimeException e) {
277: // node not found
278: targetNode = root;
279: }
280:
281: if (addAssignmentNode != null) {
282: targetNode.addChildAfter(addAssignmentNode, newNode);
283: } else {
284: targetNode.addChild(newNode);
285: }
286: //}
287: addAssignmentNodeId = newNode.getId();
288: FxJsfUtils.resetFaceletsComponent(RESET_COMPONENT_ID);
289: updateQueryStore();
290: }
291:
292: /**
293: * Remove the node identified by removeNodeId from the current query.
294: *
295: * @param event the action event
296: */
297: public void removeNode(ActionEvent event) {
298: QueryNode removeNode = getRootNode().findChild(removeNodeId);
299: if (removeNode != null && removeNode.getParent() != null) {
300: removeNode.getParent().removeChild(removeNode);
301: FxJsfUtils.resetFaceletsComponent(RESET_COMPONENT_ID);
302: updateQueryStore();
303: }
304: }
305:
306: /**
307: * Join the selected nodes with 'and'.
308: *
309: * @param event the action event
310: */
311: public void createAndSubquery(ActionEvent event) {
312: joinSelectedNodes(QueryOperatorNode.Operator.AND);
313: }
314:
315: /**
316: * Join the selected nodes with 'or'.
317: *
318: * @param event the action event
319: */
320: public void createOrSubquery(ActionEvent event) {
321: joinSelectedNodes(QueryOperatorNode.Operator.OR);
322: }
323:
324: private void joinSelectedNodes(QueryOperatorNode.Operator operator) {
325: Scanner scanner = new Scanner(nodeSelection).useDelimiter(",");
326: List<Integer> nodeIds = new ArrayList<Integer>();
327: while (scanner.hasNextInt()) {
328: nodeIds.add(scanner.nextInt());
329: }
330: if (nodeIds.size() > 1) {
331: getRootNode().joinNodes(nodeIds, operator);
332: }
333: FxJsfUtils.resetFaceletsComponent(RESET_COMPONENT_ID);
334: updateQueryStore();
335: }
336:
337: public List<FxAssignment> getProperties() {
338: if (properties == null) {
339: properties = new ArrayList<FxAssignment>();
340: }
341: return properties;
342: }
343:
344: public void setProperties(List<FxAssignment> properties) {
345: this .properties = properties;
346: }
347:
348: public long getAddAssignmentId() {
349: return addAssignmentId;
350: }
351:
352: public void setAddAssignmentId(long addPropertyId) {
353: this .addAssignmentId = addPropertyId;
354: }
355:
356: public QueryRootNode getRootNode() {
357: if (rootNode == null) {
358: rootNode = (QueryRootNode) FxJsfUtils.getSession()
359: .getAttribute(getQueryTreeStore());
360: }
361: if (rootNode == null) {
362: try {
363: rootNode = EJBLookup.getSearchEngine().loadDefault(
364: location);
365: } catch (FxApplicationException e) {
366: rootNode = new QueryRootNode(CONTENTSEARCH, location);
367: }
368: }
369: return rootNode;
370: }
371:
372: public void setRootNode(QueryRootNode rootNode) {
373: this .rootNode = rootNode;
374: updateQueryStore();
375: }
376:
377: public int getAddAssignmentNodeId() {
378: return addAssignmentNodeId;
379: }
380:
381: public void setAddAssignmentNodeId(int addAssignmentNodeId) {
382: this .addAssignmentNodeId = addAssignmentNodeId;
383: }
384:
385: public int getRemoveNodeId() {
386: return removeNodeId;
387: }
388:
389: public void setRemoveNodeId(int removeNodeId) {
390: this .removeNodeId = removeNodeId;
391: }
392:
393: public String getNodeSelection() {
394: return nodeSelection;
395: }
396:
397: public void setNodeSelection(String nodeSelection) {
398: this .nodeSelection = nodeSelection;
399: }
400:
401: /**
402: * Stores the current query in the user session.
403: */
404: private void updateQueryStore() {
405: FxJsfUtils.getSession().setAttribute(getQueryTreeStore(),
406: rootNode);
407: }
408:
409: public ResultLocation getLocation() {
410: return location;
411: }
412:
413: public void setLocation(ResultLocation location) {
414: this .location = location;
415: }
416:
417: public String getFilterTypeName() {
418: return filterTypeName;
419: }
420:
421: public void setFilterTypeName(String filterTypeName) {
422: this .filterTypeName = filterTypeName;
423: }
424:
425: public SqlQueryBuilder getQueryBuilder() {
426: if (queryBuilder == null) {
427: queryBuilder = new SqlQueryBuilder(location,
428: ResultViewType.LIST);
429: }
430: return queryBuilder;
431: }
432:
433: public void setQueryBuilder(SqlQueryBuilder queryBuilder) {
434: this .queryBuilder = queryBuilder;
435: }
436:
437: public boolean isAddNodeLive() {
438: return addNodeLive;
439: }
440:
441: public void setAddNodeLive(boolean addNodeLive) {
442: this .addNodeLive = addNodeLive;
443: }
444:
445: public boolean isSaveQuery() {
446: return saveQuery;
447: }
448:
449: public void setSaveQuery(boolean saveQuery) {
450: this .saveQuery = saveQuery;
451: }
452:
453: /**
454: * Return the current tab title of the query tab.
455: *
456: * @return the current tab title of the query tab.
457: */
458: public String getTabTitle() {
459: if (StringUtils.isBlank(getRootNode().getName())) {
460: return MessageBean.getInstance().getMessage(
461: "QueryEditor.tabtitle.query");
462: }
463: return MessageBean.getInstance().getMessage(
464: "QueryEditor.tabtitle.loadedQuery",
465: getRootNode().getName());
466: }
467:
468: /**
469: * Returns the session attribute key for storing the current query
470: *
471: * @return the session attribute key for storing the current query
472: */
473: private String getQueryTreeStore() {
474: return "FlexiveSearchQueryTree/" + location + "/"
475: + QueryRootNode.Type.CONTENTSEARCH;
476: }
477: }
|