001/**
002 * Copyright (C) 2007 - 2016, Jens Lehmann
003 *
004 * This file is part of DL-Learner.
005 *
006 * DL-Learner is free software; you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation; either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * DL-Learner is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
018 */
019package org.dllearner.algorithms.el;
020
021import org.semanticweb.owlapi.model.*;
022import uk.ac.manchester.cs.owl.owlapi.OWLDataFactoryImpl;
023
024import java.util.*;
025
026/**
027 * Represents an EL description tree, which corresponds to a
028 * description in the EL description logic. Note that an EL description tree
029 * can be a subtree of another EL description tree. In general,
030 * an EL description tree is a tree where the node label is a set
031 * of named classes and the edges are labelled with a property.
032 * 
033 * In the documentation below "this node" refers to the root node
034 * of this EL description (sub-)tree. One tree cannot be reused,
035 * i.e. used as subtree in several description trees, as some of
036 * the associated variables (level, simulation) depend on the overall
037 * tree. 
038 * 
039 * @author Jens Lehmann
040 *
041 */
042@SuppressWarnings("unused")
043public class ELDescriptionNode {
044
045        // the reference tree for storing values, must not be null
046        protected ELDescriptionTree tree;
047        
048        protected TreeSet<OWLClass> label = new TreeSet<>();
049        
050        protected List<ELDescriptionEdge> edges = new LinkedList<>();
051
052        protected int level;
053        
054        // parent node in the tree;
055        // null indicates that this node is a root node
056        protected ELDescriptionNode parent = null;
057                
058        // simulation information (list or set?)
059        protected Set<ELDescriptionNode> in = new HashSet<>();
060        protected Set<ELDescriptionNode> inSC1 = new HashSet<>();
061        protected Set<ELDescriptionNode> inSC2 = new HashSet<>();
062        protected Set<ELDescriptionNode> out = new HashSet<>();
063        protected Set<ELDescriptionNode> outSC1 = new HashSet<>();
064        protected Set<ELDescriptionNode> outSC2 = new HashSet<>();
065        
066        protected boolean isClassNode;
067        protected OWLDataRange dataRange;
068        
069        public static final OWLDataFactory df = new OWLDataFactoryImpl();
070        
071        /**
072         * Internal constructor used for cloning nodes.
073         */
074        protected ELDescriptionNode() {
075                
076        }
077        
078        /**
079         * Constructs an EL OWLClassExpression tree with empty root label.
080         */
081        public ELDescriptionNode(ELDescriptionTree tree) {
082                this(tree, new TreeSet<>());
083        }       
084        
085        // convenience constructor
086        public ELDescriptionNode(ELDescriptionTree tree, OWLClass... label) {
087                this(tree, new TreeSet<>(Arrays.asList(label)));
088        }       
089        
090        /**
091         * Constructs an EL OWLClassExpression tree given its root label.
092         * @param label Label of the root node.
093         */
094        public ELDescriptionNode(ELDescriptionTree tree, TreeSet<OWLClass> label) {
095                this.label = label;
096                this.edges = new LinkedList<>();
097                this.tree = tree;
098                level = 1;
099                parent = null;
100                // this is the root node of the overall tree
101                tree.rootNode = this;
102                tree.addNodeToLevel(this, level);
103                tree.size += label.size();
104                
105                isClassNode = true;
106        }
107        
108        /**
109         * Constructs an EL description tree node given a description tree and the data range.
110         * @param tree the description tree
111         * @param dataRange the data range
112         */
113        public ELDescriptionNode(ELDescriptionTree tree, OWLDataRange dataRange) {
114                this.dataRange = dataRange;
115                this.edges = new LinkedList<>();
116                this.tree = tree;
117                level = 1;
118                parent = null;
119                // this is the root node of the overall tree
120                tree.rootNode = this;
121                tree.addNodeToLevel(this, level);
122                tree.size += label.size();
123                
124                isClassNode = false;
125        }
126        
127        // convenience constructor
128        public ELDescriptionNode(ELDescriptionNode parentNode, OWLObjectProperty parentProperty, OWLClass... label) {
129                this(parentNode, parentProperty, new TreeSet<>(Arrays.asList(label)));
130        }
131        
132        public ELDescriptionNode(ELDescriptionNode parentNode, OWLObjectProperty parentProperty, Set<OWLClass> label) {
133//              this.label = label;
134                // we first need to add the edge and update the simulation and then add
135                // all classes iteratively to the label (each time updating the simulation again)
136                this.edges = new LinkedList<>();
137                parent = parentNode;
138                // the reference tree is the same as for the parent tree
139                tree = parentNode.tree;
140                // level increases by 1
141                level = parentNode.level + 1;
142                // we add an edge from the parent to this node
143                ELDescriptionEdge<OWLObjectProperty> edge = new ELDescriptionEdge(parentProperty, this);
144                parent.edges.add(edge);
145                // we need to update the set of nodes on a particular level
146                tree.addNodeToLevel(this, level);               
147                
148                // simulation update
149//              Monitor mon = MonitorFactory.start("simulation update");
150                // the nodes, which need to be updated
151                Set<ELDescriptionNode> update = new HashSet<>();
152                
153                // loop over all nodes on the same level, which are not in the in set
154                Set<ELDescriptionNode> nodes = tree.getNodesOnLevel(level);
155                for(ELDescriptionNode w : nodes) {
156                        // to save space, we do not add reflexive relations
157                        if(w != this) {
158                                // (w,v') is automatically added
159                                tree.extendSimulation(w, this);
160                                
161                                // check conditions for (v',w)
162                                boolean sc1 = false, sc2 = false;
163                                
164                                if(w.label.size() == 0) {
165                                        tree.extendSimulationSC1(this, w);
166                                        sc1 = true;
167                                }
168                                
169                                if(w.edges.size() == 0) {
170                                        tree.extendSimulationSC2(this, w);
171                                        sc2 = true;
172                                }
173                                
174                                if(sc1 && sc2) {
175                                        tree.extendSimulationSC12(this, w);
176                                }       
177                                
178                                update.add(w.parent);
179                        }
180                }
181                update.add(this.parent);
182                
183//              if(inSC1.contains(w) && tree.checkSC2(this, w)) {
184//                      tree.extendSimulation(this, w);
185//                      update.add(w.parent);
186//              }               
187                
188                // loop over all nodes in out set
189//              for(ELDescriptionNode w : out) {
190//                      if(!tree.checkSC1(this, w)) {
191//                              tree.shrinkSimulation(this, w);
192//                              update.add(w.parent);
193//                      }
194//              }
195                
196//              System.out.println(update);
197                
198                // apply updates recursively top-down
199                tree.updateSimulation(update);
200//              mon.stop();
201                
202                // add all classes in label
203                for(OWLClass nc : label) {
204                        extendLabel(nc);
205                }
206                
207                // 1 for the edge (labels are already taken care of by extendLabel)
208                tree.size += 1;
209                
210                isClassNode = true;
211        }
212        
213        public ELDescriptionNode(ELDescriptionNode parentNode, OWLDataProperty parentProperty, OWLDataRange dataRange) {
214                this.dataRange = dataRange;
215                //              this.label = label;
216                // we first need to add the edge and update the simulation and then add
217                // all classes iteratively to the label (each time updating the simulation again)
218                this.edges = new LinkedList<>();
219                parent = parentNode;
220                // the reference tree is the same as for the parent tree
221                tree = parentNode.tree;
222                // level increases by 1
223                level = parentNode.level + 1;
224                // we add an edge from the parent to this node
225                ELDescriptionEdge edge = new ELDescriptionEdge(parentProperty, this);
226                parent.edges.add(edge);
227                // we need to update the set of nodes on a particular level
228                tree.addNodeToLevel(this, level);               
229                
230                // simulation update
231//              Monitor mon = MonitorFactory.start("simulation update");
232                // the nodes, which need to be updated
233                Set<ELDescriptionNode> update = new HashSet<>();
234                
235                // loop over all nodes on the same level, which are not in the in set
236                Set<ELDescriptionNode> nodes = tree.getNodesOnLevel(level);
237                for(ELDescriptionNode w : nodes) {
238                        // to save space, we do not add reflexive relations
239                        if(w != this) {
240                                // (w,v') is automatically added
241                                tree.extendSimulation(w, this);
242                                
243                                // check conditions for (v',w)
244                                boolean sc1 = false, sc2 = false;
245                                
246                                if(w.label.size() == 0) {
247                                        tree.extendSimulationSC1(this, w);
248                                        sc1 = true;
249                                }
250                                
251                                if(w.edges.size() == 0) {
252                                        tree.extendSimulationSC2(this, w);
253                                        sc2 = true;
254                                }
255                                
256                                if(sc1 && sc2) {
257                                        tree.extendSimulationSC12(this, w);
258                                }       
259                                
260                                update.add(w.parent);
261                        }
262                }
263                update.add(this.parent);
264                
265//              if(inSC1.contains(w) && tree.checkSC2(this, w)) {
266//                      tree.extendSimulation(this, w);
267//                      update.add(w.parent);
268//              }               
269                
270                // loop over all nodes in out set
271//              for(ELDescriptionNode w : out) {
272//                      if(!tree.checkSC1(this, w)) {
273//                              tree.shrinkSimulation(this, w);
274//                              update.add(w.parent);
275//                      }
276//              }
277                
278//              System.out.println(update);
279                
280                // apply updates recursively top-down
281                tree.updateSimulation(update);
282//              mon.stop();
283                
284                
285                // 1 for the edge (labels are already taken care of by extendLabel)
286                tree.size += 1;
287                
288                isClassNode = false;
289        }
290        
291        /**
292         * @return the isClassNode
293         */
294        public boolean isClassNode() {
295                return isClassNode;
296        }
297        
298        /**
299         * @return the dataRange
300         */
301        public OWLDataRange getDataRange() {
302                return dataRange;
303        }
304        
305        
306        /**
307         * Constructs an EL OWLClassExpression tree given its root label and edges.
308         * @param label Label of the root node.
309         * @param edges Edges connected to the root node.
310         */
311//  TODO: probably delete as this constructor is not straightforward to
312//  implement within the new structure
313//      public ELDescriptionNode(SortedSet<OWLClass> label, List<ELDescriptionEdge> edges) {
314//              this.label = label;
315//              this.edges = edges;
316//      }
317        
318        /**
319         * Checks whether this node has a parent. If the parent link
320         * is null, the node is considered to be a root node.
321         * @return True of this is the root node and false otherwise.
322         */
323        public boolean isRoot() {
324                return parent == null;
325        }
326        
327        /**
328         * Traverses the EL OWLClassExpression tree upwards until it finds 
329         * the root and returns it.
330         * @return The root node of this EL OWLClassExpression tree.
331         */
332        public ELDescriptionNode getRoot() {
333                ELDescriptionNode root = this;
334                while(root.parent != null) {
335                        root = parent;
336                }
337                return root;
338        }
339        
340        /**
341         * Traverses the tree until the root node and counts how
342         * many edges are traversed. If this node does not have a parent,
343         * zero is returned. This method is used for checking the integrity
344         * of the tree in unit tests. Use {@link #getLevel()} to get the 
345         * level of the tree. 
346         * @return The level of this node (or more specifically the root
347         * node of this subtree) within the overall EL OWLClassExpression tree.
348         */
349        public int computeLevel() {
350                ELDescriptionNode root = this;
351                int level = 0;
352                while(root.parent != null) {
353                        root = parent;
354                        level++;
355                }
356                return level;           
357        }
358        
359        /**
360         * This method transform the tree to an EL description. The
361         * node labels are transformed to an {@link org.semanticweb.owlapi.model.OWLObjectIntersectionOf}
362         * of {@link org.semanticweb.owlapi.model.OWLClass}. Each edge is transformed to an
363         * {@link org.semanticweb.owlapi.model.OWLObjectSomeValuesFrom}, where the property is the edge
364         * label and the child description the subtree the edge points
365         * to. Edges are also added to the intersection. If the intersection
366         * is empty, {@link OWLDataFactory#getOWLThing()} is returned.
367         * @return The description corresponding to this EL description tree.
368         */
369        public OWLClassExpression transformToDescription() {
370                int nrOfElements = label.size() + edges.size();
371                // leaf labeled with \emptyset stands for owl:Thing
372                if(nrOfElements == 0) {
373                        return df.getOWLThing();
374                // we want to avoid intersections with only 1 element, so in this
375                // case we return either the OWLClass or ObjectSomeRestriction directly
376                } else if(nrOfElements == 1) {
377                        if(label.size()==1) {
378                                return label.first();
379                        } else {
380                                ELDescriptionEdge edge = edges.get(0);
381                                if(edge.isObjectProperty()){
382                                        OWLClassExpression child = edge.getNode().transformToDescription();
383                                        return df.getOWLObjectSomeValuesFrom(edge.getLabel().asOWLObjectProperty(), child);
384                                } else {
385                                        OWLDataRange range = edge.getNode().getDataRange();
386                                        return df.getOWLDataSomeValuesFrom(edge.getLabel().asOWLDataProperty(), range);
387                                }
388                                
389                        }
390                // return an intersection of labels and edges
391                } else {
392                        Set<OWLClassExpression> operands = new TreeSet<OWLClassExpression>(label);
393                        
394                        for(ELDescriptionEdge edge : edges) {
395                                if(edge.isObjectProperty()){
396                                        OWLClassExpression child = edge.getNode().transformToDescription();
397                                        OWLClassExpression osr = df.getOWLObjectSomeValuesFrom(edge.getLabel().asOWLObjectProperty(), child);
398                                        operands.add(osr);
399                                } else {
400                                        OWLDataRange range = edge.getNode().getDataRange();
401                                        OWLClassExpression dsr = df.getOWLDataSomeValuesFrom(edge.getLabel().asOWLDataProperty(), range);
402                                        operands.add(dsr);
403                                }
404                        }
405                        return df.getOWLObjectIntersectionOf(operands);
406                }
407        }
408
409        /**
410         * Gets a list describing the position of this node within the 
411         * tree. If the list is e.g. [2,5,1], then the node can be reached
412         * by picking the second child of the root node, then picking the
413         * 5th child of this node and finally selecting the first child of
414         * the previous node.
415         * @return The position number of this node within the tree as described above.
416         */
417        public int[] getCurrentPosition() {
418                int[] position = new int[level-1];
419                ELDescriptionNode root = this;
420                while(root.parent != null) {
421                        position[root.level-2] = root.getChildNumber();
422                        root = root.parent;     
423                }
424                return position;
425        }
426        
427        // returns the child number of this node, i.e. whether it is 
428        // the first, second, third etc. child;
429        // TODO: might be a bit faster to store this explicitly
430        private int getChildNumber() {
431                int count = 0;
432                for(ELDescriptionEdge edge : parent.edges) {
433                        if(edge.getNode() == this) {
434                                return count;
435                        }
436                        count++;
437                }
438                throw new RuntimeException("Inconsistent tree. Child tree not reachable from parent.");
439        }
440        
441        /**
442         * Replaces an entry in the node label.
443         * @param oldClass Class to remove from label.
444         * @param newClass Class to add to label.
445         */
446        public void replaceInLabel(OWLClass oldClass, OWLClass newClass) {
447                label.remove(oldClass);
448                label.add(newClass);
449                labelSimulationUpdate();
450        }
451        
452        /**
453         * Adds an entry to the node label.
454         * @param newClass Class to add to label.
455         */
456        public void extendLabel(OWLClass newClass) {
457                label.add(newClass);
458                labelSimulationUpdate();
459                tree.size += 1;
460//              System.out.println(tree);
461//              System.out.println(tree.size);
462        }       
463        
464        // simulation update when extending or refining label 
465        // (same in both cases)
466        private void labelSimulationUpdate() {
467//              Monitor mon = MonitorFactory.start("simulation update");
468                // compute the nodes, which need to be updated
469                Set<ELDescriptionNode> update = new HashSet<>();
470                
471                Set<ELDescriptionNode> tmp = tree.getNodesOnLevel(level);
472                for(ELDescriptionNode w : tmp) {
473                        if(w != this) {
474                                // SC1(v,w) can only change from false to true
475                                if(!inSC1.contains(w) && tree.checkSC1(this, w)) {
476                                        tree.extendSimulationSC1(this, w);
477                                        if(inSC2.contains(w)) {
478                                                tree.extendSimulationSC12(this, w);             
479                                        }
480                                        update.add(w.getParent());
481                                }
482                                // SC1(w,v) can only change from true to false
483                                if(outSC1.contains(w) && !tree.checkSC1(w, this)) {
484                                        tree.shrinkSimulationSC1(w, this);
485                                        if(outSC2.contains(w)) {
486                                                tree.shrinkSimulationSC12(w, this);             
487                                        }
488                                        update.add(w.getParent());
489                                }
490                        }
491                }
492                if(parent != null) {
493                        update.add(parent);
494                }
495                
496                /*
497                // loop over all nodes on the same level, which are not in the in set
498                Set<ELDescriptionNode> tmp = new HashSet<ELDescriptionNode>(tree.getNodesOnLevel(level));
499                tmp.removeAll(in);
500                for(ELDescriptionNode w : tmp) {
501                        if(w != this) {
502                                // we only need to recompute SC1
503                                if(inSC1.contains(w) && tree.checkSC2(this, w)) {
504                                        System.out.println("satisfied");
505                                        tree.extendSimulation(this, w);
506                                        update.add(w.parent);
507                                }
508                        }
509                }
510                
511                // loop over all nodes in out set (we make a copy, because out
512                // is potentially modified, so we cannot safely iterate over it)
513                tmp = new HashSet<ELDescriptionNode>(out);
514                for(ELDescriptionNode w : tmp) {
515                        if(w != this) {
516                                if(!tree.checkSC1(w, this)) {
517//                                      tree.shrinkSimulation(w, this);
518                                        tree.shrinkSimulationSC1(w, this);
519                                        tree.shrinkSimulationSC12(w, this);
520                                        update.add(w.parent);
521                                }
522                        }
523                }
524                */
525                
526                // apply updates recursively top-down
527                tree.updateSimulation(update);  
528//              mon.stop();
529        }
530
531        public void refineEdge(int edgeNumber, OWLProperty op) {
532                edges.get(edgeNumber).setLabel(op);
533                
534//              Monitor mon = MonitorFactory.start("simulation update");
535                // compute the nodes, which need to be updated
536                Set<ELDescriptionNode> update = new HashSet<>();
537                update.add(this);
538                
539                /*
540                // loop over all nodes on the same level, which are not in the in set
541                Set<ELDescriptionNode> tmp = new HashSet<ELDescriptionNode>(tree.getNodesOnLevel(level));
542                tmp.removeAll(in);
543                for(ELDescriptionNode w : tmp) {
544                        if(w != this) {
545                                // we only need to recompute SC1
546                                if(inSC2.contains(w) && tree.checkSC1(this, w)) {
547                                        tree.extendSimulation(this, w);
548                                        update.add(w.parent);
549                                }
550                        }
551                }
552                
553                // loop over all nodes in out set
554                for(ELDescriptionNode w : out) {
555                        if(w != this) {
556                                if(!tree.checkSC2(this, w)) {
557                                        tree.shrinkSimulation(this, w);
558                                        update.add(w.parent);
559                                }
560                        }
561                }
562                */
563                
564//              update.add(this.parent);
565                
566                // apply updates recursively top-down
567                tree.updateSimulation(update);  
568//              mon.stop();
569        }
570        
571        /**
572         * Gets the label of this node. Do not modify the returned object,
573         * but use the provided methods instead!
574         * @return The label of root node of this subtree.
575         */
576        public NavigableSet<OWLClass> getLabel() {
577                return label;
578        }
579
580        /**
581         * Gets the edges of this node. Do not modify the
582         * returned object, but use the provided methods instead!
583         * @return The outgoing edges of this subtree. 
584         */
585        public List<ELDescriptionEdge> getEdges() {
586                return edges;
587        }
588
589        /**
590         * Gets the level (distance from root) of this node. The root node
591         * has level 1.
592         * @return The level of the (root node of) this subtree in the overall tree. 
593         */
594        public int getLevel() {
595                return level;
596        }
597        
598        @Override
599        public String toString() {
600                return toString(0);
601        }
602        
603        private String toString(int indent) {
604                String indentString = "";
605                for(int i=0; i<indent; i++)
606                        indentString += "  ";
607                
608                String str = indentString + label.toString() + "\n";
609                for(ELDescriptionEdge edge : edges) {
610                        str += indentString + "-- " + edge.getLabel() + " -->\n";
611                        str += edge.getNode().toString(indent + 2);
612                }
613                return str;
614        }
615
616        public String toDescriptionString() {
617                String str = "";
618                if(isClassNode()){
619                        if(label.isEmpty()) {
620                                str = "TOP";
621                        } else {
622                                Iterator<OWLClass> it = label.iterator();
623                                while(it.hasNext()) {
624                                        OWLClass nc = it.next();
625                                        if(it.hasNext()) {
626                                                str += nc.toString() + " AND ";
627                                        } else {
628                                                str += nc.toString();
629                                        }
630                                }
631                        }
632                } else {
633                        str += dataRange;
634                }
635                for(ELDescriptionEdge edge : edges) {
636                        str += " AND EXISTS " + edge.getLabel().toString() + ".(";
637                        str += edge.getNode().toDescriptionString() + ")";
638                }
639                return str;             
640        }
641        
642        private String toDescriptionString(Set<ELDescriptionNode> nodes) {
643                String str = "";
644                // comma separated list of descriptions
645                for(ELDescriptionNode node : nodes) {
646                        str += node.toDescriptionString() + ",";
647                }
648                // remove last comma
649                if(str.length() > 0) {
650                        str = str.substring(0, str.length()-1);
651                }
652                return str;
653        }
654        
655        public String toSimulationString() {
656                String str = "";
657                str += "in: " + toDescriptionString(in) + "\n";
658                str += "inSC1: " + toDescriptionString(inSC1) + "\n";
659                str += "inSC2: " + toDescriptionString(inSC2) + "\n";
660                str += "out: " + toDescriptionString(out) + "\n";
661                str += "outSC1: " + toDescriptionString(outSC1) + "\n";
662                str += "outSC2: " + toDescriptionString(outSC2) + "\n";         
663                return str;
664        }
665        
666        /**
667         * A convenience method (for debugging purposes) to get a comma separated list of nodes, where the
668         * nodes are given names (to make them readable).
669         * @param nodes The node objects.
670         * @param nodeNames A mapping to node names.
671         * @return A comma separated list of the node names.
672         */
673        public static String toString(Set<ELDescriptionNode> nodes, Map<ELDescriptionNode,String> nodeNames) {
674                String str = "";
675                // comma separated list of descriptions
676                for(ELDescriptionNode node : nodes) {
677                        str += nodeNames.get(node) + ",";
678                }
679                // remove last comma
680                if(str.length() > 0) {
681                        str = str.substring(0, str.length()-1);
682                }
683                return str;
684        }
685        
686        public String toSimulationString(Map<ELDescriptionNode,String> nodeNames) {
687                String str = "";
688                str += "  in: " + toString(in, nodeNames) + "\n";
689                str += "  inSC1: " + toString(inSC1, nodeNames) + "\n";
690                str += "  inSC2: " + toString(inSC2, nodeNames) + "\n";
691                str += "  out: " + toString(out, nodeNames) + "\n";
692                str += "  outSC1: " + toString(outSC1, nodeNames) + "\n";
693                str += "  outSC2: " + toString(outSC2, nodeNames) + "\n";               
694                return str;
695        }
696        
697        public ELDescriptionNode getParent() {
698                return parent;
699        }
700        
701        public ELDescriptionEdge getParentEdge() {
702                int childNr = getChildNumber();
703                return parent.edges.get(childNr);
704        }
705
706        /**
707         * @return the in
708         */
709        public Set<ELDescriptionNode> getIn() {
710                return in;
711        }
712
713        /**
714         * @return the inSC1
715         */
716        public Set<ELDescriptionNode> getInSC1() {
717                return inSC1;
718        }
719
720        /**
721         * @return the inSC2
722         */
723        public Set<ELDescriptionNode> getInSC2() {
724                return inSC2;
725        }
726
727        /**
728         * @return the out
729         */
730        public Set<ELDescriptionNode> getOut() {
731                return out;
732        }
733
734        /**
735         * @return the outSC1
736         */
737        public Set<ELDescriptionNode> getOutSC1() {
738                return outSC1;
739        }
740
741        /**
742         * @return the outSC2
743         */
744        public Set<ELDescriptionNode> getOutSC2() {
745                return outSC2;
746        }
747
748        public ELDescriptionTree getTree() {
749                return tree;
750        }
751}