001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2008
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.shared.search.query;
034:
035: import com.flexive.shared.FxSharedUtils;
036: import com.flexive.shared.exceptions.FxInvalidParameterException;
037: import com.flexive.shared.exceptions.FxNotFoundException;
038: import com.flexive.shared.exceptions.FxRuntimeException;
039:
040: /**
041: * A "operator" node representing a conjunctive/disjunctive union of its children.
042: *
043: * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
044: */
045: public class QueryOperatorNode extends QueryNode {
046: private static final long serialVersionUID = -6443190344195963157L;
047: private SqlQueryBuilder queryBuilder;
048:
049: /**
050: * Node operators
051: */
052: public static enum Operator {
053: /** And operator */
054: AND("AND"),
055: /** Or operator */
056: OR("OR");
057:
058: private String sqlRepresentation;
059:
060: /**
061: * Create a new operator.
062: *
063: * @param sqlRepresentation the SQL representation of the operator
064: */
065: private Operator(String sqlRepresentation) {
066: this .sqlRepresentation = sqlRepresentation;
067: }
068:
069: public String getSqlRepresentation() {
070: return sqlRepresentation;
071: }
072:
073: public String getName() {
074: return name();
075: }
076:
077: public Operator[] getValues() {
078: return values();
079: }
080: }
081:
082: private Operator operator = Operator.AND;
083:
084: /**
085: * Default constructor.
086: */
087: public QueryOperatorNode() {
088: }
089:
090: /**
091: * Constructor.
092: * @param id the node ID
093: */
094: public QueryOperatorNode(int id) {
095: super (id);
096: }
097:
098: /**
099: * Constructor.
100: * @param id the node ID.
101: * @param operator the operator value.
102: */
103: public QueryOperatorNode(int id, Operator operator) {
104: FxSharedUtils.checkParameterEmpty(operator, "OPERATOR");
105: this .id = id;
106: this .operator = operator;
107: }
108:
109: public Operator getOperator() {
110: return operator;
111: }
112:
113: public void setOperator(Operator operator) {
114: FxSharedUtils.checkParameterEmpty(operator, "OPERATOR");
115: this .operator = operator;
116: }
117:
118: /** {@inheritDoc} */
119: @Override
120: public void addChild(QueryNode child) {
121: children.add(child);
122: child.parent = this ;
123: }
124:
125: /** {@inheritDoc} */
126: @Override
127: public void addChild(QueryNode parentNode, QueryNode child) {
128: parentNode.addChild(child);
129: }
130:
131: /** {@inheritDoc} */
132: @Override
133: public void addChildAfter(QueryNode afterNode, QueryNode child) {
134: for (int i = 0; i < children.size(); i++) {
135: QueryNode queryNode = children.get(i);
136: if (queryNode.equals(afterNode)) {
137: children.add(i + 1, child);
138: child.parent = this ;
139: return;
140: }
141: }
142: throw new FxInvalidParameterException("CHILD",
143: "ex.queryNode.child.notFound", child.getId())
144: .asRuntimeException();
145: }
146:
147: /** {@inheritDoc} */
148: @Override
149: public QueryNode removeChild(QueryNode child) {
150: QueryNode result = null;
151: if (children.indexOf(child) != -1) {
152: children.remove(child);
153: result = this ;
154: } else {
155: for (QueryNode node : children) {
156: if (node instanceof QueryOperatorNode) {
157: QueryNode removedParent = node.removeChild(child);
158: result = removedParent != null ? removedParent
159: : null;
160: if (result != null) {
161: break;
162: }
163: }
164: }
165: }
166: if (children.isEmpty() && parent != null) {
167: // remove empty operator nodes
168: result = parent.removeChild(this );
169: } else if (children.size() == 1 && parent != null) {
170: // only one child - attach it to the parent
171: parent.addChild(children.get(0));
172: children.remove(0);
173: result = parent.removeChild(this );
174: }
175: compactOperatorNodes();
176: return result;
177: }
178:
179: /**
180: * Remove nested operator nodes without leaves.
181: * E.g. AND[OR[1,2]] --> OR[1,2]
182: *
183: * @return true if the tree has been compacted
184: */
185: private boolean compactOperatorNodes() {
186: if (children.size() == 1 && !children.get(0).isValueNode()) {
187: // we got only one operator node as a child - take its children and attach it to our node
188: QueryNode operatorChild = children.get(0);
189: for (QueryNode child : operatorChild.getChildren()) {
190: addChild(child);
191: }
192: // keep our operator
193: setOperator(((QueryOperatorNode) operatorChild)
194: .getOperator());
195: children.remove(operatorChild);
196: return true;
197: }
198: return false;
199: }
200:
201: /** {@inheritDoc} */
202: @Override
203: public QueryNode getChild(int index) {
204: return children.get(index);
205: }
206:
207: /** {@inheritDoc} */
208: @Override
209: public QueryNode findChild(int childId) {
210: if (this .id == childId) {
211: return this ;
212: }
213: for (QueryNode node : children) {
214: if (node.getId() == childId) {
215: return node;
216: }
217: if (node.getChildren().size() > 0) {
218: try {
219: return node.findChild(childId);
220: } catch (FxRuntimeException e) {
221: // try other children first
222: }
223: }
224: }
225: throw new FxNotFoundException("ex.queryNode.child.notFound",
226: childId).asRuntimeException();
227: }
228:
229: /** {@inheritDoc} */
230: @Override
231: protected String getNodeName() {
232: return operator.name();
233: }
234:
235: /** {@inheritDoc} */
236: @Override
237: public boolean isValid() {
238: // the node is valid if all of its children are valid
239: for (QueryNode child : children) {
240: if (!child.isValid()) {
241: return false;
242: }
243: }
244: return true;
245: }
246:
247: /** {@inheritDoc} */
248: @Override
249: public void buildSqlQuery(SqlQueryBuilder builder) {
250: if (builder.isFrozen()) {
251: // do nothing if query already exists
252: return;
253: }
254: if (children.isEmpty()) {
255: return;
256: }
257: builder.enterSub(operator);
258: for (QueryNode child : children) {
259: child.buildSqlQuery(builder);
260: }
261: builder.closeSub();
262: }
263:
264: /**
265: * Return the SQL (sub-)query represented by this node and its children.
266: *
267: * @return the SQL (sub-)query represented by this node and its children.
268: */
269: public String getSqlQuery() {
270: SqlQueryBuilder builder = queryBuilder == null ? new SqlQueryBuilder()
271: : queryBuilder;
272: buildSqlQuery(builder);
273: return builder.getQuery();
274: }
275:
276: /** {@inheritDoc} */
277: @Override
278: public void visit(QueryNodeVisitor visitor) {
279: visitor.visit(this );
280: for (QueryNode child : children) {
281: visitor.setCurrentParent(this );
282: child.visit(visitor);
283: }
284: }
285:
286: public SqlQueryBuilder getQueryBuilder() {
287: return queryBuilder;
288: }
289:
290: public void setQueryBuilder(SqlQueryBuilder queryBuilder) {
291: this.queryBuilder = queryBuilder;
292: }
293: }
|