Specify which java type to decode an embedded document to - mongodb

I have a simple java type
public class Type1_ {
private int number1;
private int number2;
public Type1_(int number1, int number2) {
this.number1 = number1;
this.number2 = number2;
}
public int getNumber1() {
return number1;
}
public int getNumber2() {
return number2;
}
#Override
public String toString() {
return "Type1_{number1=" + number1 + ", number2=" + number2 + '}';
}
public static Type1_ random() {
return new Type1_(new SecureRandom().nextInt(), new SecureRandom().nextInt());
}
}
Created a simple Codec<Type1_> for this type (with loggers to know when or if they are being used)
public class Type1_Codec implements Codec<Type1_> {
#Override
public Type1_ decode(BsonReader reader, DecoderContext decoderContext) {
reader.readStartDocument();
final int number1 = reader.readInt32("number1");
final int number2 = reader.readInt32("number2");
reader.readEndDocument();
final Type1_ type1_ = new Type1_(number1, number2);
APP_LOGGER.debug(type1_);
return type1_;
}
#Override
public void encode(BsonWriter writer, Type1_ value, EncoderContext encoderContext) {
writer.writeStartDocument();
writer.writeInt32("number1", value.getNumber1());
writer.writeInt32("number2", value.getNumber2());
writer.writeEndDocument();
APP_LOGGER.debug(value);
}
#Override
public Class<Type1_> getEncoderClass() {
return Type1_.class;
}
}
Added the codec to a registry to be used in MongoClient
public class CustomCodecRegistriesFactory {
public static CodecRegistry getDefault() {
return MongoClientSettings.getDefaultCodecRegistry();
}
public static CodecRegistry getDefaultWithType1_() {
return CodecRegistries.fromRegistries(CodecRegistries.fromCodecs(new Type1_Codec()), getDefault());
}
}
public class MongoDBClientConfig {
public static MongoClient buildMongoClientWithCustomCodec(final CodecRegistry codecRegistry) {
return MongoClients.create(MongoClientSettings.builder()
.applyToClusterSettings(builder -> {
builder.hosts(Arrays.asList(new ServerAddress(url(), port())));
})
.codecRegistry(codecRegistry)
.build());
}
}
Created a test that uses a codec registry where the Type1_Codec is included and attempt to insert (encode) and read (decode) Type1_
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class Codecs {
#Test
public void demonstrate_simpleCustomCodec() {
final MongoClient mongoClient = buildMongoClientWithCustomCodec(CustomCodecRegistriesFactory.getDefaultWithType1_());
final MongoDatabase db = mongoClient.getDatabase("db_" + new SecureRandom().nextInt(Integer.MAX_VALUE));
final String collectionName = "col1";
db.createCollection(collectionName);
final MongoCollection<Document> col1 = db.getCollection(collectionName);
int idCounter = 0;
final String type1_field = "type1_";
printTitle("Inserting Documents");
for (int i = 0; i < 10; i++) {
/*Encoding is done at insertion*/
col1.insertOne(new Document(ID, idCounter++).append(type1_field, Type1_.random()));
}
printDocumentMongoCollection(col1);
printMongoCollectionJackson(col1);
printTitle("Attempting Decoding");
/*attempt decoding*/
// final Type1_ type1_value = col1.find().first().get(type1_field, Type1_.class);
// System.out.println(type1_value);
}
}
The problem is in the decoding part.
How do i specify for MongoClient that i want a specific document to be decoded into Type1_ type?
The commented out code will attempt to cast a Document to Type1_ and will fail.

use db.getCollection(collectionName, Type1_.class) to "type" your collection. That should pull your codec in to play automatically for you.

Related

mybatis interceptor throw Reflection exception affects cpu performence

I had implement a interceptor of myabtis. but we found a problem, execute interceptor lead to throw so many IllegalAccessException, it affects cpu performence
Shown below is where the problem is, why did not check access permision of feild befor executed code "field.get(target)".
public class GetFieldInvoker implements Invoker {
private final Field field;
public GetFieldInvoker(Field field) {
this.field = field;
}
#Override
public Object invoke(Object target, Object[] args) throws IllegalAccessException {
try {
return field.get(target);
} catch (IllegalAccessException e) {
if (Reflector.canControlMemberAccessible()) {
field.setAccessible(true);
return field.get(target);
} else {
throw e;
}
}
}
#Override
public Class<?> getType() {
return field.getType();
}
}
the intercepor of mine:
#Intercepts({
#Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class, Integer.class})
})
public class SqlIdInterceptor implements Interceptor {
private static final int MAX_LEN = 256;
private final RoomboxLogger logger = RoomboxLogManager.getLogger();
#Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
String originalSql = boundSql.getSql();
MappedStatement mappedStatement =
(MappedStatement) metaObject.getValue("delegate.mappedStatement");
String id = mappedStatement.getId();
if (id != null) {
int len = id.length();
if (len > MAX_LEN) {
logger.warn("too long id", "id", id, "len", len);
}
}
String newSQL = "# " + id + "\n" + originalSql;
metaObject.setValue("delegate.boundSql.sql", newSQL);
return invocation.proceed();
}
#SuppressWarnings("unchecked")
public static <T> T realTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return realTarget(metaObject.getValue("h.target"));
}
return (T) target;
}
}
Flame Graph
enter image description here
enter image description here
I need help, how to avoid throw exceptions, is any other way to reslove this problem?
thanks.

my area is not getting printed (abstract using interface)

I am currently new to interfaces and I encountered this problem regarding my getArea(), I would love if you guys can point out the error on my code
while this is my Triangle abstract class, I am not sure whether I putted the Semi-perimeter(sp) right or not:
private int sideA;
private int sideB;
private int sideC;
int sp = (sideA+sideB+sideC)/2;
double area = Math.sqrt(sp*(sp-sideA)*(sp-sideB)*(sp-sideC));
public Triangle() {
}
public Triangle(int sideA, int sideB, int sideC, int sp, double area) {
this.sideA=sideA;
this.sideB=sideB;
this.sideC=sideC;
}
public int getSideA() {
return sideA;
}
public void setSideA(int sideA) {
this.sideA = sideA;
}
public int getSideB() {
return sideB;
}
public void setSideB(int sideB) {
this.sideB = sideB;
}
public int getSideC() {
return sideC;
}
public void setSideC(int sideC) {
this.sideC = sideC;
}
public double getPerimeter() {
return sideA+sideB+sideC;
}
public double getArea() {
return getArea();
}
public void getDetails() {
System.out.println("\nType: Triangle" + "\nSides: " +this.sideA +", " +this.sideB+", "+this.sideC + "\nPerimeter: "
+ getPerimeter() + "\nArea: " + getArea() + "\ns: "+sp);
}
}```

kaitai struct code unable to get output in eclipse ide

hi i am very newer to kaitai struct, how do i run a code in eclipse which
is parsed to java(target langage) on kaitai struct webide, i have gone
through the kaitai struct docs, still i have some doubts, can someone help
me? i have posted parsed java class and main class also
package com.ccx.ccxgw.message.parsers;
import io.kaitai.struct.ByteBufferKaitaiStream;
import io.kaitai.struct.KaitaiStruct;
import io.kaitai.struct.KaitaiStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.nio.charset.Charset;
public class LocationRecord extends KaitaiStruct {
public Map<String, Integer> _attrStart = new HashMap<String, Integer>();
public Map<String, Integer> _attrEnd = new HashMap<String, Integer>();
public Map<String, ArrayList<Integer>> _arrStart = new HashMap<String,
ArrayList<Integer>>();
public Map<String, ArrayList<Integer>> _arrEnd = new HashMap<String,
ArrayList<Integer>>();
public static LocationRecord fromFile(String fileName) throws IOException {
return new LocationRecord(new ByteBufferKaitaiStream(fileName));
}
public static String[] _seqFields = new String[] { "imei", "reserved1",
"reserved2", "latrawintpart", "latrawfloatpart", "lonrawintpart",
"lonrawfloatpart", "reserved3", "lbsinfo", "ta", "p", "mt", "fff", "ddd" };
public LocationRecord(KaitaiStream _io) {
this(_io, null, null);
}
public LocationRecord(KaitaiStream _io, KaitaiStruct _parent) {
this(_io, _parent, null);
}
public LocationRecord(KaitaiStream _io, KaitaiStruct _parent, LocationRecord
_root) {
super(_io);
this._parent = _parent;
this._root = _root == null ? this : _root;
}
public void _read() {
_attrStart.put("imei", this._io.pos());
this.imei = this._io.readBytes(15);
_attrEnd.put("imei", this._io.pos());
_attrStart.put("reserved1", this._io.pos());
this.reserved1 = this._io.readBytes(6);
_attrEnd.put("reserved1", this._io.pos());
_attrStart.put("reserved2", this._io.pos());
this.reserved2 = this._io.readBytes(6);
_attrEnd.put("reserved2", this._io.pos());
_attrStart.put("latrawintpart", this._io.pos());
this.latrawintpart = this._io.readU2be();
_attrEnd.put("latrawintpart", this._io.pos());
_attrStart.put("latrawfloatpart", this._io.pos());
this.latrawfloatpart = this._io.readU2be();
_attrEnd.put("latrawfloatpart", this._io.pos());
_attrStart.put("lonrawintpart", this._io.pos());
this.lonrawintpart = this._io.readU2be();
_attrEnd.put("lonrawintpart", this._io.pos());
_attrStart.put("lonrawfloatpart", this._io.pos());
this.lonrawfloatpart = this._io.readU2be();
_attrEnd.put("lonrawfloatpart", this._io.pos());
_attrStart.put("reserved3", this._io.pos());
this.reserved3 = this._io.readBytes(3);
_attrEnd.put("reserved3", this._io.pos());
_attrStart.put("lbsinfo", this._io.pos());
this.lbsinfo = new String(this._io.readBytes(3),
Charset.forName("ASCII"));
_attrEnd.put("lbsinfo", this._io.pos());
_attrStart.put("ta", this._io.pos());
this.ta = new String(this._io.readBytes(8), Charset.forName("ASCII"));
_attrEnd.put("ta", this._io.pos());
_attrStart.put("p", this._io.pos());
this.p = new String(this._io.readBytes(8), Charset.forName("ASCII"));
_attrEnd.put("p", this._io.pos());
_attrStart.put("mt", this._io.pos());
this.mt = new String(this._io.readBytes(8), Charset.forName("ASCII"));
_attrEnd.put("mt", this._io.pos());
_attrStart.put("fff", this._io.pos());
this.fff = new String(this._io.readBytes(8), Charset.forName("ASCII"));
_attrEnd.put("fff", this._io.pos());
_attrStart.put("ddd", this._io.pos());
this.ddd = new String(this._io.readBytes(8), Charset.forName("ASCII"));
_attrEnd.put("ddd", this._io.pos());
}
private Double latitude;
public Double latitude() {
if (this.latitude != null)
return this.latitude;
double _tmp = (double) ((latitudeintpart() + latitudefloatpart()));
this.latitude = _tmp;
return this.latitude;
}
private Double longitude;
public Double longitude() {
if (this.longitude != null)
return this.longitude;
double _tmp = (double) ((longitudeintpart() + longitudefloatpart()));
this.longitude = _tmp;
return this.longitude;
}
private Double latitudefloatpart;
public Double latitudefloatpart() {
if (this.latitudefloatpart != null)
return this.latitudefloatpart;
double _tmp = (double) ((((((latrawintpart() * 10000) +
latrawfloatpart()) - (latitudeintpart() * 1000000)) * 0.0001) / 60));
this.latitudefloatpart = _tmp;
return this.latitudefloatpart;
}
private Double longitudefloatpart;
public Double longitudefloatpart() {
if (this.longitudefloatpart != null)
return this.longitudefloatpart;
double _tmp = (double) ((((((lonrawintpart() * 10000) +
lonrawfloatpart()) - (longitudeintpart() * 1000000)) * 0.0001) / 60));
this.longitudefloatpart = _tmp;
return this.longitudefloatpart;
}
private Integer longitudeintpart;
public Integer longitudeintpart() {
if (this.longitudeintpart != null)
return this.longitudeintpart;
int _tmp = (int) ((((lonrawintpart() * 10000) + lonrawfloatpart()) /
1000000));
this.longitudeintpart = _tmp;
return this.longitudeintpart;
}
private Integer latitudeintpart;
public Integer latitudeintpart() {
if (this.latitudeintpart != null)
return this.latitudeintpart;
int _tmp = (int) ((((latrawintpart() * 10000) + latrawfloatpart()) /
1000000));
this.latitudeintpart = _tmp;
return this.latitudeintpart;
}
private byte[] imei;
private byte[] reserved1;
private byte[] reserved2;
private int latrawintpart;
private int latrawfloatpart;
private int lonrawintpart;
private int lonrawfloatpart;
private byte[] reserved3;
private String lbsinfo;
private String ta;
private String p;
private String mt;
private String fff;
private String ddd;
private LocationRecord _root;
private KaitaiStruct _parent;
public byte[] imei() { return imei; }
public byte[] reserved1() { return reserved1; }
public byte[] reserved2() { return reserved2; }
public int latrawintpart() { return latrawintpart; }
public int latrawfloatpart() { return latrawfloatpart; }
public int lonrawintpart() { return lonrawintpart; }
public int lonrawfloatpart() { return lonrawfloatpart; }
public byte[] reserved3() { return reserved3; }
public String lbsinfo() { return lbsinfo; }
public String ta() { return ta; }
public String p() { return p; }
public String mt() { return mt; }
public String fff() { return fff; }
public String ddd() { return ddd; }
public LocationRecord _root() { return _root; }
public KaitaiStruct _parent() { return _parent; }
}
main class is below
package com.ccx.ccxgw.message.parsers;
public class test {
public static void main(String[] args) {
try {
LocationRecord lr =
LocationRecord.fromFile("D:\\Projects\\CcxParser\\CcxParsers\\loc.data");
lr.imei();
lr.reserved1();
lr.reserved2();
lr.latrawintpart();
lr.latrawfloatpart();
lr.lonrawintpart();
lr.lonrawfloatpart();
lr.reserved3();
lr.lbsinfo();
lr.ta();
lr.p();
lr.mt();
lr.fff();
lr.ddd();
} catch (Exception e) {
e.printStackTrace(System.out);
}
}
}
kindly suggest me what modification should be done,, if possible provide me
snippet for main class to call kaitai struct class methods. and i want to parse this message {354105059135656,010104,164536,12.888888,77.5564499,100,lbs,ta,p,mt,fff,ddd}

Creating custom plugin for chinese tokenization

I'm working towards properly integrating the stanford segmenter within SOLR for chinese tokenization.
This plugin involves loading other jar files and model files. I've got it working in a crude manner by hardcoding the complete path for the files.
I'm looking for methods to create the plugin where the paths need not be hardcoded and also to have the plugin in conformance with the SOLR plugin architecture. Please let me know if there are any recommended sites or tutorials for this.
I've added my code below :
public class ChineseTokenizerFactory extends TokenizerFactory {
/** Creates a new WhitespaceTokenizerFactory */
public ChineseTokenizerFactory(Map<String,String> args) {
super(args);
assureMatchVersion();
if (!args.isEmpty()) {
throw new IllegalArgumentException("Unknown parameters: " + args);
}
}
#Override
public ChineseTokenizer create(AttributeFactory factory, Reader input) {
Reader processedStringReader = new ProcessedStringReader(input);
return new ChineseTokenizer(luceneMatchVersion, factory, processedStringReader);
}
}
public class ProcessedStringReader extends java.io.Reader {
private static final int BUFFER_SIZE = 1024 * 8;
//private static TextProcess m_textProcess = null;
private static final String basedir = "/home/praveen/PDS_Meetup/solr-4.9.0/custom_plugins/";
static Properties props = null;
static CRFClassifier<CoreLabel> segmenter = null;
private char[] m_inputData = null;
private int m_offset = 0;
private int m_length = 0;
public ProcessedStringReader(Reader input){
char[] arr = new char[BUFFER_SIZE];
StringBuffer buf = new StringBuffer();
int numChars;
if(segmenter == null)
{
segmenter = new CRFClassifier<CoreLabel>(getProperties());
segmenter.loadClassifierNoExceptions(basedir + "ctb.gz", getProperties());
}
try {
while ((numChars = input.read(arr, 0, arr.length)) > 0) {
buf.append(arr, 0, numChars);
}
} catch (IOException e) {
e.printStackTrace();
}
m_inputData = processText(buf.toString()).toCharArray();
m_offset = 0;
m_length = m_inputData.length;
}
#Override
public int read(char[] cbuf, int off, int len) throws IOException {
int charNumber = 0;
for(int i = m_offset + off;i<m_length && charNumber< len; i++){
cbuf[charNumber] = m_inputData[i];
m_offset ++;
charNumber++;
}
if(charNumber == 0){
return -1;
}
return charNumber;
}
#Override
public void close() throws IOException {
m_inputData = null;
m_offset = 0;
m_length = 0;
}
public String processText(String inputText)
{
List<String> segmented = segmenter.segmentString(inputText);
String output = "";
if(segmented.size() > 0)
{
output = segmented.get(0);
for(int i=1;i<segmented.size();i++)
{
output = output + " " +segmented.get(i);
}
}
System.out.println(output);
return output;
}
static Properties getProperties()
{
if (props == null) {
props = new Properties();
props.setProperty("sighanCorporaDict", basedir);
// props.setProperty("NormalizationTable", "data/norm.simp.utf8");
// props.setProperty("normTableEncoding", "UTF-8");
// below is needed because CTBSegDocumentIteratorFactory accesses it
props.setProperty("serDictionary",basedir+"dict-chris6.ser.gz");
props.setProperty("inputEncoding", "UTF-8");
props.setProperty("sighanPostProcessing", "true");
}
return props;
}
}
public final class ChineseTokenizer extends CharTokenizer {
public ChineseTokenizer(Version matchVersion, Reader in) {
super(matchVersion, in);
}
public ChineseTokenizer(Version matchVersion, AttributeFactory factory, Reader in) {
super(matchVersion, factory, in);
}
/** Collects only characters which do not satisfy
* {#link Character#isWhitespace(int)}.*/
#Override
protected boolean isTokenChar(int c) {
return !Character.isWhitespace(c);
}
}
You can pass the argument through the Factory's args parameter.

CheckboxCell, MultiSelectionModel unwantonly reset DataGrid's data

Using GWT 2.4...
I am building upon a complex Composite dual view/edit mode implementation that is backed GWT's DataGrid and MultiSelectionModel. My goal is for a user to be able to click a checkbox in each row that they'd like to post updates for.
Here's a screenshot from a semi-functional interface:
Note the selected (highlighted) rows.
Now the problem is that when I type something in any of the cells (e.g., the first row's $ cell under the $/Mw 1 composite cell header), then click that row's checkbox (or any other row's checkbox for that matter) to select or de-select, the value gets reset to the original value when the screen's data was first requested. Not desired behavior by any stretch!
Let's take a look at my custom implementation for the grid. (Excuse the length).
public abstract class ToggleableGrid<T extends Identifiable<?>> extends Composite {
private static final int CHKBOX_COLUMN_WIDTH = App.INSTANCE.checkboxColumnWidth();
private static final DisplayMode DEFAULT_MODE = DisplayMode.VIEW;
private ProvidesKey<T> keyProvider;
private DataGrid<T> grid;
private MultiSelectionModel<T> selectionModel;
private ListDataProvider<T> dataProvider;
private int tabIndex = 0;
public ToggleableGrid() {
final DataGridConfiguration config = new DefaultDataGridConfiguration();
initGrid(config);
}
public ToggleableGrid(DataGridConfiguration config) {
initGrid(config);
}
private void initGrid(DataGridConfiguration config) {
keyProvider = new ProvidesKey<T>() {
#Override
public Object getKey(T item) {
return item == null ? null : item.getId();
}
};
grid = new DataGrid<T>(config.getPageSize(), config.getResources(), keyProvider);
// Set the message to display when the table is empty.
grid.setEmptyTableWidget(new Label(UiMessages.INSTANCE.no_results()));
initWidget(grid);
setVisible(true);
}
public void setInput(List<T> content) {
setInput(content, DEFAULT_MODE);
}
public void setInput(List<T> content, DisplayMode mode) {
resetTableColumns();
if (isInEditMode(mode)) {
// Add a selection model so we can select cells
selectionModel = new MultiSelectionModel<T>(keyProvider);
grid.setSelectionModel(selectionModel, DefaultSelectionEventManager.<T> createCheckboxManager(0));
addRowSelector();
}
dataProvider = new ListDataProvider<T>(content);
final ListHandler<T> sortHandler = new ListHandler<T>(dataProvider.getList());
grid.addColumnSortHandler(sortHandler);
initializeStructure(constructMetadata(), sortHandler, mode);
dataProvider.addDataDisplay(grid);
}
// see https://stackoverflow.com/questions/3772480/remove-all-columns-from-a-celltable
// concrete classes are forced to maintain a handle on all columns added
private void resetTableColumns() {
for (final Column<T, ?> column: allColumns()) {
grid.removeColumn(column);
}
allColumns().clear();
}
protected boolean isInEditMode(DisplayMode currentDisplayMode) {
boolean result = false;
if (currentDisplayMode.equals(DisplayMode.EDIT)) {
result = true;
}
return result;
}
protected abstract Set<Column<T, ?>> allColumns();
protected abstract TableMetadata constructMetadata();
protected abstract void initializeStructure(TableMetadata metadata, ListHandler<T> sortHandler, DisplayMode mode);
protected void setColumnHorizontalAlignment(Column<T, ?> column, HorizontalAlignmentConstant alignment) {
column.setHorizontalAlignment(alignment);
}
// TODO figure out how to add a checkbox to column header that provides select/de-select all capability
// see https://stackoverflow.com/questions/6174689/gwt-celltable-programmatically-select-checkboxcell
protected void addRowSelector() {
final Column<T, Boolean> rowSelectColumn = new Column<T, Boolean>(new CheckboxCell(true, false)) {
#Override
public Boolean getValue(T value) {
Boolean result;
// check for null value and return null;
if(value == null || value.getId() == null) {
result = null;
} else { // get value from the selection model
result = selectionModel.isSelected(value);
}
return result;
}
};
addColumn(rowSelectColumn, UiMessages.INSTANCE.select());
setColumnWidth(rowSelectColumn, CHKBOX_COLUMN_WIDTH, Unit.PX);
setColumnHorizontalAlignment(rowSelectColumn, HasHorizontalAlignment.ALIGN_CENTER);
}
protected void setColumnWidth(Column<T, ?> column, int width, Unit unit) {
grid.setColumnWidth(column, width, unit);
}
protected void addColumn(Column<T, ?> column, String columnHeaderName) {
addColumn(column, columnHeaderName, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addColumn(Column<T, ?> column, String columnHeaderName, HorizontalAlignmentConstant alignment) {
final SafeHtmlBuilder sb = new SafeHtmlBuilder();
final String divStart = "<div align=\""+ alignment.getTextAlignString() + "\" class=\"" +UiResources.INSTANCE.style().word_wrap() + "\">";
sb.appendHtmlConstant(divStart).appendEscaped(columnHeaderName).appendHtmlConstant("</div>");
final SafeHtml header = sb.toSafeHtml();
grid.addColumn(column, header);
allColumns().add(column);
}
protected CompositeCell<T> generateCompositeCell(final List<HasCell<T, ?>> hasCells) {
final CompositeCell<T> compositeCell = new CompositeCell<T>(hasCells) {
#Override
public void render(Context context, T value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<table><tbody><tr>");
super.render(context, value, sb);
sb.appendHtmlConstant("</tr></tbody></table>");
}
#Override
protected Element getContainerElement(Element parent) {
// Return the first TR element in the table.
return parent.getFirstChildElement().getFirstChildElement().getFirstChildElement();
}
#Override
protected <X> void render(Context context, T value,
SafeHtmlBuilder sb, HasCell<T, X> hasCell) {
final Cell<X> cell = hasCell.getCell();
sb.appendHtmlConstant("<td>");
cell.render(context, hasCell.getValue(value), sb);
sb.appendHtmlConstant("</td>");
}
};
return compositeCell;
}
// FIXME not working quite the way we'd expect, index incremented within column for each row, not each row by column
protected int nextTabIndex() {
tabIndex++;
return tabIndex;
}
protected AbstractCellTable<T> getGrid() {
return grid;
}
/**
* Gets the selected (row(s) of) data from grid (used in edit mode)
* #return the selected data (as per selection model)
*/
public List<T> getSelectedData() {
final List<T> data = new ArrayList<T>();
data.addAll(selectionModel.getSelectedSet());
return data;
}
/**
* Gets all (row(s) of) data in grid (used in edit mode)
* #return all data as list
*/
public List<T> getAllData() {
return dataProvider.getList();
}
/**
* Clears the currently selected (row(s) of) data (used in edit mode)
*/
public void clearSelectedData() {
selectionModel.clear();
grid.redraw();
}
}
So, the interesting methods to stare at above (I think) are setInput, generateCompositeCell and addRowSelector.
We initialize the grid with List data and set a display mode in setInput. It's here as well that the selection model is initialized. It uses GWT's DefaultSelectionEventManager createCheckboxManager().
I've been trying to grok the event model, but it eludes me. I've visited the following sources online, but have come up short on avenues to solving this problem.
-- https://groups.google.com/forum/?fromgroups#!topic/google-web-toolkit/k5sfURxDaVg
AbstractInputCell's getConsumedEventsImpl adds focus, blur and keydown, so this (I believe) is not a track I need to explore
-- GWT CellTable programmatically select CheckBoxCell
The various ways you can instantiate a CheckBoxCell got me curious, and I've tried many constructor argument permutations, but the one I settled on (true, false) is (I believe) the right one
Agreeing here and now (before being reprimanded) that there's perhaps some unnecessary complexity in my implementation, but I am looking for guidance nonetheless. Thanks!
Update
If it helps here's an impl of the aforementioned ToggleableGrid. If anything it gives you more detail on what goes into each CompositeCell. For details on AbstractValidatableColumn and ValidatableInputCell, see: In search of a GWT validation example... where art thou?.
public class EnergyOfferGrid extends ToggleableGrid<EnergyOfferDTO> {
public EnergyOfferGrid() {
super();
}
public EnergyOfferGrid(DataGridConfiguration config) {
super(config);
}
private static final int MAX_NUMBER_OF_MW_PRICE_POINTS = App.INSTANCE.maxNoOfMwPricePoints();
private Set<Column<EnergyOfferDTO, ?>> columns = new HashSet<Column<EnergyOfferDTO, ?>>();
#Override
protected Set<Column<EnergyOfferDTO, ?>> allColumns() {
return columns;
}
#Override
protected TableMetadata constructMetadata() {
final TableMetadata metadata = new TableMetadata();
// TODO Consider a predefined set of ReferenceData to be held in a common package
// Use Slope
metadata.addColumnMetadata(UiMessages.INSTANCE.use_slope(), new String[] {UiMessages.INSTANCE.yes(), UiMessages.INSTANCE.no()}, new String[] {"true", "false"});
return metadata;
}
#Override
protected void initializeStructure(TableMetadata metadata, ListHandler<EnergyOfferDTO> sortHandler, DisplayMode currentDisplayMode) {
addHourColumn(sortHandler);
addUseSlopeColumn(metadata, sortHandler, currentDisplayMode);
for (int i = 0; i < MAX_NUMBER_OF_MW_PRICE_POINTS; i++) { // zero-based indexing
addPriceMwColumn(i, currentDisplayMode);
}
}
protected void addHourColumn(ListHandler<EnergyOfferDTO> sortHandler) {
final Column<EnergyOfferDTO, String> hourColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
String result = "";
if (energyOffer.getId() != null) {
final String isoDateTime = energyOffer.getId().getOperatingHour();
if (isoDateTime != null && !isoDateTime.isEmpty()) {
final Date dateTime = CSTimeUtil.isoToDate(isoDateTime);
if (dateTime != null) {
result = CSTimeUtil.dateToHour(dateTime);
}
}
}
return result;
}
};
hourColumn.setSortable(true);
sortHandler.setComparator(hourColumn, new Comparator<EnergyOfferDTO>() {
#Override
public int compare(EnergyOfferDTO eo1, EnergyOfferDTO eo2) {
final String date1 = eo1.getId() != null ? eo1.getId().getOperatingHour() : "";
final String date2 = eo2.getId() != null ? eo2.getId().getOperatingHour() : "";
return date1.compareTo(date2);
}
});
// We know that the data is sorted by hour by default.
getGrid(). getColumnSortList().push(hourColumn);
addColumn(hourColumn, UiMessages.INSTANCE.hour());
setColumnWidth(hourColumn, 45, Unit.PX);
setColumnHorizontalAlignment(hourColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addUseSlopeColumn(TableMetadata metadata, ListHandler<EnergyOfferDTO> sortHandler, DisplayMode currentDisplayMode) {
final ReferenceData refData = metadata.allColumnMetadata().get(UiMessages.INSTANCE.use_slope());
Column<EnergyOfferDTO, String> useSlopeColumn;
Cell<String> cell;
if (isInEditMode(currentDisplayMode)) {
cell = new ReferenceDataBackedSelectionCell(refData);
} else {
cell = new TextCell();
}
useSlopeColumn = new Column<EnergyOfferDTO, String>(cell) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return refData.getDisplayValueForSubmitValue(Boolean.toString(energyOffer.isSlopeUsed()));
}
};
useSlopeColumn.setSortable(true);
sortHandler.setComparator(useSlopeColumn, new Comparator<EnergyOfferDTO>() {
#Override
public int compare(EnergyOfferDTO eo1, EnergyOfferDTO eo2) {
final String slopeUsed1 = String.valueOf(eo1.isSlopeUsed());
final String slopeUsed2 = String.valueOf(eo1.isSlopeUsed());
return slopeUsed1.compareTo(slopeUsed2);
}
});
addColumn(useSlopeColumn, UiMessages.INSTANCE.use_slope());
setColumnWidth(useSlopeColumn, 75, Unit.PX);
setColumnHorizontalAlignment(useSlopeColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected void addPriceMwColumn(final int colIndex, DisplayMode currentDisplayMode) {
// Construct a composite cell for energy offers that includes a pair of text inputs
final List<HasCell<EnergyOfferDTO, ?>> columns = new ArrayList<
HasCell<EnergyOfferDTO, ?>>();
// this DTO is passed along so that price and mw values for new entries are kept together
final OfferPriceMwPair newOfferPriceMwPair = new OfferPriceMwPair();
// Price
final Column<EnergyOfferDTO, String> priceColumn = generatePriceColumn(colIndex, newOfferPriceMwPair, currentDisplayMode);
columns.add(priceColumn);
// MW
final Column<EnergyOfferDTO, String> mwColumn = generateMwColumn(colIndex, newOfferPriceMwPair, currentDisplayMode);
columns.add(mwColumn);
// Composite
final CompositeCell<EnergyOfferDTO> priceMwColumnInnards = generateCompositeCell(columns);
final IdentityColumn<EnergyOfferDTO> priceMwColumn = new IdentityColumn<EnergyOfferDTO>(priceMwColumnInnards);
final StringBuilder colHeader = new StringBuilder();
colHeader.append(UiMessages.INSTANCE.price_mw_header()).append(" ").append(String.valueOf(colIndex + 1));
addColumn(priceMwColumn, colHeader.toString());
setColumnWidth(priceMwColumn, 7, Unit.EM);
setColumnHorizontalAlignment(priceMwColumn, HasHorizontalAlignment.ALIGN_RIGHT);
}
protected Column<EnergyOfferDTO, String> generatePriceColumn(final int colIndex, final OfferPriceMwPair newOfferPriceMwPair, DisplayMode currentDisplayMode) {
Column<EnergyOfferDTO, String> priceColumn;
if (isInEditMode(currentDisplayMode)) {
priceColumn = new BigDecimalValidatableColumn<EnergyOfferDTO, OfferPriceMwPair>(nextTabIndex(), getGrid()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return obtainPriceValue(colIndex, energyOffer, false);
}
#Override
public void doUpdate(int index, EnergyOfferDTO energyOffer, String value) {
if (value != null && !value.isEmpty()) {
// number format exceptions should be caught and handled by event bus's handle method
final double valueAsDouble = NumberFormat.getDecimalFormat().parse(value);
final BigDecimal price = BigDecimal.valueOf(valueAsDouble);
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair == null) { // we have a new price value
newOfferPriceMwPair.setPrice(price);
offerPriceCurve.add(newOfferPriceMwPair);
} else {
offerPriceMwPair.setPrice(price);
}
}
}
#Override
protected String getPropertyName() {
return "price";
}
#Override
protected Class<OfferPriceMwPair> getPropertyOwner() {
return OfferPriceMwPair.class;
}
};
} else {
priceColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
final String result = obtainPriceValue(colIndex, energyOffer, true);
return result;
}
};
}
return priceColumn;
}
private String obtainPriceValue(final int colIndex, EnergyOfferDTO energyOffer, boolean withCurrency) {
String result = "";
if (energyOffer != null) {
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final int numberOfPairs = offerPriceCurve.size();
if (colIndex < numberOfPairs) {
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair != null) {
final BigDecimal price = offerPriceMwPair.getPrice();
if (price != null) {
final double value = price.doubleValue();
if (withCurrency) {
result = NumberFormat.getCurrencyFormat().format(value);
} else {
result = NumberFormat.getDecimalFormat().format(value);
}
}
}
}
}
return result;
}
protected Column<EnergyOfferDTO, String> generateMwColumn(final int colIndex, final OfferPriceMwPair newOfferPriceMwPair, DisplayMode currentDisplayMode) {
Column<EnergyOfferDTO, String> mwColumn;
if (isInEditMode(currentDisplayMode)) {
mwColumn = new BigDecimalValidatableColumn<EnergyOfferDTO, PriceMwPair>(nextTabIndex(), getGrid()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
return obtainMwValue(colIndex, energyOffer);
}
#Override
public void doUpdate(int index, EnergyOfferDTO energyOffer, String value) {
if (value != null && !value.isEmpty()) {
// number format exceptions should be caught and handled by event bus's handle method
final double valueAsDouble = NumberFormat.getDecimalFormat().parse(value);
final BigDecimal mw = BigDecimal.valueOf(valueAsDouble);
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final OfferPriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair == null) { // we have a new price value
newOfferPriceMwPair.setMw(mw);
offerPriceCurve.add(newOfferPriceMwPair);
} else {
offerPriceMwPair.setMw(mw);
}
}
}
#Override
protected String getPropertyName() {
return "mw";
}
#Override
protected Class<PriceMwPair> getPropertyOwner() {
return PriceMwPair.class;
}
};
} else {
mwColumn = new Column<EnergyOfferDTO, String>(new TextCell()) {
#Override
public String getValue(EnergyOfferDTO energyOffer) {
final String result = obtainMwValue(colIndex, energyOffer);
return result;
}
};
}
return mwColumn;
}
private String obtainMwValue(final int colIndex, EnergyOfferDTO energyOffer) {
String result = "";
if (energyOffer != null) {
final List<OfferPriceMwPair> offerPriceCurve = energyOffer.getCurve();
final int numberOfPairs = offerPriceCurve.size();
if (colIndex < numberOfPairs) {
final PriceMwPair offerPriceMwPair = offerPriceCurve.get(colIndex);
if (offerPriceMwPair != null) {
final BigDecimal mw = offerPriceMwPair.getMw();
if (mw != null) {
result = NumberFormat.getDecimalFormat().format(mw);
}
}
}
}
return result;
}
}
All that custom work w.r.t. WrapperCell and CompositeValidatableColumn was unnecessary.
It turns out that there's a way you should not construct CompositeCells. See http://code.google.com/p/google-web-toolkit/issues/detail?id=5714. My CompositeCells were not receiving events. So, I changed the way I construct them in ToggleableGrid.
protected CompositeCell<T> generateCompositeCell(final List<HasCell<T, String>> hasCells) {
final CompositeCell<T> compositeCell = new CompositeCell<T>(hasCells) {
// to not run afoul of http://code.google.com/p/google-web-toolkit/issues/detail?id=5714
#Override
public void render(Context context, T value, SafeHtmlBuilder sb) {
sb.appendHtmlConstant("<div style=\"display: inline\">");
super.render(context, value, sb);
sb.appendHtmlConstant("</div>");
}
#Override
protected Element getContainerElement(Element parent) {
// Return the first element in the DIV.
return parent.getFirstChildElement();
}
};
return compositeCell;
}
After that change and incorporating my other validation-oriented classes: ValidatableFieldUpdater, AbstractValidatableColumn (and derivatives), ValidatableInputField and ConversionResult, life couldn't be more grand!