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.BioPAXFactory;
007import org.biopax.paxtools.model.Model;
008
009import java.util.HashSet;
010import java.util.Set;
011
012/**
013 * Specifically "Clones" the BioPAX elements set
014 * (traverses to obtain dependent elements), 
015 * puts them to the new model using the visitor and traverser framework;
016 * ignores elements that are not in the source list (compare to {@link Fetcher})
017 *
018 * @see org.biopax.paxtools.controller.Visitor
019 * @see org.biopax.paxtools.controller.Traverser
020 */
021public class Cloner implements Visitor
022{
023        private static final Log LOG = LogFactory.getLog(Cloner.class);
024        
025        Traverser traverser;
026        private BioPAXFactory factory;
027        private Model targetModel;
028
029        public Cloner(EditorMap map, BioPAXFactory factory)
030        {
031                this.factory = factory;
032                this.traverser = new Traverser(map, this);
033                this.targetModel = null;
034        }
035
036        
037        /**
038         * For each element from the 'toBeCloned' list,
039         * it creates a copy in the new model, setting all 
040         * the data properties; however, object property values
041         * that refer to BioPAX elements not in 'toBeCloned' list
042         * are ignored.
043         * 
044         * @param source model
045         * @param toBeCloned elements to clone
046         * @return a new model containing the cloned biopax objects
047         */
048        public Model clone(Model source, Set<BioPAXElement> toBeCloned)
049        {
050                this.targetModel = factory.createModel();
051
052                for (BioPAXElement bpe : new HashSet<BioPAXElement>(toBeCloned))
053                {
054                        // make a copy (all properties are empty except for ID)
055                        if(targetModel.containsID(bpe.getRDFId())) {
056                                throw new RuntimeException("There are same URI different objects "
057                                                + "in the input set, uri:" + bpe.getRDFId());
058                        }
059                        targetModel.addNew(bpe.getModelInterface(), bpe.getRDFId());
060                }
061
062                // a hack to avoid unnecessary checks for the valid sub-model being cloned, 
063                // and warnings when the Cloner copies BPS.stepProcess values, 
064                // and there is a Conversion among them (-always unless stepConversion is null).
065                AbstractPropertyEditor.checkRestrictions.set(false);
066                
067                for (BioPAXElement bpe : toBeCloned)
068                {
069                        traverser.traverse(bpe, source);
070                }
071                
072                AbstractPropertyEditor.checkRestrictions.set(true); //back to the default mode
073
074                return targetModel;
075        }
076
077// --------------------- Interface Visitor ---------------------
078
079        public void visit(BioPAXElement domain, Object range, Model model, PropertyEditor editor)
080        {
081                BioPAXElement targetDomain = targetModel.getByID(domain.getRDFId());
082                
083                if (range instanceof BioPAXElement)
084                {
085                        BioPAXElement bpe = (BioPAXElement) range;
086                        BioPAXElement existing = targetModel.getByID(bpe.getRDFId());
087                        //set the property value if the value is already present in the target 
088                        if (existing != null) {
089                                editor.setValueToBean(existing, targetDomain);
090                        }
091                }
092                else
093                {
094                        editor.setValueToBean(range, targetDomain);
095                }
096        }
097}
098
099
100