Package org.jahia.services.query

Source Code of org.jahia.services.query.QueryServiceImpl$QueryModifierAndOptimizerVisitor

/**
* This file is part of Jahia, next-generation open source CMS:
* Jahia's next-generation, open source CMS stems from a widely acknowledged vision
* of enterprise application convergence - web, search, document, social and portal -
* unified by the simplicity of web content management.
*
* For more information, please visit http://www.jahia.com.
*
* Copyright (C) 2002-2011 Jahia Solutions Group SA. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As a special exception to the terms and conditions of version 2.0 of
* the GPL (or any later version), you may redistribute this Program in connection
* with Free/Libre and Open Source Software ("FLOSS") applications as described
* in Jahia's FLOSS exception. You should have received a copy of the text
* describing the FLOSS exception, and it is also available here:
* http://www.jahia.com/license
*
* Commercial and Supported Versions of the program (dual licensing):
* alternatively, commercial and supported versions of the program may be used
* in accordance with the terms and conditions contained in a separate
* written agreement between you and Jahia Solutions Group SA.
*
* If you are unsure which license is appropriate for your use,
* please contact the sales department at sales@jahia.com.
*/

package org.jahia.services.query;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.ValueFactory;
import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.query.qom.ChildNodeJoinCondition;
import javax.jcr.query.qom.Column;
import javax.jcr.query.qom.Constraint;
import javax.jcr.query.qom.DynamicOperand;
import javax.jcr.query.qom.EquiJoinCondition;
import javax.jcr.query.qom.FullTextSearch;
import javax.jcr.query.qom.Join;
import javax.jcr.query.qom.JoinCondition;
import javax.jcr.query.qom.Literal;
import javax.jcr.query.qom.Ordering;
import javax.jcr.query.qom.PropertyExistence;
import javax.jcr.query.qom.PropertyValue;
import javax.jcr.query.qom.QueryObjectModel;
import javax.jcr.query.qom.QueryObjectModelConstants;
import javax.jcr.query.qom.QueryObjectModelFactory;
import javax.jcr.query.qom.Selector;
import javax.jcr.query.qom.Source;

import org.apache.commons.lang.StringUtils;
import org.apache.jackrabbit.spi.commons.query.qom.AbstractQOMNode;
import org.apache.jackrabbit.spi.commons.query.qom.AndImpl;
import org.apache.jackrabbit.spi.commons.query.qom.BindVariableValueImpl;
import org.apache.jackrabbit.spi.commons.query.qom.ChildNodeImpl;
import org.apache.jackrabbit.spi.commons.query.qom.ChildNodeJoinConditionImpl;
import org.apache.jackrabbit.spi.commons.query.qom.ColumnImpl;
import org.apache.jackrabbit.spi.commons.query.qom.ComparisonImpl;
import org.apache.jackrabbit.spi.commons.query.qom.ConstraintImpl;
import org.apache.jackrabbit.spi.commons.query.qom.DefaultQOMTreeVisitor;
import org.apache.jackrabbit.spi.commons.query.qom.DescendantNodeImpl;
import org.apache.jackrabbit.spi.commons.query.qom.DescendantNodeJoinConditionImpl;
import org.apache.jackrabbit.spi.commons.query.qom.DynamicOperandImpl;
import org.apache.jackrabbit.spi.commons.query.qom.EquiJoinConditionImpl;
import org.apache.jackrabbit.spi.commons.query.qom.FullTextSearchImpl;
import org.apache.jackrabbit.spi.commons.query.qom.FullTextSearchScoreImpl;
import org.apache.jackrabbit.spi.commons.query.qom.JoinConditionImpl;
import org.apache.jackrabbit.spi.commons.query.qom.JoinImpl;
import org.apache.jackrabbit.spi.commons.query.qom.LengthImpl;
import org.apache.jackrabbit.spi.commons.query.qom.LiteralImpl;
import org.apache.jackrabbit.spi.commons.query.qom.LowerCaseImpl;
import org.apache.jackrabbit.spi.commons.query.qom.NodeLocalNameImpl;
import org.apache.jackrabbit.spi.commons.query.qom.NodeNameImpl;
import org.apache.jackrabbit.spi.commons.query.qom.NotImpl;
import org.apache.jackrabbit.spi.commons.query.qom.OrImpl;
import org.apache.jackrabbit.spi.commons.query.qom.OrderingImpl;
import org.apache.jackrabbit.spi.commons.query.qom.PropertyExistenceImpl;
import org.apache.jackrabbit.spi.commons.query.qom.PropertyValueImpl;
import org.apache.jackrabbit.spi.commons.query.qom.QOMTreeVisitor;
import org.apache.jackrabbit.spi.commons.query.qom.QueryObjectModelTree;
import org.apache.jackrabbit.spi.commons.query.qom.SameNodeImpl;
import org.apache.jackrabbit.spi.commons.query.qom.SameNodeJoinConditionImpl;
import org.apache.jackrabbit.spi.commons.query.qom.SelectorImpl;
import org.apache.jackrabbit.spi.commons.query.qom.SourceImpl;
import org.apache.jackrabbit.spi.commons.query.qom.StaticOperandImpl;
import org.apache.jackrabbit.spi.commons.query.qom.UpperCaseImpl;
import org.apache.jackrabbit.value.ValueFactoryImpl;
import org.slf4j.Logger;
import org.jahia.api.Constants;
import org.jahia.services.content.JCRNodeWrapper;
import org.jahia.services.content.JCRSessionWrapper;
import org.jahia.services.content.nodetypes.ExtendedNodeType;
import org.jahia.services.content.nodetypes.ExtendedPropertyDefinition;
import org.jahia.services.content.nodetypes.NodeTypeRegistry;

/**
* The default implementation of Jahia's QueryService.
*
* Jahia's query service is based on the JCR QueryObjectModelFactory and thus supports all kinds of complex queries specified in JSR-283
* (Content Repository for Java� Technology API 2.0)
*
* Queries can be created with the API by using the QueryObjectModel. Jahia will also provide a query builder user interface. It is also
* possible to use SQL-2 and the deprecated XPATH language.
*
* As Jahia can plug-in multiple repositories via the universal content hub (UCH), the queries can be converted to other languages, like the
* EntropySoft connector query language.
*
* The query service provides methods to modify and optimize the queries to support and make use of Jahia's internal data model
* implementation.
*
* @author Benjamin Papez
*/
public class QueryServiceImpl extends QueryService {
    private static transient Logger logger = org.slf4j.LoggerFactory.getLogger(QueryService.class);

    private static QueryService singletonInstance = null;

    /**
     * The initialization mode for the first QOM traversing iteration
     */
    private static int INITIALIZE_MODE = 1;

    /**
     * The check for modification mode for the second QOM traversing iteration
     */
    private static int CHECK_FOR_MODIFICATION_MODE = 2;

    /**
     * The optional modification mode for the third QOM traversing iteration only called if query modification is necessary.
     */
    private static int MODIFY_MODE = 3;

    private ValueFactory valueFactory = ValueFactoryImpl.getInstance();

    protected QueryServiceImpl() {
    }

    /**
     * Return the unique service instance. If the instance does not exist, a new instance is created.
     *
     * @return The unique service instance.
     */
    public static QueryService getInstance() {
        if (singletonInstance == null) {
            synchronized (QueryServiceImpl.class) {
                if (singletonInstance == null) {
                    singletonInstance = new QueryServiceImpl();
                }
            }
        }
        return singletonInstance;
    }

    /**
     * Initializes the service.
     */
    public void start() {
        // do nothing
    }

    public void stop() {
        // do nothing
    }

    /*
     * (non-Javadoc)
     *
     * @see org.jahia.services.query.QueryService#modifyAndOptimizeQuery(javax.jcr.query.qom.QueryObjectModel,
     * javax.jcr.query.qom.QueryObjectModelFactory, org.jahia.services.content.JCRSessionWrapper)
     */
    public QueryObjectModel modifyAndOptimizeQuery(QueryObjectModel qom,
            QueryObjectModelFactory qomFactory, JCRSessionWrapper session)
            throws RepositoryException {
        ModificationInfo info = getModificationInfo(qom.getSource(), qom.getConstraint(),
                qom.getOrderings(), qom.getColumns(), qomFactory, session);
        return info.getNewQueryObjectModel() != null ? info.getNewQueryObjectModel() : qom;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.jahia.services.query.QueryService#modifyAndOptimizeQuery(javax.jcr.query.qom.Source, javax.jcr.query.qom.Constraint,
     * javax.jcr.query.qom.Ordering[], javax.jcr.query.qom.Column[], javax.jcr.query.qom.QueryObjectModelFactory,
     * org.jahia.services.content.JCRSessionWrapper)
     */
    public QueryObjectModel modifyAndOptimizeQuery(Source source, Constraint constraint,
            Ordering[] orderings, Column[] columns, QueryObjectModelFactory qomFactory,
            JCRSessionWrapper session) throws RepositoryException {
        ModificationInfo info = getModificationInfo(source, constraint, orderings, columns,
                qomFactory, session);
        return info.getNewQueryObjectModel() != null ? info.getNewQueryObjectModel() : qomFactory
                .createQuery(source, constraint, orderings, columns);
    }

    protected Source getModifiedSource(Source source, ModificationInfo info) {
        return info.getNewJoin() != null ? info.getNewJoin() : source;
    }

    /**
     * We use a QOMTreeVisitor implementation to traverse through the query object model three times.
     *
     * The ModificationInfo.mode changes before each iteration from INITIALIZE_MODE to CHECK_FOR_MODIFICATION_MODE and at last MODIFY_MODE,
     * which is only called if modification is necessary.
     */
    protected ModificationInfo getModificationInfo(Source source, Constraint constraint,
            Ordering[] orderings, Column[] columns, QueryObjectModelFactory qomFactory,
            JCRSessionWrapper session) throws RepositoryException {
        ModificationInfo info = new ModificationInfo(qomFactory);

        QOMTreeVisitor visitor = new QueryModifierAndOptimizerVisitor(info, source, session);

        try {
            info.setMode(INITIALIZE_MODE);
            ((SourceImpl) source).accept(visitor, null);

            if (constraint != null) {
                ((ConstraintImpl) constraint).accept(visitor, null);
            }
            if (orderings != null) {
                for (Ordering ordering : orderings) {
                    ((OrderingImpl) ordering).accept(visitor, null);
                }
            }
            if (columns != null) {
                for (Column column : columns) {
                    ((ColumnImpl) column).accept(visitor, null);
                }
            }
        } catch (Exception e) {
            throw new RepositoryException(e);
        }
        try {
            info.setMode(CHECK_FOR_MODIFICATION_MODE);
            ((SourceImpl) source).accept(visitor, null);

            if (constraint != null) {
                ((ConstraintImpl) constraint).accept(visitor, null);
            }
            if (orderings != null) {
                for (Ordering ordering : orderings) {
                    ((OrderingImpl) ordering).accept(visitor, null);
                }
            }
            if (columns != null) {
                for (Column column : columns) {
                    ((ColumnImpl) column).accept(visitor, null);
                }
            }
        } catch (Exception e) {
            throw new RepositoryException(e);
        }
        if (info.isModificationNecessary()) {
            makeModifications(source, constraint, orderings, columns, info, visitor, qomFactory);
        }

        return info;
    }

    protected void makeModifications(Source source, Constraint constraint, Ordering[] orderings,
            Column[] columns, ModificationInfo info, QOMTreeVisitor visitor,
            QueryObjectModelFactory qomFactory) throws RepositoryException {
        info.setMode(MODIFY_MODE);

        try {
            int i = 0;
            Ordering[] newOrderings = null;
            if (orderings != null) {
                newOrderings = new Ordering[orderings.length];
                for (Ordering ordering : orderings) {
                    Ordering newOrdering = ordering;

                    newOrdering = (Ordering) ((OrderingImpl) ordering).accept(visitor, null);
                    newOrderings[i++] = newOrdering;
                }
            }
            Column[] newColumns = new Column[columns.length];
            i = 0;
            for (Column column : columns) {
                Column newColumn = column;

                newColumn = (Column) ((ColumnImpl) column).accept(visitor, null);
                newColumns[i++] = newColumn;
            }
           
            Constraint newConstraint = null;
            if (constraint != null) {
                newConstraint = (Constraint) ((ConstraintImpl) constraint).accept(visitor, null);
            }
           
            for (Constraint constraintToAdd : info.getNewConstraints()) {
                if (newConstraint == null) {
                    newConstraint = constraintToAdd;
                } else {
                    newConstraint = info.getQueryObjectModelFactory().and(
                            newConstraint, constraintToAdd);
                }
            }

            Source newSource = (Source) ((SourceImpl) getModifiedSource(source, info)).accept(
                    visitor, null);           
           
            info.setNewQueryObjectModel(info.getQueryObjectModelFactory().createQuery(newSource,
                    newConstraint, newOrderings, newColumns));
        } catch (Exception e) {
            throw new RepositoryException(e);
        }

    }

    private Selector getSelector(Source source, String name) {
        Selector foundSelector = null;
        for (SelectorImpl selector : ((SourceImpl) source).getSelectors()) {
            if (StringUtils.isEmpty(name) || name.equals(selector.getSelectorName())) {
                foundSelector = selector;
                break;
            }
        }
        return foundSelector;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.jahia.services.query.QueryService#getValueFactory()
     */
    public ValueFactory getValueFactory() {
        return this.valueFactory;
    }

    /**
     * We use this QOMTreeVisitor implementation to traverse through the query object model three times.
     *
     * The ModificationInfo.mode changes before each iteration from INITIALIZE_MODE to CHECK_FOR_MODIFICATION_MODE and at last MODIFY_MODE,
     * which is only called if modification is necessary.
     *
     * In INITIALIZE_MODE we analyze the query to check, whether language constraints are already set for selectors and we check whether
     * selectors to nodes having internationlized properties are already joined with their translation nodes and we store the node types per
     * selector.
     *
     * In CHECK_FOR_MODIFICATION_MODE we analyze the query to see, whether modifications needs to be made because of Jahia's internal
     * datamodel changes (mainly internationalized properties, which are copied to subnodes and propertynames are suffixed with the language
     * code). We will also check if modifications need to be done due to performance optimizations. This mode sets the
     * ModificationInfo.modificationNecessary variable to mark the necessity of modification.
     *
     * The MODIFY_MODE is only called if modification appears necessary (after previous step). This mode returns a modified query object
     * model in ModificationInfo.newQueryObjectModel.
     *
     */
    class QueryModifierAndOptimizerVisitor extends DefaultQOMTreeVisitor {
        private ModificationInfo modificationInfo;

        private Source originalSource;

        private Map<String, Selector> selectorsJoinedWithTranslation = new HashMap<String, Selector>();

        private Map<String, Set<String>> languagesPerSelector = new HashMap<String, Set<String>>();

        private Map<String, Set<String>> newLanguagesPerSelector = new HashMap<String, Set<String>>();

        private Map<String, Set<String>> nodeTypesPerSelector = new HashMap<String, Set<String>>();

        private static final String NO_LOCALE = "no_locale";

        private JCRSessionWrapper session = null;

        /**
         * Constructor for the QueryModifierAndOptimizerVisitor
         *
         * @param modificationInfo
         *            object gathering all modification infos
         * @param originalSource
         *            Source object of original query
         * @param session
         *            the current JCR session used for the query
         */
        public QueryModifierAndOptimizerVisitor(ModificationInfo modificationInfo,
                Source originalSource, JCRSessionWrapper session) {
            super();
            this.modificationInfo = modificationInfo;
            this.originalSource = originalSource;
            this.session = session;
        }

        /**
         * In INITIALIZE_MODE checks whether a selector is set to nt:base and in such a case looks, which nodes are actually placed as child
         * nodes. If all are the same then store the primaryChildNodeType, otherwise the nodetypes, common to all child nodes. This is
         * needed to be able to obtain property definitions.
         *
         * In MODIFY_MODE return the unchanged node.
         */
        @Override
        public Object visit(ChildNodeImpl node, Object data) throws Exception {
            if (getModificationInfo().getMode() == INITIALIZE_MODE) {
                Selector selector = getSelector(getOriginalSource(), node.getSelectorName());
                if (selector != null) {
                    String nodeTypeName = selector.getNodeTypeName();
                    if (Constants.NT_BASE.equals(nodeTypeName) || Constants.JAHIANT_CONTENT.equals(nodeTypeName)) {
                        Set<String> commonChildNodeTypes = new HashSet<String>();
                        String primaryChildNodeType = getCommonChildNodeTypes(node.getParentPath(),
                                commonChildNodeTypes);
                        if (primaryChildNodeType != null) {
                            commonChildNodeTypes = new HashSet<String>();
                            commonChildNodeTypes.add(primaryChildNodeType);
                        }
                        getNodeTypesPerSelector().put(node.getSelectorName(), commonChildNodeTypes);
                    }
                }
            }

            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * In INITIALIZE_MODE checks whether the propertyName of the QOM node is internationalized. If yes and the selector for the
         * translation subnode is missing, indicate creation and that the query must be modified, which is done in MODIFY_MODE.
         *
         * In MODIFY_MODE either return the modified node or if modification is not necessary, the unchanged node.
         */
        @Override
        public Object visit(PropertyValueImpl node, Object data) throws Exception {
            Object returnedData = getNewPropertyBasedNodeIfRequired(node);
            return (getModificationInfo().getMode() == MODIFY_MODE ? returnedData : node);
        }

        /**
         * In INITIALIZE_MODE checks whether the propertyName of the QOM node is internationalized. If yes and the selector for the
         * translation subnode is missing, indicate creation and that the query must be modified, which is done in MODIFY_MODE.
         *
         * In MODIFY_MODE either return the modified node or if modification is not necessary, the unchanged node.
         */
        @Override
        public Object visit(ColumnImpl node, Object data) throws Exception {
            Object returnedData = getNewPropertyBasedNodeIfRequired(node);
            return (getModificationInfo().getMode() == MODIFY_MODE ? returnedData : node);
        }

        /**
         * In INITIALIZE_MODE checks whether the propertyName of the QOM node is internationalized. If yes and the selector for the
         * translation subnode is missing, indicate creation and that the query must be modified, which is done in MODIFY_MODE.
         *
         * In MODIFY_MODE either return the modified node or if modification is not necessary, the unchanged node.
         */
        @Override
        public Object visit(FullTextSearchImpl node, Object data) throws Exception {
            Object returnedData = getNewPropertyBasedNodeIfRequired(node);
            return (getModificationInfo().getMode() == MODIFY_MODE ? returnedData : node);
        }

        /**
         * In INITIALIZE_MODE checks whether the propertyName of the QOM node is internationalized. If yes and the selector for the
         * translation subnode is missing, indicate creation and that the query must be modified, which is done in MODIFY_MODE.
         *
         * In MODIFY_MODE either return the modified node or if modification is not necessary, the unchanged node.
         */
        @Override
        public Object visit(PropertyExistenceImpl node, Object data) throws Exception {
            Object returnedData = getNewPropertyBasedNodeIfRequired(node);
            return (getModificationInfo().getMode() == MODIFY_MODE ? returnedData : node);
        }

        /**
         * Calls accept on each of the attached constraints of the AND node.
         *
         * In MODIFY_MODE check if the constraints returned were modified, and if yes create a new node and return it, otherwise return the
         * unchanged node.
         */
        @Override
        public final Object visit(AndImpl node, Object data) throws Exception {
            Constraint constraint1 = node.getConstraint1();
            Constraint constraint2 = node.getConstraint2();
            boolean modified = false;
            Object returnedData = ((ConstraintImpl) node.getConstraint1()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE && returnedData != null
                    && !returnedData.equals(node.getConstraint1())) {
                constraint1 = (Constraint) returnedData;
                modified = true;
            }
            returnedData = ((ConstraintImpl) node.getConstraint2()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE && returnedData != null
                    && !returnedData.equals(node.getConstraint2())) {
                constraint2 = (Constraint) returnedData;
                modified = true;
            }
            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = modified ? getModificationInfo().getQueryObjectModelFactory().and(
                        constraint1, constraint2) : node;
            }
            return data;
        }

        /**
         * Calls accept on the two operands in the comparison node.
         *
         * In INITIALIZE_MODE check whether there is already a language based comparison in the original query, so that this language is
         * used instead of the current locale in the session.
         *
         * In MODIFY_MODE check if the dynamic operand returned was modified, and if yes create a new node and return it, otherwise return
         * the unchanged node.
         */
        @Override
        public Object visit(ComparisonImpl node, Object data) throws Exception {
            Object returnedData = ((DynamicOperandImpl) node.getOperand1()).accept(this, data);

            if (getModificationInfo().getMode() == INITIALIZE_MODE) {
                if (QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO.equals(node.getOperator())
                        && node.getOperand1() instanceof PropertyValue
                        && Constants.JCR_LANGUAGE.equals(((PropertyValue) node.getOperand1())
                                .getPropertyName())) {
                    Selector selector = getSelector(getOriginalSource(),
                            ((PropertyValue) node.getOperand1()).getSelectorName());
                    Set<String> languages = getLanguagesPerSelector().get(
                            selector.getSelectorName());
                    if (languages == null) {
                        languages = new HashSet<String>();
                        getLanguagesPerSelector().put(selector.getSelectorName(), languages);
                    }
                    String language = ((Literal) node.getOperand2()).getLiteralValue().getString();
                    if (!languages.contains(language)) {
                        languages.add(language);
                    }
                }
            }

            ((StaticOperandImpl) node.getOperand2()).accept(this, data);

            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = returnedData != null && !returnedData.equals(node.getOperand1()) ? getModificationInfo()
                        .getQueryObjectModelFactory().comparison((DynamicOperand) returnedData,
                                node.getOperator(), node.getOperand2()) : node;
            }

            return data;
        }

        /**
         * Calls accept on the two sources and the join condition in the join node.
         *
         * In INITIALIZE_MODE check whether the original query already contains a childnode-JOIN on the translation node, so that it then
         * does not have to be added.
         *
         * In MODIFY_MODE check if the sources or join condition returned were modified, and if yes create a new node and return it,
         * otherwise return the unchanged node.
         */
        @Override
        public Object visit(JoinImpl node, Object data) throws Exception {
            if (getModificationInfo().getMode() == INITIALIZE_MODE) {
                if (node.getJoinCondition() instanceof ChildNodeJoinCondition) {
                    ChildNodeJoinCondition childNodeJoin = (ChildNodeJoinCondition) node
                            .getJoinCondition();
                    String childSelectorName = childNodeJoin.getChildSelectorName();
                    Selector childSelector = getSelector(getOriginalSource(), childSelectorName);
                    if (Constants.JAHIANT_TRANSLATION.equals(childSelector.getNodeTypeName())) {
                        getSelectorsJoinedWithTranslation().put(
                                childNodeJoin.getParentSelectorName(), childSelector);
                    }
                }
            }

            Object returnedRight = ((SourceImpl) node.getRight()).accept(this, data);
            Object returnedLeft = ((SourceImpl) node.getLeft()).accept(this, data);
            Object returnedJoinCondition = ((JoinConditionImpl) node.getJoinCondition()).accept(
                    this, data);

            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = returnedRight != null && !returnedRight.equals(node.getRight())
                        || returnedLeft != null && !returnedLeft.equals(node.getLeft())
                        || returnedJoinCondition != null
                        && !returnedJoinCondition.equals(node.getJoinCondition()) ? getModificationInfo()
                        .getQueryObjectModelFactory()
                        .join(returnedLeft != null ? (Source) returnedLeft : node.getLeft(),
                                returnedRight != null ? (Source) returnedRight : node.getRight(),
                                node.getJoinType(),
                                returnedJoinCondition != null ? (JoinCondition) returnedJoinCondition
                                        : node.getJoinCondition())
                        : node;
            }

            return data;
        }

        /**
         * Calls accept on the property value in the length node.
         *
         * In MODIFY_MODE check if the property value returned was modified, and if yes create a new node and return it, otherwise return
         * the unchanged node.
         */
        @Override
        public Object visit(LengthImpl node, Object data) throws Exception {
            Object returnedData = ((PropertyValueImpl) node.getPropertyValue()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = returnedData != null && !returnedData.equals(node.getPropertyValue()) ? getModificationInfo()
                        .getQueryObjectModelFactory().length((PropertyValue) returnedData) : node;
            }
            return data;
        }

        /**
         * Calls accept on the dynamic operand in the lower-case node.
         *
         * In MODIFY_MODE check if the operand returned was modified, and if yes create a new node and return it, otherwise return the
         * unchanged node.
         */
        @Override
        public Object visit(LowerCaseImpl node, Object data) throws Exception {
            Object returnedData = ((DynamicOperandImpl) node.getOperand()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = returnedData != null && !returnedData.equals(node.getOperand()) ? getModificationInfo()
                        .getQueryObjectModelFactory().lowerCase((DynamicOperand) returnedData)
                        : node;
            }
            return data;
        }

        /**
         * Calls accept on the constraint in the NOT node.
         *
         * In MODIFY_MODE check if the constraint returned was modified, and if yes create a new node and return it, otherwise return the
         * unchanged node.
         */
        @Override
        public Object visit(NotImpl node, Object data) throws Exception {
            Object returnedData = ((ConstraintImpl) node.getConstraint()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = returnedData != null && !returnedData.equals(node.getConstraint()) ? getModificationInfo()
                        .getQueryObjectModelFactory().not((Constraint) data) : node;
            }
            return data;
        }

        /**
         * Calls accept on the dynamic operand in the ordering node.
         *
         * In MODIFY_MODE check if the operand returned was modified, and if yes create a new node and return it, otherwise return the
         * unchanged node.
         */
        @Override
        public Object visit(OrderingImpl node, Object data) throws Exception {
            Object returnedData = ((DynamicOperandImpl) node.getOperand()).accept(this, data);

            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = returnedData != null && !returnedData.equals(node.getOperand()) ? (node
                        .isAscending() ? getModificationInfo().getQueryObjectModelFactory()
                        .ascending((DynamicOperand) returnedData) : getModificationInfo()
                        .getQueryObjectModelFactory().descending((DynamicOperand) returnedData))
                        : node;
            }
            return data;
        }

        /**
         * Calls accept on each of the attached constraints of the OR node.
         *
         * In MODIFY_MODE check if the constraints returned were modified, and if yes create a new node and return it, otherwise return the
         * unchanged node.
         */
        @Override
        public final Object visit(OrImpl node, Object data) throws Exception {
            Constraint constraint1 = node.getConstraint1();
            Constraint constraint2 = node.getConstraint2();
            boolean modified = false;
            Object returnedData = ((ConstraintImpl) node.getConstraint1()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE && returnedData != null
                    && !returnedData.equals(node.getConstraint1())) {
                constraint1 = (Constraint) returnedData;
                modified = true;
            }
            returnedData = ((ConstraintImpl) node.getConstraint2()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE && returnedData != null
                    && !returnedData.equals(node.getConstraint2())) {
                constraint2 = (Constraint) returnedData;
                modified = true;
            }
            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = modified ? getModificationInfo().getQueryObjectModelFactory().or(
                        constraint1, constraint2) : node;
            }
            return data;
        }

        /**
         * Calls accept on the following contained QOM nodes:
         * <ul>
         * <li>Source</li>
         * <li>Constraints</li>
         * <li>Orderings</li>
         * <li>Columns</li>
         * </ul>
         *
         * In MODIFY_MODE check if the nodes returned were modified, and if yes create a new QOM and return it, otherwise return the
         * unchanged QOM.
         */
        @Override
        public Object visit(QueryObjectModelTree node, Object data) throws Exception {
            node.getSource().accept(this, data);

            ConstraintImpl constraint = node.getConstraint();
            Object newConstraint = null;
            if (constraint != null) {
                newConstraint = constraint.accept(this, data);
            }
            OrderingImpl[] orderings = node.getOrderings();
            Object[] newOrderingObjects = new Object[orderings.length];
            for (int i = 0; i < orderings.length; i++) {
                newOrderingObjects[i] = orderings[i].accept(this, data);
            }
            ColumnImpl[] columns = node.getColumns();
            Object[] newColumnObjects = new Object[columns.length];
            for (int i = 0; i < columns.length; i++) {
                newColumnObjects[i] = columns[i].accept(this, data);
            }
            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = newConstraint != null && !newConstraint.equals(constraint)
                        || newOrderingObjects != null && !newOrderingObjects.equals(orderings)
                        || newColumnObjects != null && !newColumnObjects.equals(columns) ? getModificationInfo()
                        .getQueryObjectModelFactory().createQuery(
                                getModifiedSource(node.getSource(), getModificationInfo()),
                                (Constraint) newConstraint, (Ordering[]) newOrderingObjects,
                                (Column[]) newColumnObjects) : node;
            }
            return data;
        }

        /**
         * Calls accept on the dynamic operand in the lower-case node.
         *
         * In MODIFY_MODE check if the operand returned was modified, and if yes create a new node and return it, otherwise return the
         * unchanged node.
         */
        @Override
        public Object visit(UpperCaseImpl node, Object data) throws Exception {
            Object returnedData = ((DynamicOperandImpl) node.getOperand()).accept(this, data);
            if (getModificationInfo().getMode() == MODIFY_MODE) {
                data = returnedData != null && !returnedData.equals(node.getOperand()) ? getModificationInfo()
                        .getQueryObjectModelFactory().upperCase((DynamicOperand) data) : node;
            }
            return data;
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(BindVariableValueImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(DescendantNodeImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * In MODIFY_MODE check if the selector is based on a nodetype, which is modified so that the translation subnode is used in the
         * query. In this case create a new fullTextSearchScore node pointing to the selector of the translation nodetype.
         */
        @Override
        public Object visit(FullTextSearchScoreImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? (getSelectorsJoinedWithTranslation()
                    .get(node.getSelectorName()) != null ? getModificationInfo()
                    .getQueryObjectModelFactory().fullTextSearchScore(
                            getSelectorsJoinedWithTranslation().get(node.getSelectorName())
                                    .getSelectorName()) : node) : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(LiteralImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(NodeLocalNameImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(NodeNameImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(SameNodeImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(ChildNodeJoinConditionImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(DescendantNodeJoinConditionImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * In INITIALIZE_MODE checks whether the propertyName of the QOM node is internationalized. If yes and the selector for the
         * translation subnode is missing, indicate creation and that the query must be modified, which is done in MODIFY_MODE.
         *
         * In MODIFY_MODE either return the modified node or if modification is not necessary, the unchanged node.
         */
        @Override
        public Object visit(EquiJoinConditionImpl node, Object data) throws Exception {
            Object returnedData = getNewPropertyBasedNodeIfRequired(node);
            return (getModificationInfo().getMode() == MODIFY_MODE ? returnedData : node);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(SameNodeJoinConditionImpl node, Object data) throws Exception {
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        /**
         * Does nothing and returns <code>data</code>, but in MODIFY_NODE return the unchanged node.
         */
        @Override
        public Object visit(SelectorImpl node, Object data) throws Exception {
            if (getModificationInfo().getMode() == INITIALIZE_MODE) {
                if (node.equals(getOriginalSource())
                        && Constants.JAHIANT_TRANSLATION.equals(node.getNodeTypeName())) {
                    getSelectorsJoinedWithTranslation().put(node.getSelectorName(), node);
                }
            }
            return (getModificationInfo().getMode() == MODIFY_MODE ? node : data);
        }

        private AbstractQOMNode getNewPropertyBasedNodeIfRequired(AbstractQOMNode node)
                throws RepositoryException {
            AbstractQOMNode newNode = node;
            if (node instanceof EquiJoinCondition) {
                newNode = getNewPropertyBasedNodeIfRequired(
                        ((EquiJoinCondition) node).getSelector1Name(),
                        ((EquiJoinCondition) node).getProperty1Name(), node);
                newNode = getNewPropertyBasedNodeIfRequired(
                        ((EquiJoinCondition) node).getSelector2Name(),
                        ((EquiJoinCondition) node).getProperty2Name(), newNode);
            } else {
                String selectorName = null;
                String propertyName = null;
                if (node instanceof PropertyValue) {
                    selectorName = ((PropertyValue) node).getSelectorName();
                    propertyName = ((PropertyValue) node).getPropertyName();
                } else if (node instanceof FullTextSearch) {
                    selectorName = ((FullTextSearch) node).getSelectorName();
                    propertyName = ((FullTextSearch) node).getPropertyName();
                } else if (node instanceof PropertyExistence) {
                    selectorName = ((PropertyExistence) node).getSelectorName();
                    propertyName = ((PropertyExistence) node).getPropertyName();
                } else if (node instanceof Column) {
                    selectorName = ((Column) node).getSelectorName();
                    propertyName = ((Column) node).getPropertyName();
                }
                newNode = getNewPropertyBasedNodeIfRequired(selectorName, propertyName, node);
            }
            return newNode;
        }

        private AbstractQOMNode getNewPropertyBasedNodeIfRequired(String selectorName,
                String propertyName, AbstractQOMNode node) throws RepositoryException {
            Selector selector = getSelector(getOriginalSource(), selectorName);
            if (selector == null) {
                return node;
            }

            try {
                if (getModificationInfo().getMode() == CHECK_FOR_MODIFICATION_MODE
                        || getModificationInfo().getMode() == MODIFY_MODE) {
                    // check for language dependent modifications and use the translation selector on
                    // jnt:translation node if user specified it in query
                   
                    if (getSelectorsJoinedWithTranslation().get(selector.getSelectorName()) != null) {
                        selector = getSelectorsJoinedWithTranslation().get(selector.getSelectorName());
                    }
                    Set<String> languageCodes = getLanguagesPerSelector().get(
                            selector.getSelectorName());
                    if ((languageCodes == null || languageCodes.isEmpty()) && session != null
                            && session.getLocale() != null) {
                        if (getModificationInfo().getMode() == CHECK_FOR_MODIFICATION_MODE) {
                           
                            Set<String> newLanguageCodes = getNewLanguagesPerSelector().get(
                                    selector.getSelectorName());
                            if (newLanguageCodes == null) {
                                newLanguageCodes = new HashSet<String>();
                                newLanguageCodes.add(session.getLocale().toString());
                                newLanguageCodes.add(NO_LOCALE);
                                getNewLanguagesPerSelector().put(selector.getSelectorName(),
                                        newLanguageCodes);
                            }
                            if (newLanguageCodes.contains(NO_LOCALE)) {
                                ExtendedNodeType nodeType = NodeTypeRegistry.getInstance()
                                        .getNodeType(selector.getNodeTypeName());
                                boolean isFulltextIncludingMultilingualProperties = (propertyName == null && node instanceof FullTextSearch) ? isFulltextIncludingMultilingualProperties(
                                        nodeType, selector) : false;
                                ExtendedPropertyDefinition propDef = propertyName != null ? getPropertyDefinition(
                                        nodeType, selector, propertyName) : null;
                                if (propDef != null && propDef.isInternationalized()
                                        || isFulltextIncludingMultilingualProperties) {
                                    newLanguageCodes.remove(NO_LOCALE);
                                }
                            }

                            getModificationInfo().setModificationNecessary(true);
                        } else {
                            QueryObjectModelFactory qomFactory = getModificationInfo()
                                    .getQueryObjectModelFactory();
                            Set<String> newLanguageCodes = getNewLanguagesPerSelector().get(
                                    selector.getSelectorName());
                            if (newLanguageCodes != null) {
                                Constraint langConstraint = null;
                                for (String newLanguageCode : newLanguageCodes) {
                                    Constraint currentConstraint = NO_LOCALE
                                            .equals(newLanguageCode) ? qomFactory.not(qomFactory
                                            .propertyExistence(selector.getSelectorName(),
                                                    Constants.JCR_LANGUAGE))
                                            : qomFactory
                                                    .comparison(
                                                            qomFactory.propertyValue(
                                                                    selector.getSelectorName(),
                                                                    Constants.JCR_LANGUAGE),
                                                            QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO,
                                                            qomFactory.literal(getValueFactory()
                                                                    .createValue(newLanguageCode)));
                                    langConstraint = langConstraint == null ? currentConstraint
                                            : qomFactory.or(langConstraint, currentConstraint);
                                }
                                getModificationInfo().getNewConstraints().add(langConstraint);
                                if (languageCodes == null) {
                                    languageCodes = new HashSet<String>();
                                    getLanguagesPerSelector().put(selector.getSelectorName(),
                                            languageCodes);
                                }
                                languageCodes.addAll(newLanguageCodes);
                            }
                        }
                    }
                    if (node instanceof Column) {
                        String columnName = ((Column) node).getColumnName();
                        if (StringUtils.startsWith(columnName, "rep:facet(")
                                && !StringUtils.contains(columnName, "locale=")) {
                            ExtendedNodeType nodeType = NodeTypeRegistry.getInstance().getNodeType(
                                    selector.getNodeTypeName());
                            ExtendedPropertyDefinition propDef = propertyName != null ? getPropertyDefinition(
                                    nodeType, selector, propertyName) : null;
                            if (propDef != null && propDef.isInternationalized()) {
                                if (getModificationInfo().getMode() == CHECK_FOR_MODIFICATION_MODE) {
                                    getModificationInfo().setModificationNecessary(true);
                                } else {
                                    String facetOptions = columnName.substring("rep:facet("
                                            .length());
                                    if (languageCodes == null) {
                                        languageCodes = getNewLanguagesPerSelector().get(
                                                selector.getSelectorName());
                                    }
                                    String languageCode = null;
                                    for (String currentLanguageCode : languageCodes) {
                                        if (!NO_LOCALE.equals(currentLanguageCode)) {
                                            languageCode = currentLanguageCode;
                                            break;
                                        }

                                    }
                                    if (!StringUtils.isEmpty(languageCode)) {
                                        columnName = "rep:facet(locale=" + languageCode
                                                + (facetOptions.trim().length() > 1 ? "&" : "")
                                                + facetOptions;
                                        QueryObjectModelFactory qomFactory = getModificationInfo()
                                                .getQueryObjectModelFactory();
                                        node = (AbstractQOMNode) qomFactory.column(
                                                selector.getSelectorName(), propertyName,
                                                columnName);
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (NoSuchNodeTypeException e) {
                logger.debug("Type " + selector.getNodeTypeName() + " not found in registry", e);
            }
            return node;
        }

        private ExtendedPropertyDefinition getPropertyDefinition(ExtendedNodeType nodeType,
                Selector selector, String propertyName) throws RepositoryException {
            ExtendedPropertyDefinition propDef = null;

            if (!Constants.JAHIANT_TRANSLATION.equals(nodeType.getName())) {
                if (Constants.NT_BASE.equals(nodeType.getName())
                        || Constants.JAHIANT_CONTENT.equals(nodeType.getName())) {
                    Set<String> nodeTypes = getNodeTypesPerSelector().get(
                            selector.getSelectorName());
                    if (nodeTypes != null) {
                        for (String commonNodeType : nodeTypes) {
                            nodeType = NodeTypeRegistry.getInstance().getNodeType(commonNodeType);
                            propDef = nodeType.getPropertyDefinitionsAsMap().get(propertyName);
                            if (propDef != null) {
                                break;
                            }
                        }
                    }
                }
                if (propDef == null) {
                    propDef = nodeType.getPropertyDefinitionsAsMap().get(propertyName);
                }
            }
            return propDef;
        }

        private boolean isFulltextIncludingMultilingualProperties(ExtendedNodeType nodeType,
                Selector selector) throws RepositoryException {
            boolean isFulltextIncludingMultilingualProperties = true;
            if (Constants.JAHIANT_TRANSLATION.equals(nodeType.getName())) {
                isFulltextIncludingMultilingualProperties = false;
            } else if (!Constants.NT_BASE.equals(nodeType.getName())
                    && !Constants.JAHIANT_CONTENT.equals(nodeType.getName())) {
                isFulltextIncludingMultilingualProperties = false;
                for (ExtendedPropertyDefinition propDef : nodeType.getPropertyDefinitionsAsMap()
                        .values()) {
                    if (propDef.isInternationalized()) {
                        isFulltextIncludingMultilingualProperties = true;
                        break;
                    }
                }
            }
            return isFulltextIncludingMultilingualProperties;
        }

        private String getCommonChildNodeTypes(String parentPath, Set<String> commonNodeTypes)
                throws RepositoryException {
            String commonPrimaryType = null;
            JCRNodeWrapper node = session.getNode(parentPath);
            Set<String> checkedPrimaryTypes = new HashSet<String>();
            if (node.hasNodes()) {
                NodeIterator children = node.getNodes();
                if (children.getSize() < 100) {
                    while (children.hasNext()) {

                        JCRNodeWrapper child = (JCRNodeWrapper) children.nextNode();
                        if (commonPrimaryType == null && commonNodeTypes.isEmpty()) {
                            commonPrimaryType = child.getPrimaryNodeType().getName();
                            commonNodeTypes.addAll(child.getNodeTypes());
                        } else if (commonPrimaryType != null
                                && child.getPrimaryNodeType().getName().equals(commonPrimaryType)) {
                            commonPrimaryType = null;
                        }
                        if (!checkedPrimaryTypes.contains(child.getPrimaryNodeType().getName())) {
                            checkedPrimaryTypes.add(child.getPrimaryNodeType().getName());
                            commonNodeTypes.retainAll(child.getNodeTypes());
                        }
                    }
                }
            }
            return commonPrimaryType;
        }

        /**
         * @return the object gathering the modification information
         */
        public ModificationInfo getModificationInfo() {
            return modificationInfo;
        }

        /**
         * @return the Source object of the original query
         */
        public Source getOriginalSource() {
            return originalSource;
        }

        /**
         * @return the map holding all selector names joined with a translation selector
         */
        public Map<String, Selector> getSelectorsJoinedWithTranslation() {
            return selectorsJoinedWithTranslation;
        }

        /**
         * @return a map holding per selector languages already set in the original query
         */
        public Map<String, Set<String>> getLanguagesPerSelector() {
            return languagesPerSelector;
        }

        /**
         * @return a map holding per selector new languages to be set
         */
        public Map<String, Set<String>> getNewLanguagesPerSelector() {
            return newLanguagesPerSelector;
        }

        /**
         * @return a map holding either the primary nodetype or common nodetypes per selector
         */
        public Map<String, Set<String>> getNodeTypesPerSelector() {
            return nodeTypesPerSelector;
        }

    };

    /**
     * This class is used to gather modification information mainly set during the INITIALIZE_MODE traversing. During traversing in
     * CHECK_FOR_MODIFICATION mode mainly only the modificationNecessary variable is set. The information of this object is then used during
     * the MODIFY_MODE traversing iteration.
     *
     */
    class ModificationInfo {
        private int mode = INITIALIZE_MODE;

        private boolean modificationNecessary = false;

        private Join newJoin = null;

        private QueryObjectModelFactory queryObjectModelFactory = null;

        private QueryObjectModel newQueryObjectModel = null;

        private List<Constraint> newConstraints = new ArrayList<Constraint>();

        /**
         * Constructor setting the QueryObjectModelFactory to be used for modifying the query
         *
         * @param queryObjectModelFactory
         *            to be used for modifying the query
         */
        public ModificationInfo(QueryObjectModelFactory queryObjectModelFactory) {
            super();
            this.queryObjectModelFactory = queryObjectModelFactory;
        }

        /**
         * @return true when modification of the query is found to be necessary otherwise return value is false
         */
        public boolean isModificationNecessary() {
            return modificationNecessary;
        }

        /**
         * Set true when modification of the query is found to be necessary otherwise set false
         *
         * @param modificationNecessary
         *            true when modification of the query is necessary otherwise false
         */
        public void setModificationNecessary(boolean modificationNecessary) {
            this.modificationNecessary = modificationNecessary;
        }

        /**
         * @return the QueryObjectModelFactory to be used when creating the modified query
         */
        public QueryObjectModelFactory getQueryObjectModelFactory() {
            return queryObjectModelFactory;
        }

        /**
         * @return the new Source, which in any case is a join when adding translation node queries
         */
        public Join getNewJoin() {
            return newJoin;
        }

        /**
         * Set the new Source, which in any case is a join when adding translation node queries
         *
         * @param newJoin
         *            when a new translation node is added.
         */
        public void setNewJoin(Join newJoin) {
            setModificationNecessary(true);
            this.newJoin = newJoin;
        }

        /**
         * @return the mode of the current iteration either
         *         <ul>
         *         <li>{@link org.jahia.services.query.QueryServiceImpl#INITIALIZE_MODE},</li>
         *         <li>{@link org.jahia.services.query.QueryServiceImpl#CHECK_FOR_MODIFICATION_MODE},</li>
         *         <li>{@link org.jahia.services.query.QueryServiceImpl#MODIFY_MODE}</li>
         *         </ul>
         */
        public int getMode() {
            return mode;
        }

        /**
         * Set mode for next iteration which is either
         * <ul>
         * <li>{@link org.jahia.services.query.QueryServiceImpl#INITIALIZE_MODE},</li>
         * <li>{@link org.jahia.services.query.QueryServiceImpl#CHECK_FOR_MODIFICATION_MODE},</li>
         * <li>{@link org.jahia.services.query.QueryServiceImpl#MODIFY_MODE}</li>
         * </ul>
         *
         * @param mode
         *            for the next iteration
         */
        public void setMode(int mode) {
            this.mode = mode;
        }

        /**
         * @return the modified query object model
         */
        public QueryObjectModel getNewQueryObjectModel() {
            return newQueryObjectModel;
        }

        /**
         * Set the new modified query object model
         *
         * @param newQueryObjectModel
         *            set after modification
         */
        public void setNewQueryObjectModel(QueryObjectModel newQueryObjectModel) {
            this.newQueryObjectModel = newQueryObjectModel;
        }

        /**
         * @return new constraints, which need to be added with logical conjunction (AND)
         */
        public List<Constraint> getNewConstraints() {
            return newConstraints;
        }
    }
}
TOP

Related Classes of org.jahia.services.query.QueryServiceImpl$QueryModifierAndOptimizerVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.