# Kommentare aus XML in Jtree nicht anzeigen



## sylo (14. Apr 2010)

Hi zusammen

folgendes Problem:

Ich lese eine XML-Datei mit dem DOM aus und zeige ihn in einem JTreeTable aus. Soweit funktioniert alles.
Wenn ich in einer XML Datei ein Kommentar reinschreiben, z.B."<!--Dies ist ein Kommentar-->", wird dieser vom DOM ebenfalls erkannt und im JTreeTable ausgegeben. Dies möchte ich unterbinden. Mit dem CellRender kann ich zwar sagen welcher Text ausgegeben werden soll, aber ich kann diesen Node nicht ganz weglassen.

Zur Verdeutlichung folgendes Beispiel:
[XML]<testerson xmlns:test="http://www.test.de"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.test.de C:\DOKUME~1\eyildiz\Desktop\Test\TEST.XSD">
    <Name>
<!--Dies ist ein Kommentar-->
    <Alter>20</Alter>
    <Bericht>fiktiv2</Bericht>
    <comment2/>
</Name>
</testerson>[/XML]

Nur wird im JTreeTable folgendes ausgegeben:


```
test
- Name
   - #comment
   - Alter
   - Bericht
   - comment2
```

Und nun würde ich gerne #comment ganz verschwinden lassen. Bin für jeden Tipp dankbar.

Grüße


----------



## Noctarius (14. Apr 2010)

Überprüfe einfach ob Node vom Typ Element ist, falls nicht, einfach nicht adden


----------



## sylo (14. Apr 2010)

Ich hoffe es wird so einfach wie du das sagst  

Ich glaube ich hab das JTreeTable Prinzip noch nicht ganz verstanden. Das Problem was ich habe, ich weiß nicht wo die Node geadded werden???:L

Ich habe mir das Beispiel auf der Sun Seite hergenommen. Dann habe ich mir mein eigenes TreeTableModel geschrieben. Hier sind aber nur die Methoden:

-getChildCount
-getChild
-isLeaf
-getColumnCount
-getColumnName
-getColumnClass
-getValueAt
-isCellEditable
-setValueAt
-getPathToRoot

Aber keiner von denen fügt ein Node hinzu. 
Mein TreeTabelModel erbt von AbstractTreeTableModel und implementiert TreeTableModel


----------



## Noctarius (14. Apr 2010)

Schreib mal den Code dazu, dann kann man schauen  Oder wird das XML vom JTreeTable selbstständig eingelesen? (kann ich mir fast nicht vorstellen)


----------



## sylo (14. Apr 2010)

So sorry hat etwas gedauert.

Also ich poste mal alles relevante:

Das ist das JTreeTable welches von JTable erbt:

```
package tree;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.util.EventObject;

import javax.swing.DefaultCellEditor;
import javax.swing.Icon;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeSelectionModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;

import view.CellRenderer;

/**
 * This example shows how to create a simple JTreeTable component, 
 * by using a JTree as a renderer (and editor) for the cells in a 
 * particular column in the JTable.  
 *
 * @version 1.2 10/27/98
 *
 * @author Philip Milne
 * @author Scott Violet
 */
public class JTreeTable extends JTable implements Cloneable,Serializable{
    /** A subclass of JTree. */
    protected TreeTableCellRenderer tree;

    public Object clone() {
        Object theClone = null;
        try {
          theClone = super.clone();
        }
        catch(CloneNotSupportedException e) {
        }
        return theClone;
      }
    public JTreeTable(TreeTableModel treeTableModel) {
	super();

	// Creates the tree. It will be used as a renderer and editor. 
	tree = new TreeTableCellRenderer(treeTableModel);
	
	// Installs a tableModel representing the visible rows in the tree. 
	super.setModel(new TreeTableModelAdapter(treeTableModel, tree));

	// Forces the JTable and JTree to share their row selection models. 
	ListToTreeSelectionModelWrapper selectionWrapper = new 
	                        ListToTreeSelectionModelWrapper();
	
	
	tree.setSelectionModel(selectionWrapper);
	setSelectionModel(selectionWrapper.getListSelectionModel()); 
	

		
	// Installs the tree editor renderer and editor. 
	setDefaultRenderer(TreeTableModel.class, tree); 
	setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());

	// No grid.
	setShowGrid(false);

	// No intercell spacing
	setIntercellSpacing(new Dimension(0, 0));	

	// And update the height of the trees row to match that of
	// the table.
	if (tree.getRowHeight() < 1) {
	    // Metal looks better like this.
	    setRowHeight(20);
	}
    }

    /**
     * Overridden to message super and forward the method to the tree.
     * Since the tree is not actually in the component hierarchy it will
     * never receive this unless we forward it in this manner.
     */
    public void updateUI() {
	super.updateUI();
	if(tree != null) {
	    tree.updateUI();
	    // Do this so that the editor is referencing the current renderer
	    // from the tree. The renderer can potentially change each time
	    // laf changes.
	    setDefaultEditor(TreeTableModel.class, new TreeTableCellEditor());
	}
	// Use the tree's default foreground and background colors in the
	// table. 
        LookAndFeel.installColorsAndFont(this, "Tree.background",
                                         "Tree.foreground", "Tree.font");
    }

    /**
     * Workaround for BasicTableUI anomaly. Make sure the UI never tries to 
     * resize the editor. The UI currently uses different techniques to 
     * paint the renderers and editors; overriding setBounds() below 
     * is not the right thing to do for an editor. Returning -1 for the 
     * editing row in this case, ensures the editor is never painted. 
     */
    public int getEditingRow() {
        return (getColumnClass(editingColumn) == TreeTableModel.class) ? -1 :
	        editingRow;  
    }

    /**
     * Returns the actual row that is editing as <code>getEditingRow</code>
     * will always return -1.
     */
    private int realEditingRow() {
	return editingRow;
    }

    /**
     * This is overridden to invoke super's implementation, and then,
     * if the receiver is editing a Tree column, the editor's bounds is
     * reset. The reason we have to do this is because JTable doesn't
     * think the table is being edited, as <code>getEditingRow</code> returns
     * -1, and therefore doesn't automatically resize the editor for us.
     */
    public void sizeColumnsToFit(int resizingColumn) { 
	super.sizeColumnsToFit(resizingColumn);
	if (getEditingColumn() != -1 && getColumnClass(editingColumn) ==
	    TreeTableModel.class) {
	    Rectangle cellRect = getCellRect(realEditingRow(),
					     getEditingColumn(), false);
            Component component = getEditorComponent();
	    component.setBounds(cellRect);
            component.validate();
	}
    }

    /**
     * Overridden to pass the new rowHeight to the tree.
     */
    public void setRowHeight(int rowHeight) { 
        super.setRowHeight(rowHeight); 
	if (tree != null && tree.getRowHeight() != rowHeight) {
            tree.setRowHeight(getRowHeight()); 
	}
    }

    /**
     * Returns the tree that is being shared between the model.
     */
    public JTree getTree() {
	return tree;
    }

    /**
     * Overridden to invoke repaint for the particular location if
     * the column contains the tree. This is done as the tree editor does
     * not fill the bounds of the cell, we need the renderer to paint
     * the tree in the background, and then draw the editor over it.
     */
    public boolean editCellAt(int row, int column, EventObject e){
	boolean retValue = super.editCellAt(row, column, e);
	if (retValue && getColumnClass(column) == TreeTableModel.class) {
	    repaint(getCellRect(row, column, false));
	}
	return retValue;
    }

    /**
     * A TreeCellRenderer that displays a JTree.
     */
    public class TreeTableCellRenderer extends JTree implements
	         TableCellRenderer {
	/** Last table/tree row asked to renderer. */
	protected int visibleRow;
	/** Border to draw around the tree, if this is non-null, it will
	 * be painted. */
	protected Border highlightBorder;

	public TreeTableCellRenderer(TreeModel model) {
	    super(model); 
	    this.setCellRenderer(new CellRenderer());
	}

	/**
	 * updateUI is overridden to set the colors of the Tree's renderer
	 * to match that of the table.
	 */
	public void updateUI() {
	    super.updateUI();
	    // Make the tree's cell renderer use the table's cell selection
	    // colors. 
	    TreeCellRenderer tcr = getCellRenderer();
	    if (tcr instanceof DefaultTreeCellRenderer) {
		DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); 
		// For 1.1 uncomment this, 1.2 has a bug that will cause an
		// exception to be thrown if the border selection color is
		// null.
		// dtcr.setBorderSelectionColor(null);
		dtcr.setTextSelectionColor(UIManager.getColor
					   ("Table.selectionForeground"));
		dtcr.setBackgroundSelectionColor(UIManager.getColor
						("Table.selectionBackground"));
	    }
	}

	/**
	 * Sets the row height of the tree, and forwards the row height to
	 * the table.
	 */
	public void setRowHeight(int rowHeight) { 
	    if (rowHeight > 0) {
		super.setRowHeight(rowHeight); 
		if (JTreeTable.this != null &&
		    JTreeTable.this.getRowHeight() != rowHeight) {
		    JTreeTable.this.setRowHeight(getRowHeight()); 
		}
	    }
	}

	/**
	 * This is overridden to set the height to match that of the JTable.
	 */
	public void setBounds(int x, int y, int w, int h) {
	    super.setBounds(x, 0, w, JTreeTable.this.getHeight());
	}

	/**
	 * Sublcassed to translate the graphics such that the last visible
	 * row will be drawn at 0,0.
	 */
	public void paint(Graphics g) {
	    g.translate(0, -visibleRow * getRowHeight());
	    super.paint(g);
	    // Draw the Table border if we have focus.
	    if (highlightBorder != null) {
		highlightBorder.paintBorder(this, g, 0, visibleRow *
					    getRowHeight(), getWidth(),
					    getRowHeight());
	    }
	}

	/**
	 * TreeCellRenderer method. Overridden to update the visible row.
	 */
	public Component getTableCellRendererComponent(JTable table,
						       Object value,
						       boolean isSelected,
						       boolean hasFocus,
						       int row, int column) {
	    Color background;
	    Color foreground;

	    if(isSelected) {
		background = table.getSelectionBackground();
		foreground = table.getSelectionForeground();
	    }
	    else {
		background = table.getBackground();
		foreground = table.getForeground();
	    }
	    highlightBorder = null;
	    if (realEditingRow() == row && getEditingColumn() == column) {
		background = UIManager.getColor("Table.focusCellBackground");
		foreground = UIManager.getColor("Table.focusCellForeground");
	    }
	    else if (hasFocus) {
		highlightBorder = UIManager.getBorder
		                  ("Table.focusCellHighlightBorder");
		if (isCellEditable(row, column)) {
		    background = UIManager.getColor
			         ("Table.focusCellBackground");
		    foreground = UIManager.getColor
			         ("Table.focusCellForeground");
		}
	    }

	    visibleRow = row;
	    setBackground(background);
	    
	    TreeCellRenderer tcr = getCellRenderer();
	    if (tcr instanceof DefaultTreeCellRenderer) {
		DefaultTreeCellRenderer dtcr = ((DefaultTreeCellRenderer)tcr); 
		if (isSelected) {
		    dtcr.setTextSelectionColor(foreground);
		    dtcr.setBackgroundSelectionColor(background);
		}
		else {
		    dtcr.setTextNonSelectionColor(foreground);
		    dtcr.setBackgroundNonSelectionColor(background);
		}
	    }
	    return this;
	}
    }


    /**
     * An editor that can be used to edit the tree column. This extends
     * DefaultCellEditor and uses a JTextField (actually, TreeTableTextField)
     * to perform the actual editing.
     * <p>To support editing of the tree column we can not make the tree
     * editable. The reason this doesn't work is that you can not use
     * the same component for editing and renderering. The table may have
     * the need to paint cells, while a cell is being edited. If the same
     * component were used for the rendering and editing the component would
     * be moved around, and the contents would change. When editing, this
     * is undesirable, the contents of the text field must stay the same,
     * including the caret blinking, and selections persisting. For this
     * reason the editing is done via a TableCellEditor.
     * <p>Another interesting thing to be aware of is how tree positions
     * its render and editor. The render/editor is responsible for drawing the
     * icon indicating the type of node (leaf, branch...). The tree is
     * responsible for drawing any other indicators, perhaps an additional
     * +/- sign, or lines connecting the various nodes. So, the renderer
     * is positioned based on depth. On the other hand, table always makes
     * its editor fill the contents of the cell. To get the allusion
     * that the table cell editor is part of the tree, we don't want the
     * table cell editor to fill the cell bounds. We want it to be placed
     * in the same manner as tree places it editor, and have table message
     * the tree to paint any decorations the tree wants. Then, we would
     * only have to worry about the editing part. The approach taken
     * here is to determine where tree would place the editor, and to override
     * the <code>reshape</code> method in the JTextField component to
     * nudge the textfield to the location tree would place it. Since
     * JTreeTable will paint the tree behind the editor everything should
     * just work. So, that is what we are doing here. Determining of
     * the icon position will only work if the TreeCellRenderer is
     * an instance of DefaultTreeCellRenderer. If you need custom
     * TreeCellRenderers, that don't descend from DefaultTreeCellRenderer, 
     * and you want to support editing in JTreeTable, you will have
     * to do something similiar.
     */
    public class TreeTableCellEditor extends DefaultCellEditor {
	public TreeTableCellEditor() {
	    super(new TreeTableTextField());
	}

	/**
	 * Overridden to determine an offset that tree would place the
	 * editor at. The offset is determined from the
	 * <code>getRowBounds</code> JTree method, and additionally
	 * from the icon DefaultTreeCellRenderer will use.
	 * <p>The offset is then set on the TreeTableTextField component
	 * created in the constructor, and returned.
	 */
	public Component getTableCellEditorComponent(JTable table,
						     Object value,
						     boolean isSelected,
						     int r, int c) {
	    Component component = super.getTableCellEditorComponent
		(table, value, isSelected, r, c);
	    JTree t = getTree();
	    boolean rv = t.isRootVisible();
	    int offsetRow = rv ? r : r - 1;
	    Rectangle bounds = t.getRowBounds(offsetRow);
	    int offset = bounds.x;
	    TreeCellRenderer tcr = t.getCellRenderer();
	    if (tcr instanceof DefaultTreeCellRenderer) {
		Object node = t.getPathForRow(offsetRow).
		                getLastPathComponent();
		Icon icon;
		if (t.getModel().isLeaf(node))
		    icon = ((DefaultTreeCellRenderer)tcr).getLeafIcon();
		else if (tree.isExpanded(offsetRow))
		    icon = ((DefaultTreeCellRenderer)tcr).getOpenIcon();
		else
		    icon = ((DefaultTreeCellRenderer)tcr).getClosedIcon();
		if (icon != null) {
		    offset += ((DefaultTreeCellRenderer)tcr).getIconTextGap() +
			      icon.getIconWidth();
		}
	    }
	    ((TreeTableTextField)getComponent()).offset = offset;
	    return component;
	}

	/**
	 * This is overridden to forward the event to the tree. This will
	 * return true if the click count >= 3, or the event is null.
	 */
	public boolean isCellEditable(EventObject e) {
	    if (e instanceof MouseEvent) {
		MouseEvent me = (MouseEvent)e;
		// If the modifiers are not 0 (or the left mouse button),
                // tree may try and toggle the selection, and table
                // will then try and toggle, resulting in the
                // selection remaining the same. To avoid this, we
                // only dispatch when the modifiers are 0 (or the left mouse
                // button).
		if (me.getModifiers() == 0 ||
                    me.getModifiers() == InputEvent.BUTTON1_MASK) {
		    for (int counter = getColumnCount() - 1; counter >= 0;
			 counter--) {
			if (getColumnClass(counter) == TreeTableModel.class) {
			    MouseEvent newME = new MouseEvent
			          (JTreeTable.this.tree, me.getID(),
				   me.getWhen(), me.getModifiers(),
				   me.getX() - getCellRect(0, counter, true).x,
				   me.getY(), me.getClickCount(),
                                   me.isPopupTrigger());
			    JTreeTable.this.tree.dispatchEvent(newME);
			    break;
			}
		    }
		}
		if (me.getClickCount() >= 3) {
		    return true;
		}
		return false;
	    }
	    if (e == null) {
		return true;
	    }
	    return false;
	}
    }


    /**
     * Component used by TreeTableCellEditor. The only thing this does
     * is to override the <code>reshape</code> method, and to ALWAYS
     * make the x location be <code>offset</code>.
     */
    static class TreeTableTextField extends JTextField {
	public int offset;

	public void reshape(int x, int y, int w, int h) {
	    int newX = Math.max(x, offset);
	    super.setBounds(newX, y, w - (newX - x), h);
	}
    }


    /**
     * ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel
     * to listen for changes in the ListSelectionModel it maintains. Once
     * a change in the ListSelectionModel happens, the paths are updated
     * in the DefaultTreeSelectionModel.
     */
    class ListToTreeSelectionModelWrapper extends DefaultTreeSelectionModel { 
	/** Set to true when we are updating the ListSelectionModel. */
	protected boolean         updatingListSelectionModel;

	public ListToTreeSelectionModelWrapper() {
	    super();
	    getListSelectionModel().addListSelectionListener
	                            (createListSelectionListener());
	}

	/**
	 * Returns the list selection model. ListToTreeSelectionModelWrapper
	 * listens for changes to this model and updates the selected paths
	 * accordingly.
	 */
	ListSelectionModel getListSelectionModel() {
	    return listSelectionModel; 
	}

	/**
	 * This is overridden to set <code>updatingListSelectionModel</code>
	 * and message super. This is the only place DefaultTreeSelectionModel
	 * alters the ListSelectionModel.
	 */
	public void resetRowSelection() {
	    if(!updatingListSelectionModel) {
		updatingListSelectionModel = true;
		try {
		    super.resetRowSelection();
		}
		finally {
		    updatingListSelectionModel = false;
		}
	    }
	    // Notice how we don't message super if
	    // updatingListSelectionModel is true. If
	    // updatingListSelectionModel is true, it implies the
	    // ListSelectionModel has already been updated and the
	    // paths are the only thing that needs to be updated.
	}

	/**
	 * Creates and returns an instance of ListSelectionHandler.
	 */
	protected ListSelectionListener createListSelectionListener() {
	    return new ListSelectionHandler();
	}

	/**
	 * If <code>updatingListSelectionModel</code> is false, this will
	 * reset the selected paths from the selected rows in the list
	 * selection model.
	 */
	protected void updateSelectedPathsFromSelectedRows() {
	    if(!updatingListSelectionModel) {
		updatingListSelectionModel = true;
		try {
		    // This is way expensive, ListSelectionModel needs an
		    // enumerator for iterating.
		    int        min = listSelectionModel.getMinSelectionIndex();
		    int        max = listSelectionModel.getMaxSelectionIndex();

		    clearSelection();
		    if(min != -1 && max != -1) {
			for(int counter = min; counter <= max; counter++) {
			    if(listSelectionModel.isSelectedIndex(counter)) {
				TreePath     selPath = tree.getPathForRow
				                            (counter);

				if(selPath != null) {
				    addSelectionPath(selPath);
				}
			    }
			}
		    }
		}
		finally {
		    updatingListSelectionModel = false;
		}
	    }
	}

	/**
	 * Class responsible for calling updateSelectedPathsFromSelectedRows
	 * when the selection of the list changse.
	 */
	class ListSelectionHandler implements ListSelectionListener {
	    public void valueChanged(ListSelectionEvent e) {
		
		if(e.getValueIsAdjusting()){
			updateSelectedPathsFromSelectedRows();
		}
	    }
	}
    }
}
```

Dann hab ich hier das TreeTableModel welches von JTreeTable erbt:

```
package tree;
import javax.swing.tree.TreeModel;

/**
 * TreeTableModel is the model used by a JTreeTable. It extends TreeModel
 * to add methods for getting inforamtion about the set of columns each 
 * node in the TreeTableModel may have. Each column, like a column in 
 * a TableModel, has a name and a type associated with it. Each node in 
 * the TreeTableModel can return a value for each of the columns and 
 * set that value if isCellEditable() returns true. 
 *
 * @version %I% %G%
 *
 * @author Philip Milne 
 * @author Scott Violet
 */
public interface TreeTableModel extends TreeModel
{
    /**
     * Returns the number ofs availible column.
     */
    public int getColumnCount();

    /**
     * Returns the name for column number <code>column</code>.
     */
    public String getColumnName(int column);

    /**
     * Returns the type for column number <code>column</code>.
     */
    public Class getColumnClass(int column);

    /**
     * Returns the value to be displayed for node <code>node</code>, 
     * at column number <code>column</code>.
     */
    public Object getValueAt(Object node, int column);

    /**
     * Indicates whether the the value for node <code>node</code>, 
     * at column number <code>column</code> is editable.
     */
    public boolean isCellEditable(Object node, int column);

    /**
     * Sets the value for node <code>node</code>, 
     * at column number <code>column</code>.
     */
    public void setValueAt(Object aValue, Object node, int column);
}
```


----------



## sylo (14. Apr 2010)

Dann ist hier das AbstractTreeTableModel welches TreeTableModel implementiert:

```
package tree;
import javax.swing.event.EventListenerList;

import javax.swing.tree.*;
import javax.swing.event.*;

 
/**
 * An abstract implementation of the TreeTableModel interface, handling 
 * the list of listeners. 
 *
 * @version %I% %G%
 *
 * @author Philip Milne
 */

public abstract class AbstractTreeTableModel implements TreeTableModel {
	protected Object root;     
    protected EventListenerList listenerList = new EventListenerList();
  
    public AbstractTreeTableModel(Object root) {
        this.root = root; 
    }

    //
    // Default implementations for methods in the TreeModel interface. 
    //

    public Object getRoot() {
        return root;
    }
    public void setRoot(TreeNode root) {
    	
    	Object oldRoot = this.root;
	this.root = root;
        if (root == null && oldRoot != null) {
            fireTreeStructureChanged(this, null);
        }
        else {
            nodeStructureChanged(root);
        }
    }

    public boolean isLeaf(Object node) {
        return getChildCount(node) == 0; 
    }

    public void valueForPathChanged(TreePath path, Object newValue) {}

    // This is not called in the JTree's default mode: 
    // use a naive implementation. 
    public int getIndexOfChild(Object parent, Object child) {
        for (int i = 0; i < getChildCount(parent); i++) {
	    if (getChild(parent, i).equals(child)) { 
	        return i; 
	    }
        }
	return -1; 
    }

    public void addTreeModelListener(TreeModelListener l) {
        listenerList.add(TreeModelListener.class, l);
    }

    public void removeTreeModelListener(TreeModelListener l) {
        listenerList.remove(TreeModelListener.class, l);
    }

    /*
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance 
     * is lazily created using the parameters passed into 
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeNodesChanged(Object source, Object[] path, 
                                        int[] childIndices, 
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path, 
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeNodesChanged(e);
            }          
        }
    }

    /*
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance 
     * is lazily created using the parameters passed into 
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeNodesInserted(Object source, Object[] path, 
                                        int[] childIndices, 
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path, 
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeNodesInserted(e);
            }          
        }
    }

    /*
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance 
     * is lazily created using the parameters passed into 
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeNodesRemoved(Object source, Object[] path, 
                                        int[] childIndices, 
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path, 
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeNodesRemoved(e);
            }          
        }
    }

    /*
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance 
     * is lazily created using the parameters passed into 
     * the fire method.
     * @see EventListenerList
     */
    protected void fireTreeStructureChanged(Object source, Object[] path, 
                                        int[] childIndices, 
                                        Object[] children) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path, 
                                           childIndices, children);
                ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
            }          
        }
    }
    /*
     * Notifies all listeners that have registered interest for
     * notification on this event type.  The event instance 
     * is lazily created using the parameters passed into 
     * the fire method.
     *
     * @param source the node where the tree model has changed
     * @param path the path to the root node
     * @see EventListenerList
     */
    private void fireTreeStructureChanged(Object source, TreePath path) {
        // Guaranteed to return a non-null array
        Object[] listeners = listenerList.getListenerList();
        TreeModelEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length-2; i>=0; i-=2) {
            if (listeners[i]==TreeModelListener.class) {
                // Lazily create the event:
                if (e == null)
                    e = new TreeModelEvent(source, path);
                ((TreeModelListener)listeners[i+1]).treeStructureChanged(e);
            }
        }
    }

    //
    // Default impelmentations for methods in the TreeTableModel interface. 
    //

    public Class getColumnClass(int column) { return Object.class; }

   /** By default, make the column with the Tree in it the only editable one. 
    *  Making this column editable causes the JTable to forward mouse 
    *  and keyboard events in the Tree column to the underlying JTree. 
    */ 
    public boolean isCellEditable(Object node, int column) { 
         return (getColumnClass(column) == TreeTableModel.class);
    	
    }

    public void setValueAt(Object aValue, Object node, int column) {}

    public void removeNodeFromParent(MutableTreeNode node)
    {
    	TreeNode parent = node.getParent();
    	Object[] children = new Object[1];
    	children[0] = node;
    	int[] childIndices = new int[1];
    	childIndices[0] = getIndexOfChild(parent, node);
    	node.removeFromParent();
    	nodesWereRemoved(parent, childIndices, children);
    }
    public void nodesWereRemoved(TreeNode parent, int[] childIndices, Object[] removedChildren){
    	fireTreeNodesRemoved(this, getPathToRoot(parent), childIndices, removedChildren);
    }
    public TreeNode[] getPathToRoot(TreeNode node)
    {
    return getPathToRoot(node, 0);
    }
    private TreeNode[] getPathToRoot(TreeNode node, int depth)
    {
    if (node == null)
    {
    if (depth == 0)
    return null;
    
    return new TreeNode[depth];
    }
    TreeNode[] path = getPathToRoot(node.getParent(), depth + 1);
     path[path.length - depth - 1] = node;
    return path;
    }
    public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index){
    	parent.insert(newChild, index);
    	int[] newIndexs = new int[1];
    	newIndexs[0] = index;
    	nodesWereInserted(parent, newIndexs);
    }
    public void nodesWereInserted(TreeNode node, int[] childIndices) {
        if(listenerList != null && node != null && childIndices != null
           && childIndices.length > 0) {
            int               cCount = childIndices.length;
            Object[]          newChildren = new Object[cCount];

            for(int counter = 0; counter < cCount; counter++)
                newChildren[counter] = node.getChildAt(childIndices[counter]);
            fireTreeNodesInserted(this, getPathToRoot(node), childIndices, 
                                  newChildren);
        }
    }
    public void nodeStructureChanged(TreeNode node) {
        if(node != null) {
           fireTreeStructureChanged(this, getPathToRoot(node), null, null);
        }
    }
    // Left to be implemented in the subclass:

    /* 
     *   public Object getChild(Object parent, int index)
     *   public int getChildCount(Object parent) 
     *   public int getColumnCount() 
     *   public String getColumnName(Object node, int column)  
     *   public Object getValueAt(Object node, int column) 
     */

}
```

Dann haben wir hier das TreeTableModelAdapter welches von AbstractTableModel erbt:

```
package tree;

import javax.swing.JTree;
import javax.swing.event.*;
import javax.swing.tree.*;
import javax.swing.table.*;

/**
 * This is a wrapper class takes a TreeTableModel and implements 
 * the table model interface. The implementation is trivial, with 
 * all of the event dispatching support provided by the superclass: 
 * the AbstractTableModel. 
 *
 * @version %I% %G%
 *
 * @author Philip Milne
 * @author Scott Violet
 */


public class TreeTableModelAdapter extends AbstractTableModel
{
    JTree tree;
    TreeTableModel treeTableModel;

    public TreeTableModelAdapter(TreeTableModel treeTableModel, JTree tree) {
        this.tree = tree;
        this.treeTableModel = treeTableModel;

	tree.addTreeExpansionListener(new TreeExpansionListener() {
	    // Don't use fireTableRowsInserted() here; 
	    // the selection model would get  updated twice. 
	    public void treeExpanded(TreeExpansionEvent event) {  
	      fireTableDataChanged(); 
	    }
            public void treeCollapsed(TreeExpansionEvent event) {  
	      fireTableDataChanged(); 
	    }
	});
    }

  // Wrappers, implementing TableModel interface. 

    public int getColumnCount() {
	return treeTableModel.getColumnCount();
    }

    public String getColumnName(int column) {
	return treeTableModel.getColumnName(column);
    }

    public Class getColumnClass(int column) {
	return treeTableModel.getColumnClass(column);
    }

    public int getRowCount() {
	return tree.getRowCount();
    }

    protected Object nodeForRow(int row) {
	TreePath treePath = tree.getPathForRow(row);
	return treePath.getLastPathComponent();         
    }

    public Object getValueAt(int row, int column) {
	return treeTableModel.getValueAt(nodeForRow(row), column);
    }

    public boolean isCellEditable(int row, int column) {
         return treeTableModel.isCellEditable(nodeForRow(row), column); 
    }

    public void setValueAt(Object value, int row, int column) {
	treeTableModel.setValueAt(value, nodeForRow(row), column);
    }
}
```

Und zu guter letzt noch mein eigenes TreeTableModelExample:

```
package view;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.swing.tree.*;

import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import tree.AbstractTreeTableModel;
import tree.TreeTableModel;

/**
 * The TreeTableModel which will be assigned to JTreeTable
 * 
 * 
 */
public class TreeTableModelExample extends AbstractTreeTableModel implements
		TreeTableModel {
	private Node root;
	private int row;
	private String[] columnNames;
	private String[] methodNames;
	private String[] setterMethodNames;
	private Class[] cTypes;

	/**
	 * This is the Constructor
	 * 
	 * @param root
	 * @param row
	 * @param columnNames
	 * @param methodNames
	 * @param setterMethodNames
	 * @param cTypes
	 */
	public TreeTableModelExample(Node root, int row, String[] columnNames,
			String[] methodNames, String[] setterMethodNames, Class[] cTypes) {
		super(root);
		this.root = root;
		this.row = row;
		this.columnNames = columnNames;
		this.methodNames = methodNames;
		this.setterMethodNames = setterMethodNames;
		this.cTypes = cTypes;
	}

	/**
	 * TreeModel method to return the number of children of a particular node.
	 * Since <code>node</code> is a TreeNode, this can be answered via the
	 * TreeNode method <code>getChildCount</code>.
	 */
	public int getChildCount(Object parent) {
		Node node = (Node) parent;
		NodeList children = node.getChildNodes();
		int childCount = children != null ? children.getLength() : 0;
		return (childCount / 2);
	}

	/**
	 * TreeModel method to locate a particular child of the specified node.
	 * Since <code>node</code> is a TreeNode, this can be answered via the
	 * TreeNode method <code>getChild</code>.
	 */
	public Object getChild(Object parent, int index) {
		Node node = (Node) parent;
		NodeList children = node.getChildNodes();
		return children.item((index) * 2 + 1);
	}

	/**
	 * TreeModel method to determine if a node is a leaf. Since
	 * <code>node</code> is a TreeNode, this can be answered via the TreeNode
	 * method <code>isLeaf</code>.
	 */
	public boolean isLeaf(Object node) {
		Node testNode = (Node) node;
		return getChildCount(testNode) == 0;
	}

	/**
	 * Returns the number of column names passed into the constructor.
	 */
	public int getColumnCount() {
		return columnNames.length;
	}

	/**
	 * Returns the column name passed into the constructor.
	 */
	public String getColumnName(int column) {
		if (cTypes == null || column < 0 || column >= cTypes.length) {
			return null;
		}
		return columnNames[column];
	}

	/**
	 * Returns the column class for column <code>column</code>. This is set in
	 * the constructor.
	 */
	public Class getColumnClass(int column) {
		if (cTypes == null || column < 0 || column >= cTypes.length) {
			return null;
		}
		return cTypes[column];
	}

	/**
	 * Returns the value for the column <code>column</code> and object
	 * <code>node</code>. The return value is determined by invoking the method
	 * specified in constructor for the passed in column.
	 */
	public Object getValueAt(Object node, int column) {

		Node myNode = (Node) node;
		String leerzeichen = "";

		try {
			switch (column) {
			case 0:
				return myNode.getNodeName();
			case 1:
				NamedNodeMap nm = myNode.getAttributes();
//				if (nm != null) {
					for (int i = 0; i < nm.getLength(); i++) {
						if (nm.item(i).toString().startsWith("name")) {
							return nm.item(i).getNodeValue();
						}
					}
//				}
//			else{
//					System.out.println("Test");
//				}
				return leerzeichen;
			case 2:
				return leerzeichen;
			}
		} catch (SecurityException se) {
		}
		return null;
	}

	/**
	 * Returns true if there is a setter method name for column
	 * <code>column</code>. This is set in the constructor.
	 */
	public boolean isCellEditable(Object node, int column) {
		return ((setterMethodNames != null && setterMethodNames[column] != null));
		// return ((setterMethodNames != null && setterMethodNames[column] !=
		// null) && column != 0); // Kein Erfolg
	}

	/**
	 * Sets the value to <code>aValue</code> for the object <code>node</code> in
	 * column <code>column</code>. This is done by using the setter method name,
	 * and coercing the passed in value to the specified type.
	 */
	// Note: This looks up the methods each time! This is rather inefficient;
	// it should really be changed to cache matching methods/constructors
	// based on <code>node</code>'s class, and <code>aValue</code>'s class.
	public void setValueAt(Object aValue, Object node, int column) {
		if (column == 0) {
			return;
		}
		boolean found = false;
		try {
			// We have to search through all the methods since the
			// types may not match up.
			Method[] methods = node.getClass().getMethods();

			for (int counter = methods.length - 1; counter >= 0; counter--) {
				if (methods[counter].getName()
						.equals(setterMethodNames[column])
						&& methods[counter].getParameterTypes() != null
						&& methods[counter].getParameterTypes().length == 1) {
					// We found a matching method
					Class param = methods[counter].getParameterTypes()[0];
					if (!param.isInstance(aValue)) {
						// Yes, we can use the value passed in directly,
						// no coercision is necessary!
						if (aValue instanceof String
								&& ((String) aValue).length() == 0) {
							// Assume an empty string is null, this is
							// probably bogus for here.
							aValue = null;
						} else {
							// Have to attempt some sort of coercision.
							// See if the expected parameter type has
							// a constructor that takes a String.
							Constructor cs = param
									.getConstructor(new Class[] { String.class });
							if (cs != null) {
								aValue = cs
										.newInstance(new Object[] { aValue });
							} else {
								aValue = null;
							}
						}
					}
					// null either means it was an empty string, or there
					// was no translation. Could potentially deal with these
					// differently.
					methods[counter].invoke(node, new Object[] { aValue });
					found = true;
					break;
				}
			}
		} catch (Throwable th) {
			System.out.println("exception: " + th);
		}
		if (found) {
			// The value changed, fire an event to notify listeners.
			TreeNode parent = ((TreeNode) node).getParent();
			fireTreeNodesChanged(this, getPathToRoot(parent),
					new int[] { getIndexOfChild(parent, node) },
					new Object[] { node });
		}
	}

	/**
	 * Builds the parents of the node up to and including the root node, where
	 * the original node is the last element in the returned array. The length
	 * of the returned array gives the node's depth in the tree.
	 * 
	 * @param aNode
	 *            the TreeNode to get the path for
	 * @param an
	 *            array of TreeNodes giving the path from the root to the
	 *            specified node.
	 */
	public TreeNode[] getPathToRoot(TreeNode aNode) {
		return getPathToRoot(aNode, 0);
	}

	/**
	 * Builds the parents of the node up to and including the root node, where
	 * the original node is the last element in the returned array. The length
	 * of the returned array gives the node's depth in the tree.
	 * 
	 * @param aNode
	 *            the TreeNode to get the path for
	 * @param depth
	 *            an int giving the number of steps already taken towards the
	 *            root (on recursive calls), used to size the returned array
	 * @return an array of TreeNodes giving the path from the root to the
	 *         specified node
	 */
	private TreeNode[] getPathToRoot(TreeNode aNode, int depth) {
		TreeNode[] retNodes;
		// This method recurses, traversing towards the root in order
		// size the array. On the way back, it fills in the nodes,
		// starting from the root and working back to the original node.

		/*
		 * Check for null, in case someone passed in a null node, or they passed
		 * in an element that isn't rooted at root.
		 */
		if (aNode == null) {
			if (depth == 0)
				return null;
			else
				retNodes = new TreeNode[depth];
		} else {
			depth++;
			if (aNode == root)
				retNodes = new TreeNode[depth];
			else
				retNodes = getPathToRoot(aNode.getParent(), depth);
			retNodes[retNodes.length - depth] = aNode;
		}
		return retNodes;
	}
}
```
So mehr Klassen zum TreeTable habe ich nicht.

Wahrscheinlich bin ich einfach nur blind und seh es nicht.

Grüße


----------



## Noctarius (14. Apr 2010)

Theoretisch müsstest du das bei TreeTableModelExample in den Methoden getChild und getChildCount gemacht werden. Aber irgendwo müsste doch der XML RootNode herkommen (also irgendwo müsste es ein org.w3c.dom.Document) geben, bei der Erstellung kann man sagen (DocumentBuilderFactory, dass Kommentare beim Parsen ignoriert werden sollen.
Alternativ müsstest du in den beiden oben genannten Methoden alle Childs aus der NodeList auf ihren NodeType überprüfen (ersteres dürfte aber einfacher sein).


----------



## sylo (14. Apr 2010)

Sorry glaube du hast mich missverstanden. Ich will schon dass der Parser die Kommentare mit ausliest. Der Baum soll die blos nicht anzeigen. Ich brauche die Kommentare noch.

Es existiert noch eine Klasse Parser in der ich mein XML-File parse und auch den Root her bekomme.
Wenn du den Code brauchst kann ich den posten.

Aber ich denke auch dass man bei getChild() ansetzten muss. Ich weiß blos nicht wie. 
Jeder Knoten hat ja auch einen Text-Node den z.b. kann ich ausblenden. Das geschieht einmal hier:


```
public int getChildCount(Object parent) {
        Node node = (Node) parent;
        NodeList children = node.getChildNodes();
        int childCount = children != null ? children.getLength() : 0;
        return (childCount / 2);
    }
```

und dann noch einmal hier:


```
public Object getChild(Object parent, int index) {
        Node node = (Node) parent;
        NodeList children = node.getChildNodes();
        return children.item((index) * 2 + 1);
    }
```

Dies kann ich aber nur machen, weil ich weiß das nach jedem Knoten ein TextNode kommt und blende und gebe somit nur jedes zweite Element zurück. Wenn ich aber an dieser Stelle überprüfe ob der Knoten ein Comment Knoten ist, kann ich ja nicht null zurück geben. Dann gibt es ne Exception. 

Ich hoffe ich kann mich hier überhaupt richtig ausdrücken so das man mich versteht:bahnhof:


----------



## sylo (15. Apr 2010)

HI

also ich werde mal versuchen den ganzen Baum in eine Hashtabelle zu laden und dann auszusortieren. Ich berichte dann wenn es soweit ist.

Grüße


----------



## sylo (19. Mai 2010)

Habe eine Lösung zu diesem Problem gefunden.

In der getChildCount() Methode muss man alle Kinder durchlaufen und die Anzahl der gefundenen Knoten von childcount/2 abziehen. Also sowas:


```
for (int i = 0; i < children.getLength(); i++) {
			if (children.item(i).getNodeName().equals("#comment")) {
				commentCount++;
			}
		}
		return (childCount / 2 - commentCount);
```

Danach muss man in der getChild() ebenfalls die Anzahl der comment-Elemente holen. Man überprüft wieviele Kommentare vor dem gewünschten Index drankammen und "addiert" geht im Baum um die Anzahl weiter. Also sowas:


```
while (j <= index) {
				if (j != index) {
					if (children.item(j * 2 + 1 + additionalCount).getNodeName().equals("#comment")) {
						additionalCount++;
						additionalCount++;
					}
				} else {
					if (children.item(j * 2 + 1 + additionalCount).getNodeName().equals("#comment")) {
						additionalCount += 2;
					}
					return children.item((index) * 2 + 1 + additionalCount);
				}
				j++;
			}
```


----------



## Noctarius (19. Mai 2010)

Durch 2 stimmt nicht in jedem Fall. Die IBM VM macht mehr #Text und #Comment Elemente als die JVM von Sun/Oracle und anderen.


----------



## sylo (19. Mai 2010)

Das hab ich nicht gewusst, ist dann wohl von VM zu VM unterschiedlich und man müsste diese zwei Methoden anpassen.


----------



## Noctarius (19. Mai 2010)

Ich wusste das auch nicht, bis ich an dem Parser gebastelt habe und meine automatischen Unittests auf allen VMs durchliefen, nur auf der IBM nicht. Das hat mich gute 2Std suchen gekostet 

Aber du kannst dir den Nodetype des aktuellen Node geben lassen und schauen ob es ein Kommentar ist.


----------



## sylo (19. Mai 2010)

Du hast 2 Stunden meines Lebens gerettet. Ich danke dir :applaus::toll:


----------

