001package org.biopax.paxtools.query.wrapperL3;
002
003import org.biopax.paxtools.controller.PathAccessor;
004import org.biopax.paxtools.model.BioPAXElement;
005import org.biopax.paxtools.model.level3.Level3Element;
006
007import java.util.*;
008
009/**
010 * This is the base class for filters that are filtering based on values of specific fields of the
011 * objects and their related objects.
012 *
013 * @author Ozgun Babur
014 */
015public abstract class StringFieldFilter extends Filter
016{
017        /**
018         * Accessors and their applicable classes.
019         */
020        Map<PathAccessor, Class<? extends BioPAXElement>> accessors;
021
022        /**
023         * Set of valid values (case insensitive), for filtering.
024         */
025        Set<String> validValues;
026
027        /**
028         * Option to accept objects if the field is empty or null.
029         */
030        protected boolean emptyOK;
031
032        /**
033         * Constructor.
034         * 
035         * @param emptyOK whether to always accept empty field when traversing the graph or reject
036         * @param valid filter values (strings' capitalization does not matter)
037         */
038        protected StringFieldFilter(boolean emptyOK, String[] valid)
039        {
040                setEmptyOK(emptyOK);
041                accessors = new HashMap<PathAccessor, Class<? extends BioPAXElement>>();
042                
043                if(valid == null || valid.length == 0) {
044                        validValues = Collections.EMPTY_SET;
045                } else {
046                        validValues = new HashSet<String>();
047                        //copy all the filter values, making them lower-case (will then match ignoring case)
048                        for(String val : valid)
049                                if(val != null && !val.isEmpty())
050                                        validValues.add(val.toLowerCase());
051                }
052                
053                createFieldAccessors();
054        }
055
056        /**
057         * Gets the option to accept empty fields.
058         * @return true if empty is ok
059         */
060        public boolean isEmptyOK()
061        {
062                return emptyOK;
063        }
064
065        /**
066         * Sets the parameter to accept empty field values.
067         * @param emptyOK parameter to accept empty filed
068         */
069        public void setEmptyOK(boolean emptyOK)
070        {
071                this.emptyOK = emptyOK;
072        }
073
074        /**
075         * The child class should populate the list of PathAccessor object using the
076         * <code>addAccessor</code> method.
077         */
078        public abstract void createFieldAccessors();
079
080        /**
081         * Adds the given <code>PathAccessor</code> to the list of accessors to use to get field values
082         * of objects and their related objects.
083         * @param acc accessor
084         * @param clazz the type of element that the accessor is applied
085         */
086        protected void addAccessor(PathAccessor acc, Class<? extends BioPAXElement> clazz)
087        {
088                accessors.put(acc, clazz);
089        }
090
091        /**
092         * Adds the given valid value to the set of valid values.
093         * @param value a valid value
094         */
095        protected void addValidValue(String value)
096        {
097                if(value!=null && !value.isEmpty())
098                        validValues.add(value.toLowerCase());
099        }
100
101        /**
102         * Checks if the related values of the object are among valid values. Returns true if none of
103         * the accessors is applicable to the given object.
104         * @param ele level 3 element to check
105         * @return true if ok to traverse
106         */
107        @Override
108        public boolean okToTraverse(Level3Element ele)
109        {
110                if(validValues.isEmpty())
111                        return true;
112                
113                boolean empty = true;
114                boolean objectRelevant = false;
115
116                for (PathAccessor acc : accessors.keySet())
117                {
118                        Class clazz = accessors.get(acc);
119
120                        if (!clazz.isAssignableFrom(ele.getClass())) continue;
121
122                        objectRelevant = true;
123
124                        Set values = acc.getValueFromBean(ele);
125                        if (empty) empty = values.isEmpty();
126
127                        for (Object o : values)
128                                //ignoring capitalization (case)
129                                if (validValues.contains(o.toString().toLowerCase())) 
130                                        return true;
131                }
132
133                return !objectRelevant || (empty && isEmptyOK());
134        }
135}