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 * This is an all-in-one Traverser/Visitor combination 
014 * to use when deep recursive visiting is required. 
015 * Unlike {@link Traverser}, it keeps track of where current 
016 * object is in the model and whether it's been already visited, 
017 * which helps prevent infinite loops.
018 * Like it's for the {@link Traverser}, there is no any 
019 * particular order in which it processes properties.
020 * 
021 * @see Fetcher
022 * @see Traverser
023 * @see Visitor
024 * 
025 * @author rodch
026 */
027public abstract class AbstractTraverser extends Traverser implements Visitor
028{
029        private final static Log log = LogFactory.getLog(AbstractTraverser.class);
030        protected final Set<BioPAXElement> visited;
031                
032        public AbstractTraverser(EditorMap editorMap, 
033                @SuppressWarnings("rawtypes") Filter<PropertyEditor>... filters)
034        {
035                super(editorMap, null, filters);
036                setVisitor(this);
037                visited = new HashSet<BioPAXElement>();
038        }
039
040        /**
041         * This is to implement a real action here: 
042         * do something, return or even to continue (traverse)
043         * into the child (range) element's properties if it's a BioPAX object.
044         * 
045         * @param range property value
046         * @param domain parent/owner BioPAX element
047         * @param model the BioPAX model of interest
048         * @param editor the property editor
049         */
050        protected abstract void visit(Object range, BioPAXElement domain, Model model, PropertyEditor<?,?> editor);
051                
052        /**
053         * Calls the protected abstract method visit that is to be
054         * implemented in subclasses of this abstract class.
055         * 
056         * @param domain BioPAX Element
057         * @param range property value (can be BioPAX element, primitive, enum, string)
058         * @param model the BioPAX model of interest
059         * @param editor parent's property PropertyEditor
060         */
061        public void visit(BioPAXElement domain, Object range, Model model, PropertyEditor<?,?> editor) {
062                // actions
063                visit(range, domain, model, editor);
064        }
065
066
067        @Override
068        public <D extends BioPAXElement> void traverse(D element, Model model) {
069                if(visited.add(element)) {
070                        super.traverse(element, model);//calls visit method for each property value, taking prop. filters into acc.
071                } else {
072                        log.debug("Escaped a loop: again " + element.getRDFId());
073                }
074        }
075
076
077        /**
078         * Clears the internal set of traversed biopax objects.
079         * Apply if you're re-using the same traverser instance but
080         * start over from a different root biopax element.
081         */
082        public void reset() {
083                visited.clear();
084        }
085}