FlexTable Errors with setWidget

186 views
Skip to first unread message

jobo...@gmail.com

unread,
May 18, 2006, 1:30:18 PM5/18/06
to Google Web Toolkit
>From the description of FlexTable, it sounded like it could handle
adding new cells anywhere, regardless of if those rows/cells existed.
Somehow I managed to break it though. I'm also pretty new to Java, so
that could be my problem too.


Anyway, the code below works fine:
Button myButton2=new Button("Add Cell");
myButton2.addClickListener(new ClickListener(){
public void onClick(Widget sender){
t.insertRow(2);
t.setText(2,0,"blah");
}
});

Behaves as expected, every time you click the button it adds a new row,
with a cell in col 1 that says "blah." Now when I try to do this with a
text box:
Button myButton2=new Button("Add Cell");
myButton2.addClickListener(new ClickListener(){
public void onClick(Widget sender){
t.insertRow(2);
t.setWidget(2,0,new TextBox());
}
});

It only works the first time, and every other time. The second time
(and each even try), the console tells me this:
[ERROR] Uncaught exception escaped
java.lang.RuntimeException: JavaScript method
'@com.google.gwt.user.client.impl.DOMImpl::removeChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;)'
threw an exception
at
com.google.gwt.dev.shell.ie.ModuleSpaceIE6.invokeNative(ModuleSpaceIE6.java:365)
at
com.google.gwt.dev.shell.ie.ModuleSpaceIE6.invokeNativeVoid(ModuleSpaceIE6.java:254)
at
com.google.gwt.dev.shell.JavaScriptHost.invokeNativeVoid(JavaScriptHost.java:127)
at
com.google.gwt.user.client.impl.DOMImpl.removeChild(DOMImpl.java:222)
at com.google.gwt.user.client.DOM.removeChild(DOM.java:716)
at
com.google.gwt.user.client.ui.HTMLTable.removeWidget(HTMLTable.java:737)
at
com.google.gwt.user.client.ui.HTMLTable.clearCell(HTMLTable.java:332)
at
com.google.gwt.user.client.ui.HTMLTable.cleanCell(HTMLTable.java:695)
at
com.google.gwt.user.client.ui.HTMLTable.setWidget(HTMLTable.java:544)
at
com.company.client.prototype_double_row_app$2.onClick(prototype_double_row_app.java:75)
Caused by: com.google.gwt.core.client.JavaScriptException: JavaScript
Error exception: Invalid argument.
at
com.google.gwt.dev.shell.ModuleSpace.createJavaScriptException(ModuleSpace.java:267)
at
com.google.gwt.dev.shell.ie.ModuleSpaceIE6.exceptionCaught(ModuleSpaceIE6.java:76)
at
com.google.gwt.dev.shell.JavaScriptHost.exceptionCaught(JavaScriptHost.java:31)
at
com.google.gwt.dev.shell.StaticJavaDispatch.callMethod(StaticJavaDispatch.java:45)
at
com.google.gwt.dev.shell.ie.IDispatchProxy.invoke(IDispatchProxy.java:117)
at
com.google.gwt.dev.shell.ie.IDispatchImpl.Invoke(IDispatchImpl.java:199)
at
com.google.gwt.dev.shell.ie.IDispatchImpl.method6(IDispatchImpl.java:108)
at
org.eclipse.swt.internal.ole.win32.COMObject.callback6(COMObject.java:117)
at
org.eclipse.swt.internal.ole.win32.IDispatch.Invoke(IDispatch.java:64)
at
org.eclipse.swt.ole.win32.OleAutomation.invoke(OleAutomation.java:487)


I'm not even sure why remove child is getting called, any ideas?

jobo...@gmail.com

unread,
May 18, 2006, 2:30:49 PM5/18/06
to Google Web Toolkit
I get the same error with this code:

public class prototype_double_row_app implements EntryPoint {

/**
* This is the entry point method.
*/
public void onModuleLoad() {
FlexTable t = new FlexTable();

TextBox tb=new TextBox();


Button myButton=new Button("Add (from after table)");
myButton.addClickListener(new ClickListener(){
public void onClick(Widget sender){
t.insertRow(1);
t.setText(1,0,"More text");
}
});

Button myButton2=new Button("Add (from in table");


myButton2.addClickListener(new ClickListener(){
public void onClick(Widget sender){
t.insertRow(2);

t.setText(2,0,"More Text");
}
});

t.setBorderWidth(5);

t.setText(0, 0, "");
t.setWidget(1, 0, myButton2);
t.setText(2, 0, "");
t.setText(3, 0, "");

RootPanel.get().add(t);
RootPanel.get().add(myButton);
}
}

The two "onClick" fuctions do almost exactly the same thing (they used
to be identifical). I changed them to demonstrate this:
1)Click the In table button as much as you want, it adds rows below it
2)Click the after table button once. It generates the exception in the
above message
3) I can still click the "after table" button, but the in table button
now does nothing.


If I change the setText to column 1 (or any column that the button
isn't it) it works fine. What's going on?

Michael Lovitt

unread,
May 22, 2006, 2:51:14 PM5/22/06
to Google Web Toolkit
I encountered the same error; I think there's a bug in the toolkit:

The HTMLTable class (from which FlexTable extends) has an internal data
structure, widgetMap, which keeps track of the cells in which each of
its widgets live. This structure is updated whenever the contents of
one of its cells changes. Unfortunately, it is NOT updated whenever
insertRow() is called, so its coordinates get out of sync.

Does the Google team accept patches for bugs like this? Is there a
better way to report them than here on this forum?

Michael

Emily

unread,
May 25, 2006, 6:46:50 AM5/25/06
to Google Web Toolkit
I'll take a look at it, as I think you are right!

rroland99

unread,
May 28, 2006, 2:52:43 PM5/28/06
to Google Web Toolkit
This definately appears to be a bug. The internal WidgetMap is keyed
with a string composed of the row and column ids when the widget is
added to the table. Unfortunately when rows (or columns, I suspect)
are added or removed the table the keys for all other widgets in the
table (after the altered cell) are altered (since their row or column
have changed). When these widgets are looked-up in the WidgetMap at a
later time the proper widgets are not found - leading to all sorts of
fun errors.

I note that this bug is not currently listed on the online bug-tracker.

Michael - you mentioned a patch for this bug - Do you happen to have
one already? If so, could you possibly post it? It would save me the
hassle of trying to fix it myself. -- Thanks!

rroland99

unread,
May 28, 2006, 7:13:17 PM5/28/06
to Google Web Toolkit
Below is a hacked HTMLTable.java that fixes up the errant widgetMap
entries when the structure of the table changes. Hopefully the toolkit
developers will correct this in a cleaner way. I was just trying to
make it work, the fact the widgetMap is private prevents this from
being patched via a subclass and unfortunately requires the
gwt-user.jar to be cracked open, patched and re-jar'd; however, if you
want to alter the structure of an HTMLTable or FlexTable at runtime
this appears necessary...

--Ralph

/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
may not
* use this file except in compliance with the License. You may obtain
a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
the
* License for the specific language governing permissions and
limitations under
* the License.
*/
package com.google.gwt.user.client.ui;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import
com.google.gwt.user.client.ui.HasHorizontalAlignment.HorizontalAlignmentConstant;
import
com.google.gwt.user.client.ui.HasVerticalAlignment.VerticalAlignmentConstant;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
* HTMLTable contains the common table algorithms for
* {@link com.google.gwt.user.client.ui.Grid} and
* {@link com.google.gwt.user.client.ui.FlexTable}.
*
* <p>
* <img class='gallery' src='Table.png'/>
* </p>
*/
public abstract class HTMLTable extends Panel implements
SourcesTableEvents {

/**
* This class contains methods used to format a table's cells.
*/
public class CellFormatter {

/**
* Adds a style to the specified cell.
*
* @param row the cell's row
* @param column the cell's column
* @param styleName the style name to be added
* @see UIObject#addStyleName(String)
*/
public void addStyleName(int row, int column, String styleName) {
prepareCell(row, column);
UIObject.setStyleName(getElement(row, column), styleName, true);
}

/**
* Gets the TD element representing the specified cell.
*
* @param row the row of the cell to be retrieved
* @param column the column of the cell to be retrieved
* @return the column's TD element
* @throws IndexOutOfBoundsException
*/
public Element getElement(int row, int column) {
checkCellBounds(row, column);
return DOM.getChild(rowFormatter.getElement(row), column);
}

/**
* Gets a style from a specified row.
*
* @param row the row of the cell which the style while be added to
* @param column the column of the cell which the style will be
added to
* @param styleName the style name to be added
* @see UIObject#getStyleName(String)
* @throws IndexOutOfBoundsException
*/
public String getStyleName(int row, int column) {
return DOM.getAttribute(getElement(row, column), "className");
}

/**
* Removes a style from the specified cell.
*
* @param row the cell's row
* @param column the cell's column
* @param styleName the style name to be removed
* @see UIObject#removeStyleName(String)
* @throws IndexOutOfBoundsException
*/
public void removeStyleName(int row, int column, String styleName)
{
checkCellBounds(row, column);
UIObject.setStyleName(getElement(row, column), styleName, false);
}

/**
* Sets the horizontal and vertical alignment of the specified
cell's
* contents.
*
* @param row the row of the cell whose alignment is to be set
* @param column the cell whose alignment is to be set
* @param hAlign the cell's new horizontal alignment as specified
in
* {@link HasHorizontalAlignment}
* @param vAlign the cell's new vertical alignment as specified in
* {@link HasVerticalAlignment}
* @throws IndexOutOfBoundsException
*/
public void setAlignment(int row, int column,
HorizontalAlignmentConstant hAlign, VerticalAlignmentConstant
vAlign) {
prepareCell(row, column);
setHorizontalAlignment(row, column, hAlign);
setVerticalAlignment(row, column, vAlign);
}

/**
* Sets the height of the specified cell.
*
* @param row the row of the cell whose height is to be set
* @param column the cell whose height is to be set
* @param height the cell's new height, in CSS units
* @throws IndexOutOfBoundsException
*/
public void setHeight(int row, int column, String height) {
prepareCell(row, column);
DOM
.setAttribute(getCellElement(tableElem, row, column), "height",
height);
}

/**
* Sets the horizontal alignment of the specified cell.
*
* @param row the row of the cell whose alignment is to be set
* @param column the cell whose alignment is to be set
* @param align the cell's new horizontal alignment as specified in
* {@link HasHorizontalAlignment}.
* @throws IndexOutOfBoundsException
*/
public void setHorizontalAlignment(int row, int column,
HorizontalAlignmentConstant align) {
prepareCell(row, column);
DOM.setStyleAttribute(getCellElement(tableElem, row, column),
"textAlign", align.getTextAlignString());
}

/**
* Sets the style name associated with the specified cell.
*
* @param row the row of the cell whose style name is to be set
* @param column the column of the cell whose style name is to be
set
* @param styleName the new style name
* @see UIObject#setStyleName(String)
* @throws IndexOutOfBoundsException
*/
public void setStyleName(int row, int column, String styleName) {
prepareCell(row, column);
setAttr(row, column, "className", styleName);
}

/**
* Sets the vertical alignment of the specified cell.
*
* @param row the row of the cell whose alignment is to be set
* @param column the cell whose alignment is to be set
* @param align the cell's new vertical alignment as specified in
* {@link HasVerticalAlignment}.
* @throws IndexOutOfBoundsException
*/
public void setVerticalAlignment(int row, int column,
VerticalAlignmentConstant align) {
prepareCell(row, column);
DOM.setStyleAttribute(getCellElement(tableElem, row, column),
"verticalAlign", align.getVerticalAlignString());
}

/**
* Sets the width of the specified cell.
*
* @param row the row of the cell whose width is to be set
* @param column the cell whose width is to be set
* @param width the cell's new width, in CSS units
* @throws IndexOutOfBoundsException
*/
public void setWidth(int row, int column, String width) {
// Give the subclass a chance to prepare the cell.
prepareCell(row, column);
DOM.setAttribute(getCellElement(tableElem, row, column), "width",
width);
}

/**
* Sets whether the specified cell will allow word wrapping of its
contents.
*
* @param row the row of the cell whose word-wrap is to be set
* @param column the cell whose word-wrap is to be set
* @param wrap <code>false </code> to disable word wrapping in this
cell
* @throws IndexOutOfBoundsException
*/
public void setWordWrap(int row, int column, boolean wrap) {
prepareCell(row, column);
DOM.setStyleAttribute(getElement(row, column), "whiteSpace", wrap
? ""
: "nowrap");
}

/**
* Gets the element associated with a cell. If it does not exist
and the
* subtype allows creation of elements, creates it.
*
* @param row the cell's row
* @param column the cell's column
* @return the cell's element
* @throws IndexOutOfBoundsException
*/
protected Element ensureElement(int row, int column) {
prepareCell(row, column);
return DOM.getChild(rowFormatter.ensureElement(row), column);
}

/**
* Convenience methods to get an attribute on a cell.
*
* @param row cell's row
* @param column cell's column
* @param attr attribute to get
* @return the attribute's value
* @throws IndexOutOfBoundsException
*/
protected String getAttr(int row, int column, String attr) {
Element elem = getElement(row, column);
return DOM.getAttribute(elem, attr);
}

/**
* Convenience methods to set an attribute on a cell.
*
* @param row cell's row
* @param column cell's column
* @param attr attribute to set
* @param value value to set
* @throws IndexOutOfBoundsException
*/
protected void setAttr(int row, int column, String attrName, String
value) {
Element elem = ensureElement(row, column);
DOM.setAttribute(elem, attrName, value);
}

/**
* Native method to get a cell's element.
*
* @param table the table element
* @param row the row of the cell
* @param col the column of the cell
* @return the element
*/
private native Element getCellElement(Element table, int row, int
col) /*-{
return table.rows[row].cells[col];
}-*/;

/**
* Gets the TD element representing the specified cell unsafely
(meaning
* that it doesn't ensure that the row and column are valid).
*
* @param row the row of the cell to be retrieved
* @param column the column of the cell to be retrieved
* @return the column's TD element
*/
private Element getRawElement(int row, int column) {
return getCellElement(tableElem, row, column);
}
}

/**
* This class contains methods used to format a table's rows.
*/
public class RowFormatter {

/**
* Adds a style to the specified row.
*
* @param row the row to which the style while be added
* @param styleName the style name to be added
* @see UIObject#addStyleName(String)
* @throws IndexOutOfBoundsException
*/
public void addStyleName(int row, String styleName) {
UIObject.setStyleName(ensureElement(row), styleName, true);
}

/**
* Gets the TR element representing the specified row.
*
* @param row the row whose TR element is to be retrieved
* @return the row's TR element
* @throws IndexOutOfBoundsException
*/
public Element getElement(int row) {
checkRowBounds(row);
return DOM.getChild(bodyElem, row);
}

/**
* Gets a style from a specified row.
*
* @param row the row to which the style while be added
* @see UIObject#getStyleName(String)
* @throws IndexOutOfBoundsException
*/
public String getStyleName(int row) {
return DOM.getAttribute(getElement(row), "className");
}

/**
* Removes a style from the specified row.
*
* @param row the row to which the style while be removed
* @param styleName the style name to be removed
* @see UIObject#removeStyleName(String)
* @throws IndexOutOfBoundsException
*/
public void removeStyleName(int row, String styleName) {
UIObject.setStyleName(getElement(row), styleName, false);
}

/**
* Sets the style name associated with the specified row.
*
* @param row the row whose style name is to be set
* @param styleName the new style name
* @see UIObject#setStyleName(String)
* @throws IndexOutOfBoundsException
*/
public void setStyleName(int row, String styleName) {
Element elem = ensureElement(row);
DOM.setAttribute(elem, "className", styleName);
}


/**
* Sets the vertical alignment of the specified row.
*
* @param row the row whose alignment is to be set
* @param align the row's new vertical alignment as specified in
* {@link HasVerticalAlignment}
* @throws IndexOutOfBoundsException
*/
public void setVerticalAlign(int row, VerticalAlignmentConstant
align) {
DOM.setStyleAttribute(ensureElement(row), "verticalAlign", align
.getVerticalAlignString());
}

/**
* Ensure the TR element representing the specified row exists for
* subclasses that allow dynamic addition of elements.
*
* @param row the row whose TR element is to be retrieved
* @return the row's TR element
* @throws IndexOutOfBoundsException
*/
protected Element ensureElement(int row) {
prepareRow(row);
return DOM.getChild(bodyElem, row);
}
}

/**
* Table's body
*/
private Element bodyElem;

/**
* Current cell formatter
*/
private CellFormatter cellFormatter;

/**
* Current row formatter
*/
private RowFormatter rowFormatter;

/**
* Table element
*/
private Element tableElem;

/**
* Current table listener
*/
private TableListenerCollection tableListeners;

/**
* The element map, used to quickly look up the Widget in a
particular cell.
* We have to use a map here, because hanging references to Widgets
from
* Elements would cause memory leaks.
*/
protected Map widgetMap = new HashMap();

public HTMLTable() {
tableElem = DOM.createTable();
bodyElem = DOM.createTBody();
DOM.appendChild(tableElem, bodyElem);
setElement(tableElem);
sinkEvents(Event.ONCLICK);
}

/**
* This method is not implemented, as widgets must be added to
specific cells
* in the table.
*/
public boolean add(Widget w) {
return false;
}

public void addTableListener(TableListener listener) {
if (tableListeners == null)
tableListeners = new TableListenerCollection();
tableListeners.add(listener);
}

/**
* Removes all widgets from this table, but does not remove other
HTML or text
* contents of cells.
*/
public void clear() {
for (int row = 0; row < getRowCount(); ++row) {
for (int col = 0; col < getCellCount(row); ++col) {
Widget child = getWidget(row, col);
if (child != null) {
removeWidget(row, col, child);
}
}
}
}

/**
* Clears the given row and column. If it contains a Widget, it will
be
* removed from the table. If not, its contents will simply be
cleared.
*
* @param row the widget's column
* @param column the widget's column
* @return true if a widget was removed
* @throws IndexOutOfBoundsException
*/
public boolean clearCell(int row, int column) {
Element td = getCellFormatter().getElement(row, column);
boolean b = internalClearCell(row, column, td);
return b;
}


/**
* Gets the number of cells in a given row.
*
* @param row the row whose cells are to be counted
* @return the number of cells present in the row
*/
public abstract int getCellCount(int row);

/**
* Gets the {@link CellFormatter} associated with this table.
*
* @return this table's cell formatter
*/
public CellFormatter getCellFormatter() {
return cellFormatter;
}

/**
* Gets the amount of padding that is added around all cells.
*
* @return the cell padding, in pixels
*/
public int getCellPadding() {
return DOM.getIntAttribute(tableElem, "cellPadding");
}

/**
* Gets the amount of spacing that is added around all cells.
*
* @return the cell spacing, in pixels
*/
public int getCellSpacing() {
return DOM.getIntAttribute(tableElem, "cellSpacing");
}

/**
* Gets the HTML contents of the specified cell.
*
* @param row the cell's row
* @param column the cell's column
* @return the cell's HTML contents
* @throws IndexOutOfBoundsException
*/
public String getHTML(int row, int column) {
return DOM.getInnerHTML(cellFormatter.getElement(row, column));
}

/**
* Gets the number of rows present in this table.
*
* @return the table's row count
*/
public abstract int getRowCount();

/**
* Gets the RowFormatter associated with this table.
*
* @return the table's row formatter
*/
public RowFormatter getRowFormatter() {
return rowFormatter;
}

/**
* Gets the text within the specified cell.
*
* @param row the cell's row
* @param column the cell's column
* @return the cell's text contents
* @throws IndexOutOfBoundsException
*/
public String getText(int row, int column) {
checkCellBounds(row, column);
Element e = cellFormatter.getElement(row, column);
return DOM.getInnerText(e);
}

/**
* Gets the widget in the specified cell.
*
* @param row the cell's row
* @param column the cell's column
* @return the widget in the specified cell, or <code>null</code> if
none is
* present
* @throws IndexOutOfBoundsException
*/
public Widget getWidget(int row, int column) {
checkCellBounds(row, column);
String key = computeKey(row, column);
return (Widget) widgetMap.get(key);
}

/**
* Determines whether the specified cell exists.
*
* @param row the cell's row
* @param column the cell's column
* @return <code>true</code> if the specified cell exists
*/
public boolean isCellPresent(int row, int column) {
if (row >= getRowCount() && row < 0) {
return false;
}
if (column < 0 || column >= getCellCount(row)) {
return false;
} else {
return true;
}
}

public Iterator iterator() {
return widgetMap.values().iterator();
}

public void onBrowserEvent(Event event) {
switch (DOM.eventGetType(event)) {
case Event.ONCLICK: {
if (tableListeners != null) {
// Find out which cell was actually clicked.
Element td = getEventTargetCell(event);
if (td == null)
return;
Element tr = DOM.getParent(td);
Element body = DOM.getParent(tr);
int row = DOM.getChildIndex(body, tr);
int column = DOM.getChildIndex(tr, td);

// Fire the event.
tableListeners.fireCellClicked(this, row, column);
}
break;
}
}
}

public boolean remove(Widget widget) {
// Make sure the Widget is actually contained in this table.
if (widget.getParent() != this)
return false;

// Get the row and column of the cell containing this widget.
Element td = DOM.getParent(widget.getElement());
Element tr = DOM.getParent(td);
int row = DOM.getChildIndex(bodyElem, tr);
int column = DOM.getChildIndex(tr, td);

removeWidget(row, column, widget);
return true;
}

public void removeTableListener(TableListener listener) {
if (tableListeners != null)
tableListeners.remove(listener);
}

/**
* Sets the width of the table's border. This border is displayed
around all
* cells in the table.
*
* @param width the width of the border, in pixels
*/
public void setBorderWidth(int width) {
DOM.setAttribute(tableElem, "border", "" + width);
}

/**
* Sets the amount of padding to be added around all cells.
*
* @param padding the cell padding, in pixels
*/
public void setCellPadding(int padding) {
DOM.setIntAttribute(tableElem, "cellPadding", padding);
}

/**
* Sets the amount of spacing to be added around all cells.
*
* @return the cell spacing, in pixels
*/
public void setCellSpacing(int spacing) {
DOM.setIntAttribute(tableElem, "cellSpacing", spacing);
}

/**
* Sets the HTML contents of the specified cell.
*
* @param row the cell's row
* @param column the cell's column
* @param html the cell's HTML contents
* @throws IndexOutOfBoundsException
*/
public void setHTML(int row, int column, String html) {
prepareCell(row, column);
Element td = cleanCell(row, column);
DOM.setInnerHTML(td, html);
}

/**
* Sets the text within the specified cell.
*
* @param row the cell's row
* @param column cell's column
* @param text the cell's text contents
* @throws IndexOutOfBoundsException
*/
public void setText(int row, int column, String text) {
prepareCell(row, column);
Element td = cleanCell(row, column);
DOM.setInnerText(td, text);
}

/**
* Sets the widget within the specified cell.
*
* <p>
* Inherited implementations may either throw IndexOutOfBounds
exception if
* the cell does not exist, or allocate a new cell to store the
content
* </p>
*
* @param widget The widget to be added
* @param row the cell's row
* @param column the cell's column
* @throws IndexOutOfBoundsException
*/
public void setWidget(int row, int column, Widget widget) {
prepareCell(row, column);

// Attach it to the cell's TD.
Element td = cleanCell(row, column);
DOM.appendChild(td, widget.getElement());

// Add the widget to the map.
widgetMap.put(computeKey(row, column), widget);

// Set the widget's parent.
adopt(widget);
}

/**
* Bounds checks that the cell exists at the specified location
*
* @param row cell's row
* @param column cell's column
* @throws IndexOutOfBoundsException
*/
protected void checkCellBounds(int row, int column) {
checkRowBounds(row);
if (column < 0) {
throw new IndexOutOfBoundsException("Column " + column
+ " must be non-negative: " + column);
}
int cellSize = getCellCount(row);
if (cellSize < column) {
throw new IndexOutOfBoundsException("Column " + column
+ " does not exist, col at row " + row + " size is "
+ getCellCount(row) + "cell(s)");
}
}

/**
* Checks that the row is within the correct bounds.
*
* @param row row index to check
* @throws IndexOutOfBoundsException
*/
protected void checkRowBounds(int row) {
int rowSize = getRowCount();
if (row >= rowSize || row < 0) {
throw new IndexOutOfBoundsException("Row " + row
+ " does not exist, row size is " + getRowCount());
}
}

/**
* Gets the key associated with the cell. This key is used within the
widget
* map.
*
* @param row the cell's row
* @param column the cell's column
* @return the associated key
* @skip
*/
protected String computeKey(int row, int column) {
return row + "-" + column;
}

/**
* Creates a new cell. Override this method if the cell should have
initial
* contents.
*
* @return the newly created TD
*/
protected Element createCell() {
return DOM.createTD();
}

/**
* Gets the table's TBODY element.
*
* @return the TBODY element
*/
protected Element getBodyElement() {
return bodyElem;
}

/**
* Directly ask the underlying DOM what the cell count on the given
row is.
*
* @param row the row
* @return number of columns in the row
*/
protected int getDOMCellCount(int row) {
return DOM.getChildCount(DOM.getChild(bodyElem, row));
}

/**
* Directly ask the underlying DOM what the row count is.
*
* @return Returns the number of rows in the table
*/
protected int getDOMRowCount() {
return DOM.getChildCount(bodyElem);
}

/**
* Inserts a new cell into the specified row.
*
* @param row the row into which the new cell will be inserted
* @param column the column before which the cell will be inserted
* @throws IndexOutOfBoundsException
*/
protected void insertCell(int row, int column) {
Element tr = DOM.getChild(bodyElem, row);
Element td = createCell();
DOM.insertChild(tr, td, column);
fixupWidgetMap(row, 0, column, 1);
}

/**
* Inserts a number of cells before the specified cell.
*
* @param row the row into which the new cells will be inserted
* @param column the column before which the new cells will be
inserted
* @param count number of cells to be inserted
* @throws IndexOutOfBoundsException
*/
protected void insertCells(int row, int column, int count) {
Element tr = DOM.getChild(bodyElem, row);

for (int i = column; i < column + count; i++) {
Element td = createCell();
DOM.insertChild(tr, td, i);
}
fixupWidgetMap(row, 0, column, count);
}

/**
* Inserts a new row into the table.
*
* @param beforeRow the index before which the new row will be
inserted
* @return the index of the newly-created row
* @throws IndexOutOfBoundsException
*/
protected int insertRow(int beforeRow) {
// Specifically allow the row count as an insert position.
if (beforeRow != getRowCount())
checkRowBounds(beforeRow);

Element tr = DOM.createTR();
DOM.insertChild(bodyElem, tr, beforeRow);
fixupWidgetMap(beforeRow, 1, 0, 0);
return beforeRow;
}

/**
* Does actual clearing, used by clearCell and cleanCell. All
HTMLTable
* methods should use internalClearCell rather than clearCell, as
clearCell
* may be overridden in subclasses to format an empty cell.
*/
protected boolean internalClearCell(int row, int column, Element td)
{
Widget widget = (Widget) widgetMap.get(computeKey(row, column));
if (widget != null) {
// If there is a widget, remove it.
removeWidget(row, column, widget);
return true;
} else {
// Otherwise, simply clear whatever text and/or HTML may be
there.
DOM.setInnerHTML(td, "");
return false;
}
}

/**
* Subclasses must implement this method. It allows them to decide
what to do
* just before a cell is accessed. If the cell already exists, this
method
* must do nothing. Otherwise, a subclass must either ensure that the
cell
* exists or throw an {@link IndexOutOfBoundsException}.
*
* @param row the cell's row
* @param column the cell's column
*/
protected abstract void prepareCell(int row, int column);

/**
* Subclasses must implement this method. It allows them to decide
what to do
* just before a row is accessed. If the row already exists, this
method must
* do nothing. Otherwise, a subclass must either ensure that the row
exists or
* throw an {@link IndexOutOfBoundsException}.
*
* @param row the cell's row
*/
protected abstract void prepareRow(int row);

/**
* Removes the specified cell from the table.
*
* @param row the row of the cell to remove
* @param column the column of cell to remove
* @throws IndexOutOfBoundsException
*/
protected void removeCell(int row, int column) {
checkCellBounds(row, column);
Element td = cleanCell(row, column);
Element tr = DOM.getChild(bodyElem, row);
DOM.removeChild(tr, td);
fixupWidgetMap(row, 0, column, -1);
}

/**
* Removes the specified row from the table.
*
* @param row the index of the row to be removed
* @throws IndexOutOfBoundsException
*/
protected void removeRow(int row) {
int columnCount = getCellCount(row);
for (int column = 0; column < columnCount; ++column) {
cleanCell(row, column);
}
DOM.removeChild(bodyElem, DOM.getChild(bodyElem, row));
fixupWidgetMap(row, -1, 0, 0);
}

/**
* Sets the table's CellFormatter.
*
* @param cellFormatter the table's cell formatter
*/
protected void setCellFormatter(CellFormatter cellFormatter) {
this.cellFormatter = cellFormatter;
}

/**
* Sets the table's RowFormatter.
*
* @param rowFormatter the table's row formatter
*/
protected void setRowFormatter(RowFormatter rowFormatter) {
this.rowFormatter = rowFormatter;
}

/**
* Removes any widgets, text, and HTML within the cell. This method
assumes
* that the requested cell already exists.
*
* @param row the cell's row
* @param column the cell's column
*/
private Element cleanCell(int row, int column) {
// Clear whatever is in the cell.
Element td = getCellFormatter().getRawElement(row, column);
internalClearCell(row, column, td);
return td;
}

/**
* Determines the TD associated with the specified event.
*
* @param event the event to be queried
* @return the TD associated with the event, or <code>null</code> if
none is
* found.
*/
private Element getEventTargetCell(Event event) {
Element td = DOM.eventGetTarget(event);
while (!DOM.getAttribute(td, "tagName").equalsIgnoreCase("td")) {
// If we run out of elements, or run into the table itself, then
give up.
if ((td == null) || DOM.compare(td, getElement()))
return null;
td = DOM.getParent(td);
}

return td;
}

/**
* Removes the given widget from a cell. The widget must not be
* <code>null</code>.
*
* @param row cell's row
* @param column cell's column
* @param widget widget to be removed
*/
private boolean removeWidget(int row, int column, Widget widget) {
// Clear the widget's parent.
disown(widget);

// Remove the widget from the map.
String key = computeKey(row, column);
widgetMap.remove(key);

// And disconnect it from the TD it's contained in.
Element td = getCellFormatter().getRawElement(row, column);
Element child = widget.getElement();
DOM.removeChild(td, child);
return true;
}

private void fixupWidgetMap(int startRow, int addRow, int startCol,
int addCol) {
int maxRow = -1;
int maxCol = -1;
for (Iterator it = widgetMap.keySet().iterator(); it.hasNext(); ) {
String key = (String) it.next();
int row = parseRowFromKey(key);
int col = parseColFromKey(key);
if (row > maxRow) maxRow = row;
if (col > maxCol) maxCol = col;
}
if ((maxRow == -1) || (maxCol == -1)) return;
Object[][] holdArray = new Object[maxRow + 1][maxCol + 1];
for (Iterator it = widgetMap.keySet().iterator(); it.hasNext(); ) {
String key = (String) it.next();
int row = parseRowFromKey(key);
int col = parseColFromKey(key);
holdArray[row][col] = widgetMap.get(key);
}
widgetMap.clear();
for (int row = 0; row < holdArray.length; row++) {
if ((row == startRow) && (addRow == -1)) continue; //skipping a
row
for (int col = 0; col < holdArray[row].length; col++) {
if (holdArray[row][col] != null) {
int newRow = row;
int newCol = col;
if (row >= startRow) newRow += addRow;
if ((row == startRow) && (col >= startCol)) newCol += addCol;

widgetMap.put(computeKey(newRow, newCol),
holdArray[row][col]);
}
}
}
}

private int parseRowFromKey(String key) {
return Integer.parseInt(key.substring(0, key.indexOf("-")));
}

private int parseColFromKey(String key) {
return Integer.parseInt(key.substring(key.indexOf("-") + 1));
}
}

rroland99

unread,
May 29, 2006, 2:23:56 PM5/29/06
to Google Web Toolkit
I uncovered a bug in my patch, the modified code is below...

private void fixupWidgetMap(int startRow, int addRow, int startCol,
int addCol) {
int maxRow = -1;
int maxCol = -1;

int rowBump = 1;
if (addRow <= 0) rowBump = 0;
int colBump = addCol;
if (addCol <= 0) colBump = 0;


for (Iterator it = widgetMap.keySet().iterator(); it.hasNext(); ) {
String key = (String) it.next();
int row = parseRowFromKey(key);
int col = parseColFromKey(key);
if (row > maxRow) maxRow = row;
if (col > maxCol) maxCol = col;
}
if ((maxRow == -1) || (maxCol == -1)) return;

Object[][] holdArray = new Object[maxRow + rowBump + 1][maxCol +
colBump + 1];

Joel Webber

unread,
May 31, 2006, 11:18:35 AM5/31/06
to Google-We...@googlegroups.com
This is a confirmed bug, with a fix in progress.  Unfortunately, there's not really a simple workaround, but you won't have to wait too long for the fix.

joel.

mP

unread,
Jun 19, 2006, 9:25:47 AM6/19/06
to Google Web Toolkit
Hmm it seems that quite a few widgets have problems with removing due
to indexes or in this case rows/columns getting screwed up when an
earlier widget is inserted/removed.
I posted this a little earlier yesterday about selectTab indexes
becoming invalid if the a tab before or even current tab is removed.

I have encountered a similar problem - a ffew cells are created and
then recreated resulting in an exception.

Why is the row/column used as a key ? Why isnt the table rows/cells
themselves lookup using the row/column instead of using the nasty
composite key made up or row/cell ?

joel...@gmail.com

unread,
Jul 13, 2006, 5:00:03 AM7/13/06
to Google Web Toolkit
Hi,
I'm quite surprise, because I have the same problem with adding rows in
a flextable that produce the error :
"java.lang.RuntimeException: JavaScript method
'...@com.google.gwt.user.client.impl.DOMImpl::removeChild(Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/Element;)'
threw an exception " etc...

I found this post, I found the FIXED Issue #4387606 that link to this
discussion.
Does anyone knows when this problem with flextable will be fixed ?
tx

danox

unread,
Aug 2, 2006, 9:19:25 PM8/2/06
to Google Web Toolkit
having the same problem. anyone got a timeframe for when soon is? as in
we get a patch soon?

Reply all
Reply to author
Forward
0 new messages