001package org.biopax.paxtools.controller;
002
003import org.apache.commons.logging.Log;
004import org.apache.commons.logging.LogFactory;
005import org.biopax.paxtools.model.BioPAXElement;
006import org.biopax.paxtools.model.Model;
007import org.biopax.paxtools.util.Filter;
008
009import java.util.HashSet;
010import java.util.Set;
011
012/**
013 * A bi-directional BioPAX properties traverser.
014 * 
015 * To traverse both biopax element's standard properties, such as 'xref',  
016 * and (Paxtools') inverse properties, such as 'xrefOf', etc.
017 * 
018 * @author Ozgun Babur 
019 */
020public class TraverserBilinked extends Traverser
021{
022        private boolean isInverseOnly = false;
023        private final static Log log = LogFactory.getLog(TraverserBilinked.class);
024        
025        /**
026         * Constructor.
027         * 
028         * @param editorMap biopax property editors map
029         * @param visitor user's implementation; if it recursively 
030         *              calls {@link #traverse(BioPAXElement, Model)} method, then care must be taken to prevent infinite loops.
031         * @param filters bidirectional property filters
032         */
033        public TraverserBilinked(EditorMap editorMap, Visitor visitor, PropertyFilterBilinked... filters)
034        {
035                super(editorMap, visitor, filters);
036        }
037
038        public boolean isInverseOnly() {
039                return isInverseOnly;
040        }
041        public void setInverseOnly(boolean isInverseOnly) {
042                this.isInverseOnly = isInverseOnly;
043        }
044        
045        @Override
046        public void traverse(BioPAXElement element, Model model)
047        {
048                if(!isInverseOnly)
049                        super.traverse(element, model);
050
051                // Now traverse the inverse links
052
053                Set<ObjectPropertyEditor> editors = editorMap.getInverseEditorsOf(element);
054
055                if(editors == null)
056                {
057                        log.warn("No editors for : " + element.getModelInterface());
058                        return;
059                }
060                for (ObjectPropertyEditor editor : editors)
061                {
062                        if (filterInverse(editor))
063                        {
064                                        Set<BioPAXElement> valueSet = new HashSet(editor.getInverseAccessor().getValueFromBean(element));
065                                        if (!valueSet.isEmpty()) for (BioPAXElement value : valueSet)
066                                        {
067                                                if(value != null) {
068                                                        //TODO how visitor knows whether it's called from inverse or normal property (e.g., to modify a value)?
069                                                        visitor.visit(element, value, model, editor); 
070                                                }
071                                        }
072                        }
073                }
074        }
075
076        protected boolean filterInverse(PropertyEditor editor)
077        {
078                for (Filter<PropertyEditor> filter : filters)
079                {
080                        if (!((PropertyFilterBilinked) filter).filterInverse(editor))
081                        {
082                                return false;
083                        }
084                }
085                return true;
086        }
087
088}