axiom.scripting.rhino.QueryBean.java Source code

Java tutorial

Introduction

Here is the source code for axiom.scripting.rhino.QueryBean.java

Source

/*
 * Axiom Stack Web Application Framework
 * Copyright (C) 2008  Axiom Software Inc.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Axiom Software Inc., 11480 Commerce Park Drive, Third Floor, Reston, VA 20191 USA
 * email: info@axiomsoftwareinc.com
 */
package axiom.scripting.rhino;

import java.util.*;

import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.TermQuery;
import org.mozilla.javascript.*;

import axiom.framework.ErrorReporter;
import axiom.framework.core.Application;
import axiom.framework.core.Prototype;
import axiom.framework.core.RequestEvaluator;
import axiom.framework.core.TypeManager;
import axiom.objectmodel.INode;
import axiom.objectmodel.db.DbKey;
import axiom.objectmodel.db.DbMapping;
import axiom.objectmodel.db.DbSource;
import axiom.objectmodel.db.Node;
import axiom.scripting.rhino.extensions.filter.*;
import axiom.util.ResourceProperties;

public final class QueryBean {

    private RhinoCore core;
    private Application app;
    private LuceneQueryDispatcher lqd = null;
    private RelationalQueryDispatcher rqd = null;

    private HashMap<String, QueryDispatcher> querydispatchers = new HashMap<String, QueryDispatcher>();

    public QueryBean(Application app, String name) throws Exception {
        this.core = null;
        this.app = app;
        lqd = new LuceneQueryDispatcher(app, name + "lqd");
        rqd = new RelationalQueryDispatcher(app, name + "rqd");

        ArrayList names = app.getDbNames();
        for (int i = 0; i < names.size(); i++) {
            try {
                String dbname = names.get(i).toString();
                DbSource dbsource = app.getDbSource(dbname);
                String initClass = dbsource.getProperty("queryDispatcherClass", null);
                if (initClass != null) {
                    Class[] parameters = { Application.class, String.class };
                    QueryDispatcher qd = (QueryDispatcher) Class.forName(initClass).getConstructor(parameters)
                            .newInstance(new Object[] { this.app, name + dbname + "" });
                    querydispatchers.put(dbsource.getType(), qd);
                }
            } catch (Exception e) {
                app.logError(ErrorReporter.errorMsg(this.getClass(), "ctor") + "Error during " + names.get(i)
                        + "'s initialization", e);
            }
        }
    }

    public void finalize() throws Throwable {
        super.finalize();
        lqd.shutdown();
        rqd.shutdown();
        Iterator<QueryDispatcher> i = querydispatchers.values().iterator();
        while (i.hasNext()) {
            i.next().shutdown();
        }
    }

    public void shutdown() {
        lqd.shutdown();
        rqd.shutdown();
        Iterator<QueryDispatcher> i = querydispatchers.values().iterator();
        while (i.hasNext()) {
            i.next().shutdown();
        }
    }

    public String toString() {
        return "[Query]";
    }

    public void setRhinoCore(RhinoCore core) {
        this.core = core;
        lqd.setRhinoCore(this.core);
        rqd.setRhinoCore(this.core);
        Iterator<QueryDispatcher> i = querydispatchers.values().iterator();
        while (i.hasNext()) {
            i.next().setRhinoCore(this.core);
        }
    }

    public static IFilter getFilterFromObject(Object filter) throws Exception {
        IFilter theFilter = null;
        if (filter == null || filter == Undefined.instance) {
            theFilter = new FilterObject();
        } else if (!(filter instanceof IFilter)) {
            if (filter instanceof String) {
                theFilter = new NativeFilterObject(new Object[] { filter });
            } else if (filter instanceof Scriptable) {
                Scriptable s = (Scriptable) filter;
                if (s.getClassName().equals("String")) {
                    theFilter = new NativeFilterObject(new Object[] { filter });
                } else {
                    theFilter = new FilterObject(filter, null, null);
                }
            }
        } else {
            theFilter = (IFilter) filter;
        }
        return theFilter;
    }

    private static void setEmptyEmbeddedArray(Application app, ArrayList embeddedDbProtos) throws Exception {
        ArrayList specialPrototypes = app.getSearchablePrototypes();
        Collection c = app.getPrototypes();
        Iterator i = c.iterator();
        while (i.hasNext()) {
            String proto = ((Prototype) i.next()).getName();
            DbMapping dbmap = app.getDbMapping(proto);
            String type = dbmap != null ? dbmap.getType() : null;
            if (type.equalsIgnoreCase("lucene") && specialPrototypes.contains(proto)) {
                embeddedDbProtos.add(proto);
            }
        }
    }

    public static void setPrototypeArrays(Application app, Object prototype, ArrayList embeddedDbProtos,
            ArrayList relationalDbProtos, HashMap<String, ArrayList> extDbProtos) throws Exception {
        if (prototype == null || prototype == Undefined.instance) {
            setEmptyEmbeddedArray(app, embeddedDbProtos);
        } else if (prototype instanceof String) {
            String proto = prototype.toString();
            DbMapping dbmap = app.getDbMapping(proto);
            String type = dbmap != null ? dbmap.getType() : null;
            if (dbmap != null && app.getExtDBTypes().contains(type)) {
                ArrayList<DbMapping> externalDbProtos = new ArrayList<DbMapping>();
                externalDbProtos.add(dbmap);
                extDbProtos.put(type, externalDbProtos);
            } else if (dbmap != null && dbmap.isRelational()) {
                relationalDbProtos.add(dbmap);
            } else {
                embeddedDbProtos.add(proto);
            }
        } else if (prototype instanceof NativeArray) {
            final NativeArray na = (NativeArray) prototype;
            final int length = (int) na.getLength();
            if (length > 0) {
                for (int i = 0; i < length; i++) {
                    Object o = na.get(i, na);
                    if (o instanceof String) {
                        String proto = o.toString();
                        DbMapping dbmap = app.getDbMapping(proto);
                        String type = dbmap != null ? dbmap.getType() : null;
                        if (dbmap != null && app.getExtDBTypes().contains(type)) {
                            ArrayList externalDbProtos = extDbProtos.get(type);
                            if (externalDbProtos == null) {
                                externalDbProtos = new ArrayList();
                            } else {
                                extDbProtos.remove(type);
                            }
                            externalDbProtos.add(dbmap);
                            extDbProtos.put(type, externalDbProtos);
                        } else if (dbmap != null && dbmap.isRelational()) {
                            relationalDbProtos.add(dbmap);
                        } else {
                            embeddedDbProtos.add(proto);
                        }
                    } else {
                        throw new Exception("query: The prototypes array contains an invalid entry at index " + i);
                    }
                }
            } else {
                setEmptyEmbeddedArray(app, embeddedDbProtos);
            }
        }
    }

    public static boolean validExtDb(HashMap<String, ArrayList> hm) {
        int aggCount = 0;
        int extCount = 0;
        Iterator<ArrayList> i = hm.values().iterator();
        while (i.hasNext()) {
            ArrayList al = i.next();
            if (al.size() > 0) {
                extCount = al.size();
                aggCount++;
            }
        }
        return !(aggCount > 1) && extCount > 0;

    }

    public static String getQueryDispatcher(HashMap<String, ArrayList> hm) {
        String name = null;
        Iterator i = hm.keySet().iterator();
        while (i.hasNext()) {
            String key = i.next().toString();
            ArrayList value = hm.get(key);
            if (value.size() > 0) {
                name = key;
            }
        }
        return name;
    }

    public Scriptable fields(Object field, Object prototype, Object filter) throws Exception {
        return this.fields(field, prototype, filter, null);
    }

    public Scriptable fields(Object field, Object prototype, Object filter, Object optional1) throws Exception {

        if (field == null || field == Undefined.instance) {
            throw new Exception("query.fields(): Must specify the field as the first parameter.");
        }

        IFilter theFilter = getFilterFromObject(filter);
        if (theFilter == null) {
            throw new Exception("query.fields(): Could not determine the query filter.");
        }

        ArrayList ldbProtos = new ArrayList();
        ArrayList rdbProtos = new ArrayList();
        HashMap<String, ArrayList> extDbProtos = new HashMap<String, ArrayList>();
        setPrototypeArrays(this.app, prototype, ldbProtos, rdbProtos, extDbProtos);

        Scriptable options = null;
        if (optional1 != null && optional1 instanceof Scriptable) {
            options = (Scriptable) optional1;
        } else {
            options = Context.getCurrentContext().newObject(this.core.global);
        }
        options.put("field", options, (String) field);

        boolean validExtDb = validExtDb(extDbProtos);
        ArrayList embeddedResults = null;
        if (ldbProtos.size() > 0 && (rdbProtos.size() > 0 || validExtDb)
                || rdbProtos.size() > 0 && (ldbProtos.size() > 0 || validExtDb)
                || validExtDb && (rdbProtos.size() > 0 || ldbProtos.size() > 0)) {
            this.app.logError(
                    ErrorReporter.errorMsg(this.getClass(), "fields") + "Does not support aggregate data stores");
            throw new Exception("app.getFields does not support aggregate data stores");
        } else if (validExtDb) {
            String queryDispatcher = getQueryDispatcher(extDbProtos);
            QueryDispatcher qd = querydispatchers.get(queryDispatcher);
            if (qd != null) {
                ArrayList qdDbProtos = extDbProtos.get(queryDispatcher);
                embeddedResults = qd.executeQuery(qdDbProtos, theFilter, options);
            }
        } else {
            embeddedResults = this.lqd.executeQuery(ldbProtos, theFilter, options);
        }

        return Context.getCurrentContext().newArray(this.core.global, embeddedResults.toArray());
    }

    public Scriptable objects(Object prototype, Object filter) throws Exception {
        return objects(prototype, filter, null);
    }

    public Scriptable objects(Object prototype, Object filter, Object optional1) throws Exception {
        IFilter theFilter = getFilterFromObject(filter);
        if (theFilter == null) {
            throw new Exception("query.objects(): Could not determine the query filter.");
        }

        ArrayList ldbProtos = new ArrayList();
        ArrayList rdbProtos = new ArrayList();
        HashMap<String, ArrayList> extDbProtos = new HashMap<String, ArrayList>();
        setPrototypeArrays(this.app, prototype, ldbProtos, rdbProtos, extDbProtos);
        boolean validExtDb = validExtDb(extDbProtos);
        ArrayList embeddedResults = new ArrayList();
        if (ldbProtos.size() > 0 && (rdbProtos.size() > 0 || validExtDb)
                || rdbProtos.size() > 0 && (ldbProtos.size() > 0 || validExtDb)
                || validExtDb && (rdbProtos.size() > 0 || ldbProtos.size() > 0)) {
            this.app.logError(
                    ErrorReporter.errorMsg(this.getClass(), "objects") + "Does not support aggregate data stores");
            throw new Exception("app.getObjects does not support aggregate data stores");
        } else if (validExtDb) {
            String queryDispatcher = getQueryDispatcher(extDbProtos);
            QueryDispatcher qd = querydispatchers.get(queryDispatcher);
            if (qd != null) {
                ArrayList qdDbProtos = extDbProtos.get(queryDispatcher);
                embeddedResults = qd.executeQuery(qdDbProtos, theFilter, optional1);
            }
        } else if (ldbProtos.size() > 0) {
            embeddedResults = this.lqd.executeQuery(ldbProtos, theFilter, optional1);
        } else if (rdbProtos.size() > 0) {
            embeddedResults = this.rqd.executeQuery(rdbProtos, theFilter, optional1);
        }

        return Context.getCurrentContext().newArray(this.core.global, embeddedResults.toArray());
    }

    public int getHitCount(Object prototype, Object filter, Object options) throws Exception {

        IFilter theFilter = getFilterFromObject(filter);
        if (theFilter == null) {
            throw new Exception("query.objects(): Could not determine the query filter.");
        }

        ArrayList ldbProtos = new ArrayList();
        ArrayList rdbProtos = new ArrayList();
        HashMap<String, ArrayList> extDbProtos = new HashMap<String, ArrayList>();
        setPrototypeArrays(this.app, prototype, ldbProtos, rdbProtos, extDbProtos);
        boolean validExtDb = validExtDb(extDbProtos);

        int length = 0;
        if (ldbProtos.size() > 0) {
            length = lqd.getHitCount(ldbProtos, theFilter, options);
        } else if (rdbProtos.size() > 0) {
            length = rqd.getHitCount(rdbProtos, theFilter, options);
        } else if (validExtDb) {
            String queryDispatcher = getQueryDispatcher(extDbProtos);
            QueryDispatcher qd = querydispatchers.get(queryDispatcher);
            if (qd != null) {
                ArrayList qdDbProtos = extDbProtos.get(queryDispatcher);
                length = qd.getHitCount(qdDbProtos, theFilter, options);
            }
        }

        return length;
    }

    public Object hits(Object prototype, Object filter) throws Exception {
        return hits(prototype, filter, null);
    }

    public Object hits(Object prototype, Object filter, Object optional1) throws Exception {
        IFilter theFilter = getFilterFromObject(filter);
        if (theFilter == null) {
            throw new Exception("query.hits(): Could not determine the query filter.");
        }

        ArrayList embeddedDbProtos = new ArrayList();
        ArrayList relationalDbProtos = new ArrayList();
        HashMap<String, ArrayList> extDbProtos = new HashMap<String, ArrayList>();
        setPrototypeArrays(this.app, prototype, embeddedDbProtos, relationalDbProtos, extDbProtos);
        boolean validExtDb = validExtDb(extDbProtos);
        Object results = null;
        if (embeddedDbProtos.size() > 0 && (relationalDbProtos.size() > 0 || validExtDb)
                || relationalDbProtos.size() > 0 && (embeddedDbProtos.size() > 0 || validExtDb)
                || validExtDb && (relationalDbProtos.size() > 0 || embeddedDbProtos.size() > 0)) {
            this.app.logError(
                    ErrorReporter.errorMsg(this.getClass(), "hits") + "Does not support aggregate data stores");
            throw new Exception("app.getHits() does not support aggregate data stores");
        } else if (relationalDbProtos.size() > 0) {
            throw new Exception("Relational Prototype do not support the Hits Object");
        } else if (validExtDb) {
            String queryDispatcher = getQueryDispatcher(extDbProtos);
            QueryDispatcher qd = querydispatchers.get(queryDispatcher);
            if (qd != null) {
                ArrayList qdDbProtos = extDbProtos.get(queryDispatcher);
                results = qd.hits(qdDbProtos, theFilter, optional1);
            }
        } else {
            results = lqd.hits(embeddedDbProtos, theFilter, optional1);
        }
        return results;
    }

    public Object targets(Object source) throws Exception {
        return lqd.getDirectionalNodesFor(source, 0, null, null, null);
    }

    public Object targets(Object source, Object prototypes) throws Exception {
        return lqd.getDirectionalNodesFor(source, 0, prototypes, null, null);
    }

    public Object targets(Object source, Object prototypes, Object filter) throws Exception {
        return lqd.getDirectionalNodesFor(source, 0, prototypes, filter, null);
    }

    public Object targets(Object source, Object prototypes, Object filter, Object options) throws Exception {
        return lqd.getDirectionalNodesFor(source, 0, prototypes, filter, options);
    }

    public Object sources(Object target) throws Exception {
        return lqd.getDirectionalNodesFor(target, 1, null, null, null);
    }

    public Object sources(Object target, Object prototypes) throws Exception {
        return lqd.getDirectionalNodesFor(target, 1, prototypes, null, null);
    }

    public Object sources(Object target, Object prototypes, Object filter) throws Exception {
        return lqd.getDirectionalNodesFor(target, 1, prototypes, filter, null);
    }

    public Object sources(Object target, Object prototypes, Object filter, Object options) throws Exception {
        return lqd.getDirectionalNodesFor(target, 1, prototypes, filter, options);
    }

    public int targetCount(Object source) throws Exception {
        return lqd.getDirectionalNodesForCount(source, 0, null, null, null);
    }

    public int targetCount(Object source, Object prototypes) throws Exception {
        return lqd.getDirectionalNodesForCount(source, 0, prototypes, null, null);
    }

    public int targetCount(Object source, Object prototypes, Object filter) throws Exception {
        return lqd.getDirectionalNodesForCount(source, 0, prototypes, filter, null);
    }

    public int targetCount(Object source, Object prototypes, Object filter, Object options) throws Exception {
        return lqd.getDirectionalNodesForCount(source, 0, prototypes, filter, options);
    }

    public int sourceCount(Object target) throws Exception {
        return lqd.getDirectionalNodesForCount(target, 1, null, null, null);
    }

    public int sourceCount(Object target, Object prototypes) throws Exception {
        return lqd.getDirectionalNodesForCount(target, 1, prototypes, null, null);
    }

    public int sourceCount(Object target, Object prototypes, Object filter) throws Exception {
        return lqd.getDirectionalNodesForCount(target, 1, prototypes, filter, null);
    }

    public int sourceCount(Object target, Object prototypes, Object filter, Object options) throws Exception {
        return lqd.getDirectionalNodesForCount(target, 1, prototypes, filter, options);
    }

    public Scriptable references(Object source, Object target) throws Exception {
        if (source == null || source == Undefined.instance) {
            throw new Exception("query.references(): The source parameter is not defined.");
        }
        if (target == null || target == Undefined.instance) {
            throw new Exception("query.references(): The target parameter is not defined.");
        }

        RhinoCore core = this.core;
        Application app = this.app;
        INode node = null;

        ArrayList source_objects = null;
        if (source instanceof AxiomObject) {
            source_objects = new ArrayList();
            node = ((AxiomObject) source).node;
            if (node != null) {
                source_objects.add(node.getID());
            }
        } else if (source instanceof Node) {
            source_objects = new ArrayList();
            node = (Node) source;
            if (node != null) {
                source_objects.add(node.getID());
            }
        } else if (source instanceof String) {
            source_objects = lqd.getNodesByPath((String) source);
        }

        final int source_size = source_objects == null ? 0 : source_objects.size();
        if (source_size == 0) {
            return Context.getCurrentContext().newArray(core.global, new Object[0]);
        }

        ArrayList target_objects = null;
        if (target instanceof AxiomObject) {
            target_objects = new ArrayList();
            node = ((AxiomObject) target).node;
            if (node != null) {
                target_objects.add(node.getID());
            }
        } else if (target instanceof Node) {
            target_objects = new ArrayList();
            node = (Node) target;
            if (node != null) {
                target_objects.add(node.getID());
            }
        } else if (target instanceof String) {
            target_objects = lqd.getNodesByPath((String) target);
        }

        final int targets_size = target_objects == null ? 0 : target_objects.size();
        if (targets_size == 0) {
            return Context.getCurrentContext().newArray(core.global, new Object[0]);
        }

        final int mode;
        RequestEvaluator req_eval = app.getCurrentRequestEvaluator();
        if (req_eval != null) {
            mode = req_eval.getLayer();
        } else {
            mode = DbKey.LIVE_LAYER;
        }

        return lqd.getReferences(source_objects, target_objects, mode);
    }

    public static ResourceProperties getResourceProperties(Application app, ArrayList prototypes) {
        int length = 0;
        final TypeManager tmgr = app.typemgr;
        ResourceProperties combined_props = new ResourceProperties();
        if (prototypes != null && (length = prototypes.size()) > 0) {
            BooleanQuery proto_query = new BooleanQuery();
            for (int i = 0; i < length; i++) {
                String prototype = (String) prototypes.get(i);

                Prototype proto = tmgr.getPrototype(prototype);
                Stack protos = new Stack();
                while (proto != null) {
                    protos.push(proto);
                    proto = proto.getParentPrototype();
                }
                final int protoChainSize = protos.size();
                for (int j = 0; j < protoChainSize; j++) {
                    proto = (Prototype) protos.pop();
                    combined_props.putAll(proto.getTypeProperties());
                }
            }
        } else {
            ArrayList protoarr = app.getSearchablePrototypes();
            BooleanQuery proto_query = new BooleanQuery();
            for (int i = protoarr.size() - 1; i > -1; i--) {
                String protoName = (String) protoarr.get(i);

                Prototype proto = tmgr.getPrototype(protoName);
                Stack protos = new Stack();
                while (proto != null) {
                    protos.push(proto);
                    proto = proto.getParentPrototype();
                }
                final int protoChainSize = protos.size();
                for (int j = 0; j < protoChainSize; j++) {
                    proto = (Prototype) protos.pop();
                    combined_props.putAll(proto.getTypeProperties());
                }
            }
        }

        return combined_props;
    }

    public Scriptable getVersionFields(Object obj, Object fields, Object prototypes, Object filter, Object options)
            throws Exception {
        if (obj == null) {
            throw new Exception(
                    "app.getVersions(): First parameter cannot be null, please specify an object _id or an AxiomObject");
        }

        if (fields == null || fields == Undefined.instance) {
            throw new Exception(
                    "app.getVersions(): Fields cannot be null, fields must be a string or an Javascript Object");
        }

        IFilter _filter = QueryBean.getFilterFromObject(filter);
        if (_filter == null) {
            throw new Exception("LuceneQueryDispatcher.getVersions(): Could not determine the query filter.");
        }

        ArrayList ldbProtos = new ArrayList();
        ArrayList rdbProtos = new ArrayList();
        HashMap<String, ArrayList> extDbProtos = new HashMap<String, ArrayList>();
        setPrototypeArrays(this.app, prototypes, ldbProtos, rdbProtos, extDbProtos);
        boolean validExtDb = validExtDb(extDbProtos);
        ArrayList results = new ArrayList();
        if (ldbProtos.size() > 0 && (rdbProtos.size() > 0 || validExtDb)
                || rdbProtos.size() > 0 && (ldbProtos.size() > 0 || validExtDb)
                || validExtDb && (rdbProtos.size() > 0 || ldbProtos.size() > 0)) {
            throw new Exception("app.getVersionFields does not support aggregate data stores");
        } else if (validExtDb) {
            throw new Exception("app.getVersionFields does not support external data stores");
        } else if (ldbProtos.size() > 0) {
            results = this.lqd.getVersionFields(obj, fields, ldbProtos, _filter, options);
        } else if (rdbProtos.size() > 0) {
            throw new Exception("app.getVersionFields does not support relational data stores");
        }
        return Context.getCurrentContext().newArray(this.core.global, results.toArray());
    }
}