View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  
13  package com.eviware.soapui.support.xml;
14  
15  import com.eviware.soapui.impl.wsdl.support.xsd.SchemaUtils;
16  import com.eviware.soapui.support.types.StringToStringMap;
17  import org.apache.log4j.Logger;
18  import org.apache.xmlbeans.*;
19  import org.apache.xmlbeans.XmlCursor.TokenType;
20  import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl;
21  import org.jdesktop.swingx.treetable.TreeTableModel;
22  import org.w3c.dom.Element;
23  import org.w3c.dom.Node;
24  
25  import javax.swing.event.TreeModelEvent;
26  import javax.swing.event.TreeModelListener;
27  import javax.swing.tree.TreePath;
28  import javax.xml.namespace.QName;
29  import java.util.*;
30  
31  public class XmlObjectTreeModel implements TreeTableModel
32  {
33  	private XmlObject xmlObject;
34  	private Set<TreeModelListener> listeners = new HashSet<TreeModelListener>();
35  	private XmlCursor cursor;
36  	private Map<XmlObject, XmlTreeNode> treeNodeMap = new HashMap<XmlObject,XmlTreeNode>();
37  	
38  	public final static Class<?> hierarchicalColumnClass = TreeTableModel.class;
39  	private SchemaTypeSystem typeSystem;
40  	private RootXmlTreeNode root;
41  	@SuppressWarnings("unused")
42  	private final static Logger log = Logger.getLogger(XmlObjectTreeModel.class);
43  	
44  	public XmlObjectTreeModel(XmlObject xmlObject)
45  	{
46  		this( XmlBeans.getBuiltinTypeSystem(), xmlObject  );
47  	}
48  
49  	public XmlObjectTreeModel()
50  	{
51  		this( XmlObject.Factory.newInstance() );
52  	}
53  
54  	public XmlObjectTreeModel(SchemaTypeSystem typeSystem, XmlObject xmlObject)
55  	{
56  		if( typeSystem == null )
57  			typeSystem = XmlBeans.getBuiltinTypeSystem();
58  		
59  		this.typeSystem = typeSystem;
60  		this.xmlObject = xmlObject;
61  		init();
62  	}
63  
64  	public XmlObjectTreeModel(SchemaTypeSystem typeSystem)
65  	{
66  		this( typeSystem, XmlObject.Factory.newInstance()  );
67  	}
68  
69  	private void init()
70  	{
71  		cursor = null;
72  		
73  		if( xmlObject != null )
74  		{
75  			cursor = xmlObject.newCursor();
76  			cursor.toStartDoc();
77  		}
78  		
79  		root = new RootXmlTreeNode( cursor );
80  	}
81  
82  	public SchemaTypeSystem getTypeSystem()
83  	{
84  		return typeSystem;
85  	}
86  
87  	public void setTypeSystem(SchemaTypeSystem typeSystem)
88  	{
89  		if( typeSystem == null )
90  			typeSystem = XmlBeans.getBuiltinTypeSystem();
91  		
92  		this.typeSystem = typeSystem;
93  	}
94  
95  	public XmlObject getXmlObject()
96  	{
97  		return xmlObject;
98  	}
99  
100 	public void setXmlObject(XmlObject xmlObject)
101 	{
102 		if( cursor != null )
103 			cursor.dispose();
104 		
105 		this.xmlObject = xmlObject;
106 		init();
107 		
108 		XmlTreeNode xmlTreeNode = ((XmlTreeNode)getRoot());
109 		fireTreeStructureChanged(xmlTreeNode);
110 	}
111 
112 	protected void fireTreeStructureChanged(XmlTreeNode rootNode)
113 	{
114 		for( TreeModelListener listener : listeners )
115 		{
116 			listener.treeStructureChanged( new XmlTreeTableModelEvent( this, rootNode.getTreePath(), -1 ));
117 		}
118 	}
119 
120 	public Class<?> getColumnClass(int arg0)
121 	{
122 		return arg0 == 0 ? hierarchicalColumnClass : XmlTreeNode.class;
123 	}
124 
125 	public int getColumnCount()
126 	{
127 		return 3;
128 	}
129 
130 	public String getColumnName(int arg0)
131 	{
132 		return null;
133 	}
134 
135 	public Object getValueAt(Object arg0, int arg1)
136 	{
137 		return arg0; //((XmlTreeNode)arg0).getValue( arg1 );
138 	}
139 
140 	public boolean isCellEditable(Object arg0, int arg1)
141 	{
142 		return ((XmlTreeNode)arg0).isEditable( arg1 );
143 	}
144 
145 	public void setValueAt(Object arg0, Object arg1, int arg2)
146 	{
147 		XmlTreeNode treeNode = (XmlTreeNode) arg1;
148 		if( treeNode.setValue( arg2, arg0 ) )
149 		{
150 			fireTreeNodeChanged( treeNode, arg2 );
151 		}			
152 	}
153 
154 	protected void fireTreeNodeChanged(XmlTreeNode treeNode, int column)
155 	{
156 		for( TreeModelListener listener : listeners )
157 		{
158 			listener.treeNodesChanged( new XmlTreeTableModelEvent( this, treeNode.getTreePath(), column ));
159 		}
160 	}
161 
162 	public void addTreeModelListener(TreeModelListener l)
163 	{
164 		listeners.add( l );
165 	}
166 
167 	public Object getChild(Object parent, int index)
168 	{
169 		return ((XmlTreeNode)parent).getChild( index );
170 	}
171 
172 	public int getChildCount(Object parent)
173 	{
174 		return ((XmlTreeNode)parent).getChildCount();
175 	}
176 
177 	public int getIndexOfChild(Object parent, Object child)
178 	{
179 		return ((XmlTreeNode)parent).getIndexOfChild( (XmlTreeNode) child );
180 	}
181 
182 	public Object getRoot()
183 	{
184 		return getRootNode();
185 	}
186 	
187 	public RootXmlTreeNode getRootNode()
188 	{
189 		return root;
190 	}
191 
192 	public boolean isLeaf(Object node)
193 	{
194 		return ((XmlTreeNode)node).isLeaf();
195 	}
196 
197 	public void removeTreeModelListener(TreeModelListener l)
198 	{
199 		listeners.remove( l );
200 	}
201 
202 	public void valueForPathChanged(TreePath path, Object newValue)
203 	{
204 	}
205 	
206 	private class TreeBookmark extends XmlCursor.XmlBookmark
207 	{}
208 	
209 	public interface XmlTreeNode
210 	{
211 		public int getChildCount();
212 		
213 		public XmlTreeNode getChild( int ix );
214 		
215 		public int getIndexOfChild( XmlTreeNode childNode );
216 		
217 		public String getNodeName();
218 		
219 		public String getNodeText();
220 		
221 		public boolean isEditable( int column );
222 		
223 		public boolean isLeaf();
224 		
225 		public boolean setValue( int column, Object value );
226 		
227 		public XmlLineNumber getNodeLineNumber();
228 
229 		public XmlLineNumber getValueLineNumber();
230 
231 		public XmlObject getXmlObject();
232 		
233 		public Node getDomNode();
234 
235 		public TreePath getTreePath();
236 
237 		public XmlTreeNode getParent();
238 
239 		public SchemaType getSchemaType();
240 		
241 		public String getDocumentation();
242 	}
243 
244 	private abstract class AbstractXmlTreeNode implements XmlTreeNode
245 	{
246 		protected Node node;
247 		protected TreeBookmark bm;
248 		private final XmlTreeNode parent;
249 		private XmlLineNumber lineNumber;
250 		protected SchemaType schemaType;
251 		protected String documentation;
252 		
253 		@SuppressWarnings("unchecked")
254 		protected AbstractXmlTreeNode( XmlCursor cursor, XmlTreeNode parent )
255 		{
256 			this.parent = parent;
257 			
258 			if( cursor != null )
259 			{
260 				node = cursor.getDomNode();
261 				
262 				ArrayList list = new ArrayList();
263 				cursor.getAllBookmarkRefs( list );
264 				
265 				for( Object o : list )
266 					if( o instanceof XmlLineNumber )
267 						lineNumber = (XmlLineNumber) o;
268 				
269 				bm = new TreeBookmark();
270 				cursor.setBookmark( bm );
271 				
272 				treeNodeMap.put( cursor.getObject(), this );
273 			}
274 		}
275 
276 		protected SchemaType findSchemaType()
277 		{
278 			if( cursor == null )
279 				return null;
280 			
281 			positionCursor( cursor );
282 			
283 			SchemaType resultType = null;
284 			XmlObject xo = cursor.getObject();
285 			if( xo != null )
286 			{
287 				Node domNode = xo.getDomNode();
288 				
289 				// check for xsi:type
290 				if( domNode.getNodeType() == Node.ELEMENT_NODE )
291 				{
292 					Element elm = (Element) domNode;
293 					String xsiType = elm.getAttributeNS( "http://www.w3.org/2001/XMLSchema-instance", "type" );
294 					if( xsiType != null && xsiType.length() > 0 )
295 					{
296 						resultType = findXsiType(xsiType);
297 					}
298 				}
299 
300 				if( resultType == null )
301 					resultType = typeSystem.findType( xo.schemaType().getName() );
302 				
303 				if( resultType == null )
304 					resultType = xo.schemaType();
305 				
306 				if( resultType.isNoType() )
307 				{
308 					QName nm = cursor.getName();
309 					
310 					if( parent != null && parent.getSchemaType() != null )
311 					{
312 						SchemaType parentSchemaType = parent.getSchemaType();
313 						SchemaParticle contentModel = parentSchemaType.getContentModel();
314 						
315 						if( contentModel != null )
316 						{
317 							SchemaParticle[] children = contentModel.getParticleChildren();
318 							
319 							for( int c = 0; children != null && c < children.length; c++ )
320 							{
321 								if( nm.equals( children[c].getName()))
322 								{
323 									resultType = children[c].getType();
324 									documentation = SchemaUtils.getDocumentation( resultType );
325 									break;
326 								}
327 							}
328 							
329 							if( resultType.isNoType() && nm.equals( contentModel.getName() ))
330 								resultType = contentModel.getType();
331 							
332 							if( resultType.isNoType() )
333 							{
334 								SchemaType[] anonymousTypes = parentSchemaType.getAnonymousTypes();
335 								for( int c = 0; anonymousTypes != null && c < anonymousTypes.length; c++ )
336 								{
337 									QName name = anonymousTypes[c].getName();
338 									if( name != null && name.equals( nm ))
339 									{
340 										resultType = anonymousTypes[c];
341 										break;
342 									}
343 									else if( anonymousTypes[c].getContainerField().getName().equals( nm ))
344 									{
345 										resultType = anonymousTypes[c].getContainerField().getType();
346 										break;
347 									}
348 								}
349 							}
350 						}
351 					}
352 					
353 					if( resultType.isNoType() )
354 					{
355 						SchemaGlobalElement elm = typeSystem.findElement( nm );
356 						if( elm != null )
357 						{
358 							resultType = elm.getType();
359 						}
360 						else if( typeSystem.findDocumentType( nm ) != null )
361 						{
362 							resultType = typeSystem.findDocumentType( nm );
363 						}
364 					}
365 				}
366 			}
367 			
368 			if( resultType == null )
369 				resultType = XmlAnyTypeImpl.type;
370 			
371 			if( documentation == null )
372 				documentation = SchemaUtils.getDocumentation( resultType );
373 			
374 			return resultType;
375 		}
376 		
377 		@SuppressWarnings( "unused" )
378 		protected String getUserInfo( SchemaType schemaType )
379 		{
380 			if( schemaType.getAnnotation() != null )
381 			{
382 				XmlObject[] userInformation = schemaType.getAnnotation().getUserInformation();
383 				if( userInformation != null && userInformation.length > 0 )
384 				{
385 					return userInformation[0].toString(); //XmlUtils.getElementText( ( Element ) userInformation[0].getDomNode());
386 				}
387 			}
388 			
389 			return null;
390 		}
391 		
392 		public String getDocumentation()
393 		{
394 			return documentation;
395 		}
396 
397 		private SchemaType findXsiType(String xsiType)
398 		{
399 			SchemaType resultType;
400 			int ix = xsiType.indexOf( ':' );
401 			QName name = null;
402 			
403 			if( ix == -1 )
404 			{
405 				name = new QName( xsiType );
406 				resultType = typeSystem.findType( name);
407 			}
408 			else
409 			{
410 				StringToStringMap map = new StringToStringMap();
411 				cursor.getAllNamespaces( map );
412 				
413 				name = new QName( map.get( xsiType.substring( 0, ix )), xsiType.substring( ix +1 ) );
414 				resultType = typeSystem.findType( name );
415 			}
416 			
417 			return resultType;
418 		}
419 		
420 		public XmlTreeNode getParent()
421 		{
422 			return parent;
423 		}
424 
425 		protected void positionCursor(XmlCursor cursor)
426 		{
427 			cursor.toBookmark( bm );
428 		}
429 		
430 		public XmlTreeNode getChild(int ix)
431 		{
432 			return null;
433 		}
434 		
435 		public int getChildCount()
436 		{
437 			return 0;
438 		}
439 
440 		public int getIndexOfChild(XmlTreeNode childNode)
441 		{
442 			return -1;
443 		}
444 
445 		@SuppressWarnings( "unused" )
446 		public Object getValue(int column)
447 		{
448 			if( column == 0 )
449 				return getNodeName();
450 			else if( column == 1 )
451 				return getNodeText();
452 			
453 			return null;
454 		}
455 
456 		public Node getDomNode()
457 		{
458 			return node;
459 		}
460 		
461 		public String getNodeName()
462 		{
463 			return node == null ? null : node.getNodeName();
464 		}
465 
466 		public String getNodeText()
467 		{
468 			if( node == null )
469 				return null;
470 			
471 			String nodeValue = node.getNodeValue();
472 			return nodeValue == null ? null : nodeValue.trim();
473 		}		
474 
475 		public boolean isEditable(int column)
476 		{
477 			return false;
478 		}
479 
480 		public boolean isLeaf()
481 		{
482 			return getChildCount() == 0;
483 		}
484 
485 		public boolean setValue(int column, Object value)
486 		{
487 			return false;
488 		}
489 
490 		public String toString()
491 		{
492 			return getNodeName();
493 		}
494 
495 		public boolean equals(Object obj)
496 		{
497 			if( obj == this )
498 				return true;
499 			if( obj instanceof AbstractXmlTreeNode )
500 				return ((AbstractXmlTreeNode)obj).node == this.node;
501 			else
502 				return super.equals(obj);
503 		}
504 		
505 		public XmlLineNumber getNodeLineNumber()
506 		{
507 			return lineNumber;
508 		}
509 		
510 		public XmlLineNumber getValueLineNumber()
511 		{
512 			return lineNumber;
513 		}
514 
515 		public XmlObject getXmlObject()
516 		{
517 			if( cursor != null && cursor.toBookmark( bm ))
518 			{
519 				XmlObject object = cursor.getObject();
520 				
521 				if( object != null )
522 					return object;
523 				else if( parent != null )
524 					return parent.getXmlObject();
525 			}
526 
527 			return null;
528 		}
529 
530 		public TreePath getTreePath()
531 		{
532 			List<XmlTreeNode> nodes = new ArrayList<XmlTreeNode>();
533 			nodes.add( this );
534 			
535 			XmlTreeNode node = this;
536 			
537 			while( node.getParent() != null )
538 			{
539 				nodes.add( 0, node.getParent() );
540 				node = node.getParent();
541 			}
542 			
543 			return new TreePath( nodes.toArray() );
544 		}
545 
546 		public SchemaType getSchemaType()
547 		{
548 			if( schemaType == null )
549 				schemaType = findSchemaType();
550 			
551 			return schemaType; 
552 		}
553 	}
554 	
555 	public class RootXmlTreeNode extends AbstractXmlTreeNode
556 	{
557 		private ElementXmlTreeNode rootNode;
558 
559 		protected RootXmlTreeNode( XmlCursor cursor )
560 		{
561 			super( cursor, null );
562 			
563 			if( cursor != null )
564 			{
565 				cursor.toFirstContentToken();
566 				rootNode = new ElementXmlTreeNode( cursor, this );
567 			}
568 		}
569 
570 		public XmlTreeNode getChild(int ix)
571 		{
572 			return ix == 0 ? rootNode : null;
573 		}
574 
575 		public int getChildCount()
576 		{
577 			return rootNode == null ? 0 : 1;
578 		}
579 
580 		public int getIndexOfChild(XmlTreeNode childNode)
581 		{
582 			return childNode == rootNode ? 0 : -1;
583 		}
584 	}
585 	
586 	public class ElementXmlTreeNode extends AbstractXmlTreeNode
587 	{
588 		private LinkedList<XmlTreeNode> elements = new LinkedList<XmlTreeNode>();
589 		private TextXmlTreeNode textTreeNode;
590 		private int attrCount;
591 		
592 		protected ElementXmlTreeNode( XmlCursor cursor, XmlTreeNode parent )
593 		{
594 			super( cursor, parent );
595 			
596 			TokenType token = cursor.toNextToken();
597 			while( token == TokenType.ATTR || token == TokenType.NAMESPACE  )
598 			{
599 				if( token == TokenType.ATTR )
600 			{
601 				elements.add( new AttributeXmlTreeNode( cursor, this ));
602 				}
603 				
604 				token = cursor.toNextToken();
605 			}
606 			
607 			attrCount = elements.size();
608 			
609 			positionCursor( cursor );
610 			cursor.toFirstContentToken();
611 			
612 			while( true )
613 			{
614 				while( cursor.isComment() || cursor.isProcinst() )
615 					cursor.toNextToken();
616 				
617 				if( cursor.isContainer())
618 				{
619 					elements.add( new ElementXmlTreeNode( cursor, this ));
620 					cursor.toEndToken();
621 					cursor.toNextToken();
622 				}
623 				
624 				if( cursor.isText() )
625 				{
626 					elements.add( new TextXmlTreeNode( cursor, this ));
627 					cursor.toNextToken();
628 				}
629 				
630 				if( cursor.isEnd() || cursor.isEnddoc() )
631 					break;
632 			}
633 			
634 			if( elements.size() == attrCount+1 && (elements.get( attrCount ) instanceof TextXmlTreeNode) )
635 			{
636 				textTreeNode = (TextXmlTreeNode) elements.remove( attrCount );
637 			}
638 			else
639 			{
640 				for( int c = attrCount; c < elements.size(); c++ )
641 				{
642 					if( elements.get( c ) instanceof TextXmlTreeNode )
643 					{
644 						TextXmlTreeNode treeNode = (TextXmlTreeNode) elements.get( c );
645 						String text = treeNode.getNodeText().trim();
646 						if( text.length() == 0 )
647 						{
648 							elements.remove( c );
649 							c--;
650 						}
651 					}
652 				}
653 			}
654 			
655 			positionCursor(cursor);
656 		}
657 
658 		public XmlTreeNode getChild(int ix)
659 		{
660 			return elements.get( ix );
661 		}
662 
663 		public boolean isEditable(int column)
664 		{
665 			return column == 1 && elements.size() == attrCount;
666 		}
667 		
668 		public boolean setValue(int column, Object value)
669 		{
670 			if( column == 1 )
671 			{
672 				if( textTreeNode != null )
673 				{
674 					textTreeNode.setValue( 1, value );
675 				}
676 				else
677 				{
678 					positionCursor( cursor );
679 					cursor.toEndToken();
680 					cursor.insertChars( value.toString() );
681 					positionCursor( cursor );
682 					cursor.toFirstContentToken();
683 					
684 					textTreeNode = new TextXmlTreeNode( cursor, this );
685 				}
686 			}
687 			return column == 1;
688 		}
689 
690 		public int getChildCount()
691 		{
692 			return elements.size();
693 		}
694 
695 		public int getIndexOfChild(XmlTreeNode childNode)
696 		{
697 			return elements.indexOf( childNode );
698 		}
699 
700 		public String getNodeText()
701 		{
702 			return textTreeNode == null ? "" : textTreeNode.getNodeText();
703 		}
704 		
705 		public XmlLineNumber getValueLineNumber()
706 		{
707 			return textTreeNode == null ? super.getValueLineNumber() : textTreeNode.getValueLineNumber();
708 		}
709 	}
710 	
711 	public class AttributeXmlTreeNode extends AbstractXmlTreeNode
712 	{
713 		private boolean checkedType;
714 
715 		protected AttributeXmlTreeNode( XmlCursor cursor, ElementXmlTreeNode parent )
716 		{
717 			super( cursor, parent );
718 		}
719 
720 		public String getNodeName()
721 		{
722 			return "@" + super.getNodeName();
723 		}
724 		
725 		public XmlLineNumber getNodeLineNumber()
726 		{
727 			return getParent().getNodeLineNumber();
728 		}
729 
730 		public boolean isEditable(int column)
731 		{
732 			return column == 1;
733 		}
734 
735 		public boolean setValue(int column, Object value)
736 		{
737 			if( column == 1 )
738 				node.setNodeValue( value.toString() );
739 			
740 			return column == 1;
741 		}
742 
743 		public SchemaType getSchemaType()
744 		{
745 			if( schemaType == null && !checkedType )
746 			{
747 				SchemaType parentSchemaType = getParent().getSchemaType();
748 				if( parentSchemaType != null )
749 				{
750 					positionCursor( cursor );
751 					SchemaProperty attributeProperty = parentSchemaType.getAttributeProperty( cursor.getName() );
752 					if( attributeProperty != null )
753 					{
754 						schemaType = attributeProperty.getType();
755 						documentation = SchemaUtils.getDocumentation( schemaType );
756 						
757 //						SchemaAnnotation annotation = schemaType.getAnnotation();
758 //						if( annotation != null )
759 //						{
760 //							XmlObject[] userInformation = annotation.getUserInformation();
761 //							if( userInformation != null && userInformation.length > 0 )
762 //							{
763 //								//userInformation[0].toString(); //XmlUtils.getElementText( ( Element ) userInformation[0].getDomNode());
764 //							}
765 //						}
766 					}
767 				}
768 				
769 				checkedType = true;
770 			}
771 			
772 			return schemaType;
773 		}
774 	}
775 	
776 	public class TextXmlTreeNode extends AbstractXmlTreeNode
777 	{
778 		protected TextXmlTreeNode( XmlCursor cursor, ElementXmlTreeNode parent )
779 		{
780 			super( cursor, parent );
781 		}
782 		
783 		public boolean isEditable(int column)
784 		{
785 			return column == 1;
786 		}
787 
788 		public boolean setValue(int column, Object value)
789 		{
790 			if( column == 1 )
791 				node.setNodeValue( node == null ? null : value.toString() );
792 			
793 			return column == 1;
794 		}
795 
796 		public TreePath getTreePath()
797 		{
798 			return super.getTreePath().getParentPath();
799 		}
800 	}
801 
802 	public TreePath findXmlTreeNode(int line, int column)
803 	{
804 		line++;
805 		
806 		XmlTreeNode treeNode = findXmlTreeNode( root, line, column );
807 		if( treeNode instanceof AttributeXmlTreeNode )
808 			return treeNode.getParent().getTreePath();
809 		else if( treeNode != null )
810 			return treeNode.getTreePath();
811 		
812 	   return null;
813 	}
814 	
815 	private XmlTreeNode findXmlTreeNode( XmlTreeNode treeNode, int line, int column )
816 	{
817 		for( int c = 0; c < treeNode.getChildCount(); c++ )
818 		{
819 			XmlTreeNode child = treeNode.getChild( c );
820 	   	XmlLineNumber ln = child.getNodeLineNumber();
821 	   	if( ln != null && (line < ln.getLine() || ( line == ln.getLine() && column <= ln.getColumn() )))
822 	   	{
823 	   		if( c == 0 )
824 	   			return treeNode;
825 	   		else
826 	   			return findXmlTreeNode( treeNode.getChild( c-1 ), line, column );
827 	   	}
828 		}
829 		
830 		if( treeNode.getChildCount() > 0 )
831 		{
832 			return findXmlTreeNode( treeNode.getChild( treeNode.getChildCount()-1 ), line, column );
833 		}
834 		
835 		return treeNode;
836 	}
837 
838 	public class XmlTreeTableModelEvent extends TreeModelEvent
839 	{
840 		private final int column;
841 
842 		public XmlTreeTableModelEvent(Object source, Object[] path, int[] childIndices, Object[] children, int column )
843 		{
844 			super(source, path, childIndices, children);
845 			this.column = column;
846 		}
847 
848 		public XmlTreeTableModelEvent(Object source, Object[] path, int column)
849 		{
850 			super(source, path);
851 			this.column = column;
852 		}
853 
854 		public XmlTreeTableModelEvent(Object source, TreePath path, int[] childIndices, Object[] children, int column)
855 		{
856 			super(source, path, childIndices, children);
857 			this.column = column;
858 		}
859 
860 		public XmlTreeTableModelEvent(Object source, TreePath path, int column)
861 		{
862 			super(source, path);
863 			this.column = column;
864 		}
865 
866 		public int getColumn()
867 		{
868 			return column;
869 		}
870 	}
871 
872 	public XmlTreeNode getXmlTreeNode(XmlObject object)
873 	{
874 		return treeNodeMap.get( object );
875 	}
876 
877 	public XmlTreeNode [] selectTreeNodes(String xpath)
878 	{
879 		XmlObject[] nodes = xmlObject.selectPath( xpath );
880 		List<XmlTreeNode> result = new ArrayList<XmlTreeNode>();
881 		
882 		for( XmlObject xmlObject : nodes )
883 		{
884 			XmlTreeNode tn = getXmlTreeNode( xmlObject );
885 			if( tn != null )
886 				result.add( tn );
887 		}
888 		
889 		return result.toArray( new XmlTreeNode[result.size()] );
890 	}
891 
892 	public void release()
893 	{
894 		typeSystem = null;
895 		treeNodeMap.clear();
896 		
897 		listeners.clear();
898 	}
899 
900 	public int getHierarchicalColumn()
901 	{
902 		return 0;
903 	}
904 }