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}