Problem mit TableCellRenderer / TableModel

Hallo!

Diese Methode wird bei Änderungen meiner JTable aufgerufen:

		UploadTableModel model = guiManager.getUploadTableModel();
		// (...)
		for (int i = 0; i < model.getRowCount(); i++) {
			// (...)
			// ******************************
			// hier kommt eine Exception
			AbloadFile ablFile = (AbloadFile) model.getValueAt(i,
					UploadTableModel.COLUMN_URL);
			// UploadTableModel.COLUMN_URL ist 2 (static final)
			// ******************************
			// (...)
		}
		// (...)
}```

In der im Code angegeben Zeile kommt folgende Exception:

Exception in thread “Thread-4” java.lang.ClassCastException: java.lang.String cannot be cast
to pcworld.fider.upload.abload.AbloadFile
at pcworld.fider.gui.MultiLinkCopyBox.updateLinkArea(MultiLinkCopyBox.java:119)
at pcworld.fider.gui.MultiLinkCopyBox$2.tableChanged(MultiLinkCopyBox.java:170)
at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
at javax.swing.table.AbstractTableModel.fireTableCellUpdated(Unknown Source)
at javax.swing.table.DefaultTableModel.setValueAt(Unknown Source)
at pcworld.fider.gui.UploadAction$1.run(UploadAction.java:115)
at java.lang.Thread.run(Unknown Source)



Und das, obwohl alle Values der Spalte UploadTableModel.COLUMN_URL (also Spalte 2) vom Typ AbloadFile sein müssten.
Hat jemand eine Ahnung, woran das liegen könnte?

Hier noch die Klasse UploadTableModel, mein TableModel:
```package pcworld.fider.gui.uploadTable;

import java.io.File;

import javax.swing.table.DefaultTableModel;

import pcworld.fider.gui.GUIManager;
import pcworld.fider.upload.UploadFile;
import pcworld.fider.upload.abload.AbloadFile;

public class UploadTableModel extends DefaultTableModel {

	public static final int COLUMN_STATE = 0;
	public static final int COLUMN_FILE = 1;
	public static final int COLUMN_URL = 2;

	public UploadTableModel(Object[] arg0, int arg1) {
		super(arg0, arg1);
	}

	@Override
	public boolean isCellEditable(int row, int column) {
		return false;
	}

	@Override
	public Class<? extends Object> getColumnClass(int column) {
		switch (column) {
		case UploadTableModel.COLUMN_FILE:
			return File.class;
		case UploadTableModel.COLUMN_URL:
			System.out.println(column + ": AbloadFile.class");
			return AbloadFile.class;
		default:
			System.out.println(column + ": default - "
					+ super.getColumnClass(column));
			return super.getColumnClass(column);
		}
	}

	public void addFile(File file) {
		addRow(new Object[] { GUIManager.NOT_UPLOADED, file, null });
	}

}```

Gruß,
pcworld

Wahrscheinlich hast Du die Zelle editiert. Der Editor hat dann einen String reingeschrieben. Vielleicht liegt das Problem aber auch woanders.

Erweitere mal zum Test Dein Tabellenmodell und schau Dir die Ausgaben genau an. Ggf. im Debugger da anhalten: @Override public void setValueAt(Object value, int row, int column) { System.out.printf("Adding %s::%s at %dx%d", value == null ? null : value.getClass(), value, column, row); System.out.println(); super.setValueAt(value, row, column); }
Grüße,
-Ebenius

isCellEditable gibt “false” zurück.
Wieso ist jede zweite Zeile eine Zeilennummer wenn ich den Code aus dem Browser kopiere? :mad:

Stimmt zwar, trotzdem zeigt der StackTrace oben deutlich, dass setValueAt(…) aufgerufen wurde. Und zwar aus UploadAction.java, Zeile 115.

Weil Dein Browser klüger ist als die Forensoftware. :slight_smile: Nimm den Opera, der hat das Problem nicht.

Ebenius

Großes Dankeschön!
Das Problem war tatsächlich, dass ich bei setValueAt für diese Spalte einen String übergeben hatte, statt ein Objekt der Klasse AbloadFile :rolleyes: - könnte Java aber eigentlich eine Exception werfen, ich pflege ja nicht umsonst mein TableModel… :wink:

Ich habe jetzt noch ein kleines Problem:
Bevor ich diesen Fehler korrigiert habe, gab es auch Probleme bei der Renderung der Zelle. Jetzt wird wieder der gewünschte Wert angezeigt.
Aber wenn ich jetzt den Wert per - in die Zwischenablage kopiere, wird der Wert der Methode AbloadFile#toString (von mir nicht überschrieben) zurückgegeben: „pcworld.fider.upload.abload.AbloadFile@181ed9e“

Meine Frage: Wie geht das nochmal, seine eigene „CopyAction“ über getActionMap().put(„copy“, meineCopyAction) zu implementieren, welche Methoden muss ich von AbstractAction überschreiben?

Früher hatte ich den gleichen Wert, den mein TableCellRenderer zurückgibt, über AbloadFile#toString zurückgegeben. Das will ich aber nicht mehr, weil ich bei Renderer und Zwischenablage immer den gleichen Wert „returnen“ und somit möglichst flexibel sein will. Außerdem kann statt AbloadFile auch der Wert null sein. Der TableCellRenderer macht dann „-“ hin, und die CopyAction soll dasselbe machen.

Gruß,
pcworld

Noch was an den Unregistrierten ;-):

Wie Ebenius oben schon tlw. erwähnt hat, lässt sich der Wert trotzdem noch über TableModel#setValueAt ändern. Nur per Doppelklick auf die Zelle öffnet sich kein Editor.

Du kannst auch einfach bei dem entsprechenden Beitrag auf „Zitieren“ klicken und dann den Java-Code aus dem BBCode-Quelltext rauskopieren.

Gruß,
pcworld

Auf die Frage antworte ich mal nicht, weil sie Dein Problem nicht so sinnvoll löst. Copy & Paste wird per TransferHandler realisiert. Jeder Swing-Komponente kann man einen TransferHandler setzen. Normaler Weise kümmert sich darum das jeweilige UI-Delegate; im Falle der JTable also die BasicTableUI-Klasse. Diese setzt der Tabelle diesen TransferHandler:


    /**
     * Create a Transferable to use as the source for a data transfer.
     *
     * @param c  The component holding the data to be transfered.  This
     *  argument is provided to enable sharing of TransferHandlers by
     *  multiple components.
     * @return  The representation of the data to be transfered.
     *
     */
    protected Transferable createTransferable(JComponent c) {
        if (c instanceof JTable) {
            JTable table = (JTable) c;
            int[] rows;
            int[] cols;

            if (!table.getRowSelectionAllowed() && !table.getColumnSelectionAllowed()) {
                return null;
            }

            if (!table.getRowSelectionAllowed()) {
                int rowCount = table.getRowCount();

                rows = new int[rowCount];
                for (int counter = 0; counter < rowCount; counter++) {
                    rows[counter] = counter;
                }
            } else {
                rows = table.getSelectedRows();
            }

            if (!table.getColumnSelectionAllowed()) {
                int colCount = table.getColumnCount();

                cols = new int[colCount];
                for (int counter = 0; counter < colCount; counter++) {
                    cols[counter] = counter;
                }
            } else {
                cols = table.getSelectedColumns();
            }

            if (rows == null || cols == null || rows.length == 0 || cols.length == 0) {
                return null;
            }

            StringBuffer plainBuf = new StringBuffer();
            StringBuffer htmlBuf = new StringBuffer();

            htmlBuf.append("<html>
<body>
<table>
");

            for (int row = 0; row < rows.length; row++) {
                htmlBuf.append("<tr>
");
                for (int col = 0; col < cols.length; col++) {
                    Object obj = table.getValueAt(rows[row], cols[col]);
                    String val = ((obj == null) ? "" : obj.toString());
                    plainBuf.append(val + "	");
                    htmlBuf.append("  <td>" + val + "</td>
");
                }
                // we want a newline at the end of each line and not a tab
                plainBuf.deleteCharAt(plainBuf.length() - 1).append("
");
                htmlBuf.append("</tr>
");
            }

            // remove the last newline
            plainBuf.deleteCharAt(plainBuf.length() - 1);
            htmlBuf.append("</table>
</body>
</html>");

            return new BasicTransferable(plainBuf.toString(), htmlBuf.toString());
        }

        return null;
    }

    public int getSourceActions(JComponent c) {
        return COPY;
    }

}```
Speichere Dir einfach den Quelltext als eigene TransferHandler-Klasse ab, modifiziere ihn nach Deinen Wünschen und füge ihn dann der Tabelle hinzu. Fertig.

Happy Hacking!
Ebenius

Das Problem gibt’s nur bei Byte-Welt und wäre demnach lösbar, ohne den zu benutzenden Browser vorzuschreiben, wenn die nötige Kompetenz vorhanden wäre.
Zitieren und dan kopieren, wie oben vorgeschlagen, wäre bestenfalls ein Workaround, ergibt aber bei mir dies:
1.
public void updateLinkArea() {
2.
UploadTableModel model = guiManager.getUploadTableModel();
3.
// (…)
4.
for (int i = 0; i < model.getRowCount(); i++) {
5.
// (…)
6.
// ******************************
7.
// hier kommt eine Exception
8.
AbloadFile ablFile = (AbloadFile) model.getValueAt(i,
9.
UploadTableModel.COLUMN_URL);
10.
// UploadTableModel.COLUMN_URL ist 2 (static final)
11.
// ******************************
12.
// (…)
13.
}
14.
// (…)
15.
}

Das Problem gibt’s in fast allen Foren in denen Quelltext mit Zeilennummern gelistet wird. Die meisten realisieren die Zeilennumerierung per

  • -Tag.

    Ebenius

  • Wozu die Nummerierung überhaupt gut ist, ausser um Probleme zu verursachen und Leute zu ärgern, ist mir schleierhaft. Schliesslich trägt sie nur äusserst wenig zum Informationsgehalt des Quellcodes bei, wenn überhaupt. Zeilennummern brauch ich jedenfalls so gut wie gar nicht beim Quellcode im Browser. Da zudem niemand die Kompetenzen zu haben scheint, um die dadurch verursachten Probleme zu lösen, lassen die klügeren unter den Foren die Nummerierung wohl lieber weg.

    Wie polemisch. :slight_smile: Ich beziehe mich oft auf Zeilennummern im Quelltext. Compiler übrigens auch gern. :wink:

    Ebenius

    Ich habe mir jetzt mal die Klasse TableTransferHandler in Eclipse rauskopiert.
    Allerdings kann mir Eclipse keinen Vorschlage machen, wo sich die Klasse BasicTransferable befinden könnte - wo kriege ich die her?:
    return new BasicTransferable(plainBuf.toString(), htmlBuf.toString());

    Zur Zeilennummerierung:
    Also ich finde sie schon praktisch. Wenn man auf „Zitieren“ klickt und nicht den WYSIWYG-Editor benutzt, kann man den Code ohne Probleme rauskopieren.
    Man könnte auch ein JavaScript programmieren, welches die Nummerierung ein- und ausschaltet - so schwierig ist das auch wieder nicht. Falls sich da niemand drum kümmert, programmiere ich mir ein Greasemonkey-Script für den guten alten Firefox :wink:

    Gruß,
    pcworld

    CTRL+SHIFT+T, dann „BasicTransferable“ eintippen. Und schon zeigt Dir Eclipse die Klasse javax.swing.plaf.basic.BasicTransferable an. :slight_smile: Die ist allerdings package-visible und damit kannst Du sie nicht so einfach benutzen. Wirst Dir dann also Dein eigenes Transferable bauen müssen.

    Ebenius

    Finde ich auch irgendwo den Quellcode dieser Klasse? Ich will da auch kein Riesenprojekt draus machen… :slight_smile:

    Gruß,
    pcworld

    Brauchst ne Eclipse-Schulung, oder? :wink: Click auf „Attach Source“. Und wähl dir den Quelltext aus Deinem JDK aus. Liegt direkt im JDK-Verzeichnis als src.zip. Wenn es da nicht liegt, dann hast Du kein Sun JDK (sondern beispielsweise nur das JRE) installiert. Das wäre dann zu ändern.

    Ebenius

    Probier ich gleich mal aus.

    Noch was wegen der Zeilennummerierung: Ich habe für Mozilla Firefox gerade ein Script programmiert, welches die Zeilennummerierung hier in Byte-Welt ein- und ausschalten lässt. Alles weitere in diesem Thread: Java-Code-Copy ohne Zeilennummerierung

    Gruß,
    pcworld

    Danke an euch!

    Jetzt funktioniert alles bestens (hoffe ich zumindest ;-))!
    Wenn das value einer Tabellenzelle null ist, muss man das noch extra behandeln.
    Das ist jetzt mein Code: TableTransferHandler (verändert) - BasicTransferable (nicht verändert, nur aus der JDK-Source rauskopiert)

    Gruß,
    pcworld

    [quote=Ebenius]Wie polemisch. Ich beziehe mich oft auf Zeilennummern im Quelltext. Compiler übrigens auch gern.

    Ebenius[/quote]
    Du hast recht.

    (Ich sprach allerdings vom Browser und daß sogar große Java Foren dort keine Nummerierung haben, ganz sicher aus triftigen Gründen. Ich mach mir jedoch keine grossen Hoffnungen, daß das hier auch so gemacht wird, ist klar, wollte es nur mal gesagt haben. Glücklicherweise ist das Forum noch klein, so daß sein Nummerierungsproblem mich nur ab und zu ärgert. Vielleicht ist das Problem aber auch mit ein Grund, weshalb das Forum nicht schneller Zuwachs erhält. Sozusagen ein “Abschreckeffekt”.)