[Resolvido] JTree Dinâmica - método fireLastPathComponentRemoved = ArrayIndexOutOfBoundsException:-1

Olá,

Estou utilizando o código http://www.guj.com.br/posts/list/200192.java#1009903  (treeSample.zip) para gerar uma Jtree dinamica. O problema está ao tentar excluir um livro,

onde gera o erro abaixo.


Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
	at java.util.Vector.elementAt(Vector.java:430)
	at javax.swing.tree.DefaultMutableTreeNode.getChildAt(DefaultMutableTreeNode.java:230)
	at javax.swing.tree.VariableHeightLayoutCache.treeNodesRemoved(VariableHeightLayoutCache.java:543)
	at javax.swing.plaf.basic.BasicTreeUI$Handler.treeNodesRemoved(BasicTreeUI.java:3810)
	at treeSample.AbstractTreeModel.fireTreeNodesRemoved(AbstractTreeModel.java:120)
	at treeSample.AbstractTreeModel.fireLastPathComponentRemoved(AbstractTreeModel.java:178)
	at treeSample.AbstractTreeModel.fireLastPathComponentRemoved(AbstractTreeModel.java:184)
	at treeSample.LivroTreeModel.removerLivro(LivroTreeModel.java:101)
	at treeSample.JTreeFrame.btnRemoverLivroActionPerformed(JTreeFrame.java:203)
	at treeSample.JTreeFrame.access$1(JTreeFrame.java:195)
	at treeSample.JTreeFrame$2.actionPerformed(JTreeFrame.java:87)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
	at java.awt.Component.processMouseEvent(Component.java:6263)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
	at java.awt.Component.processEvent(Component.java:6028)
	at java.awt.Container.processEvent(Container.java:2041)
	at java.awt.Component.dispatchEventImpl(Component.java:4630)
	at java.awt.Container.dispatchEventImpl(Container.java:2099)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
	at java.awt.Container.dispatchEventImpl(Container.java:2085)
	at java.awt.Window.dispatchEventImpl(Window.java:2478)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
  Pesquisei bastante sobre isso, porém nao consegui encontrar nada que ajudasse, pois todos os exemplos sejam em foruns brasileiros ou nao estão usando DefaultMutableTreeNode e seus métodos default. Desenvolvi toda a lógica da tree do meu software baseado nestas classes, como podem ver: http://www.guj.com.br/posts/list/212286.java . Só está faltando a funcionalidade de exclusao. Exclusao de livros e exclusao de autores de um determinado livro.

Se alguém souber uma forma de resolver, agradeço.

Pode postar seu livrosTreeModel? E, de preferência, destacar qual é a linha 101?

Ele acusa um erro no Vector e no DefaultMutableTreeNode, classes que o TreeSample não usa.

[quote=ViniGodoy]Pode postar seu livrosTreeModel? E, de preferência, destacar qual é a linha 101?

Ele acusa um erro no Vector e no DefaultMutableTreeNode, classes que o TreeSample não usa.[/quote]

Posso sim, claro. Este erro é da que vc postou no topico citado acima. Segue o que vc pediu:

LivroTreeModel :

package treeSample;

import java.util.List;

public class LivroTreeModel extends AbstractTreeModel {
	// Raiz da nossa árvore, vamos exibir uma lista de livros.
	private List<Livro> livros;
        private String fakeRoot = "Livros";

	public LivroTreeModel(List<Livro> livros) {
		this.livros = livros;
	}

	/**
	 * Com esse método, o Java quem é o objeto que está num determinado índice
	 * do pai. Cada nó de uma árvore pode ser encarado como uma lista. Sendo o
	 * pai a lista e o índice um dos filhos.
	 *
	 * @param parent
	 *            � o pai, que tem os filhos. No caso do Livro, o próprio livro.
	 * @param index
	 *            �ndice do filho. No caso do livro, o índice corresponde aos
	 *            autores.
	 */
	public Object getChild(Object parent, int index) {
		if (parent == fakeRoot) // � o nó principal?
			return livros.get(index); // Pegamos da lista de livro

		if (parent instanceof Livro) // O pai é um livro?
		{
			// Devolvemos um autor
			return ((Livro) parent).getAutores().get(index);
		}

		// Se o pai não é nenhum desses. Melhor dar erro.
		throw new IllegalArgumentException("Invalid parent class"
				+ parent.getClass().getSimpleName());
	}

	/**
	 * Retornamos quantos filhos um pai tem. No caso de um livro, é a contagem
	 * de autores. No caso da lista de livros, é a quantidade de livros.
	 */
	public int getChildCount(Object parent) {
		// Mesma lógica.
		if (parent == fakeRoot)
			return livros.size();

		if (parent instanceof Livro) // O pai é um livro?
			return ((Livro) parent).getAutores().size();

		// Se o pai não é nenhum desses. Melhor dar erro.
		throw new IllegalArgumentException("Invalid parent class"
				+ parent.getClass().getSimpleName());
	}

	/**
	 * Dado um pai, indicamos qual é o índice do filho correspondente.
	 */
	public int getIndexOfChild(Object parent, Object child) {
		if (parent == fakeRoot)
			return livros.indexOf(child);
		if (parent instanceof Livro)
			return ((Livro) parent).getAutores().indexOf(child);

		return 0;
	}

	/**
	 * Devemos retornar quem é o nó raiz da árvore. Afinal, a árvore tem que
	 * começar em algum lugar.
	 */
	public Object getRoot() {
		return fakeRoot;
	}

	/**
	 * Indicamos se um nó é ou não uma folha. Isso é, se ele não tem filhos. No
	 * nosso caso, os autores são as folhas da árvore.
	 */
	public boolean isLeaf(Object node) {
		return node instanceof Autor;
	}

        public void adicionarLivro(Livro livro)
        {
            livros.add(livro);
            fireLastPathComponentInserted(fakeRoot, livro);
        }

        public void adicionarAutor(Livro livro, Autor autor)
        {
            livro.addAutor(autor);
            fireLastPathComponentInserted(fakeRoot, livro, autor);
        }

        public void removerLivro(Livro livro)
        {
            if (livros.remove(livro))
            {
                fireLastPathComponentRemoved(fakeRoot, livro);
            }
        }
}

A linha 101 é esta:

 fireLastPathComponentRemoved(fakeRoot, livro);

Obrigado.

Mude o método fire do AbstractTreeModel para:

[code]
public void fireLastPathComponentRemoved(int oldIndex, Object… path)
{
fireLastPathComponentRemoved(new TreePath(path));
}

public void fireLastPathComponentRemoved(int oldIndex, TreePath treePath)
{
    Object leaf = treePath.getLastPathComponent();

     fireTreeNodesRemoved(this, treePath.getParentPath().getPath(),
            new int[] {oldIndex}, new Object[] {leaf});
}[/code]

E faça o remove assim:

public void removerLivro(Livro livro) { int index = livros.indexOf(livro); if (index != -1) { livros.remove(index); fireLastPathComponentRemoved(index, fakeRoot, livro); } }

É , o erro continua.


Exception in thread "AWT-EventQueue-0" java.lang.ArrayIndexOutOfBoundsException: -1
	at java.util.Vector.elementAt(Vector.java:430)
	at javax.swing.tree.DefaultMutableTreeNode.getChildAt(DefaultMutableTreeNode.java:230)
	at javax.swing.tree.VariableHeightLayoutCache.treeNodesRemoved(VariableHeightLayoutCache.java:543)
	at javax.swing.plaf.basic.BasicTreeUI$Handler.treeNodesRemoved(BasicTreeUI.java:3810)
	at treeSample.AbstractTreeModel.fireTreeNodesRemoved(AbstractTreeModel.java:120)
	at treeSample.AbstractTreeModel.fireLastPathComponentRemoved(AbstractTreeModel.java:178)
	at treeSample.AbstractTreeModel.fireLastPathComponentRemoved(AbstractTreeModel.java:189)
	at treeSample.LivroTreeModel.removerLivro(LivroTreeModel.java:109)
	at treeSample.JTreeFrame.btnRemoverLivroActionPerformed(JTreeFrame.java:203)
	at treeSample.JTreeFrame.access$1(JTreeFrame.java:195)
	at treeSample.JTreeFrame$2.actionPerformed(JTreeFrame.java:87)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
	at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:236)
	at java.awt.Component.processMouseEvent(Component.java:6263)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
	at java.awt.Component.processEvent(Component.java:6028)
	at java.awt.Container.processEvent(Container.java:2041)
	at java.awt.Component.dispatchEventImpl(Component.java:4630)
	at java.awt.Container.dispatchEventImpl(Container.java:2099)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
	at java.awt.Container.dispatchEventImpl(Container.java:2085)
	at java.awt.Window.dispatchEventImpl(Window.java:2478)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

Inseri os novos métodos, e deixei o método antigo:

fireLastPathComponentRemoved(TreePath treePath)

Pois o novo que me passou precisa dele.

Debugando percebi que na linha :

        int index = getIndexOfChild(parent, leaf);

o resultado do index é -1.
Talvez esteja ai o problema.

Sim. Era pra usar o novo:

[code] public void fireLastPathComponentRemoved(int oldIndex, Object… path)
{
fireLastPathComponentRemoved(new TreePath(oldIndex, path));
}

public void fireLastPathComponentRemoved(int oldIndex, TreePath treePath)
{
    Object leaf = treePath.getLastPathComponent();

     fireTreeNodesRemoved(this, treePath.getParentPath().getPath(),
            new int[] {oldIndex}, new Object[] {leaf});
}[/code]

O erro ocorre pq é necessário obter o índice antes de remover o elemento.

[quote=ViniGodoy]Sim. Era pra usar o novo:

[code] public void fireLastPathComponentRemoved(int oldIndex, Object… path)
{
fireLastPathComponentRemoved(new TreePath(oldIndex, path));
}

public void fireLastPathComponentRemoved(int oldIndex, TreePath treePath)
{
    Object leaf = treePath.getLastPathComponent();

     fireTreeNodesRemoved(this, treePath.getParentPath().getPath(),
            new int[] {oldIndex}, new Object[] {leaf});
}[/code]

O erro ocorre pq é necessário obter o índice antes de remover o elemento.[/quote]

Legal , agora funcionou !

Só uma correção:
Ao invez de :

 fireLastPathComponentRemoved(new TreePath(oldIndex, path));

é:

 fireLastPathComponentRemoved(oldIndex,new TreePath(path));

Pois o construtor do TreePath nao aceita index.

Preciso também ter a opçao de excluir autores de livros, aco que estou quase conseguindo. Imagino que tenha que passar o autor, o index do autor, o livro que o autor é filho e o root.

conforme:

        public void removerAutor(Livro livro, Autor autor)
        {
     	   int indexLivro = livros.indexOf(livro);
     	   int indexAutor = livros.get(indexLivro).getAutores().indexOf(autor);
    	   if (indexLivro != -1 && indexAutor != -1)  {  
    	      livro.getAutores().remove(indexAutor);
    	      fireLastPathComponentRemoved(indexAutor, fakeRoot, livro, autor);  
    	   } 
        }

O problema é que nao sei como pegar o objeto livro do autor no actionperformed do botao excluir.


    private void btnRemoverLivroActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoverLivroActionPerformed
        Object selected = treeLivros.getLastSelectedPathComponent();
        if (selected == null )// || !(selected instanceof Livro))
        {
            JOptionPane.showMessageDialog(this, "Selecione um livro ou um autor a remover!");
            return;
        }
        else if(selected instanceof Livro)
        {
            getModel().removerLivro((Livro)selected);        	
        }
        else if(selected instanceof Autor)
        {
           // getModel().removerAutor(livro, (Autor)selected);
        }
    }//GEN-LAST:event_btnRemoverLivroActionPerformed

O que acha ?

Ao final do post postarei o código completo para que outros usuários que precisem tenham o código funcionando.

Obrigado.

Acho que é por aí mesmo. Quando vc postar o código com as correções, atualizo lá no post original também.

Estou conseguindo remover o autor da tree, só que quando eu tento remover ele da list pertencente ao modelo Livro ele da um erro:

java.lang.UnsupportedOperationException
	at java.util.Collections$UnmodifiableList.remove(Collections.java:1162)
	at sentry.nocviewer.view.MouseHandler$2.actionPerformed(MouseHandler.java:106)
	at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1995)
	at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2318)
	at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:387)
	at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:242)
	at javax.swing.AbstractButton.doClick(AbstractButton.java:357)
	at javax.swing.plaf.basic.BasicMenuItemUI.doClick(BasicMenuItemUI.java:1225)
	at javax.swing.plaf.basic.BasicMenuItemUI$Handler.mouseReleased(BasicMenuItemUI.java:1266)
	at java.awt.Component.processMouseEvent(Component.java:6263)
	at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
	at java.awt.Component.processEvent(Component.java:6028)
	at java.awt.Container.processEvent(Container.java:2041)
	at java.awt.Component.dispatchEventImpl(Component.java:4630)
	at java.awt.Container.dispatchEventImpl(Container.java:2099)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4574)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4238)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
	at java.awt.Container.dispatchEventImpl(Container.java:2085)
	at java.awt.Window.dispatchEventImpl(Window.java:2475)
	at java.awt.Component.dispatchEvent(Component.java:4460)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:599)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

É porque você tem que usar o método de remoção da classe Livro, não da lista.

O Livro retorna uma lista imodificável de autores, pois seria uma violação do encapsulamento da classe modificar a lista dele diretamente. Se o livro não tiver um método remover, crie um.

Exato Vini. Com isso fechamos a versão rsrsrs.

Implementada a opção de remover autores.

Agora não falta mais nada hehe.

http://rapidshare.com/files/415174497/TreeSample_final.zip

Obrigado.