org.alfresco.rest.framework.resource.parameters.where.QueryHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.rest.framework.resource.parameters.where.QueryHelper.java

Source

/*
 * #%L
 * Alfresco Remote API
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.rest.framework.resource.parameters.where;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.alfresco.rest.antlr.WhereClauseParser;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.Tree;
import org.apache.commons.lang.StringUtils;

/**
 * Provides helper methods for handling a WHERE query.
 * 
 * @author Gethin James
 */
public abstract class QueryHelper {
    /**
     * An interface used when walking a query tree.  Calls are made to methods when the particular clause is encountered.
     */
    public static interface WalkerCallback {
        /**
         * Called any time an EXISTS clause is encountered.
         * @param propertyName Name of the property
           * @param negated returns true if "NOT EXISTS" was used
         */
        void exists(String propertyName, boolean negated);

        /**
         * Called any time a BETWEEN clause is encountered.
         * @param propertyName Name of the property
         * @param firstValue String
         * @param secondValue String
           * @param negated returns true if "NOT BETWEEN" was used
         */
        void between(String propertyName, String firstValue, String secondValue, boolean negated);

        /**
         * One of EQUALS LESSTHAN GREATERTHAN LESSTHANOREQUALS GREATERTHANOREQUALS;
         */
        void comparison(int type, String propertyName, String propertyValue);

        /**
         * Called any time an IN clause is encountered.
         * @param property Name of the property
         * @param negated returns true if "NOT IN" was used
         * @param propertyValues the property values
         */
        void in(String property, boolean negated, String... propertyValues);

        /**
         * Called any time a MATCHES clause is encountered.
         * @param property Name of the property
         * @param propertyValue String
           * @param negated returns true if "NOT MATCHES" was used
         */
        void matches(String property, String propertyValue, boolean negated);

        /**
         * Called any time an AND is encountered.
         */
        void and();

        /**
         * Called any time an OR is encountered.
         */
        void or();
    }

    /**
     * Default implementation.  Override the methods you are interested in. If you don't
     * override the methods then an InvalidQueryException will be thrown.
     */
    private static final String UNSUPPORTED_TEXT = "Unsupported Predicate";
    private static final InvalidQueryException UNSUPPORTED = new InvalidQueryException(UNSUPPORTED_TEXT);

    public static class WalkerCallbackAdapter implements WalkerCallback {
        @Override
        public void exists(String propertyName, boolean negated) {
            throw UNSUPPORTED;
        }

        @Override
        public void between(String propertyName, String firstValue, String secondValue, boolean negated) {
            throw UNSUPPORTED;
        }

        @Override
        public void comparison(int type, String propertyName, String propertyValue) {
            throw UNSUPPORTED;
        }

        @Override
        public void in(String propertyName, boolean negated, String... propertyValues) {
            throw UNSUPPORTED;
        }

        @Override
        public void matches(String property, String value, boolean negated) {
            throw UNSUPPORTED;
        }

        @Override
        public void and() {
            throw UNSUPPORTED;
        }

        @Override
        public void or() {
            throw UNSUPPORTED;
        }
    }

    /**
     * Walks a query with a callback for each operation
     * @param query the query
     * @param callback a callback
     */
    public static void walk(Query query, WalkerCallback callback) {

        CommonTree tree = query.getTree();
        if (tree != null) {
            LinkedList<Tree> stack = new LinkedList<Tree>();
            stack.push(tree);
            callbackTree(tree, callback, false);
        }
    }

    /**
     * Processes a tree type and calls the corresponding callback method.
     * @param tree Tree
     * @param callback WalkerCallback
     * @param negated boolean
     */
    protected static void callbackTree(Tree tree, WalkerCallback callback, boolean negated) {
        if (tree != null) {
            switch (tree.getType()) {
            case WhereClauseParser.EXISTS:
                if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType()) {
                    callback.exists(tree.getChild(0).getText(), negated);
                    return;
                }
                break;
            case WhereClauseParser.MATCHES:
                if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType()) {
                    callback.matches(tree.getChild(0).getText(), stripQuotes(tree.getChild(1).getText()), negated);
                    return;
                }
                break;
            case WhereClauseParser.IN:
                if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType()) {
                    List<Tree> children = getChildren(tree);
                    //Don't need the first item because its the property name
                    String[] inVals = new String[children.size() - 1];
                    for (int i = 1; i < children.size(); i++) {
                        inVals[i - 1] = stripQuotes(children.get(i).getText());
                    }
                    callback.in(tree.getChild(0).getText(), negated, inVals);
                    return;
                }
                break;
            case WhereClauseParser.BETWEEN:
                if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType()) {
                    callback.between(tree.getChild(0).getText(), stripQuotes(tree.getChild(1).getText()),
                            stripQuotes(tree.getChild(2).getText()), negated);
                    return;
                }
                break;
            case WhereClauseParser.EQUALS: //fall through (comparison)
            case WhereClauseParser.LESSTHAN: //fall through (comparison)
            case WhereClauseParser.GREATERTHAN: //fall through (comparison)
            case WhereClauseParser.LESSTHANOREQUALS: //fall through (comparison)
            case WhereClauseParser.GREATERTHANOREQUALS:
                if (WhereClauseParser.PROPERTYNAME == tree.getChild(0).getType()
                        && WhereClauseParser.PROPERTYVALUE == tree.getChild(1).getType()) {
                    callback.comparison(tree.getType(), tree.getChild(0).getText(),
                            stripQuotes(tree.getChild(1).getText()));
                    return;
                }
                break;
            case WhereClauseParser.NEGATION:
                //Negate the next element
                callbackTree(tree.getChild(0), callback, true);
                return;
            case WhereClauseParser.OR:
                callback.or();
                List<Tree> children = getChildren(tree);
                for (Tree child : children) {
                    callbackTree(child, callback, negated);
                }
                return;
            case WhereClauseParser.AND:
                callback.and();
                List<Tree> childrenOfAnd = getChildren(tree);
                for (Tree child : childrenOfAnd) {
                    callbackTree(child, callback, negated);
                }
                return;
            default:
            }
            callbackTree(tree.getChild(0), callback, negated); //Callback on the next node
        }
    }

    /**
     * Finds the siblings of the current tree item (does not include the current item)
     * that are after it in the tree (but at the same level).
     * @param tree the current tree
     * @return siblings - all the elements at the same level in the tree
     */
    //    public static List<Tree> getYoungerSiblings(Tree tree) 
    //    {
    //       Tree parent = tree.getParent();
    //       
    //       if (parent!=null && parent.getChildCount() > 0)
    //       {
    //          List<Tree> sibs = new ArrayList<Tree>(parent.getChildCount()-1);
    //          boolean laterChildren = false;
    //          for (int i = 0; i < parent.getChildCount(); i++) {
    //             Tree child = parent.getChild(i);
    //             if (tree.equals(child))
    //             {
    //                laterChildren = true;
    //             }
    //             else
    //             {
    //                if (laterChildren)   sibs.add(child);
    //             }
    //         }
    //          return sibs;
    //       }
    //       
    //
    //   }

    /**
     * Gets the children as a List
     * @param tree Tree
     * @return either emptyList or the children.
     */
    public static List<Tree> getChildren(Tree tree) {
        if (tree != null && tree.getChildCount() > 0) {
            List<Tree> children = new ArrayList<Tree>(tree.getChildCount());
            for (int i = 0; i < tree.getChildCount(); i++) {
                Tree child = tree.getChild(i);
                children.add(child);
            }
            return children;
        }

        //Default
        return Collections.emptyList();
    }

    private static final String SINGLE_QUOTE = "'";

    /**
     * Strips off any leading or trailing single quotes.
     * @param toBeStripped String
     * @return the String that has been stripped
     */
    public static String stripQuotes(String toBeStripped) {
        if (StringUtils.isNotEmpty(toBeStripped) && toBeStripped.startsWith(SINGLE_QUOTE)
                && toBeStripped.endsWith(SINGLE_QUOTE)) {
            return toBeStripped.substring(1, toBeStripped.length() - 1);
        }
        return toBeStripped; //default to return the String unchanged.
    }
}