/** * Copyright 2014 National University of Ireland, Galway. * * This file is part of the SIREn project. Project and contact information: * * https://github.com/rdelbru/SIREn * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sindice.siren.qparser.keyword.nodes; import java.util.ArrayList; import java.util.List; import org.apache.lucene.queryparser.flexible.core.nodes.FieldableNode; import org.apache.lucene.queryparser.flexible.core.nodes.GroupQueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; import org.apache.lucene.queryparser.flexible.core.nodes.QueryNodeImpl; import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax; import org.sindice.siren.search.node.NodeQuery; import org.sindice.siren.search.node.TwigQuery; /** * A {@link TwigQueryNode} represents a structured query, i.e., a {@link TwigQuery}. * * <p> * * It is composed of two nodes, a root and of a direct child. * * <p> * * A node with multiple children can be built by passing an * {@link ArrayQueryNode} to the {@link #setChild(QueryNode)}. * * <p> * * A complex twig can be built by chaining multiple {@link TwigQueryNode}. */ public class TwigQueryNode extends QueryNodeImpl implements FieldableNode { /** The root of the Twig Query */ private int rootLevel = -1; /** Position in the list of the root of the twig */ private static final int ROOT_POS = 0; /** Position in the list of the child of the twig */ private static final int CHILD_POS = 1; /** * Build a {@link TwigQueryNode} where the root can be either * an {@link WildcardNodeQueryNode} or a {@link NodeBooleanQueryNode}. * In addition to the root, the child node can be an {@link ArrayQueryNode} * or another {@link TwigQueryNode}. * * @param root the {@link QueryNode} as the root of the twig * @param child the {@link QueryNode} as the child of the twig */ public TwigQueryNode(final QueryNode root, final QueryNode child) { this.allocate(); this.setLeaf(false); this.add(root); this.add(child); } /** * Set the root of the twig */ public void setRoot(final QueryNode root) { final ArrayList<QueryNode> newChildren = new ArrayList<QueryNode>(); newChildren.add(root); newChildren.add(this.getChild()); this.set(newChildren); } /** * Returns the root of the twig. */ public QueryNode getRoot() { return this.getChildren().get(ROOT_POS); } /** * Set the child of the twig. */ public void setChild(final QueryNode child) { final ArrayList<QueryNode> newChildren = new ArrayList<QueryNode>(); newChildren.add(this.getRoot()); newChildren.add(child); this.set(newChildren); } /** * Returns the child of the twig. */ public QueryNode getChild() { return this.getChildren().get(CHILD_POS); } /** * Set the level of the root node in the document tree. * @see NodeQuery#setLevelConstraint(int) */ public void setRootLevel(final int rootLevel) { this.rootLevel = rootLevel; } /** * Get the level of the root node in the document tree. * * <p> * * If no root level has been set, <code>-1</code> is returned. * @see NodeQuery#setLevelConstraint(int) */ public int getRootLevel() { return rootLevel; } @Override public CharSequence getField() { return this.doGetField(this.getChildren()); } private CharSequence doGetField(final List<QueryNode> children) { if (children != null) { for (final QueryNode child : children) { if (child instanceof FieldableNode) { return ((FieldableNode) child).getField(); } else if (child instanceof TwigQueryNode) { return ((TwigQueryNode) child).getField(); } final CharSequence field = this.doGetField(child.getChildren()); if (field != null) { return field; } } } return null; } @Override public void setField(final CharSequence fieldName) { this.doSetField(this.getChildren(), fieldName); } private void doSetField(final List<QueryNode> children, final CharSequence fieldName) { if (children != null) { for (final QueryNode child : children) { this.doSetField(child.getChildren(), fieldName); if (child instanceof FieldableNode) { ((FieldableNode) child).setField(fieldName); } else if (child instanceof TwigQueryNode) { ((TwigQueryNode) child).setField(fieldName); } } } } @Override public String toString() { final QueryNode att = this.getRoot(); final QueryNode child = this.getChild(); final String s = "<twig root=\"" + this.getRootLevel() + "\">\n" + "<root>\n" + (att instanceof WildcardNodeQueryNode ? "" : att + "\n") + "</root>\n" + "<child>\n" + (child instanceof WildcardNodeQueryNode ? "" : child + "\n") + "</child>\n" + "</twig>"; return s; } @Override public CharSequence toQueryString(final EscapeQuerySyntax escapeSyntaxParser) { if (this.getChildren() == null || this.getChildren().size() == 0) return ""; final StringBuilder sb = new StringBuilder(); final QueryNode root = this.getRoot(); final QueryNode child = this.getChild(); if (root instanceof WildcardNodeQueryNode) { sb.append("* : "); } else { sb.append(root.toQueryString(escapeSyntaxParser)).append(" : "); } if (child instanceof WildcardNodeQueryNode) { sb.append("*"); } else { sb.append(child.toQueryString(escapeSyntaxParser)); } // in case is root or the parent is a group node avoid parenthesis if ((this.getParent() != null && this.getParent() instanceof GroupQueryNode) || this.isRoot()) { return sb.toString(); } else { return "( " + sb.toString() + " )"; } } }