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.value.FxString;
036:
037: import java.io.Serializable;
038: import java.util.ArrayList;
039: import java.util.List;
040:
041: /**
042: * A node in a search query expression.
043: *
044: * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
045: */
046: public abstract class QueryNode implements Serializable {
047: private static final long serialVersionUID = 3912814336085148874L;
048: protected int id = -1;
049: protected QueryNode parent = null;
050: protected List<QueryNode> children = new ArrayList<QueryNode>();
051:
052: /**
053: * Default constructor
054: */
055: protected QueryNode() {
056: }
057:
058: /**
059: * Protected constructor.
060: * @param id the node ID
061: */
062: protected QueryNode(int id) {
063: this .id = id;
064: }
065:
066: /**
067: * Return true if the node's value is valid, or false if it is not.
068: *
069: * @return true if the node's value is valid, or false if it is not.
070: */
071: public abstract boolean isValid();
072:
073: /**
074: * Build the query represented by this node and its children.
075: *
076: * @param builder an sql query builder
077: */
078: protected abstract void buildSqlQuery(SqlQueryBuilder builder);
079:
080: /**
081: * Visit this node.
082: *
083: * @param visitor the visitor
084: */
085: public abstract void visit(QueryNodeVisitor visitor);
086:
087: public int getId() {
088: return id;
089: }
090:
091: public void setId(int id) {
092: this .id = id;
093: }
094:
095: public QueryNode getParent() {
096: return parent;
097: }
098:
099: /**
100: * Return the nesting level of the given node in the query tree,
101: * i.e. the number of parent links one has to traverse to reach the root node.
102: * A node attached to the root node has a level of 1.
103: *
104: * @return the node level
105: */
106: public int getLevel() {
107: return parent != null ? 1 + parent.getLevel() : 0;
108: }
109:
110: /**
111: * Set the parent of this node. If the node is not included
112: * in the parent's children list, it is added automatically.
113: *
114: * @param parent the parent to be sed
115: */
116: public void setParent(QueryNode parent) {
117: if (this .parent != null) {
118: // detach from previous parent
119: this .parent.getChildren().remove(this );
120: }
121: this .parent = parent;
122: if (parent != null) {
123: if (parent.getChildren().indexOf(this ) == -1) {
124: // add as child to new parent
125: parent.addChild(this );
126: }
127: }
128: }
129:
130: public List<QueryNode> getChildren() {
131: return children;
132: }
133:
134: public void setChildren(List<QueryNode> children) {
135: this .children = children;
136: }
137:
138: /**
139: * Add a new child node after the last child.
140: * @param child The child node to be added
141: */
142: public void addChild(QueryNode child) {
143: throw new UnsupportedOperationException(
144: "Child nodes not supported for this node type: "
145: + getClass().getCanonicalName());
146: }
147:
148: /**
149: * Add a new child to the given parent node. The parent node may be
150: * the current node, or any of its immediate or transitive child nodes.
151: *
152: * @param parentNode The parent node where the child should be inserted
153: * @param child The child node to be added
154: */
155: public void addChild(QueryNode parentNode, QueryNode child) {
156: throw new UnsupportedOperationException(
157: "Child nodes not supported for this node type: "
158: + getClass().getCanonicalName());
159: }
160:
161: /**
162: * Add a child after the given node. The after node may be
163: * an immediate or transitive child of the current node.
164: *
165: * @param afterNode node after which the child should be inserted
166: * @param child child node to be added
167: */
168: public void addChildAfter(QueryNode afterNode, QueryNode child) {
169: throw new UnsupportedOperationException(
170: "Child nodes not supported for this node type: "
171: + getClass().getCanonicalName());
172: }
173:
174: /**
175: * Remove a child node. Also searches all subtrees if the node
176: * is node an immediate child.
177: * @param child the child to be deleted
178: * @return the parent node of the deleted child, or null if the child does not exist
179: */
180: public QueryNode removeChild(QueryNode child) {
181: throw new UnsupportedOperationException(
182: "Child nodes not supported for this node type: "
183: + getClass().getCanonicalName());
184: }
185:
186: /**
187: * Return the child node at the given index.
188: *
189: * @param index the child index in this parent's children list
190: * @return the child node at the given index.
191: */
192: public QueryNode getChild(int index) {
193: throw new UnsupportedOperationException(
194: "Child nodes not supported for this node type: "
195: + getClass().getCanonicalName());
196: }
197:
198: /**
199: * Return the child node with the given ID.
200: *
201: * @param childId the requested child node ID
202: * @return the child node with the given ID
203: */
204: public QueryNode findChild(int childId) {
205: throw new UnsupportedOperationException(
206: "Child nodes not supported for this node type: "
207: + getClass().getCanonicalName());
208: }
209:
210: /*
211: protected void _writeTreeString(Appendable out) throws IOException, FxInvalidParameterException {
212: if (this.getClass().isAnonymousClass() || this.getClass().getEnclosingClass() != null) {
213: throw new FxInvalidParameterException("NODE", "ex.search.query.invalidClass=",
214: this.getClass().getCanonicalName());
215: }
216: out.append("<node id=\"").append(String.valueOf(id))
217: .append("\" class=\"").append(this.getClass().getCanonicalName()).append("\"");
218: if (children.isEmpty()) {
219: out.append("/>");
220: } else {
221: for (QueryNode child: children) {
222: child._writeTreeString(out);
223: }
224: out.append("</node>");
225: }
226: }
227: */
228:
229: /** {@inheritDoc} */
230: @Override
231: public boolean equals(Object anotherNode) {
232: return anotherNode instanceof QueryNode
233: && ((QueryNode) anotherNode).getId() == this .id;
234: }
235:
236: /** {@inheritDoc} */
237: @Override
238: public int hashCode() {
239: return this .id;
240: }
241:
242: /** {@inheritDoc} */
243: @Override
244: public String toString() {
245: if (children.size() == 0) {
246: return this .getNodeName();
247: } else {
248: StringBuilder out = new StringBuilder();
249: out.append(getNodeName()).append("[");
250: int ctr = 0;
251: for (QueryNode child : children) {
252: if (ctr++ > 0) {
253: out.append(',');
254: }
255: out.append(child.toString());
256: }
257: out.append("]");
258: return out.toString();
259: }
260: }
261:
262: /**
263: * Return the informal representation of a node to be used in toString.
264: *
265: * @return the informal representation of a node to be used in toString.
266: */
267: protected String getNodeName() {
268: return String.valueOf(id);
269: }
270:
271: /**
272: * Return the label to be displayed for this query node.
273: *
274: * @return the label to be displayed for this query node.
275: */
276: public FxString getLabel() {
277: return new FxString(getNodeName());
278: }
279:
280: /**
281: * Return true if this node is a "value" node, i.e. is used to represent
282: * a scalar value in the query. Used for JSTL node handling.
283: *
284: * @return true for scalar value nodes
285: */
286: public boolean isValueNode() {
287: return false;
288: }
289:
290: /**
291: * Returns true if the node and its children should not be displayed in a
292: * query editor or report.
293: *
294: * @return true if the node and its children should not be displayed
295: */
296: public boolean isHidden() {
297: return false;
298: }
299:
300: /**
301: * Returns true if the node needs a wide input field, e.g. a datepicker
302: * or file download field.
303: *
304: * @return true if the node needs a wide input field
305: */
306: public boolean isWideInput() {
307: return false;
308: }
309:
310: /**
311: * Returns the complete node path of the current node, i.e. the node ids
312: * traversed to the root node separated by dashes ("-").
313: * e.g.
314: * 1.path --> 1
315: * |- 2.path --> 1-2
316: * |- 3.path --> 1-2-3
317: *
318: * @return the complete node path of the current node
319: */
320: public String getPath() {
321: if (parent == null) {
322: return String.valueOf(id);
323: } else {
324: return parent.getPath() + "-" + String.valueOf(id);
325: }
326: }
327:
328: }
|