I have a problem in implementing ColumnGenerator in Vaadin. I am reading the Book of Vaadin, at chapter 5.21.5. Generated Table Columns, but it seems to be a little bit of lack of instructions in how actually you can implement such functionality.
The book says you need to use generated columns when e.g. you have a column which value is given by values of the other columns, or when you want to format table columns in a particular way, then it makes an incomplete example where only an implementation of a single ColumnGenerator is given without actually showing how when this generator is used when rows are added to the table.
So I tried to implement such functionality by myself, here is what I got:
// Generated Table columns
Table tableWithGeneratedCol = new Table();
tableWithGeneratedCol.addContainerProperty(
"date", Date.class, null, "Date", null, null);
tableWithGeneratedCol.addContainerProperty(
"quantity", Double.class, null, "Quantity (l)", null, null);
tableWithGeneratedCol.addContainerProperty(
"price", Double.class, null, "Price (e/l)", null, null);
tableWithGeneratedCol.addContainerProperty(
"total", Double.class, null, "Total (e)", null, null);
tableWithGeneratedCol.addGeneratedColumn("date", new DateColumnGenerator());
tableWithGeneratedCol.addGeneratedColumn("quantity", new ValueColumnGenerator("%.2f l"));
tableWithGeneratedCol.addGeneratedColumn("price", new PriceColumnGeneretor());
tableWithGeneratedCol.addGeneratedColumn("total",new TotalColumnGenerator("%.2f e", "quantity", "price"));
// adding some fake rows
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(), // date column
new Double(10), // quantity column
new Double(10), // price column
// nothing here // total column
}, 1); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(16.2), // quantity column
new Double(21.2), // price column
// nothing here // total column
}, 2); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(10), // quantity column
new Double(22), // price column
// nothing here // total column
}, 3); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(10), // quantity column
new Double(20), // price column
// nothing here // total column
}, 4); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(15), // quantity column
new Double(19.12), // price column
}, 5); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(10), // quantity column
new Double(20.30), // price column
// nothing here // total column
}, 6); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(50), // quantity column
new Double(32.89), // price column
// nothing here // total column
}, 7); // itemId
tableWithGeneratedCol.setVisibleColumns(new Object[] {"date", "quantity", "price", "total"});
tableWithGeneratedCol.setPageLength(tableWithGeneratedCol.size());
layout.addComponent(tableWithGeneratedCol);
This is a snippet from a UI's init() method. As you can see, I create a table with 4 columns, Date, Quantity, Price, and Total (which is given by multiplying Quantity * Price).
I add some fake rows and for the total column I actually don't add any value because it's value should be determined by quantity and price while the table rows are created (the book doesn't tell how you actually do that, so I am guessing how it should work).
Here are the Table.ColumnGenerator implementations (I use a DateColumnGenerator for the Date column, ValueColumnGenerator for the quantity, PriceColumnGenerator for the price column, TotalColumnGenerator for the total column). So in order:
DateColumnGenerator:
public class DateColumnGenerator implements ColumnGenerator {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Component generateCell(Table source, Object itemId, Object columnId) {
Property<?> prop = source.getItem(itemId).getItemProperty(columnId);
if (prop.getType().equals(Date.class)) {
Date date = (Date) prop.getValue();
SimpleDateFormat sdf = new SimpleDateFormat("MMM dd, yyyy, HH:mm:ss");
return new Label(sdf.format(date));
}
return null;
}
}
ValueColumnGenerator (actually this is the only column generator implementation made by the book):
/** Formats the value in a column containing Double objects. */
class ValueColumnGenerator implements Table.ColumnGenerator {
/**
*
/
private static final long serialVersionUID = 1L;
String format; / Format string for the Double values. */
/**
* Creates double value column formatter with the given
* format string.
*/
public ValueColumnGenerator(String format) {
this.format = format;
}
/**
* Generates the cell containing the Double value.
* The column is irrelevant in this use case.
*/
public Component generateCell(Table source, Object itemId,
Object columnId) {
// Get the object stored in the cell as a property
Property<?> prop = source.getItem(itemId).getItemProperty(columnId);
if (prop.getType().equals(Double.class)) {
Label label = new Label(String.format(format, new Object[] { (Double) prop.getValue() }));
// Set styles for the column: one indicating that it's
// a value and a more specific one with the column
// name in it. This assumes that the column name
// is proper for CSS.
label.addStyleName("column-type-value");
label.addStyleName("column-" + (String) columnId);
return label;
}
return null;
}
}
I have just copied their code. So let's move forward.
PriceColumnGenerator:
public class PriceColumnGeneretor implements ColumnGenerator {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public Component generateCell(Table source, Object itemId, Object columnId) {
Property<?> prop = source.getItem(itemId).getItemProperty(columnId);
if (prop.getClass().equals(Double.class)) {
Double price = (Double) prop.getValue();
String priceStr = String.format("%.2 €", price);
return new Label(priceStr);
}
return null;
}
}
And finally, PriceColumnGenerator:
public class TotalColumnGenerator implements Table.ColumnGenerator {
/**
*
*/
private static final long serialVersionUID = 1L;
protected String format;
protected String quantityId;
protected String priceId;
public TotalColumnGenerator(String format, String quantityId, String priceId) {
this.format = format;
this.quantityId = quantityId;
this.priceId = priceId;
}
#Override
public Component generateCell(Table source, Object itemId, Object columnId) {
Double quantity = (Double) source.getItem(itemId).getItemProperty(this.quantityId).getValue();
Integer price = (Integer) source.getItem(itemId).getItemProperty(this.priceId).getValue();
String res = String.format(this.format, new Double(quantity * price));
return new Label(res);
}
}
But the result is this beautiful empty table, as you can see from the screenshot:
Now, how can I actually make this column generators work?
Thank you for the attention!
Don't add properties with the same name as the generated columns.
This should be enough
// Generated Table columns
Table tableWithGeneratedCol = new Table();
tableWithGeneratedCol.addGeneratedColumn("date", new DateColumnGenerator());
tableWithGeneratedCol.addGeneratedColumn("quantity", new ValueColumnGenerator("%.2f l"));
tableWithGeneratedCol.addGeneratedColumn("price", new PriceColumnGeneretor());
tableWithGeneratedCol.addGeneratedColumn("total",new TotalColumnGenerator("%.2f e", "quantity", "price"));
// adding some fake rows
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(), // date column
new Double(10), // quantity column
new Double(10), // price column
// nothing here // total column
}, 1); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(16.2), // quantity column
new Double(21.2), // price column
// nothing here // total column
}, 2); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(10), // quantity column
new Double(22), // price column
// nothing here // total column
}, 3); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(10), // quantity column
new Double(20), // price column
// nothing here // total column
}, 4); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(15), // quantity column
new Double(19.12), // price column
}, 5); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(10), // quantity column
new Double(20.30), // price column
// nothing here // total column
}, 6); // itemId
tableWithGeneratedCol.addItem(new Object[] { new GregorianCalendar().getTime(),
new Double(50), // quantity column
new Double(32.89), // price column
// nothing here // total column
}, 7); // itemId
tableWithGeneratedCol.setVisibleColumns(new Object[] {"date", "quantity", "price", "total"});
tableWithGeneratedCol.setPageLength(tableWithGeneratedCol.size());
layout.addComponent(tableWithGeneratedCol);
There is problem with calling method addItem. If you use ColumnGeneretor do not add item into column which is generated by ColumnGenerator. This item must "generate" ColumnGenerator.
Related
I am still developing stock management system with netbeans ide using java. In one interface I have three different jtables, one for current materials quantity in stock, one for required materials quantity for fulfill order, and other one for print is the quantity in stock is greater or lesser than the quantity in order. How can I compare this two quantity columns and print results?
/***
these are the codes tried till now
****/
//global variables
Vector v=new Vector();
Object listOrder=new Object();
Vector v1=new Vector();
Object listOrder1=new Object();
Vector v2=new Vector();
Object listOrder2=new Object();
int tranval[];
int stkval[];
//button action for import database values to tables
private void RequiredActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
tranval =new int[20];
stkval =new int[20];
try{
ResultSet rs = new getMatierialDataM().searchItems("SELECT icode,qty from transactiono where oid ='"+jComboBox2_oid.getSelectedItem()+"'");
//define tables
DefaultTableModel dtm = (DefaultTableModel)jTable2_required.getModel(); //required materials
dtm.setRowCount(0);
Vector v = null;
DefaultTableModel dtm1 = (DefaultTableModel)jTable1_availability.getModel();
dtm1.setRowCount(0); //available materials in stock
Vector v1 =null;
while(rs.next()){
String ico=rs.getString("icode");
String q1=rs.getString("qty");
int q11=Integer.parseInt(q1);
ResultSet rs1 = new getMatierialDataM().searchItems("SELECT transactionbom.mid,transactionbom.qty,component.comid,matierial.stockqty from transactionbom,component,matierial where transactionbom.icode ='"+ico+"' AND transactionbom.icode=component.icode AND transactionbom.mid=matierial.mid");
int len=0;
while(rs1.next()){
String q2=rs1.getString("qty");
int q22=Integer.parseInt(q2);
int qty =q11*q22; //calculation
String cid,mid,stq;
cid=rs1.getString("comid");
mid=rs1.getString("mid");
stq=rs1.getString("stockqty");
tranval[len]=qty;
stkval[len]=q22;
len++;
v = new Vector();
v.add(cid);
v.add(mid);
v.add(qty);
dtm.addRow(v);
v1 = new Vector();
v1.add(cid);
v1.add(mid);
v1.add(stq);
dtm1.addRow(v1);
}
}
}
catch (Exception e) {
e.printStackTrace();
//Logger.getLogger(ItemCosting.class.getName()).log(Level.SEVERE, null, e);
}
}
//button action for compare above two tables and get stock status in third table
private void jButton1_checkActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
DefaultTableModel dtm1 = (DefaultTableModel)jTable1_status.getModel();
dtm1.setRowCount(0);
Vector v2 =null;
int x=0;
int y=3;
int len=jTable1_availability.getRowCount();
v2 = new Vector();
while(x<len){
if(tranval[x]>stkval[x]){
v2.add("mid");
v2.add("OK");
}else{
v2.add("mid");
v2.add("NO");
}
dtm1.addRow(v2);
tranval[x]++;
stkval[x]++;
x++;
}
}
That above codes print only one same result in all rows in third table.
I expected some NO status in few rows but is prints OK in all of the rows.
I would like to ask how to display all rows in Java using JTable.
I tried using a while condition inside is a result.next() to be evaluated.
I tried printing it using System.out.println and it display all records however when I tried it on JTable it only displays the last row.
while(results.next()) {
String id = results.getString("id");
String date_collected = results.getString("date_collected");
String date_disposed = results.getString("date_disposed");
String person_in_charged = results.getString("person_in_charged");
String status = results.getString("status");
jTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][]){
{id, date_collected, date_disposed, person_in_charged, status}
},
new String [] {
"ID", "Date collected", "Date disposed", "person in charged",
"status"
}
));
jScrollPane1.setViewportView(jTable1);
if (jTable1.getColumnModel().getColumnCount() > 0) {
jTable1.getColumnModel().getColumn(0).setResizable(false);
jTable1.getColumnModel().getColumn(1).setResizable(false);
jTable1.getColumnModel().getColumn(2).setResizable(false);
jTable1.getColumnModel().getColumn(3).setResizable(false);
}
}
Can you help me? Thanks!
Summary of the solution:
Solved by Usagi Miyamoto and isaace
The solution was to create the List this way
List<Object[]> data = new ArrayList<>()
Reason:
List is an interface, an abstract, and cannot be instantiated directly
ArrayList is a class that implements List
Complete solution of Usagi Miyamoto
// to hold the data
List<Object[]> data = new ArrayList<>();
// loop through the ResultSet
while(results.next())
{
String id = results.getString("id");
String date_collected = results.getString("date_collected");
String date_disposed = results.getString("date_disposed");
String person_in_charged = results.getString("person_in_charged");
String status = results.getString("status");
// add a new row of data
data.add(new Object[] {id, date_collected, date_disposed, person_in_charged, status});
}
// after the loop: set the data as the model
jTable1.setModel(new javax.swing.table.DefaultTableModel(
// convert from List to array
data.toArray(new Object[][data.size()],
// column headers
new String [] { "ID", "Date collected", "Date disposed", "person in charged", "status" }
));
// other details from your code: moved outside of the loop...
jScrollPane1.setViewportView(jTable1);
if (jTable1.getColumnModel().getColumnCount() > 0)
{
jTable1.getColumnModel().getColumn(0).setResizable(false);
jTable1.getColumnModel().getColumn(1).setResizable(false);
jTable1.getColumnModel().getColumn(2).setResizable(false);
jTable1.getColumnModel().getColumn(3).setResizable(false);
}
Try cumulating the data, something like this:
// to hold the data
List<Object[]> data = new ArrayList<>();
// loop through the ResultSet
while(results.next())
{
String id = results.getString("id");
String date_collected = results.getString("date_collected");
String date_disposed = results.getString("date_disposed");
String person_in_charged = results.getString("person_in_charged");
String status = results.getString("status");
// add a new row of data
data.add(new Object[] {id, date_collected, date_disposed, person_in_charged, status});
}
// after the loop: set the data as the model
jTable1.setModel(new javax.swing.table.DefaultTableModel(
// convert from List to array
data.toArray(new Object[][data.size()],
// column headers
new String [] { "ID", "Date collected", "Date disposed", "person in charged", "status" }
));
// other details from your code: moved outside of the loop...
jScrollPane1.setViewportView(jTable1);
if (jTable1.getColumnModel().getColumnCount() > 0)
{
jTable1.getColumnModel().getColumn(0).setResizable(false);
jTable1.getColumnModel().getColumn(1).setResizable(false);
jTable1.getColumnModel().getColumn(2).setResizable(false);
jTable1.getColumnModel().getColumn(3).setResizable(false);
}
The DefaultTableModel should be created and set outside of the loop.
DefaultTableModel model = new DefaultTableModel();
model.addColumn("you column names");
model.addColumn("another column name");
//you can add all your column headers here
JTable table = new JTable();
table.setModel(model);
while(rs.next())
{
String id = results.getString("id");
String date_collected = results.getString("date_collected");
String date_disposed = results.getString("date_disposed");
String person_in_charged = results.getString("person_in_charged");
String status = results.getString("status");
model.addRow(new Object[]{id, date_collected, date_disposed, person_in_charge, status });
}
and continue with the rest of your code here.
I am creating an inventory app.It has a fab button an menu button with two items.Everything works fine but whenever I am trying to insert a new product through the item from the menu.It doesn't add immediately.
First I have to go back from the application or kill it and after when I enter again the the new product with the dummy value is recorded there.I want to resolve the issue that when I add a new product it immediately add the data with values and I don't have to go back every time to see it is inserted.Also If added a new product using fab button the data I added previously using the insert item is being added and displayed.
I have seen the notification uri and there is no error showing in the LOGCAT
ProductProvider.java
package com.example.bahubali.inventoryapp.data;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
public class ProductProvider extends ContentProvider {
//Tag for the log messages
public static final String LOG_TAG = ProductProvider.class.getSimpleName();
//Database helper that will provide access to the database.
private ProductDbHelper mDbHelper;
/** URI matcher code for the content URI for the product table */
public static final int PRODUCTS = 100;
/** URI matcher code for the content URI for a single product in the pets table */
public static final int PRODUCT_ID = 101;
/** URI matcher object to match a context URI to a corresponding code.
* The input passed into the constructor represents the code to return for the root URI.
* It's common to use NO_MATCH as the input for this case.
*/
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// Static initializer. This is run the first time anything is called from this class.
static {
// The calls to addURI() go here, for all of the content URI patterns that the provider
// should recognize. All paths added to the UriMatcher have a corresponding code to return
// when a match is found.
sUriMatcher.addURI(ProductContract.CONTENT_AUTHORITY, ProductContract.PATH_PRODUCTS, PRODUCTS);
// The content URI of the form "content://com.example.android.pets/pets/#" will map to the
// integer code {#link #PETS_ID}. This URI is used to provide access to ONE single row
// of the pets table.
sUriMatcher.addURI(ProductContract.CONTENT_AUTHORITY, ProductContract.PATH_PRODUCTS + "/#", PRODUCT_ID);
}
/**
* Initialize the provider and the database helper object.
*/
#Override
public boolean onCreate()
{
/*
* Creates a new helper object. This method always returns quickly.
* until SQLiteOpenHelper.getWritableDatabase is called
*/
mDbHelper = new ProductDbHelper(getContext());
return true;
}
/*
*Perform the query to the given URI.Use the given projection,selection,selectionArgs, and sort
* order.
*/
#Override
public Cursor query(#NonNull Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
// Get readable database
SQLiteDatabase database = mDbHelper.getReadableDatabase();
// This cursor will hold the result of the query
Cursor cursor ;
// Figure out if the URI matcher can match the URI to a specific code
int match = sUriMatcher.match(uri);
switch (match) {
case PRODUCTS:
// For the PRODUCTS code, query the pets table directly with the given
// projection, selection, selection arguments, and sort order. The cursor
// could contain multiple rows of the products table.
// Perform database query on pets
cursor = database.query(ProductContract.ProductEntry.TABLE_NAME,
projection,selection,selectionArgs,null,null,sortOrder);
break;
case PRODUCT_ID:
// For the PRODUCT_ID code, extract out the ID from the URI.
// For an example URI such as "content://com.example.android.products/products/3",
// the selection will be "_id=?" and the selection argument will be a
// String array containing the actual ID of 3 in this case.
//
// For every "?" in the selection, we need to have an element in the selection
// arguments that will fill in the "?". Since we have 1 question mark in the
// selection, we have 1 String in the selection arguments' String array.
selection = PRODUCT_ID + "=?";
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
// This will perform a query on the products table where the _id equals 3 to return a
// Cursor containing that row of the table.
cursor = database.query(ProductContract.ProductEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
break;
default:
throw new IllegalArgumentException("Cannot query unknown URI " + uri);
}
//Set notification URI to the cursor,
//So we know what content URI the cursor was created for.
cursor.setNotificationUri(getContext().getContentResolver(),uri);
//If the data at this URI changes, then we need to update the cursor.
return cursor;
}
/*
*Insert a new data into the provider with the given Constructor.
*/
#Nullable
#Override
public Uri insert(#NonNull Uri uri,
#Nullable ContentValues contentValues) {
final int match = sUriMatcher.match(uri);
switch (match){
case PRODUCTS:
return insertProduct(uri,contentValues);
default:
throw new IllegalArgumentException("Insertion is not supported for " +uri);
}
}
/*
Insert a product in the database with the given content values.Return the new content URI
for that specific row in the database.
*/
private Uri insertProduct(Uri uri, ContentValues contentValues){
//Check that the name is not null
String name = contentValues.getAsString(ProductContract.ProductEntry.COLUMN_PRODUCT_NAME);
if (name == null){
throw new IllegalArgumentException("Product requires a name");
}
//Check that the price is not null
Integer price = contentValues.getAsInteger(ProductContract.ProductEntry.COLUMN_PRODUCT_PRICE);
if (price != null && price < 0 ){
throw new IllegalArgumentException("Product requires valid price");
}
//Get writeable database
SQLiteDatabase database = mDbHelper.getWritableDatabase();
//Insert the new product with the given values
long id = database.insert(ProductContract.ProductEntry.TABLE_NAME,null,contentValues);
//If the Id is -1, then the insertion failed.Log on an error to return null
if (id == -1){
Log.e(LOG_TAG,"Failed to insert a row" + uri);
return null;
}
//Notify all listeners that the data has changed for the product content URI
getContext().getContentResolver().notifyChange(uri,null);
return ContentUris.withAppendedId(uri,id);
}
/*
*Updates the data at the given selection and the selection arguments, with the new content
* values.
*/
#Override
public int update(#NonNull Uri uri,
#Nullable ContentValues contentValues,
#Nullable String selection,
#Nullable String[] selectionArgs) {
final int match = sUriMatcher.match(uri);
switch (match) {
case PRODUCTS:
return updateProduct(uri, contentValues, selection, selectionArgs);
case PRODUCT_ID:
// For the PRODUCT_D code, extract out the ID from the URI,
// so we know which row to update. Selection will be "_id=?" and selection
// arguments will be a String array containing the actual ID.
selection = ProductContract.ProductEntry._ID + "=?";
selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
return updateProduct(uri, contentValues, selection, selectionArgs);
default:
throw new IllegalArgumentException("Update is not supported for " + uri);
}
}
/**
* Update products in the database with the given content values. Apply the changes to the rows
* specified in the selection and selection arguments (which could be 0 or 1 or more pets).
* Return the number of rows that were successfully updated.
*/
private int updateProduct(Uri uri,ContentValues contentValues,String selection,String[] selectionArgs){
// If the {#link ProductEntry#COLUMN_PRODUCT_NAME} key is present,
// check that the name value is not null.
if (contentValues.containsKey(ProductContract.ProductEntry.COLUMN_PRODUCT_NAME)) {
String name = contentValues.getAsString(ProductContract.ProductEntry.COLUMN_PRODUCT_NAME);
if (name == null) {
throw new IllegalArgumentException("Product requires a name");
}
}
// If the {#link ProductEntry#COLUMN_PRODUCT_PRICE} key is present,
// check that the price value is valid.
if (contentValues.containsKey(ProductContract.ProductEntry.COLUMN_PRODUCT_PRICE)) {
// Check that the weight is greater than or equal to 0 kg
Integer price = contentValues.getAsInteger(ProductContract.ProductEntry.COLUMN_PRODUCT_PRICE);
if (price!= null && price < 0) {
throw new IllegalArgumentException("Product requires valid weight");
}
}
// If there are no values to update, then don't try to update the database
if (contentValues.size() == 0) {
return 0;
}
// Otherwise, get writeable database to update the data
SQLiteDatabase database = mDbHelper.getWritableDatabase();
//perform the update on the database and the get the number of rows affected
int rowsUpdated = database.update(ProductContract.ProductEntry.TABLE_NAME,contentValues,selection
,selectionArgs);
//If 1 or more rows were updated, then notify all the listeners that the data at the
//given URI has changed
if (rowsUpdated != 0){
getContext().getContentResolver().notifyChange(uri,null);
}
//Return the no. of rows updated
return rowsUpdated;
}
/*
*Delete the data at the given selection and selection arguments.
*/
#Override
public int delete(#NonNull Uri uri,
#Nullable String selection,
#Nullable String[] selectionArgs) {
// Get writeable database
SQLiteDatabase database = mDbHelper.getWritableDatabase();
//Track the no. of rows that were deleted
int rowsDeleted;
final int match = sUriMatcher.match(uri);
switch (match) {
case PRODUCTS:
// Delete all rows that match the selection and selection args
rowsDeleted = database.delete(ProductContract.ProductEntry.TABLE_NAME,selection,selectionArgs);
break;
case PRODUCT_ID:
// Delete a single row given by the ID in the URI
selection = ProductContract.ProductEntry._ID + "=?";
selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};
rowsDeleted = database.delete(ProductContract.ProductEntry.TABLE_NAME,selection,selectionArgs);
break;
default:
throw new IllegalArgumentException("Deletion is not supported for " + uri);
}
//If 1 or more rows were deleted, then notify all listeners that the data at the given
//given uri has changed
if (rowsDeleted != 0){
getContext().getContentResolver().notifyChange(uri,null);
}
return rowsDeleted;
}
/*
*Return the MIME type of data for the content URI.
*/
#Nullable
#Override
public String getType(#NonNull Uri uri) {
final int match = sUriMatcher.match(uri);
switch (match){
case PRODUCTS:
return ProductContract.ProductEntry.CONTENT_LIST_TYPE;
case PRODUCT_ID:
return ProductContract.ProductEntry.CONTENT_ITEM_TYPE;
default:
throw new IllegalStateException("UNKNOWN URI" + uri + " with match" + match);
}
}
}
I'm getting a "TableView(ScrollView).calculateVerticalScrollAmount(XYRect)" exception when trying to create a table using the following code. I've tried simplifying the fields, but nothing seems to help, any thoughts? The code is similar to that in the Tables Demo supplied with the BB 6 SDK.
It looks like a layout issue, but I can't seem to pin down the error.
// Create and apply style
RegionStyles style = new RegionStyles(BorderFactory.createSimpleBorder(new XYEdges(1, 1, 1, 1), Border.STYLE_SOLID), null, null,
null, RegionStyles.ALIGN_LEFT, RegionStyles.ALIGN_TOP);
// Create the view and controller
TableView tableView = new TableView(_tableModel);
TableController tableController = new TableController(_tableModel, tableView);
// Set the controller focus policy to highlight rows
tableController.setFocusPolicy(TableController.ROW_FOCUS);
// Set the behaviour of the controller when a table item is clicked
tableController.setCommand(new CommandHandler()
{
/**
* #see CommandHandler#execute(ReadOnlyCommandMetadata, Object)
*/
public void execute(ReadOnlyCommandMetadata metadata, Object context)
{
Dialog.alert("Command Executed");
}
}, null, null);
tableView.setController(tableController);
// Create a DataTemplate that suppresses the third column
DataTemplate dataTemplate = new DataTemplate(tableView, 2, 3)
{
/**
* #see DataTemplate#getDataFields(int)
*/
public Field[] getDataFields(int modelRowIndex)
{
Object[] data = (Object[]) ((TableModel) getView().getModel()).getRow(modelRowIndex);
Field[] fields = new Field[4];
fields[0] = new BitmapField((Bitmap) data[0]);
fields[1] = new LabelField(data[1], Field.FOCUSABLE);
fields[2] = new LabelField(data[2], Field.FOCUSABLE);
fields[3] = new LabelField(data[3], Field.FOCUSABLE);
return fields;
}
};
// Set up regions
dataTemplate.createRegion(new XYRect(0, 0, 1, 2), style);
dataTemplate.createRegion(new XYRect(1, 0, 2, 1), style);
dataTemplate.createRegion(new XYRect(1, 1, 1, 1), style);
dataTemplate.createRegion(new XYRect(2, 1, 1, 1), style);
// Specify the size of each column by percentage, and the height of a row
dataTemplate.setColumnProperties(0, new TemplateColumnProperties(15, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setColumnProperties(1, new TemplateColumnProperties(15, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setColumnProperties(2, new TemplateColumnProperties(70, TemplateColumnProperties.PERCENTAGE_WIDTH));
dataTemplate.setRowProperties(0, new TemplateRowProperties(ROW_HEIGHT));
dataTemplate.setRowProperties(1, new TemplateRowProperties(ROW_HEIGHT));
// Apply the template to the view
tableView.setDataTemplate(dataTemplate);
dataTemplate.useFixedHeight(true);
add(tableView);
Not sure if you are still struggling with this - may be benefits for others
in the statement
DataTemplate dataTemplate = new DataTemplate(tableView, 2, 3)
you have initialized the DataTemplate with 2 rows and 3 columns (assuming you want to suppress 4th column). But inside the function getDataFields you are returning 4 fields.
This is causing the crash (the internal code is not monkey proof).
Drop the 4th field from array and it should work.
I have been reading and searching for quite some time and can't seem to wrap my head around how to use Lucene in my Java program that contains a jTable.
What I want to be able to do is search the table when submitting a new row entry. Do I simply create a TableModel from my jTable and pass it into the TableSearcher constructor if I include the 3.3 Lucene jar file in my path? I have included some of my code...please, I welcome any direction! Thanks.
Here is my method for populating my jTable from data stored in vectors:
...
public static boolean hasSearchResults = false;
public String selectedRequirementName;
public TableSearcher ts;
DefaultTableModel sc;
public TablePanel(int z, String name, String reason) { //pulls in panel number, project name, and whether the project was new or opened, from the MainGUI class
initComponents();
sc=(DefaultTableModel) reqTable.getModel();
ts = new TableSearcher(sc);
aProject = new Project(reason, name); //calls Project class to control the project
try{
requirementsVector = new Vector(aProject.getRequirementsList());
requirementsList =new String[requirementsVector.size()];
for(int i=0; i < requirementsVector.size(); i++){
requirementsList[i] = requirementsVector.get(i).getName();
System.out.println(requirementsList[i]);
sc.addRow(new Object[]{
requirementsVector.get(i).getName(),
requirementsVector.get(i).getDefinition(),
requirementsVector.get(i).getType(),
requirementsVector.get(i).getPriority(),
requirementsVector.get(i).getAssigned(),
requirementsVector.get(i).getDue(),
requirementsVector.get(i).getStatus()});
//this.editingProjectName = name;
}
}catch(NullPointerException e1){
System.out.println(e1);
} ....
Here is my code that inserts a new row into the jTable:
private void ConfirmActionPerformed(java.awt.event.ActionEvent evt) {
String ReqName = nameTextField.getText();
String ReqDescription = descriptionTextField.getText();
String Type = (String)jType.getSelectedItem();
String Priority = (String)PriorityComboBox.getSelectedItem();
String Assigned = (String)jDateAssignedMonth.getSelectedItem() + "/" + (String)jDateAssignedDay.getSelectedItem() + "/" + (String)jDateAssignedYear.getSelectedItem();
String Due = (String)jDateDueMonth.getSelectedItem() + "/" + (String)jDateDueDay.getSelectedItem() + "/" + (String)jDateDueYear.getSelectedItem();
String Status = (String)jStatus.getSelectedItem();
// search string pass to TableSearcher
ts.search(ReqDescription);
if (editingaRow == false && !hasSearchResults){
sc.addRow(new Object[]{
ReqName,ReqDescription,Type,Priority,Assigned, Due, Status
});
requirementsVector.add(new Requirement(ReqName, ReqDescription, Type, Priority, Assigned, Due,Status));
aProject.saveRevision(requirementsVector, name);
}else if (editingaRow == true){
sc.setValueAt(ReqName,selectedRow,0);
sc.setValueAt(ReqDescription,selectedRow,1);
sc.setValueAt(Type,selectedRow,2);
sc.setValueAt(Priority,selectedRow,3);
sc.setValueAt(Assigned,selectedRow,4);
sc.setValueAt(Due,selectedRow,5);
sc.setValueAt(Status,selectedRow,6);
requirementsVector.setElementAt(new Requirement(ReqName, ReqDescription, Type, Priority, Assigned, Due,Status),(selectedRow));
aProject.saveRevision(requirementsVector, name);
editingaRow = false;
}
disableRequirementEditor();
newReq.setEnabled(true);
reqTable.clearSelection();
}
And here is the TableSearcher model that I am using (I added new functionality to the search method). What is happening is when I enter a new entry, the search finds that entry as a duplicate and returns it in my jFrame I implement within search method of the TableSearcher. This only happens with a unique entry that has no match in the indexed table. If there is a search result in the index that matches my new entry, then only the existing entry displays in my jFrame, not the new one I am attempting.
// provided by Jonathan Simon <jonathan_s_simon#yahoo.com>
class TableSearcher extends AbstractTableModel {
/**
* The inner table model we are decorating
*/
protected TableModel tableModel;
/**
* This listener is used to register this class as a listener to
* the decorated table model for update events
*/
private TableModelListener tableModelListener;
/**
* these keeps reference to the decorated table model for data
* only rows that match the search criteria are linked
*/
private ArrayList rowToModelIndex = new ArrayList();
//Lucene stuff.
/**
* In memory lucene index
*/
private RAMDirectory directory;
/**
* Cached lucene analyzer
*/
private Analyzer analyzer;
/**
* Links between this table model and the decorated table model
* are maintained through links based on row number. This is a
* key constant to denote "row number" for indexing
*/
private static final String ROW_NUMBER = "ROW_NUMBER";
/**
* Cache the current search String. Also used internally to
* key whether there is an active search running or not. i.e. if
* searchString is null, there is no active search.
*/
private String searchString = null;
/**
* #param tableModel The table model to decorate
*/
public TableSearcher(TableModel tableModel) {
analyzer = new WhitespaceAnalyzer();
tableModelListener = new TableModelHandler();
setTableModel(tableModel);
tableModel.addTableModelListener(tableModelListener);
clearSearchingState();
}
/**
*
* #return The inner table model this table model is decorating
*/
public TableModel getTableModel() {
return tableModel;
}
/**
* Set the table model used by this table model
* #param tableModel The new table model to decorate
*/
public final void setTableModel(TableModel tableModel) {
//remove listeners if there...
if (this.tableModel != null) {
this.tableModel.removeTableModelListener(tableModelListener);
}
this.tableModel = tableModel;
if (this.tableModel != null) {
this.tableModel.addTableModelListener(tableModelListener);
}
//recalculate the links between this table model and
//the inner table model since the decorated model just changed
reindex();
// let all listeners know the table has changed
fireTableStructureChanged();
}
/**
* Reset the search results and links to the decorated (inner) table
* model from this table model.
*/
private void reindex() {
try {
// recreate the RAMDirectory
directory = new RAMDirectory();
IndexWriter writer = new IndexWriter(directory, analyzer, true, IndexWriter.MaxFieldLength.UNLIMITED);
// iterate through all rows
for (int row=0; row < tableModel.getRowCount(); row++){
//for each row make a new document
Document document = new Document();
//add the row number of this row in the decorated table model
//this will allow us to retrive the results later
//and map this table model's row to a row in the decorated
//table model
document.add(new Field(ROW_NUMBER, "" + row, Field.Store.YES, Field.Index.ANALYZED));
//iterate through all columns
//index the value keyed by the column name
//NOTE: there could be a problem with using column names with spaces
for (int column=0; column < tableModel.getColumnCount(); column++){
String columnName = tableModel.getColumnName(column);
String columnValue = String.valueOf(tableModel.getValueAt(row, column)).toLowerCase();
document.add(new Field(columnName, columnValue, Field.Store.YES, Field.Index.ANALYZED));
}
writer.addDocument(document);
}
writer.optimize();
writer.close();
} catch (Exception e){
e.printStackTrace();
}
}
/**
* #return The current lucene analyzer
*/
public Analyzer getAnalyzer() {
return analyzer;
}
/**
* #param analyzer The new analyzer to use
*/
public void setAnalyzer(Analyzer analyzer) {
this.analyzer = analyzer;
//reindex from the model with the new analyzer
reindex();
//rerun the search if there is an active search
if (isSearching()){
search(searchString);
}
}
/**
* Run a new search.
*
* #param searchString Any valid lucene search string
*/
public void search(String searchString){
// to store row numbers of results rows to display later
Vector<String> rowNums = new Vector();
//if search string is null or empty, clear the search == search all
if (searchString == null || searchString.equals("")){
clearSearchingState();
fireTableDataChanged();
return;
}
try {
//cache search String
this.searchString = searchString;
//make a new index searcher with the in memory (RAM) index.
IndexSearcher is = new IndexSearcher(directory);
//make an array of fields - one for each column
String[] fields = new String[tableModel.getColumnCount()];
for (int t=0; t<tableModel.getColumnCount(); t++){
fields[t]=tableModel.getColumnName(t);
}
//build a query based on the fields, searchString and cached analyzer
//NOTE: This is an area for improvement since the MultiFieldQueryParser
// has some weirdness.
MultiFieldQueryParser parser = new MultiFieldQueryParser(fields, analyzer);
Query query = parser.parse(searchString);
//run the search
Hits hits = is.search(query);
for (int t=0; t<hits.length(); t++){
Document document = hits.doc(t);
Fieldable field = document.getField(ROW_NUMBER);
// adding row numbers to vector
rowNums.add(field.stringValue());
}
// trying to display search results in new table
if(!rowNums.isEmpty()){
TablePanel.hasSearchResults = true;
for (int v=0; v<rowNums.size(); v++){
System.out.println("Match in row number " + rowNums.elementAt(v));
}
JFrame frame = new JFrame("Possible Duplicates");
String colNames[] = {"Name", "Definition", "Type", "Priority", "Date Assigned", "Due Date", "Status"};
DefaultTableModel dtm = new DefaultTableModel(null,colNames);
for (int r = 0; r<rowNums.size(); r++){
String ReqName = (String) tableModel.getValueAt(Integer.parseInt(rowNums.elementAt(r)), 0);
String ReqDescription = (String) tableModel.getValueAt(Integer.parseInt(rowNums.elementAt(r)), 1);
String Type = (String) tableModel.getValueAt(Integer.parseInt(rowNums.elementAt(r)), 2);
String Priority = (String) tableModel.getValueAt(Integer.parseInt(rowNums.elementAt(r)), 3);
String Assigned = (String) tableModel.getValueAt(Integer.parseInt(rowNums.elementAt(r)), 4);
String Due = (String) tableModel.getValueAt(Integer.parseInt(rowNums.elementAt(r)), 5);
String Status = (String) tableModel.getValueAt(Integer.parseInt(rowNums.elementAt(r)), 6);
dtm.addRow(new Object[]{
ReqName,ReqDescription,Type,Priority,Assigned, Due, Status
});
}
JTable tblResults = new JTable(dtm);
JScrollPane sp = new JScrollPane(tblResults);
JPanel panel = new JPanel(new BorderLayout());
panel.setPreferredSize(new Dimension(900,300));
panel.add(sp,BorderLayout.CENTER);
panel.add(new JLabel("Possible Duplicates",JLabel.CENTER),BorderLayout.SOUTH);
JOptionPane.showConfirmDialog(null,panel);
}
//reset this table model with the new results
resetSearchResults(hits);
} catch (Exception e){
e.printStackTrace();
}
//notify all listeners that the table has been changed
fireTableStructureChanged();
}
/**
*
* #param hits The new result set to set this table to.
*/
private void resetSearchResults(Hits hits) {
try {
//clear our index mapping this table model rows to
//the decorated inner table model
rowToModelIndex.clear();
//iterate through the hits
//get the row number stored at the index
//that number is the row number of the decorated
//tabble model row that we are mapping to
for (int t=0; t<hits.length(); t++){
Document document = hits.doc(t);
Fieldable field = document.getField(ROW_NUMBER);
rowToModelIndex.add(new Integer(field.stringValue()));
System.out.println("Something " + rowToModelIndex.add(new Integer(field.stringValue())));
clearSearchingState();
}
} catch (Exception e){
e.printStackTrace();
}
}
private int getModelRow(int row){
return ((Integer) rowToModelIndex.get(row)).intValue();
}
/**
* Clear the currently active search
* Resets the complete dataset of the decorated
* table model.
*/
private void clearSearchingState(){
searchString = null;
rowToModelIndex.clear();
for (int t=0; t<tableModel.getRowCount(); t++){
rowToModelIndex.add(new Integer(t));
}
}
// TableModel interface methods
public int getRowCount() {
return (tableModel == null) ? 0 : rowToModelIndex.size();
}
public int getColumnCount() {
return (tableModel == null) ? 0 : tableModel.getColumnCount();
}
public String getColumnName(int column) {
return tableModel.getColumnName(column);
}
public Class getColumnClass(int column) {
return tableModel.getColumnClass(column);
}
public boolean isCellEditable(int row, int column) {
return tableModel.isCellEditable(getModelRow(row), column);
}
public Object getValueAt(int row, int column) {
return tableModel.getValueAt(getModelRow(row), column);
}
public void setValueAt(Object aValue, int row, int column) {
tableModel.setValueAt(aValue, getModelRow(row), column);
}
private boolean isSearching() {
return searchString != null;
}
private class TableModelHandler implements TableModelListener {
public void tableChanged(TableModelEvent e) {
// If we're not searching, just pass the event along.
if (!isSearching()) {
clearSearchingState();
reindex();
fireTableChanged(e);
return;
}
// Something has happened to the data that may have invalidated the search.
reindex();
search(searchString);
fireTableDataChanged();
return;
}
}
It looks like your code doesn't index the table data with Lucene. One easy way to do this is to make your own table model that indexes internally, with an exposed search function that you can connect to limit the data displayed. This is what I wrote about in Swing Hacks a few years back.
Without the indexing, Lucene won't just automatically 'work' because it's in your classpath.
Jonathan
take a look at the very simple example
in your case, you would do something like this to create the document, that would get saved in the index.
this code is not complete or necessarily error free,; but is more of a starting point to get you going
Document doc = new Document();
doc.add(new Field("ReqName", ReqName, Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("ReqDescription", ReqDescription, Field.Store.YES, Field.Index.ANALYZED));
doc.add(new Field("Type", Type, Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("Priority", Priority, Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("Assigned", Assigned, Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("Due", Due, Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("Status", Status, Field.Store.YES, Field.Index.NOT_ANALYZED));
writer.addDocument(doc);
writer.close();