Package org.apache.xindice.core.query

Source Code of org.apache.xindice.core.query.QueryEngine

/*
* 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.
*
* $Id: QueryEngine.java 511426 2007-02-25 03:25:02Z vgritsenko $
*/

package org.apache.xindice.core.query;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xindice.core.Collection;
import org.apache.xindice.core.DBException;
import org.apache.xindice.core.Database;
import org.apache.xindice.core.data.Key;
import org.apache.xindice.core.data.NodeSet;
import org.apache.xindice.core.indexer.IndexMatch;
import org.apache.xindice.util.Configuration;
import org.apache.xindice.util.ConfigurationCallback;
import org.apache.xindice.util.SimpleConfigurable;
import org.apache.xindice.util.XindiceException;
import org.apache.xindice.xml.NamespaceMap;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;

/**
* QueryEngine is the Xindice Query Engine.  Its purpose is to orchestrate
* query operations against the Xindice repository.  The QueryEngine
* basically just manages a set of QueryResolvers that actually perform
* the work.
*
* @version $Revision: 511426 $, $Date: 2007-02-24 22:25:02 -0500 (Sat, 24 Feb 2007) $
*/
public class QueryEngine extends SimpleConfigurable {

    private static final Log log = LogFactory.getLog(QueryEngine.class);

    private static final String[] EmptyStrings = new String[0];
    private static final Key[] EmptyKeys = new Key[0];

    private static final String RESOLVER = "resolver";
    private static final String CLASS = "class";

    private final Database db;
    private final Map resolvers;


    public QueryEngine(Database db) {
        this.db = db;
        this.resolvers = new HashMap();
    }

    public void setConfig(Configuration config) throws XindiceException {
        super.setConfig(config);
        config.processChildren(RESOLVER, new ConfigurationCallback() {
            public void process(Configuration cfg) {
                final String className = cfg.getAttribute(CLASS);
                try {
                    QueryResolver res = (QueryResolver) Class.forName(className).newInstance();
                    res.setConfig(cfg);
                    res.setQueryEngine(QueryEngine.this);
                    resolvers.put(res.getQueryStyle(), res);
                } catch (Exception e) {
                    if (log.isWarnEnabled()) {
                        log.warn("Unable to load query resolver: " + className + ". " +
                                 "This query resolver will not be available.", e);
                    }
                }
            }
        });
    }

    public Database getDatabase() {
        return db;
    }

    /**
     * listStyles returns a list of styles supported by the
     * QueryEngine (ex: XPath, XUpdate)
     *
     * @return The supported styles
     */
    public String[] listStyles() {
        return (String[]) resolvers.keySet().toArray(EmptyStrings);
    }

    private QueryResolver getResolver(String style) throws QueryException {
        QueryResolver res = (QueryResolver) resolvers.get(style);
        if (res == null) {
            throw new StyleNotFoundException("No Resolver available for '" + style + "' queries");
        }
        return res;
    }

    /**
     * query performs the specified query and returns a NodeSet with
     * any possible results from that query.  The query is performed
     * in the context of a Collection.
     *
     * @param col The Collection context
     * @param style The query style (XPath, Fulltext, etc...)
     * @param query The Query
     * @param nsMap The namespace Map (if any)
     * @param keys The initial Key set to use (if any)
     * @return A NodeSet with the query results
     */
    public NodeSet query(Collection col, String style, String query, NamespaceMap nsMap, Key[] keys) throws DBException, QueryException {
        QueryResolver res = getResolver(style);
        return res.query(col, query, nsMap, keys);
    }

    /**
     * compileQuery compiles a Query against the specified Collection
     * context and returns the compiled Query.  This DOES NOT actually
     * run the query, merely just parses it and primes any possible
     * Indexers that the query might need.
     *
     * @param col The Collection context
     * @param style The query style (XPath, Fulltext, etc...)
     * @param query The Query
     * @param nsMap The namespace Map (if any)
     * @param keys The initial Key set to use (if any)
     * @return The compiled Query
     */
    public Query compileQuery(Collection col, String style, String query, NamespaceMap nsMap, Key[] keys) throws DBException, QueryException {
        QueryResolver res = getResolver(style);
        return res.compileQuery(col, query, nsMap, keys);
    }

    // Utility methods

    /**
     * getUniqueKeys takes a set of IndexMatch objects and extracts
     * all of its unique Keys in sorted order.
     *
     * @param matches The Match Set
     * @return The unique Keys in order
     */
    public static Key[] getUniqueKeys(IndexMatch[] matches) {
        SortedSet set = new TreeSet();
        for (int i = 0; i < matches.length; i++) {
            set.add(matches[i].getKey());
        }
        return (Key[]) set.toArray(EmptyKeys);
    }

    /**
     * andKeySets takes several sets of unique Keys and returns the
     * ANDed set (elements that exist in all sets).  The first dimension
     * of the array holds the individual sets, the second holds the
     * actual Keys.
     *
     * @param keySets 2-dimensional set of Keys
     * @return The ANDed set of Keys in order
     */
    public static Key[] andKeySets(Key[][] keySets) {
        int[] ptrs;

        if (keySets.length == 0) {
            return EmptyKeys;
        } else if (keySets.length == 1) {
            return keySets[0];
        } else {
            ptrs = new int[keySets.length];
            for (int i = 0; i < keySets.length; i++) {
                if (keySets[i].length == 0) {
                    return EmptyKeys;
                } else {
                    ptrs[i] = 0;
                }
            }
        }

        SortedSet set = new TreeSet();
        boolean done = false;
        List highs = new ArrayList();
        Key highest = null;
        while (!done) {
            boolean eq = true;

            for (int i = 0; i < ptrs.length; i++) {
                Key comp = keySets[i][ptrs[i]];
                if (highest == null) {
                    highest = comp;
                    highs.add(new Integer(i));
                } else {
                    int c = highest.compareTo(comp);
                    if (c != 0) {
                        eq = false;
                    }
                    if (c < 0) {
                        highest = comp;
                        highs.clear();
                        highs.add(new Integer(i));
                    } else if (c == 0) {
                        highs.add(new Integer(i));
                    }
                }
            }
            if (eq) {
                set.add(highest);
                highs.clear();
                highest = null;
            }
            for (int i = 0; i < ptrs.length; i++) {
                if (!highs.contains(new Integer(i))) {
                    ptrs[i]++;
                }
                if (ptrs[i] >= keySets[i].length) {
                    done = true;
                }
            }
            if (!eq) {
                highs.clear();
            }
        }

        return (Key[]) set.toArray(EmptyKeys);
    }

    /**
     * orKeySets takes several sets of unique Keys and returns the
     * ORed set (all unique elements).  The first dimension of the
     * array holds the individual sets, the second holds the actual
     * Keys.
     *
     * @param keySets 2-dimensional set of Keys
     * @return The ORed set of Keys in order
     */
    public static Key[] orKeySets(Key[][] keySets) {
        if (keySets.length == 0) {
            return EmptyKeys;
        } else if (keySets.length == 1) {
            return keySets[0];
        } else if (keySets.length == 2) {
            // Optimization since most ORs will be 2 sets only
            if (keySets[1].length == 0) {
                return keySets[0];
            } else if (keySets[0].length == 0) {
                return keySets[1];
            }
        }

        SortedSet set = new TreeSet();
        for (int i = 0; i < keySets.length; i++) {
            for (int j = 0; j < keySets[i].length; j++) {
                set.add(keySets[i][j]);
            }
        }

        return (Key[]) set.toArray(EmptyKeys);
    }

    /**
     * normalizeString normalizes the specific String by stripping
     * all leading, trailing, and continuous runs of white space.
     *
     * @param value The value to normalize
     * @return The result
     */
    public static String normalizeString(String value) {
        char[] c = value.toCharArray();
        char[] n = new char[c.length];
        boolean white = true;
        int pos = 0;
        for (int i = 0; i < c.length; i++) {
            if (" \t\n\r".indexOf(c[i]) != -1) {
                if (!white) {
                    n[pos++] = ' ';
                    white = true;
                }
            } else {
                n[pos++] = c[i];
                white = false;
            }
        }
        if (white && pos > 0) {
            pos--;
        }

        return new String(n, 0, pos);
    }

    /**
     * expandEntities expands the String's pre-defined XML entities
     * (&lt;, &gt;, etc...) into their actual character representations.
     *
     * @param value The value to expand entities for
     * @return The expanded String
     */
    public static String expandEntities(String value) {
        int idx = value.indexOf('&');
        if (idx == -1) {
            return value;
        }

        StringBuffer sb = new StringBuffer(value.length());
        int pos = 0;
        while (pos < value.length()) {
            if (idx != -1) {
                if (idx > pos) {
                    sb.append(value.substring(pos, idx));
                }

                int end = value.indexOf(';', idx) + 1;
                if (end == 0) {
                    // Some sort of error
                    return value;
                }

                String token = value.substring(idx + 1, end - 1);
                if (token.equals("apos")) {
                    sb.append("'");
                } else if (token.equals("quot")) {
                    sb.append("\"");
                } else if (token.equals("amp")) {
                    sb.append("&");
                } else if (token.equals("lt")) {
                    sb.append("<");
                } else if (token.equals("gt")) {
                    sb.append(">");
                } else {
                    // Some sort of error
                    return value;
                }

                pos = end;
                idx = value.indexOf('&', pos);
            } else {
                sb.append(value.substring(pos));
                break;
            }
        }
        return sb.toString();
    }
}
TOP

Related Classes of org.apache.xindice.core.query.QueryEngine

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.