I want to bind an indexed property to JFace ComboViewer.
Lets say that I have a DataModel class like this:
class DataModel {
private String[] props = {"A","B","C"};
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public String getProperties( int idx ){
return props[idx];
}
public void setProperties( int idx, String value ){
String oldVal = props[idx];
props[idx] = value;
pcs.fireIndexedPropertyChange( "properties", idx, oldVal, value );
}
// code to add/remove PropertyChangeListener
// ...
}
The data binding code for simple property would look like this:
DataModel dataModel = ...
ComboViewer propertyChoice = ...
DataBindingContext ctx = new DataBindingContext();
IObservableValue target = ViewerProperties.singleSelection().observe( propertyChoice );
IObservableValue model = BeanProperties.value( DataModel.class, "properties" ).observe(dataModel);
ctx.bindValue( target, model );
but with an indexed property I have to inform the ctx at which index is the value that I want to bind. I have tried
IObservableValue model = BeanProperties.value( DataModel.class, "properties[0]" ).observe(dataModel);
but it doesn't work.
Is it possible to bind indexed property instead of simple property? How?
Unfortunately this seems to be unsupported. I was looking for exactly the same functionality. There is no documentation in BeanProperties that says it is supported.
When looking into the implementation of BeanProperties.value, you find that it delegates to BeanPropertyHelper for reading and writing a property. The method Object readProperty(Object source, PropertyDescriptor propertyDescriptor) does not know about the subclass IndexedPropertyDescriptor. When it is invoked for an indexed property, readProperty tries to use a read method that reads the entire array. I think this method is optional for indexed properties. For indexed properties it should use the IndexedPropertyDescriptor.getIndexedReadMethod().
Depending on your use case you may be able to workaround the problem by using BeanProperties.list. However you cannot use this in combination with indexed properties. I tried this by adding a method that returns the entire array but still keeping the method that does a "fireIndexedPropertyChange". Unfortunately this gives a ClassCastException: Eclipse's BeanListProperty seems to suppose that the value in the change event is an array or list. However for an indexed property it is a single element of the array.
Or perhaps you can use an observable map instead?
Related
I'm using https://github.com/leangen/graphql-spqr with spring-boot java application. I can reach to alias name easily but how can I reach to original fieldName?
class Food {
#GraphQLQuery(name = "aliasNameX", description = "A food's name")
private String originalName;
...
}
....
#GraphQLQuery(name = "foods") // READ ALL
#Override
public List<Food> getFoods(#GraphQLEnvironment ResolutionEnvironment env) {
DataFetchingFieldSelectionSet selectionSet = env.dataFetchingEnvironment.getSelectionSet();
List<SelectedField> fields = selectionSet.getFields();
for (SelectedField f: fields)
{
System.out.println(f.getName());
}
return foodRepository.findAll();
}
When I run this code, Output looks like with alias fields: "aliasNameX", ..., but I need original name like "originalName". Is there a way to do it?
Solved, according to:
https://github.com/leangen/graphql-spqr/issues/381
Posting my original answer here as well.
You want the underlying field names, but from a level above. Still possible, but ugly :(
for (SelectedField selectedField : env.dataFetchingEnvironment.getSelectionSet().getImmediateFields()) {
Optional<Operation> operation = Directives.getMappedOperation(selectedField.getFieldDefinition());
String javaName = operation.map(op -> ((Member) op.getTypedElement().getElement()).getName()).orElse(null);
}
Be very careful though. If there's more than one Java element exposed per GraphQL field, getTypedElement().getElement() will explode. So to be sure you'd have to call getTypedElement().getElements() (plural) instead and decide what to do. ClassUtils#getPropertyMembers might also be useful, or the ClassUtils.findXXX family of methods.
You'd basically have to do this:
List<AnnotatedElement> elements = getTypedElement().getElements();
//Look for a field and use its name
Optional<String> field = Utils.extractInstances(elements, Field.class).findFirst().map(Field::getName);
//Look for a getter and find its associated field name
Optional<String> getter = Utils.extractInstances(elements, Method.class).findFirst().map(ClassUtils::getFieldNameFromGetter);
This API might have to change in future, as SDL-based tools are proliferating, so complex directives like the ones SPQR is using are causing problems...
We are storing complex objects in Hazelcast maps and need the possibility to search for objects not only based on the key but also on the content of these complex objects. In order to not take too large a performance hit, we are using indices on those search terms.
We are also using spring-data-hazelcast which provides repositories that allow us to use findByAbcXyz() type semantic queries. For some of the more complex queries we are using the #Query annotation (which spring-data-hazelcast internally translates to SqlPredicates).
We have now encountered an issue where under certain situations these #Query based search methods did not return any values, even if we could verify that the searched objects did in fact exist in the map.
I have managed to reproduce this issue with core hazelcast (i.e. without the use of spring-data-hazelcast).
Here is our object structure:
BetriebspunktKey.java
public class BetriebspunktKey implements Serializable {
private Integer uicLand;
private Integer nummer;
public BetriebspunktKey(final Integer uicLand, final Integer nummer) {
this.uicLand = uicLand;
this.nummer = nummer;
}
public Integer getUicLand() {
return uicLand;
}
public Integer getNummer() {
return nummer;
}
}
Betriebspunkt.java
public class Betriebspunkt implements Serializable {
private BetriebspunktKey key;
private List<BetriebspunktVersion> versionen;
public Betriebspunkt(final BetriebspunktKey key, final List<BetriebspunktVersion> versionen) {
this.key = key;
this.versionen = versionen;
}
public BetriebspunktKey getKey() {
return key;
}
}
BetriebspunktVersion.java
public class BetriebspunktVersion implements Serializable {
private List<BetriebspunktKey> zusatzbetriebspunkte;
public BetriebspunktVersion(final List<BetriebspunktKey> zusatzbetriebspunkte) {
this.zusatzbetriebspunkte = zusatzbetriebspunkte;
}
}
In my main file, I am now setting up hazelcast:
Config config = new Config();
final MapConfig mapConfig = config.getMapConfig("points");
mapConfig.addMapIndexConfig(new MapIndexConfig("versionen[any].zusatzbetriebspunkte[any].nummer", false));
HazelcastInstance instance = Hazelcast.newHazelcastInstance(config);
IMap<BetriebspunktKey, Betriebspunkt> map = instance.getMap("points");
I am also preparing my search criteria for later on:
Predicate equalPredicate = Predicates.equal("versionen[any].zusatzbetriebspunkte[any].nummer", 53090);
Predicate sqlPredicate = new SqlPredicate("versionen[any].zusatzbetriebspunkte[any].nummer=53090");
Next, I am creating two objects, one with the "full depth" of information, the other does not contain any "zusatzbetriebspunkte":
final Betriebspunkt abc = new Betriebspunkt(
new BetriebspunktKey(80, 166),
Collections.singletonList(new BetriebspunktVersion(
Collections.singletonList(new BetriebspunktKey(80, 53090))
))
);
final Betriebspunkt def = new Betriebspunkt(
new BetriebspunktKey(83, 141),
Collections.singletonList(new BetriebspunktVersion(
Collections.emptyList()
))
);
Here is, where things become interesting. If I first insert the "full" object into the map, the search using both the EqualPredicate as well as the SqlPredicate works:
map.put(abc.getKey(), abc);
map.put(def.getKey(), def);
Collection<Betriebspunkt> equalResults = map.values(equalPredicate);
Collection<Betriebspunkt> sqlResults = map.values(sqlPredicate);
assertEquals(1, equalResults.size()); // contains "abc"
assertEquals(1, sqlResults.size()); // contains "abc"
However, if I insert the objects into my map in reverse order (i.e. first the "partial" object and then the "full" one), only the EqualPredicate works correctly, the SqlPredicate returns an empty list, no matter what the content of the map or the search criteria.
map.put(abc.getKey(), abc);
map.put(def.getKey(), def);
Collection<Betriebspunkt> equalResults = map.values(equalPredicate);
Collection<Betriebspunkt> sqlResults = map.values(sqlPredicate);
assertEquals(1, equalResults.size()); // contains "abc"
assertEquals(1, sqlResults.size()); // --> this fails, it returns en empty list
What is the reason for this behaviour? It looks like a bug in the hazelcast code.
The reason for failing
After a lot of debugging, I have found the reason for this issue. The reasons can indeed be found in the hazelcast code.
When putting a value into a hazelcast map DefaultRecordStore.putInternal is called. At the end of this method DefaultRecordStore.saveIndex is called which finds the corresponding indexes and then calls Indexes.saveEntryIndex. This method iterates over each index and calls InternalIndex.saveEntryIndex (or rather its implementation IndexImpl.saveEntryIndex. The interesting part of that method are the following lines:
if (this.converter == null || this.converter == TypeConverters.NULL_CONVERTER) {
this.converter = entry.getConverter(this.attributeName);
}
Aparently each index stores a converter class when the first element is put into the map. Looking at QueryableEntry.getConverter explains what happens:
TypeConverter getConverter(String attributeName) {
Object attribute = this.getAttributeValue(attributeName);
if (attribute == null) {
return TypeConverters.NULL_CONVERTER;
} else {
AttributeType attributeType = this.extractAttributeType(attributeName, attribute);
return attributeType == null ? TypeConverters.IDENTITY_CONVERTER : attributeType.getConverter();
}
}
When first inserting the "full" object, extractAttributeType() will follow the "path" of our index definition "versionen[any].zusatzbetriebspunkte[any].nummer" and find out that nummer is an integer type, accordingly a TypeConverters.IntegerConverter will be returned and stored.
When first inserting the "partial" object, "zusatzbetriebspunkte[any]" is emtpy, and there is no way for extractAttributeType to find out what type nummer hast, it therefore returns null which means that TypeConverters.IdentityConverter is used.
Also, whenever a "full" element is inserted an entry is written into the index map using nummer as key, i.e. the index-map is of type Map.
So much for writing to the map. Let's now look at how data is read from the map. When calling map.values(predicate) we will eventually get to QueryRunner.runUsingGlobalIndexSafely which contains a line:
Collection<QueryableEntry> entries = indexes.query(predicate);
this will in turn after some boilerplate code call
Set<QueryableEntry> result = indexAwarePredicate.filter(queryContext);
For both of our predicates we will eventually get to IndexImpl.getRecords() which looks as follows:
public Set<QueryableEntry> getRecords(Comparable attributeValue) {
long timestamp = this.stats.makeTimestamp();
if (this.converter == null) {
this.stats.onIndexHit(timestamp, 0L);
return new SingleResultSet((Map)null);
} else {
Set<QueryableEntry> result = this.indexStore.getRecords(this.convert(attributeValue));
this.stats.onIndexHit(timestamp, (long)result.size());
return result;
}
}
The crucial call is this.convert(attributeValue) where attributeValue is the value of the predicate.
If we compare our two predicates, we can see that the EqualPredicate has two members:
attributeName = "versionen[any].zusatzbetriebspunkte[any].nummer"
value = {Integer} 53090
The SqlPredicate contains the initial string (which we passed to its constructor) but which at constructions was also parsed and mapped to a internal EqualPredicate (which when evaluating the predicate is eventually used and passed to getRecords() above):
sql = "versionen[any].zusatzbetriebspunkte[any].nummer=53090"
predicate = {EqualPredicate}
attributeName = "versionen[any].zusatzbetriebspunkte[any].nummer"
value = {String} "53090"
And this explains why the manually created EqualPredicate works in both cases: Its value is an integer. When passed to the converter, it does not matter whether it is the IntegerConverter or the IdentityConverter, as both will return the integer which can then be used as key in the index-map (which uses an integer as key).
With the SqlPredicate however, the value is a String. If this is passed to the IntegerConverter, it is converted to its corresponding integer value and accessing the index-map works. If it is passed to the IdentityConverter, the string is returned by the conversion and trying to access the index-map with a string will never find any results.
A possible solution
How can we solve this issue? I see several possibilities:
insert a "fully built" dummy value into our map during startup to ensure the converter is correctly initialised. While this works, it is ugly and not maintenance friendly
avoid using SqlPredicate and use the integer based EqualPredicate. This is not an option when working with spring-data-hazelcast as it always converts #Query based searches to SqlPredicates. We could of course use hazelcast directly and circumvent the spring-data wrapper but while that would work it means having two ways of accessing hazelcast which is also not very maintainable
use hazelcast's ValueExtractor class. This is the elegant solution that works both natively and using spring-data-hazelcast. I will outline what that looks like:
First we need to implement a value extractor which returns all zusatzbetriebspunkte of our Betriebspunkt in a form suitable for us
public class BetriebspunktExtractor extends ValueExtractor<Betriebspunkt, String> implements Serializable {
#Override
public void extract(final Betriebspunkt betriebspunkt, final String argument, final ValueCollector valueCollector) {
betriebspunkt.getVersionen().stream()
.map(BetriebspunktVersion::getZusatzbetriebspunkte)
.flatMap(List::stream)
.map(zbp -> zbp.getUicLand() + "_" + zbp.getNummer())
.forEach(valueCollector::addObject);
}
}
You'll notice that I am not only returning the nummer field but also include the uicLand field this is something we really wanted but couldn't get working using the "...[any]..." notation. We could of course only return the nummer if we wanted the exact same behavior as outlined above.
Now we need to modify our hazelcast configuration slightly:
Config config = new Config();
final MapConfig mapConfig = config.getMapConfig("points");
//mapConfig.addMapIndexConfig(new MapIndexConfig("versionen[any].zusatzbetriebspunkte[any].nummer", false));
mapConfig.addMapIndexConfig(new MapIndexConfig("zusatzbetriebspunkt", false));
mapConfig.addMapAttributeConfig(new MapAttributeConfig("zusatzbetriebspunkt", BetriebspunktExtractor.class.getName()));
You'll notice that the "long" index definition using the "...[any]..." notation is no longer needed.
Now we can use this "pseudo attribute" to query our values and it doesn't matter in which order the objects have been added to the map:
Predicate keyPredicate = Predicates.equal("zusatzbetriebspunkt", "80_53090");
Collection<Betriebspunkt> keyResults = map.values(keyPredicate);
assertEquals(1, keyResults.size()); // always contains "abc"
And in our spring-data-hazelcast repository we can now do this:
#Query("zusatzbetriebspunkt=%d_%d")
List<StammdatenBetriebspunkt> findByZusatzbetriebspunkt(Integer uicLand, Integer nummer);
If you do not need to use spring-data-hazelcast, instead of returning a string to the ValueCollector, you could return the BetriebspunktKey directly and then use it in the predicate as well. That would be the cleanest solution:
public class BetriebspunktExtractor extends ValueExtractor<Betriebspunkt, String> implements Serializable {
#Override
public void extract(final Betriebspunkt betriebspunkt, final String argument, final ValueCollector valueCollector) {
betriebspunkt.getVersionen().stream()
.map(BetriebspunktVersion::getZusatzbetriebspunkte)
.flatMap(List::stream)
//.map(zbp -> zbp.getUicLand() + "_" + zbp.getNummer())
.forEach(valueCollector::addObject);
}
}
and then
Predicate keyPredicate = Predicates.equal("zusatzbetriebspunkt", new BetriebspunktKey(80, 53090));
However, for this to work, BetriebspunktKey needs to implement Comparable and must also provide its own equals and hashCode methods.
I have a property file (a.txt) which has the values (Example values given below) like below
test1=10
test2=20
test33=34
test34=35
By reading this file, I need to produce an output like below
value = 35_20_34_10
which means => I have a pattern like test34_test2_test33_test1
Note, If the 'test33' has any value other than 34 then I need to produce the value like below
value = 35_20_10
which means => I have a pattern like test34_test2_test1
Now my problem is, every time when the customer is making the change in the logic, I am making the change in the code. So what I expect is, I want to keep the logic (pattern) in another property file so I will be sending the two inputs to the util (one input is the property file (A.txt) another input will be the 'pattern.txt'),
My util has to be compare the A.txt and the business logic 'pattern.txt' and produce the output like
value = 35_20_34_10 (or)
value = 35_20_10
If there an example for such pattern based logic as I expect?
Any predefined util / java class does this?
Any help would be Great.
thanks,
Harry
First of all, svasa's answer makes a lot of sense, but covers different level of
abstraction. I recommend you read his answer too, that pattern should
be useful.
You may wanna look at Apache Velocity and FreeMarker libraries to see how they structure their API.
Those are template engines - they usually have some abstraction of pattern or format, and abstraction of variable/value binding (or namespace, or source). You can render a template by binding it with a binding/namespace, which yields the result.
For example, you may wanna have a pattern "<a> + <b>", and binding that looks like a map: {a: "1", b: "2"}. By binding that binding to that pattern you'll get "1 + 2", when interpreting <...> as variables.
You basically load the pattern from your pattern.txt, then load your data file A.txt (for example, by treating it as properties and using Properties class) and construct binding based on these properties. You'll get your output and possibility to customize the pattern all the time.
You may call the sequences like test34_test2_test33_test1 as a pattern, let me call them as constraints when building something.
To me this problem best fits into a
builder pattern.
When building the value you want, you tell the builder that these are my constraints(pattern) and these are my original properties like below:
new MyPropertiesBuilder().setConstraints(constraints).setProperties(original).buildValue();
Details:
Set some constraints in a separate file where you specify the order of the properties and their values like :
test34=desiredvalue-could-be-empty
test2=desiredvalue-could-be-empty
test33=34
test1=desiredvalue-could-be-empty
The builder goes over the constraints in the order specified, but get the values from the original properties and build the desired string.
One way to achieve your requirement through builder pattern is to define classes like below :
Interface:
public interface IMyPropertiesBuilder
{
public void setConstraints( Properties properties );
public void setProperties( Properties properties );
public String buildValue();
}
Builder
public class MyPropertiesBuilder implements IMyPropertiesBuilder
{
private Properties constraints;
private Properties original;
#Override
public void setConstraints( Properties constraints )
{
this.constraints = constraints;
}
#Override
public String buildValue()
{
StringBuilder value = new StringBuilder();
Iterator it = constraints.keySet().iterator();
while ( it.hasNext() )
{
String key = (String) it.next();
if ( original.containsKey( key ) && constraints.getProperty( key ) != null && original.getProperty( key ).equals( constraints.getProperty( key ) ) )
{
value.append( original.getProperty( key ) );
value.append( "_" );
}
}
return value.toString();
}
#Override
public void setProperties( Properties properties )
{
this.original = properties;
}
}
User
public class MyPropertiesBuilderUser
{
private Properties original = new Properties().load(new FileInputStream("original.properties"));;
private Properties constraints = new Properties().load(new FileInputStream("constraints.properties"));
public String getValue()
{
String value = new MyPropertiesBuilder().setConstraints(constraints).setProperties(original).buildValue();
}
}
I have a Table whose DataSource is set to a IndexedContainer. I also have multiple selection enabled on my Table. The Question is, how do I get all the selected values.. as an array perhaps?
My IndexedContainer:
private void populateAnalyteTable () {
Analyte[] analytes = Analyte.getAnalytes();
for (Analyte analyte : analytes) {
Object id = ic_analytes.addItem();
ic_analytes.getContainerProperty(id, "ID").setValue(analyte.getId());
ic_analytes.getContainerProperty(id, "Analyte Name").setValue(analyte.getAnalyteName());
}
// Bind indexed container to table
tbl_analytes.setContainerDataSource(ic_analytes);
}
What I'm eventually trying to get is an array of Analyte objects
Why do you want to use IndexContainer? Why don't you use BeanItemCotainer?
Please find the snippet of code below
table.setMultiSelect(true);
BeanItemContainer<Analyte> container = new BeanItemContainer<Analyte>(Analyte.class);
container.addAll(Arrays.asList(Analyte.getAnalytes()));
table.setContainerDatasource(container);
// Add some Properties of Analyte class that you want to be shown to user
table.setVisibleColumns(new Object[]{"ID","Analyte Name"});
//User selects Multiple Values, mind you this is an Unmodifiable Collection
Set<Analyte> selectedValues = (Set<Analyte>)table.getValue();
Please let me know in case it doesn't solve the issue
The vaadin objects supporting MultiSelect all return a set of the selected items.
https://www.vaadin.com/api/com/vaadin/ui/AbstractSelect.html#getValue%28%29
The drawback of this, if you need the selected items in "real" order (as displayed onscreen)
you will then have to find them from the Set to the Container
Just add your object as the Item-ID, like luuksen already propesed. Just change the initialisation of yout IndexedContainer to:
for (Analyte analyte : analytes) {
Object id = ic_analytes.addItem(analyte);
ic_analytes.getContainerProperty(id, "ID").setValue(analyte.getId());
ic_analytes.getContainerProperty(id, "Analyte Name").setValue(analyte.getAnalyteName());
}
table.getValue() is what you are looking for.
This method gives you an Object (if table is single select) or a Set<Object> (if multiselect) of the ID(s) of selected item(s). Runtime type depends on runtime id type, but if you do not need the value you can go around with Object .
If you are looking for Analytes as an array you can do
#SuppressWarnings("unchecked")
Set<Object> selectedIds = (Set<Object>) tbl_analytes.getValue();
List<Analyte> listAnalytes = new ArrayList<Analyte>();
for (Object id : selectedIds) {
listAnalytes.get(tbl_analytes.getItem(id));
}
listAnalytes.toArray();
Note that this approach works with every standard container you may use in Vaadin.
Regards!
EDIT: actually what .getValue() returns depends on the used container. In most of the cases it's the ID.
I have a situation where I want to bind a BooleanProperty to the non-empty state of an ObservableList wrapped inside an ObjectProperty.
Here's a basic synopsis of the behavior I'm looking for:
ObjectProperty<ObservableList<String>> obp = new SimpleObjectProperty<ObservableList<String>>();
BooleanProperty hasStuff = new SimpleBooleanProperty();
hasStuff.bind(/* What goes here?? */);
// ObservableProperty has null value
assertFalse(hasStuff.getValue());
obp.set(FXCollections.<String>observableArrayList());
// ObservableProperty is no longer null, but the list has not contents.
assertFalse(hasStuff.getValue());
obp.get().add("Thing");
// List now has something in it, so hasStuff should be true
assertTrue(hasStuff.getValue());
obp.get().clear();
// List is now empty.
assertFalse(hasStuff.getValue());
I'd like to use the builders in the Bindings class rather than implementing a chain of custom bindings.
The Bindings.select(...) method theoretically does what I want, except that there's no Bindings.selectObservableCollection(...) and casting the return value from the generic select(...) and passing it to Bindings.isEmpty(...) doesn't work. That is, the result of this:
hasStuff.bind(Bindings.isEmpty((ObservableList<String>) Bindings.select(obp, "value")));
causes a ClassCastException:
java.lang.ClassCastException: com.sun.javafx.binding.SelectBinding$AsObject cannot be cast to javafx.collections.ObservableList
Is this use case possible using just the Bindings API?
Solution
Based on answer from #fabian, here's the solution that worked:
ObjectProperty<ObservableList<String>> obp = new SimpleObjectProperty<ObservableList<String>>();
ListProperty<String> lstProp = new SimpleListProperty<>();
lstProp.bind(obp);
BooleanProperty hasStuff = new SimpleBooleanProperty();
hasStuff.bind(not(lstProp.emptyProperty()));
assertFalse(hasStuff.getValue());
obp.set(FXCollections.<String>observableArrayList());
assertFalse(hasStuff.getValue());
obp.get().add("Thing");
assertTrue(hasStuff.getValue());
obp.get().clear();
assertFalse(hasStuff.getValue());
I don't see a way to do this using Bindings API only. ObservableList doesn't have a property empty, so you can't use
Bindings.select(obp, "empty").isEqualTo(true)
and
ObjectBinding<ObservableList<String>> lstBinding = Bindings.select(obp);
hasStuff.bind(lstBinding.isNotNull().and(lstBinding.isNotEqualTo(Collections.EMPTY_LIST)));
doesn't work since it only updates when the list changes, but not when it's contents change (i.e. the third assertion fails).
But the custom chain of bindings you have to create is very simple:
SimpleListProperty lstProp = new SimpleListProperty();
lstProp.bind(obp);
hasStuff.bind(lstProp.emptyProperty());
It could be done with fewer variables:
SimpleListProperty<String> listProperty = new SimpleListProperty<>(myObservableList);
BooleanProperty hasStuff = new SimpleBooleanProperty();
hasStuff.bind(not(listProperty.emptyProperty()));
Does it really have to be an ObjectProperty<ObservableList<String>>? If so, this answer does not solve your problem...
But, I think that if you change the type of obp like this:
Property<ObservableList<String>> obp = new SimpleListProperty<>();
You should be able to use:
hasStuff.bind(Bindings.isEmpty((ListProperty<String>) obp));