org.topazproject.otm.query.GenericQueryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.topazproject.otm.query.GenericQueryImpl.java

Source

/* $HeadURL::                                                                            $
 * $Id$
 *
 * Copyright (c) 2007-2008 by Topaz, Inc.
 * http://topazproject.org
 *
 * 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.topazproject.otm.query;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;

import org.topazproject.otm.Criteria;
import org.topazproject.otm.OtmException;
import org.topazproject.otm.Session;
import org.topazproject.otm.SessionFactory;

import antlr.collections.AST;

/**
 * This implements the generic part of the query processing, i.e. the parsing and transforms that
 * are not specific to the target query language. The first method invoked must be {@link
 * #prepareQuery prepareQuery()}, only after which {@link #getParameters getParameters()} and
 * {@link #applyParameterValues applyParameterValues()} may be invoked.
 *
 * @author Ronald Tschalr
 */
public class GenericQueryImpl extends QueryImplBase implements GenericQuery, Cloneable {
    private final String query;
    private final Log log;
    private List<String> prepareWarnings = new ArrayList<String>();
    private final QueryParser parsedQuery;
    private FieldTranslator preparedQuery;
    private ParameterResolver resolvedQuery;

    /**
     * Create a new generic query impl instance.
     *
     * @param query the query string
     * @param log   the logger to use
     */
    public GenericQueryImpl(String query, Log log) throws OtmException {
        this.query = query;
        this.log = log;

        QueryLexer lexer = new QueryLexer(new StringReader(query));
        parsedQuery = doStep("parsing query", new QueryParser(lexer), new QueryStep<QueryParser>() {
            public void run(QueryParser parser) throws Exception {
                parser.query();
            }
        });
    }

    /**
     * Prepare the query for parameter resolving.
     *
     * @param sf the session-factory to use
     * @throws OtmException if an error occurred
     */
    public void prepareQuery(SessionFactory sf) throws OtmException {
        if (preparedQuery != null)
            return;

        preparedQuery = doStep("parsing query", new FieldTranslator(sf), new QueryStep<FieldTranslator>() {
            public void run(FieldTranslator ft) throws Exception {
                ft.query(parsedQuery.getAST());
            }
        });
        prepareWarnings.addAll(warnings);
    }

    /**
     * Apply the given parameter values to the current query.
     *
     * @param params a map of parameter-name to parameter-value
     * @param sf     the session-factory to use
     * @throws OtmException if an error occurred setting a parameter value
     * @throws IllegalStateException if {@link #prepareQuery prepareQuery()} hasn't been invoked yet
     */
    public void applyParameterValues(final Map<String, Object> params, SessionFactory sf) throws OtmException {
        if (preparedQuery == null)
            throw new IllegalStateException("prepareQuery() hasn't been invoked yet");

        warnings.clear();
        warnings.addAll(prepareWarnings);

        resolvedQuery = doStep("setting parameter values for", new ParameterResolver(sf),
                new QueryStep<ParameterResolver>() {
                    final FieldTranslator pq = preparedQuery;

                    public void run(ParameterResolver pr) throws Exception {
                        pr.query(pq.getAST(), params);
                    }
                });
    }

    /**
     * Get the list of parameter names found in the query.
     *
     * @return the found parameter names
     */
    public Set<String> getParameterNames() {
        return parsedQuery.getParameterNames();
    }

    /**
     * Get the list of parameters found in the query. This is only valid after invoking
     * {@link #prepareQuery prepareQuery()}.
     *
     * @return the found parameters; the keys are the parameter names.
     * @throws IllegalStateException if {@link #prepareQuery prepareQuery()} hasn't been invoked yet
     */
    public Map<String, ExprType> getParameters() {
        if (preparedQuery == null)
            throw new IllegalStateException("prepareQuery() hasn't been invoked yet");

        return preparedQuery.getParameters();
    }

    /**
     * Get the AST of the query with all parameters resolved. This is only valid after invoking
     * {@link #applyParameterValues applyParameterValues()}.
     *
     * @return the query AST
     */
    public AST getResolvedQuery() {
        if (resolvedQuery == null)
            throw new IllegalStateException("applyParameterValues() hasn't been invoked yet");
        return resolvedQuery.getAST();
    }

    /**
     * Turn this query into a Criteria, if possible.
     *
     * @param sess the session
     * @throws OtmException if an error occurred
     */
    public Criteria toCriteria(Session sess) throws OtmException {
        CriteriaGenerator cg = doStep("creating criteria for", new CriteriaGenerator(sess),
                new QueryStep<CriteriaGenerator>() {
                    public void run(CriteriaGenerator cg) throws Exception {
                        cg.query(parsedQuery.getAST());
                    }
                });
        return cg.getCriteria();
    }

    @Override
    public String toString() {
        return query;
    }

    @Override
    public GenericQueryImpl clone() {
        try {
            GenericQueryImpl c = (GenericQueryImpl) super.clone();
            c.prepareWarnings = new ArrayList<String>(prepareWarnings);
            return c;
        } catch (CloneNotSupportedException cnse) {
            throw new Error("Unexpected exception", cnse); // can't happen
        }
    }

    /**
     * Do one step in query processing. This will do the various error handling.
     */
    private <T extends ErrorCollector> T doStep(String op, T parser, QueryStep<T> qStep) throws OtmException {
        try {
            qStep.run(parser);
            checkMessages(parser, query);
            return parser;
        } catch (OtmException oe) {
            throw oe;
        } catch (Exception e) {
            if (parser != null && parser.getErrors().size() > 0) {
                // exceptions are usually the result of aborted parsing due to errors
                log.debug("error parsing query: " + parser.getErrors(null), e);
                throw new QueryException("error " + op + " '" + query + "'", parser.getErrors());
            } else
                throw new QueryException("error " + op + " '" + query + "'", e);
        }
    }

    /** query-step runner used by {@link @doStep doStep()} */
    private static interface QueryStep<T> {
        /** do the single query proecessing step */
        public void run(T parser) throws Exception;
    }
}