2 * This file is part of the Vamsas Client version 0.2.
\r
3 * Copyright 2010 by Jim Procter, Iain Milne, Pierre Marguerite,
\r
4 * Andrew Waterhouse and Dominik Lindner.
\r
6 * Earlier versions have also been incorporated into Jalview version 2.4
\r
7 * since 2008, and TOPALi version 2 since 2007.
\r
9 * The Vamsas Client is free software: you can redistribute it and/or modify
\r
10 * it under the terms of the GNU Lesser General Public License as published by
\r
11 * the Free Software Foundation, either version 3 of the License, or
\r
12 * (at your option) any later version.
\r
14 * The Vamsas Client is distributed in the hope that it will be useful,
\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
17 * GNU Lesser General Public License for more details.
\r
19 * You should have received a copy of the GNU Lesser General Public License
\r
20 * along with the Vamsas Client. If not, see <http://www.gnu.org/licenses/>.
\r
24 // http://evolution.genetics.washington.edu/phylip/newick_doc.html
\r
25 // TODO: Implement Basic NHX tag parsing and preservation
\r
26 // TODO: http://evolution.genetics.wustl.edu/eddy/forester/NHX.html
\r
27 // TODO: Extended SequenceNodeI to hold parsed NHX strings
\r
28 package uk.ac.vamsas.objects.utils.trees;
\r
31 import java.util.Enumeration;
\r
32 import java.util.Hashtable;
\r
33 import java.util.Vector;
\r
34 import java.util.regex.Matcher;
\r
35 import java.util.regex.Pattern;
\r
37 import uk.ac.vamsas.client.Vobject;
\r
38 import uk.ac.vamsas.objects.core.Treenode;
\r
39 import uk.ac.vamsas.objects.core.Vref;
\r
45 * @version $Revision: 1.12 $
\r
47 public class NewickFile {
\r
50 private boolean HasBootstrap = false;
\r
52 private boolean HasDistances = false;
\r
54 private boolean RootHasDistance = false;
\r
57 boolean ReplaceUnderscores = true;
\r
59 boolean printRootInfo = true; // in case root has anything to preserve
\r
61 private Pattern[] NodeSafeName = new Pattern[] {
\r
62 Pattern.compile("[\\[,:'()]"), // test for requiring quotes
\r
63 Pattern.compile("'"), // escaping quote characters
\r
64 Pattern.compile("\\s") // unqoted whitespace transformation
\r
67 char QuoteChar = '\'';
\r
69 String newickFile = null;
\r
72 * Creates a new NewickFile object
\r
75 * Newick style tree string
\r
77 * @throws IOException
\r
78 * if string is not a valid newick file
\r
80 public NewickFile(String inStr) throws IOException {
\r
85 public NewickFile(File inFile) throws IOException {
\r
86 errormessage = "Problem's reading file " + inFile;
\r
87 dataIn = new java.io.BufferedReader(new InputStreamReader(
\r
88 new java.io.FileInputStream(inFile)));
\r
93 * Creates a new NewickFile object.
\r
98 public NewickFile(SequenceNode newtree) {
\r
103 * Creates a new NewickFile object.
\r
110 public NewickFile(SequenceNode newtree, boolean bootstrap) {
\r
111 HasBootstrap = bootstrap;
\r
116 * Creates a new NewickFile object.
\r
125 public NewickFile(SequenceNode newtree, boolean bootstrap, boolean distances) {
\r
127 HasBootstrap = bootstrap;
\r
128 HasDistances = distances;
\r
132 * Creates a new NewickFile object.
\r
140 * @param rootdistance
\r
143 public NewickFile(SequenceNode newtree, boolean bootstrap, boolean distances,
\r
144 boolean rootdistance) {
\r
146 HasBootstrap = bootstrap;
\r
147 HasDistances = distances;
\r
148 RootHasDistance = rootdistance;
\r
157 * Message qualifier (ie the incorrect data)
\r
159 * range of characters either side to dump
\r
161 * character position
\r
163 * newick string being parsed
\r
165 * @return composed error message
\r
167 private String ErrorStringrange(String Error, String Er, int r, int p,
\r
169 return ((Error == null) ? "" : Error)
\r
174 + s.substring(((p - r) < 0) ? 0 : (p - r), ((p + r) > s.length()) ? s
\r
175 .length() : (p + r)) + " )\n";
\r
180 * @return true if tree has bootstrap values
\r
182 public boolean HasBootstrap() {
\r
183 return HasBootstrap;
\r
188 * @return true if tree has distances on branches
\r
190 public boolean HasDistances() {
\r
191 return HasDistances;
\r
196 * @return true if root has a stem distance
\r
198 public boolean HasRootDistance() {
\r
199 return RootHasDistance;
\r
203 * hacked out of jalview code
\r
207 String errormessage;
\r
209 java.io.BufferedReader dataIn = null;
\r
211 public String nextLine() throws IOException {
\r
212 if (dataIn == null && newickFile == null)
\r
213 throw new IOException(
\r
214 "IMPLEMENTATION ERROR: NewickFile has not been initialised for reading a newick string.");
\r
215 if (dataIn == null) {
\r
216 dataIn = new BufferedReader(new StringReader(newickFile));
\r
220 return dataIn.readLine();
\r
221 throw new IOException("Invalid Source Stream:" + errormessage);
\r
225 * call this to convert the newick string into a binary node linked tree Note:
\r
226 * this is automatically called by the constructors, so you normally wouldn't
\r
227 * need to use this.
\r
229 * @throws IOException
\r
230 * if the newick string cannot be parsed.
\r
232 public void parse() throws IOException {
\r
234 if (newickFile == null) {
\r
235 // fill nf with complete tree file
\r
237 StringBuffer file = new StringBuffer();
\r
239 while ((nf = nextLine()) != null) {
\r
243 nf = file.toString();
\r
248 root = new SequenceNode();
\r
250 SequenceNode realroot = null;
\r
251 SequenceNode c = root;
\r
255 // int flen = nf.length();
\r
257 String Error = null;
\r
258 String nodename = null;
\r
260 float DefDistance = (float) 0.001; // @param Default distance for a node -
\r
262 int DefBootstrap = -1; // @param Default bootstrap for a node
\r
264 float distance = DefDistance;
\r
265 int bootstrap = DefBootstrap;
\r
267 boolean ascending = false; // flag indicating that we are leaving the
\r
270 Pattern majorsyms = Pattern.compile("[(\\['),;]");
\r
272 Matcher mjsyms = majorsyms.matcher(nf);
\r
276 while (mjsyms.find(cp) && (Error == null)) {
\r
277 int fcp = mjsyms.start();
\r
279 switch (schar = nf.charAt(fcp)) {
\r
282 // ascending should not be set
\r
283 // New Internal node
\r
285 Error = ErrorStringrange(Error, "Unexpected '('", 7, fcp, nf);
\r
293 if (c.right() == null) {
\r
294 c.setRight(new SequenceNode(null, c, null, DefDistance, DefBootstrap,
\r
296 c = (SequenceNode) c.right();
\r
298 if (c.left() != null) {
\r
299 // Dummy node for polytomy - keeps c.left free for new node
\r
300 SequenceNode tmpn = new SequenceNode(null, c, null, 0, 0, true);
\r
301 tmpn.SetChildren(c.left(), c.right());
\r
305 c.setLeft(new SequenceNode(null, c, null, DefDistance, DefBootstrap,
\r
307 c = (SequenceNode) c.left();
\r
310 if (realroot == null) {
\r
315 distance = DefDistance;
\r
316 bootstrap = DefBootstrap;
\r
321 // Deal with quoted fields
\r
324 Matcher qnodename = Pattern.compile("([^']|'')+'").matcher(nf);
\r
325 if (qnodename.find(fcp)) {
\r
326 nodename = new String(qnodename.group(1));
\r
327 cp = qnodename.end(0);
\r
329 Error = ErrorStringrange(Error, "Unterminated quotes for nodename",
\r
336 // Reached termininating root node label.
\r
337 if (schar == ';' && d != -1) {
\r
338 Error = ErrorStringrange(Error,
\r
339 "Wayward semicolon (depth=" + d + ")", 7, fcp, nf);
\r
342 // Skip Comment or structured/extended NH format info
\r
343 if (schar == '[') {
\r
344 if ((nextcp = nf.indexOf(']', fcp)) > -1) {
\r
345 // verified that comment is properly terminated.
\r
346 // now skip the comment field
\r
348 break; // go and search for the next node separator, leaving ncp at
\r
349 // beginning of node info
\r
351 Error = ErrorStringrange(Error, "Unterminated comment", 3, fcp, nf);
\r
357 // Parse simpler field strings from substring between ncp and node
\r
359 String fstring = nf.substring(ncp, fcp);
\r
360 // extract any comments from the nodeinfo.
\r
361 while (fstring.indexOf(']') > -1) {
\r
362 int cstart = fstring.indexOf('[');
\r
363 int cend = fstring.indexOf(']');
\r
364 String comment = fstring.substring(cstart + 1, cend); // TODO: put
\r
367 fstring = fstring.substring(0, cstart) + fstring.substring(cend + 1);
\r
369 Matcher uqnodename = Pattern.compile("^([^' :;\\](),]+).*").matcher(
\r
371 if (uqnodename.matches()
\r
372 && ((uqnodename.start(1) == 0) || (fstring.charAt(uqnodename
\r
373 .start(1) - 1) != ':'))) // JBPNote HACK!
\r
375 if (nodename == null) {
\r
376 if (ReplaceUnderscores) {
\r
377 nodename = uqnodename.group(1).replace('_', ' ');
\r
379 nodename = uqnodename.group(1);
\r
382 Error = ErrorStringrange(Error,
\r
383 "File has broken algorithm - overwritten nodename", 10, fcp, nf);
\r
387 Matcher nbootstrap = Pattern.compile("\\s*([+0-9]+)\\s*:.*").matcher(
\r
390 if (nbootstrap.matches()) {
\r
391 if (nodename != null && nbootstrap.group(1).equals(nodename)) {
\r
392 nodename = null; // empty nodename - only bootstrap value
\r
394 if ((nodename == null || nodename.length() == 0)
\r
395 || nbootstrap.start(1) >= uqnodename.end(1)) {
\r
397 bootstrap = (new Integer(nbootstrap.group(1))).intValue();
\r
398 HasBootstrap = true;
\r
399 } catch (Exception e) {
\r
400 Error = ErrorStringrange(Error, "Can't parse bootstrap value", 4,
\r
401 ncp + nbootstrap.start(0), nf);
\r
406 Matcher ndist = Pattern.compile(".*:([-0-9Ee.+]+)").matcher(fstring);
\r
407 boolean nodehasdistance = false;
\r
409 if (ndist.matches()) {
\r
411 distance = (new Float(ndist.group(1))).floatValue();
\r
412 HasDistances = true;
\r
413 nodehasdistance = true;
\r
414 } catch (Exception e) {
\r
415 Error = ErrorStringrange(Error, "Can't parse node distance value",
\r
416 7, ncp + ndist.start(0), nf);
\r
421 // Write node info here
\r
422 c.setName(nodename);
\r
423 // Trees without distances still need a render distance
\r
424 c.dist = (HasDistances) ? distance : DefDistance;
\r
425 // be consistent for internal bootstrap defaults too
\r
426 c.setBootstrap((HasBootstrap) ? bootstrap : DefBootstrap);
\r
427 if (c == realroot) {
\r
428 RootHasDistance = nodehasdistance; // JBPNote This is really
\r
429 // UGLY!!! Ensure root node gets
\r
430 // its given distance
\r
433 // Find a place to put the leaf
\r
434 SequenceNode newnode = new SequenceNode(null, c, nodename,
\r
435 (HasDistances) ? distance : DefDistance,
\r
436 (HasBootstrap) ? bootstrap : DefBootstrap, false);
\r
438 if (c.right() == null) {
\r
439 c.setRight(newnode);
\r
441 if (c.left() == null) {
\r
442 c.setLeft(newnode);
\r
444 // Insert a dummy node for polytomy
\r
445 // dummy nodes have distances
\r
446 SequenceNode newdummy = new SequenceNode(null, c, null,
\r
447 (HasDistances ? 0 : DefDistance), 0, true);
\r
448 newdummy.SetChildren(c.left(), newnode);
\r
449 c.setLeft(newdummy);
\r
455 // move back up the tree from preceding closure
\r
456 c = c.AscendTree();
\r
458 if ((d > -1) && (c == null)) {
\r
459 Error = ErrorStringrange(
\r
461 "File broke algorithm: Lost place in tree (is there an extra ')' ?)",
\r
466 if (nf.charAt(fcp) == ')') {
\r
470 if (nf.charAt(fcp) == ',') {
\r
474 // Just advance focus, if we need to
\r
475 if ((c.left() != null) && (!c.left().isLeaf())) {
\r
476 c = (SequenceNode) c.left();
\r
482 // Reset new node properties to obvious fakes
\r
484 distance = DefDistance;
\r
485 bootstrap = DefBootstrap;
\r
487 // Advance character pointers if necessary
\r
489 ncp = cp = fcp + 1;
\r
496 if (Error != null) {
\r
497 throw (new IOException("NewickFile: " + Error + "\n"));
\r
500 root = (SequenceNode) root.right().detach(); // remove the imaginary root.
\r
502 if (!RootHasDistance) {
\r
503 root.dist = (HasDistances) ? 0 : DefDistance;
\r
510 * @return DOCUMENT ME!
\r
512 public SequenceNode getTree() {
\r
516 public uk.ac.vamsas.objects.core.Treenode[] matchTreeNodeNames(
\r
517 String[] names, Vobject[] boundObjects) {
\r
519 // also - need to reconstruct a names object id mapping (or BInaryNode)
\r
520 // mapping for the parsed tree file
\r
525 * Generate a newick format tree according to internal flags for bootstraps,
\r
526 * distances and root distances.
\r
528 * @return new hampshire tree in a single line
\r
530 public String print() {
\r
531 synchronized (this) {
\r
532 StringBuffer tf = new StringBuffer();
\r
535 return (tf.append(";").toString());
\r
542 * Generate a newick format tree according to internal flags for distances and
\r
543 * root distances and user specificied writing of bootstraps.
\r
545 * @param withbootstraps
\r
546 * controls if bootstrap values are explicitly written.
\r
548 * @return new hampshire tree in a single line
\r
550 public String print(boolean withbootstraps) {
\r
551 synchronized (this) {
\r
552 boolean boots = this.HasBootstrap;
\r
553 this.HasBootstrap = withbootstraps;
\r
555 String rv = print();
\r
556 this.HasBootstrap = boots;
\r
564 * Generate newick format tree according to internal flags for writing root
\r
567 * @param withbootstraps
\r
568 * explicitly write bootstrap values
\r
570 * explicitly write distances
\r
572 * @return new hampshire tree in a single line
\r
574 public String print(boolean withbootstraps, boolean withdists) {
\r
575 synchronized (this) {
\r
576 boolean dists = this.HasDistances;
\r
577 this.HasDistances = withdists;
\r
579 String rv = print(withbootstraps);
\r
580 this.HasDistances = dists;
\r
587 * Generate newick format tree according to user specified flags
\r
589 * @param withbootstraps
\r
590 * explicitly write bootstrap values
\r
592 * explicitly write distances
\r
593 * @param printRootInfo
\r
594 * explicitly write root distance
\r
596 * @return new hampshire tree in a single line
\r
598 public String print(boolean withbootstraps, boolean withdists,
\r
599 boolean printRootInfo) {
\r
600 synchronized (this) {
\r
601 boolean rootinfo = printRootInfo;
\r
602 this.printRootInfo = printRootInfo;
\r
604 String rv = print(withbootstraps, withdists);
\r
605 this.printRootInfo = rootinfo;
\r
614 * @return DOCUMENT ME!
\r
616 char getQuoteChar() {
\r
626 * @return DOCUMENT ME!
\r
628 char setQuoteChar(char c) {
\r
629 char old = QuoteChar;
\r
641 * @return DOCUMENT ME!
\r
643 private String nodeName(String name) {
\r
644 if (NodeSafeName[0].matcher(name).find()) {
\r
645 return QuoteChar + NodeSafeName[1].matcher(name).replaceAll("''")
\r
646 + QuoteChar; // quite
\r
648 return NodeSafeName[2].matcher(name).replaceAll("_"); // whitespace
\r
658 * @return DOCUMENT ME!
\r
660 private String printNodeField(SequenceNode c) {
\r
661 return // c.getNewickNodeName()
\r
662 ((c.getName() == null) ? "" : nodeName(c.getName()))
\r
663 + ((HasBootstrap) ? ((c.getBootstrap() > -1) ? (((c.getName() == null) ? " "
\r
664 : "") + c.getBootstrap())
\r
666 : "") + ((HasDistances) ? (":" + c.dist) : "");
\r
675 * @return DOCUMENT ME!
\r
677 private String printRootField(SequenceNode root) {
\r
678 return (printRootInfo) ? (((root.getName() == null) ? "" : nodeName(root
\r
680 + ((HasBootstrap) ? ((root.getBootstrap() > -1) ? ((root.getName() != null ? " "
\r
681 : "") + root.getBootstrap())
\r
683 : "") + ((RootHasDistance) ? (":" + root.dist) : ""))
\r
687 // Non recursive call deals with root node properties
\r
688 public void print(StringBuffer tf, SequenceNode root) {
\r
689 if (root != null) {
\r
690 if (root.isLeaf() && printRootInfo) {
\r
691 tf.append(printRootField(root));
\r
693 if (root.isDummy()) {
\r
694 _print(tf, (SequenceNode) root.right());
\r
695 _print(tf, (SequenceNode) root.left());
\r
698 _print(tf, (SequenceNode) root.right());
\r
700 if (root.left() != null) {
\r
704 _print(tf, (SequenceNode) root.left());
\r
705 tf.append(")" + printRootField(root));
\r
711 // Recursive call for non-root nodes
\r
712 public void _print(StringBuffer tf, SequenceNode c) {
\r
715 tf.append(printNodeField(c));
\r
718 _print(tf, (SequenceNode) c.left());
\r
719 if (c.left() != null) {
\r
722 _print(tf, (SequenceNode) c.right());
\r
725 _print(tf, (SequenceNode) c.right());
\r
727 if (c.left() != null) {
\r
731 _print(tf, (SequenceNode) c.left());
\r
732 tf.append(")" + printNodeField(c));
\r
739 public static void main(String[] args) {
\r
741 if (args == null || args.length != 1) {
\r
743 .println("Takes one argument - file name of a newick tree file.");
\r
747 File fn = new File(args[0]);
\r
749 StringBuffer newickfile = new StringBuffer();
\r
750 BufferedReader treefile = new BufferedReader(new FileReader(fn));
\r
753 while ((l = treefile.readLine()) != null) {
\r
754 newickfile.append(l);
\r
758 System.out.println("Read file :\n");
\r
759 NewickFile trf = new NewickFile(fn);
\r
761 System.out.println("Original file :\n");
\r
763 System.out.println(Pattern.compile("\n+").matcher(newickfile.toString())
\r
767 System.out.println("Parsed file.\n");
\r
768 System.out.println("Default output type for original input.\n");
\r
769 System.out.println(trf.print());
\r
770 System.out.println("Without bootstraps.\n");
\r
771 System.out.println(trf.print(false));
\r
772 System.out.println("Without distances.\n");
\r
773 System.out.println(trf.print(true, false));
\r
774 System.out.println("Without bootstraps but with distanecs.\n");
\r
775 System.out.println(trf.print(false, true));
\r
776 System.out.println("Without bootstraps or distanecs.\n");
\r
777 System.out.println(trf.print(false, false));
\r
778 System.out.println("With bootstraps and with distances.\n");
\r
779 System.out.println(trf.print(true, true));
\r
780 System.out.println("leaves.\n");
\r
781 Vector lvs = new Vector();
\r
782 trf.findLeaves(trf.root, lvs);
\r
783 Enumeration lv = lvs.elements();
\r
784 while (lv.hasMoreElements()) {
\r
785 BinaryNode leave = (BinaryNode) lv.nextElement();
\r
786 if (leave.getName() != null) {
\r
787 System.out.println("Node:'" + leave.getName() + "'");
\r
790 } catch (java.io.IOException e) {
\r
791 System.err.println("Exception\n" + e);
\r
792 e.printStackTrace();
\r
797 * Search for leaf nodes.
\r
800 * root node to search from
\r
802 * Vector of leaves to add leaf node objects too.
\r
804 * @return Vector of leaf nodes on binary tree
\r
806 public Vector findLeaves(SequenceNode node, Vector leaves) {
\r
807 if (node == null) {
\r
811 if ((node.left() == null) && (node.right() == null)) // Interior node
\r
814 leaves.addElement(node);
\r
819 * TODO: Identify internal nodes... if (node.isSequenceLabel()) {
\r
820 * leaves.addElement(node); }
\r
822 findLeaves((SequenceNode) node.left(), leaves);
\r
823 findLeaves((SequenceNode) node.right(), leaves);
\r
830 * make tree node vector from a newick tree structure with associated vamsas
\r
834 * @return Treenode definitions for nodes with associated objects
\r
836 public Treenode[] makeTreeNodes() {
\r
837 return makeTreeNodes(true);
\r
841 * make treenode vector for a parsed tree with/out leaf node associations
\r
843 * @param ignoreplaceholders
\r
844 * if true means only associated nodes are returned
\r
845 * @return treenode vector for associated or all leaves
\r
847 public Treenode[] makeTreeNodes(boolean ignoreplaceholders) {
\r
848 Vector leaves = new Vector();
\r
849 findLeaves(root, leaves);
\r
850 Vector tnv = new Vector();
\r
851 Enumeration l = leaves.elements();
\r
852 Hashtable nodespecs = new Hashtable();
\r
853 while (l.hasMoreElements()) {
\r
854 BinaryNode tnode = (BinaryNode) l.nextElement();
\r
855 if (tnode instanceof SequenceNode) {
\r
856 if (!(ignoreplaceholders && ((SequenceNode) tnode).isPlaceholder())) {
\r
857 Object assocseq = ((SequenceNode) tnode).element();
\r
858 if (assocseq instanceof Vobject) {
\r
859 Vobject vobj = (Vobject) assocseq;
\r
860 if (vobj != null) {
\r
861 Treenode node = new Treenode();
\r
862 node.setNodespec(makeNodeSpec(nodespecs, tnode));
\r
863 node.setName(tnode.getName());
\r
864 Vref vr = new Vref();
\r
867 tnv.addElement(node);
\r
869 System.err.println("WARNING: Unassociated treeNode "
\r
870 + tnode.element().toString()
\r
872 + ((tnode.getName() != null) ? " label " + tnode.getName()
\r
879 if (tnv.size() > 0) {
\r
880 Treenode[] tn = new Treenode[tnv.size()];
\r
884 return new Treenode[] {};
\r
887 private String makeNodeSpec(Hashtable nodespecs, BinaryNode tnode) {
\r
888 String nname = new String(tnode.getName());
\r
889 Integer nindx = (Integer) nodespecs.get(nname);
\r
890 if (nindx == null) {
\r
891 nindx = new Integer(1);
\r
893 nname = nindx.toString() + " " + nname;
\r
898 * call to match up Treenode specs to NJTree parsed from document object.
\r
902 * as returned from NJTree.findLeaves( .., ..) ..
\r
905 private BinaryNode findNodeSpec(String nodespec, Vector leaves) {
\r
906 int occurence = -1;
\r
907 String nspec = nodespec.substring(nodespec.indexOf(' ') + 1);
\r
908 String oval = nodespec.substring(0, nodespec.indexOf(' '));
\r
910 occurence = new Integer(oval).intValue();
\r
911 } catch (Exception e) {
\r
912 System.err.println("Invalid nodespec '" + nodespec + "'");
\r
915 BinaryNode bn = null;
\r
918 Enumeration en = leaves.elements();
\r
919 while (en.hasMoreElements() && nocc < occurence) {
\r
920 bn = (BinaryNode) en.nextElement();
\r
921 if (bn instanceof SequenceNode && bn.getName().equals(nspec)) {
\r
931 * re-decorate the newick node representation with the VorbaId of an object
\r
932 * mapped by its corresponding TreeNode. Note: this only takes the first
\r
933 * object in the first set of references.
\r
936 * @return vector of mappings { treenode, SequenceNode, Vobject for VorbaId on
\r
939 public Vector attachTreeMap(Treenode[] tn) {
\r
940 if (root != null || tn == null)
\r
942 Vector leaves = new Vector();
\r
943 Vector nodemap = new Vector();
\r
944 findLeaves(root, leaves);
\r
945 int sz = tn.length;
\r
949 Treenode node = tn[i++];
\r
950 BinaryNode mappednode = findNodeSpec(node.getNodespec(), leaves);
\r
951 if (mappednode != null && mappednode instanceof SequenceNode) {
\r
952 SequenceNode leaf = (SequenceNode) leaves.elementAt(i++);
\r
953 // check if we can make the specified association
\r
954 Vobject noderef = null;
\r
955 int vrf = 0, refv = 0;
\r
956 while (noderef == null && vrf < node.getVrefCount()) {
\r
957 if (refv < node.getVref(vrf).getRefsCount()) {
\r
958 Object ref = node.getVref(vrf).getRefs(refv++);
\r
959 if (ref instanceof Vobject) {
\r
960 noderef = (Vobject) ref;
\r
967 if (noderef != null) {
\r
968 nodemap.addElement(new Object[] { node, leaf, noderef });
\r
969 leaf.setElement(noderef);
\r
970 leaf.setPlaceholder(false);
\r
972 leaf.setPlaceholder(true);
\r