/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.exoplatform.services.jcr.impl.core.query;

import org.exoplatform.services.jcr.datamodel.QPath;
import org.exoplatform.services.jcr.datamodel.QPathEntry;

import javax.jcr.RepositoryException;




/**
 * Implements a query node that defines a textsearch clause.
 */
public class TextsearchQueryNode extends QueryNode {

    /**
     * The query statement inside the textsearch clause
     */
    private final String query;

    /**
     * Limits the scope of this textsearch clause to a node or a property with
     * the given relative path.
     * If <code>null</code> the scope of this textsearch clause is the fulltext
     * index of all properties of the context node.
     */
    private QPath relPath;

    /**
     * If set to <code>true</code> {@link #relPath} references a property,
     * otherwise references a node.
     */
    private boolean propertyRef;

    /**
     * Creates a new <code>TextsearchQueryNode</code> with a <code>parent</code>
     * and a textsearch <code>query</code> statement. The scope of the query
     * is the fulltext index of the node, that contains all properties.
     *
     * @param parent the parent node of this query node.
     * @param query  the textsearch statement.
     */
    protected TextsearchQueryNode(QueryNode parent, String query) {
        super(parent);
        this.query = query;
        this.relPath = null;
        this.propertyRef = false;
    }

    /**
     * {@inheritDoc}
     * @throws RepositoryException
     */
    public Object accept(QueryNodeVisitor visitor, Object data) throws RepositoryException {
        return visitor.visit(this, data);
    }

    /**
     * Returns the type of this node.
     *
     * @return the type of this node.
     */
    public int getType() {
        return QueryNode.TYPE_TEXTSEARCH;
    }

    /**
     * Returns the textsearch statement.
     *
     * @return the textsearch statement.
     */
    public String getQuery() {
        return query;
    }

    /**
     * @return the relative path that references the item where the textsearch
     *         is performed. Returns <code>null</code> if the textsearch is
     *         performed on the context node.
     */
    public QPath getRelativePath() {
        return relPath;
    }

    /**
     * Sets the relative path to the item where the textsearch is performed. If
     * <code>relPath</code> is <code>null</code> the textsearch is performed on
     * the context node.
     *
     * @param relPath the relative path to an item.
     * @throws IllegalArgumentException if <code>relPath</code> is absolute.
     */
    public void setRelativePath(QPath relPath) {
        if (relPath != null && relPath.isAbsolute()) {
            throw new IllegalArgumentException("relPath must be relative");
        }
        this.relPath = relPath;
        if (relPath == null) {
            // context node is never a property
            propertyRef = false;
        }
    }

    /**
     * Adds a path element to the existing relative path. To add a path element
     * which matches all node names use {@link RelationQueryNode#STAR_NAME_TEST}.
     *
     * @param element the path element to append.
     */
    public void addPathElement(QPathEntry element) {
       if (relPath == null)
          this.relPath = new QPath(new QPathEntry[]{element});
       else
          this.relPath = QPath.makeChildPath(relPath, element, element.getIndex());
    }

    /**
     * @return <code>true</code> if {@link #getRelativePath()} references a
     *         property, returns <code>false</code> if it references a node.
     */
    public boolean getReferencesProperty() {
        return propertyRef;
    }

    /**
     * Is set to <code>true</code>, indicates that {@link #getRelativePath()}
     * references a property, if set to <code>false</code> indicates that it
     * references a node.
     *
     * @param b flag whether a property is referenced.
     */
    public void setReferencesProperty(boolean b) {
        propertyRef = b;
    }

    /**
     * {@inheritDoc}
     */
    public boolean equals(Object obj) {
        if (obj instanceof TextsearchQueryNode) {
            TextsearchQueryNode other = (TextsearchQueryNode) obj;
            return (query == null ? other.query == null : query.equals(other.query))
                    && (relPath == null ? other.relPath == null : relPath.equals(other.relPath)
                    && propertyRef == other.propertyRef);
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    public boolean needsSystemTree() {
        return false;
    }

}
