<commit_before>package org.intermine.web;

/*
 * Copyright (C) 2002-2005 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.TreeSet;
import java.util.Collection;
import java.util.Date;
import java.math.BigDecimal;

import org.intermine.metadata.ClassDescriptor;
import org.intermine.metadata.AttributeDescriptor;
import org.intermine.metadata.FieldDescriptor;
import org.intermine.metadata.Model;
import org.intermine.metadata.ReferenceDescriptor;
import org.intermine.objectstore.query.ConstraintOp;
import org.intermine.objectstore.query.ConstraintSet;
import org.intermine.objectstore.query.ContainsConstraint;
import org.intermine.objectstore.query.Query;
import org.intermine.objectstore.query.QueryClass;
import org.intermine.objectstore.query.QueryCollectionReference;
import org.intermine.objectstore.query.QueryEvaluable;
import org.intermine.objectstore.query.QueryField;
import org.intermine.objectstore.query.QueryFunction;
import org.intermine.objectstore.query.QueryNode;
import org.intermine.objectstore.query.QueryObjectReference;
import org.intermine.objectstore.query.QueryReference;
import org.intermine.objectstore.query.QueryValue;
import org.intermine.objectstore.query.SimpleConstraint;
import org.intermine.objectstore.query.BagConstraint;
import org.intermine.objectstore.query.ClassConstraint;
import org.intermine.objectstore.ObjectStore;
import org.intermine.util.TypeUtil;
import org.intermine.util.StringUtil;
import org.intermine.web.bag.InterMineBag;
import org.intermine.web.bag.InterMineIdBag;

/**
 * Helper methods for main controller and main action
 * @author <NAME>
 */
public class MainHelper
{
    /**
     * Given a path, render a set of metadata Nodes to the relevant depth
     * @param path of form Gene.organism.name
     * @param model the model used to resolve class names
     * @return an ordered Set of nodes
     */
    public static Collection makeNodes(String path, Model model) {
        String className, subPath;
        if (path.indexOf(".") == -1) {
            className = path;
            subPath = "";
        } else {
            className = path.substring(0, path.indexOf("."));
            subPath = path.substring(path.indexOf(".") + 1);
        }
        Map nodes = new LinkedHashMap();
        nodes.put(className, new MetadataNode(className));
        makeNodes(getClassDescriptor(className, model), subPath, className, nodes);
        return nodes.values();
    }

    /**
     * Recursive method used to add nodes to a set representing a path from a given ClassDescriptor
     * @param cld the root ClassDescriptor
     * @param path current path prefix (eg Gene)
     * @param currentPath current path suffix (eg organism.name)
     * @param nodes the current Node set
     */
    protected static void makeNodes(ClassDescriptor cld, String path, String currentPath,
                                    Map nodes) {
        List sortedNodes = new ArrayList();

        // compare FieldDescriptors by name
        Comparator comparator = new Comparator() {
            public int compare(Object o1, Object o2) {
                String fieldName1 = ((FieldDescriptor) o1).getName().toLowerCase();
                String fieldName2 = ((FieldDescriptor) o2).getName().toLowerCase();
                return fieldName1.compareTo(fieldName2);
            }
        };

        Set attributeNodes = new TreeSet(comparator);
        Set referenceAndCollectionNodes = new TreeSet(comparator);
        for (Iterator i = cld.getAllFieldDescriptors().iterator(); i.hasNext();) {
            FieldDescriptor fd = (FieldDescriptor) i.next();
            if (!fd.isReference() && !fd.isCollection()) {
                attributeNodes.add(fd);
            } else {
                referenceAndCollectionNodes.add(fd);
            }
        }

        sortedNodes.addAll(attributeNodes);
        sortedNodes.addAll(referenceAndCollectionNodes);

        for (Iterator i = sortedNodes.iterator(); i.hasNext();) {
            FieldDescriptor fd = (FieldDescriptor) i.next();
            String fieldName = fd.getName();

            if (fieldName.equals("id")) {
                continue;
            }

            String head, tail;
            if (path.indexOf(".") != -1) {
                head = path.substring(0, path.indexOf("."));
                tail = path.substring(path.indexOf(".") + 1);
            } else {
                head = path;
                tail = "";
            }

            String button;
            if (fieldName.equals(head)) {
                button = "-";
            } else if (fd.isReference() || fd.isCollection()) {
                button = "+";
            } else {
                button = " ";
            }

            MetadataNode parent = (MetadataNode) nodes.get(currentPath);
            MetadataNode node = new MetadataNode(parent, fieldName, button);
            node.setModel(cld.getModel());

            nodes.put(node.getPath(), node);
            if (fieldName.equals(head)) {
                ClassDescriptor refCld = ((ReferenceDescriptor) fd).getReferencedClassDescriptor();
                makeNodes(refCld, tail, currentPath + "." + head, nodes);
            }
        }
    }

    /**
     * Make an InterMine query from a path query
     * @param query the PathQuery
     * @param savedBags the current saved bags map
     * @return an InterMine Query
     */
    public static Query makeQuery(PathQuery query, Map savedBags, ObjectStore os) {
        return makeQuery(query, savedBags, null, os);
    }

    /**
     * Make an InterMine query from a path query
     * @param query the PathQuery
     * @param savedBags the current saved bags map
     * @param pathToQueryNode optional parameter in which path to QueryNode map can be returned
     * @param os ObjectStore from which to load objects for bags
     * @return an InterMine Query
     */
    public static Query makeQuery(PathQuery query, Map savedBags, Map pathToQueryNode,
                                  ObjectStore os) {
        query = (PathQuery) query.clone();
        Map qNodes = query.getNodes();
        List view = query.getView();
        Model model = query.getModel();

        //first merge the query and the view
        for (Iterator i = view.iterator(); i.hasNext();) {
            String path = (String) i.next();
            if (!qNodes.containsKey(path)) {
                query.addNode(path);
            }
        }

        //create the real query
        Query q = new Query();
        ConstraintSet cs = new ConstraintSet(ConstraintOp.AND);
        q.setConstraint(cs);

        Map queryBits = new HashMap();

        //build the FROM and WHERE clauses
        for (Iterator i = query.getNodes().values().iterator(); i.hasNext();) {
            PathNode node = (PathNode) i.next();
            String path = node.getPath();
            QueryReference qr = null;

            if (path.indexOf(".") == -1) {
                QueryClass qc = new QueryClass(getClass(node.getType(), model));
                q.addFrom(qc);
                queryBits.put(path, qc);
            } else {
                String fieldName = node.getFieldName();
                QueryClass parentQc = (QueryClass) queryBits.get(node.getPrefix());

                if (node.isAttribute()) {
                    QueryField qf = new QueryField(parentQc, fieldName);
                    queryBits.put(path, qf);
                } else {
                    if (node.isReference()) {
                        qr = new QueryObjectReference(parentQc, fieldName);
                    } else {
                        qr = new QueryCollectionReference(parentQc, fieldName);
                    }
                    QueryClass qc = new QueryClass(getClass(node.getType(), model));
                    cs.addConstraint(new ContainsConstraint(qr, ConstraintOp.CONTAINS, qc));
                    q.addFrom(qc);
                    queryBits.put(path, qc);
                }
            }

            QueryNode qn = (QueryNode) queryBits.get(path);
            for (Iterator j = node.getConstraints().iterator(); j.hasNext();) {
                Constraint c = (Constraint) j.next();
                if (BagConstraint.VALID_OPS.contains(c.getOp())) {
                    Collection bag //= (Collection) savedBags.get(c.getValue());
                            = ((InterMineBag) savedBags.get(c.getValue())).toObjectCollection(os);
                    /*if (bag instanceof InterMineIdBag) {
                        // constrain the id of the object
                        QueryField qf = new QueryField((QueryClass) qn, "id");
                        cs.addConstraint(new BagConstraint(qf,
                                                        c.getOp(),
                                                        bag);
                    } else*/ {
                        cs.addConstraint(new BagConstraint(qn,
                                                       c.getOp(),
                                                       bag));
                    }
                } else if (node.isAttribute()) { //assume, for now, that it's a SimpleConstraint
                    if (c.getOp() == ConstraintOp.IS_NOT_NULL
                        || c.getOp() == ConstraintOp.IS_NULL) {
                        cs.addConstraint(new SimpleConstraint((QueryEvaluable) qn,
                                                              c.getOp()));
                    } else {
                        if (qn.getType().equals(String.class)) {
                            // do a case-insensitive search
                            QueryFunction qf = new QueryFunction((QueryField) qn,<commit_msg>fixes #335 queries that constrain an object to a bag just constrain the object id<commit_after>package org.intermine.web;

/*
 * Copyright (C) 2002-2005 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.Set;
import java.util.TreeSet;
import java.util.Collection;
import java.util.Date;
import java.math.BigDecimal;

import org.intermine.metadata.ClassDescriptor;
import org.intermine.metadata.AttributeDescriptor;
import org.intermine.metadata.FieldDescriptor;
import org.intermine.metadata.Model;
import org.intermine.metadata.ReferenceDescriptor;
import org.intermine.objectstore.query.ConstraintOp;
import org.intermine.objectstore.query.ConstraintSet;
import org.intermine.objectstore.query.ContainsConstraint;
import org.intermine.objectstore.query.Query;
import org.intermine.objectstore.query.QueryClass;
import org.intermine.objectstore.query.QueryCollectionReference;
import org.intermine.objectstore.query.QueryEvaluable;
import org.intermine.objectstore.query.QueryField;
import org.intermine.objectstore.query.QueryFunction;
import org.intermine.objectstore.query.QueryNode;
import org.intermine.objectstore.query.QueryObjectReference;
import org.intermine.objectstore.query.QueryReference;
import org.intermine.objectstore.query.QueryValue;
import org.intermine.objectstore.query.SimpleConstraint;
import org.intermine.objectstore.query.BagConstraint;
import org.intermine.objectstore.query.ClassConstraint;
import org.intermine.objectstore.ObjectStore;
import org.intermine.util.TypeUtil;
import org.intermine.util.StringUtil;
import org.intermine.web.bag.InterMineBag;
import org.intermine.web.bag.InterMineIdBag;

/**
 * Helper methods for main controller and main action
 * @author <NAME>
 */
public class MainHelper
{
    /**
     * Given a path, render a set of metadata Nodes to the relevant depth
     * @param path of form Gene.organism.name
     * @param model the model used to resolve class names
     * @return an ordered Set of nodes
     */
    public static Collection makeNodes(String path, Model model) {
        String className, subPath;
        if (path.indexOf(".") == -1) {
            className = path;
            subPath = "";
        } else {
            className = path.substring(0, path.indexOf("."));
            subPath = path.substring(path.indexOf(".") + 1);
        }
        Map nodes = new LinkedHashMap();
        nodes.put(className, new MetadataNode(className));
        makeNodes(getClassDescriptor(className, model), subPath, className, nodes);
        return nodes.values();
    }

    /**
     * Recursive method used to add nodes to a set representing a path from a given ClassDescriptor
     * @param cld the root ClassDescriptor
     * @param path current path prefix (eg Gene)
     * @param currentPath current path suffix (eg organism.name)
     * @param nodes the current Node set
     */
    protected static void makeNodes(ClassDescriptor cld, String path, String currentPath,
                                    Map nodes) {
        List sortedNodes = new ArrayList();

        // compare FieldDescriptors by name
        Comparator comparator = new Comparator() {
            public int compare(Object o1, Object o2) {
                String fieldName1 = ((FieldDescriptor) o1).getName().toLowerCase();
                String fieldName2 = ((FieldDescriptor) o2).getName().toLowerCase();
                return fieldName1.compareTo(fieldName2);
            }
        };

        Set attributeNodes = new TreeSet(comparator);
        Set referenceAndCollectionNodes = new TreeSet(comparator);
        for (Iterator i = cld.getAllFieldDescriptors().iterator(); i.hasNext();) {
            FieldDescriptor fd = (FieldDescriptor) i.next();
            if (!fd.isReference() && !fd.isCollection()) {
                attributeNodes.add(fd);
            } else {
                referenceAndCollectionNodes.add(fd);
            }
        }

        sortedNodes.addAll(attributeNodes);
        sortedNodes.addAll(referenceAndCollectionNodes);

        for (Iterator i = sortedNodes.iterator(); i.hasNext();) {
            FieldDescriptor fd = (FieldDescriptor) i.next();
            String fieldName = fd.getName();

            if (fieldName.equals("id")) {
                continue;
            }

            String head, tail;
            if (path.indexOf(".") != -1) {
                head = path.substring(0, path.indexOf("."));
                tail = path.substring(path.indexOf(".") + 1);
            } else {
                head = path;
                tail = "";
            }

            String button;
            if (fieldName.equals(head)) {
                button = "-";
            } else if (fd.isReference() || fd.isCollection()) {
                button = "+";
            } else {
                button = " ";
            }

            MetadataNode parent = (MetadataNode) nodes.get(currentPath);
            MetadataNode node = new MetadataNode(parent, fieldName, button);
            node.setModel(cld.getModel());

            nodes.put(node.getPath(), node);
            if (fieldName.equals(head)) {
                ClassDescriptor refCld = ((ReferenceDescriptor) fd).getReferencedClassDescriptor();
                makeNodes(refCld, tail, currentPath + "." + head, nodes);
            }
        }
    }

    /**
     * Make an InterMine query from a path query
     * @param query the PathQuery
     * @param savedBags the current saved bags map
     * @return an InterMine Query
     */
    public static Query makeQuery(PathQuery query, Map savedBags, ObjectStore os) {
        return makeQuery(query, savedBags, null, os);
    }

    /**
     * Make an InterMine query from a path query
     * @param query the PathQuery
     * @param savedBags the current saved bags map
     * @param pathToQueryNode optional parameter in which path to QueryNode map can be returned
     * @param os ObjectStore from which to load objects for bags
     * @return an InterMine Query
     */
    public static Query makeQuery(PathQuery query, Map savedBags, Map pathToQueryNode,
                                  ObjectStore os) {
        query = (PathQuery) query.clone();
        Map qNodes = query.getNodes();
        List view = query.getView();
        Model model = query.getModel();

        //first merge the query and the view
        for (Iterator i = view.iterator(); i.hasNext();) {
            String path = (String) i.next();
            if (!qNodes.containsKey(path)) {
                query.addNode(path);
            }
        }

        //create the real query
        Query q = new Query();
        ConstraintSet cs = new ConstraintSet(ConstraintOp.AND);
        q.setConstraint(cs);

        Map queryBits = new HashMap();

        //build the FROM and WHERE clauses
        for (Iterator i = query.getNodes().values().iterator(); i.hasNext();) {
            PathNode node = (PathNode) i.next();
            String path = node.getPath();
            QueryReference qr = null;

            if (path.indexOf(".") == -1) {
                QueryClass qc = new QueryClass(getClass(node.getType(), model));
                q.addFrom(qc);
                queryBits.put(path, qc);
            } else {
                String fieldName = node.getFieldName();
                QueryClass parentQc = (QueryClass) queryBits.get(node.getPrefix());

                if (node.isAttribute()) {
                    QueryField qf = new QueryField(parentQc, fieldName);
                    queryBits.put(path, qf);
                } else {
                    if (node.isReference()) {
                        qr = new QueryObjectReference(parentQc, fieldName);
                    } else {
                        qr = new QueryCollectionReference(parentQc, fieldName);
                    }
                    QueryClass qc = new QueryClass(getClass(node.getType(), model));
                    cs.addConstraint(new ContainsConstraint(qr, ConstraintOp.CONTAINS, qc));
                    q.addFrom(qc);
                    queryBits.put(path, qc);
                }
            }

            QueryNode qn = (QueryNode) queryBits.get(path);
            for (Iterator j = node.getConstraints().iterator(); j.hasNext();) {
                Constraint c = (Constraint) j.next();
                if (BagConstraint.VALID_OPS.contains(c.getOp())) {
                    Collection bag = (Collection) savedBags.get(c.getValue());
                    //        = ((InterMineBag) savedBags.get(c.getValue())).toObjectCollection(os);
                    if (bag instanceof InterMineIdBag) {
                        // constrain the id of the object
                        QueryField qf = new QueryField((QueryClass) qn, "id");
                        cs.addConstraint(new BagConstraint(qf, c.getOp(), bag));
                    } else {
                        cs.addConstraint(new BagConstraint(qn,
                                                       c.getOp(),
                                                       bag));
                    }
                } else if (node.isAttribute()) { //assume, for now, that it's a SimpleConstraint
                    if (c.getOp() == ConstraintOp.IS_NOT_NULL
                        || c.getOp() == ConstraintOp.IS_NULL) {
                        cs.addConstraint(new SimpleConstraint((QueryEvaluable) qn,
                                                              c.getOp()));
                    } else {
                        if (qn.getType().equals(String.class)) {
                            // do a case-insensitive search
                            QueryFunction qf = new QueryFunction((QueryField) qn,
{'fraction_non_alphanumeric': 0.1028458302400396, 'fraction_numerical': 0.002425142291512002, 'mean_word_length': 1.954525515426232, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 20, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5931145', 'n_tokens_mistral': 5252, 'n_tokens_neox': 5086, 'n_words': 1343}