Spring Data Neo4j 4 : Failed to convert from type java.util.LinkedHashSet<?> to type org.springframework.data.domain.Page<?> - spring-data-neo4j

Having this Repository method
#Query("MATCH (i:`Interest`) WHERE not(i-[:PARENT]->()) return i")
public Page<Interest> findAllByParentIsNull(Pageable pageRequest);
It cause (it didn't respect the specification):
org.springframework.core.convert.ConversionFailedException: Failed to convert from type java.util.LinkedHashSet<?> to type org.springframework.data.domain.Page<?> for value '[com.nearofme.model.Interest#12a4479, com.nearofme.model.Interest#15bdfb3, com.nearofme.model.Interest#1af6067, com.nearofme.model.Interest#1c17d4d, com.nearofme.model.Interest#df65f4, com.nearofme.model.Interest#3b140d, com.nearofme.model.Interest#1e24566, com.nearofme.model.Interest#da49c9]'; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type java.util.LinkedHashSet<?> to type org.springframework.data.domain.Page<?>
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:192)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:176)
at org.springframework.data.repository.core.support.QueryExecutionResultHandler.postProcessInvocationResult(QueryExecutionResultHandler.java:75)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:439)
Debugging the code shows that a conversion is needed at the GraphRepositoryQuery level :
#Override
public final Object execute(Object[] parameters) {
Class<?> returnType = graphQueryMethod.getMethod().getReturnType();
Class<?> concreteType = graphQueryMethod.resolveConcreteReturnType();
Map<String, Object> params = resolveParams(parameters);
// could be converted here
return execute(returnType, concreteType, getQueryString(), params);
}
The current code convert the result at GraphRepositoryImpl with the private method updatePage that should be used in the GraphRepositoryQuery
#Override
public Page<T> findAll(Pageable pageable, int depth) {
Collection<T> data = session.loadAll(clazz, convert(pageable.getSort()), new Pagination(pageable.getPageNumber(), pageable.getPageSize()), depth);
return updatePage(pageable, new ArrayList<T>(data));
}

So my current temporary solution is to update GraphRepositoryQuery with :
#Override
public final Object execute(Object[] parameters) {
Class<?> returnType = graphQueryMethod.getMethod().getReturnType();
Class<?> concreteType = graphQueryMethod.resolveConcreteReturnType();
Map<String, Object> params = resolveParams(parameters);
Object result = execute(returnType, concreteType, getQueryString(), params);
if (params.size()>0){
Object param = params.values().toArray()[0];
if (param instanceof Pageable){
Pageable pageable = (Pageable) param;
result = updatePage(pageable, new ArrayList((Collection) result));
}
}
return result;
}
private Page updatePage(Pageable pageable, List results) {
int pageSize = pageable.getPageSize();
int pageOffset = pageable.getOffset();
int total = pageOffset + results.size() + (results.size() == pageSize ? pageSize : 0);
return new PageImpl(results, pageable, total);
}
No more need to convert inside the GraphRepositoryImpl but it still working

Related

JustMock Arranging a method that returns an object whose value needs to be propagated into the SUT

I feel like I have to be missing something that is obvious, or I am overcomplication what I am doing. I am attempting to test a method that contains several other methods. One method is passed an object to write data to a database, in which the ID will be updated. This ID is then set to a local variable and used in other methods and the return. I can't get my Assert.AreEqual to work because the ID out is always 0 when I expect it to be 12. I have not had a lot of experience with UnitTesting and less with JuskMock. I assume I am doing something wrong.
This simplified pseudo code demonstrates my issue.
public class MyObj: IMyObject
{
public int ID { get; set; }
public string Name {get;set;}
}
public int Query(string Name)
{
int ID = 0;
ID = _setID.FindPerson(Name);
if(ID = 0)
{
IMyObject myObj = new MyObj(0, Name);
_setID.WritePerson(myObj);
ID = myObj.ID;
}
_setID.WriteSomethingElse(ID)
return ID;
}
public delegate void SetIDDelegate<T1, T2>(T1 arg1, T2 arg2);
[TestMethod]
public void TestQuery_ReturnID()
{
IMyObject UTobj = new MyObj {
ID = 12,
msg = string.Empty
};
Mock.Arrange(() => _mockSetID.WritePerson(
Arg.IsAny<IMyObject>(),
))
.DoInstead(new SetIDDelegate<IMyObject, string>
((IMyObject a, string b) =>
{
a = UTobj;
}
)).MustBeCalled();
int IDout = _objProcessObj.Query();
Mock.Assert(_mockSetID);
Assert.AreEqual(UTobj.ID, IDout);
}
I was able to figure out my issue with my UT. I needed to update the object in the delegate, not replace it.
.DoInstead(new SetIDDelegate<IMyObject, string>
((IMyObject a, string b) =>
{
a.ID = UTobj.ID;
}

Error "Expected invocation on the mock once, but was 0 times" when unit testing with moq

I have the following class I want to test:
public interface ISqlServiceByModule
{
DataSet GetPagedAggregateData(int clientId, int moduleId, int billTypeId, PagedTable result);
}
public class IncidentModuleService : IIncidentModuleService
{
private readonly ISqlServiceByModule sqlServiceByModule;
public IncidentModuleService(ISqlServiceByModule sqlServiceByModule)
{
if (sqlServiceByModule == null) throw new ArgumentNullException("sqlServiceByModule");
// Inject the ISqlServiceByModule dependency into the constructor
this.sqlServiceByModule = sqlServiceByModule;
}
public PagedTable GetData(int clientId, int moduleId, int billTypeId, Dictionary<string, string> queryStringParameters)
{
PagedTable result = new PagedTable(queryStringParameters);
DataSet dataSet = this.sqlServiceByModule.GetPagedAggregateData(clientId, moduleId, billTypeId, result);
// Map the DatSet to a PagedTable
if (dataSet == null || dataSet.Tables.Count == 0)
{
result.SetPagesFromTotalItems(0);
}
else
{
result.SetPagesFromTotalItems(Convert.ToInt16(dataSet.Tables[1].Rows[0][0]));
result.Listings = dataSet.Tables[0];
}
return result;
}
}
Specifically, I want to test the GetData method. My unit test looks like this:
[TestClass]
public class IncidentModuleServiceUnitTest
{
private DataSet incidentsData;
[TestInitialize]
public void SetUp()
{
this.incidentsData = new DataSet();
}
[TestMethod]
public void GetDataTestGetPagedAggregateDataIsCalled()
{
//-- Arrange
int billTypeId = 1;
int clientId = 1;
int moduleId = 1;
Dictionary<string, string> queryStringParameters = new Dictionary<string,string>();
PagedTable tempResult = new PagedTable(queryStringParameters);
DataSet dataSet = new DataSet();
dataSet.Tables.Add(new DataTable());
var mockSqlService = new Mock<ISqlServiceByModule>();
mockSqlService.Setup(r => r.GetPagedAggregateData(clientId, moduleId, billTypeId, tempResult)).Returns(this.incidentsData);
IncidentModuleService target = new IncidentModuleService(mockSqlService.Object);
//-- Act
var actual = target.GetData(clientId, moduleId, billTypeId, queryStringParameters);
//-- Assert
Assert.IsNull(actual.Listings);
mockSqlService.Verify(r => r.GetPagedAggregateData(clientId, moduleId, billTypeId, tempResult), Times.Once);
}
}
The error I am getting happens on the last line:
mockSqlService.Verify(r => r.GetPagedAggregateData(clientId, moduleId, billTypeId, tempResult), Times.Once);
And the exact error message is this:
{"\r\nExpected invocation on the mock once, but was 0 times: r =>
r.GetPagedAggregateData(.clientId, .moduleId, .billTypeId, .tempResult
Configured setups:\r\nr => r.GetPagedAggregateData(.clientId,
.moduleId, .billTypeId, .tempResult),
Times.Never
Performed invocations:\r\nISqlServiceByModule.GetPagedAggregateData(1,
1, 1, PagedTable)"}
Any idea why this is happening? It looks to me like the method in question is being called, but Moq doesn't like the parameters for some reason, even though they are the exact same ones in all three invocations, as far as I can tell.
PagedTable is a reference type not a value type. Therefore the parameters in Setup don't match what was called even though they look like they should be the same. You could use It.IsAny<PagedTable>() instead of tempResult.
See this answer for an example of how to check that the PagedTable parameter was the correct one.

MapReduce Hadoop - mapside joining the 2 data sets using customInputFormat

I am learning hadoop mapreduce framework ,i am trying to join 2 data sets have first record(Text) in the line as the Key , i tried to search in stackoverflow previous posts but nothing worked out.Here i am trying to customize the InputFormat and trying to join with the ID which is first record in the each line of data set.
My input file1:
cdd8dde3-0349-4f0d-b97a-7ae84b687f9c,Esther,Garner,4071 Haven Lane,Okemos,MI
81a43486-07e1-4b92-b92b-03d0caa87b5f,Timothy,Duncan,753 Stadium Drive,Taunton,MA
File2 :
cdd8dde3-0349-4f0d-b97a-7ae84b687f9c,517-706-9565,EstherJGarner#teleworm.us,Waskepter38,noL2ieghie,MasterCard,5305687295670850
81a43486-07e1-4b92-b92b-03d0caa87b5f,508-307-3433,TimothyDDuncan#einrot.com,Conerse,Gif4Edeiba,MasterCard,5265896533330445
**Driver class:**
conf.setInputFormat(CompositeInputFormat.class);
String strJoinStmt = CompositeInputFormat.compose("inner",
KeyValueLongInputFormat.class, dirEmployeesData, dirSalaryData);
conf.set("mapred.join.expr", strJoinStmt);
conf.setNumReduceTasks(0);
dirOutput.getFileSystem(conf).delete(dirOutput);
TextOutputFormat.setOutputPath(conf, dirOutput);
conf.setOutputKeyClass(Text.class);
conf.setOutputValueClass(Text.class);
conf.setOutputFormat(TextOutputFormat.class);
**Custom RecordReader class:**
public class KeyValueLongLineRecordReader implements
RecordReader<Text, Text> {
private final LineRecordReader lineRecordReader;
private byte separator = (byte) ',';
private LongWritable dummyKey;
private Text innerValue;
public Class getKeyClass() {
return Text.class;
}
public Text createKey() {
return new Text("");
}
public Text createValue() {
return new Text();
}
public KeyValueLongLineRecordReader(Configuration job, FileSplit split)
throws IOException {
lineRecordReader = new LineRecordReader(job, split);
dummyKey = lineRecordReader.createKey();
innerValue = lineRecordReader.createValue();
String sepStr = job.get("key.value.separator.in.input.line", ",");
this.separator = (byte) sepStr.charAt(0);
}
public static int findSeparator(byte[] utf, int start, int length, byte sep) {
for (int i = start; i < (start + length); i++) {
if (utf[i] == sep) {
return i;
}
}
return -1;
}
/** Read key/value pair in a line. */
public synchronized boolean next(Text key, Text value)
throws IOException {
Text tKey = key;
Text tValue = value;
byte[] line = null;
int lineLen = -1;
if (!lineRecordReader.next(dummyKey, innerValue)) {
return false;
}
else
line = innerValue.getBytes();
lineLen = innerValue.getLength();
if (line == null)
return false;
int pos = findSeparator(line, 0, lineLen, this.separator);
if (pos == -1) {
tKey.set(new String(line, 0, lineLen));
tValue.set("");
} else {
int keyLen = pos;
byte[] keyBytes = new byte[keyLen];
System.arraycopy(line, 0, keyBytes, 0, keyLen);
int valLen = lineLen - keyLen - 1;
byte[] valBytes = new byte[valLen];
System.arraycopy(line, pos + 1, valBytes, 0, valLen);
tKey.set(new String(keyBytes));
tValue.set(valBytes);
}
return true;
}
}
**InputFormat class:**
public class KeyValueLongInputFormat extends
FileInputFormat<Text, Text> implements JobConfigurable {
private CompressionCodecFactory compressionCodecs = null;
#Override
public void configure(JobConf conf) {
compressionCodecs = new CompressionCodecFactory(conf);
}
protected boolean isSplitable(FileSystem fs, Path file) {
return compressionCodecs.getCodec(file) == null;
}
#Override
public RecordReader<Text, Text> getRecordReader(
InputSplit genericSplit, JobConf job, Reporter reporter)
throws IOException {
reporter.setStatus(genericSplit.toString());
return new KeyValueLongLineRecordReader(job, (FileSplit) genericSplit);
}
}
**Finally Mapper class:**
enter code here
public class MapperMapSideJoinLargeDatasets extends MapReduceBase implements
Mapper<Text, TupleWritable, Text, Text> {
Text txtKey = new Text("");
Text txtValue = new Text("");
#Override
public void map(Text key, TupleWritable value,
OutputCollector<Text, Text> output, Reporter reporter)
throws IOException {
if (value.toString().length() > 0) {
txtKey.set(key.toString());
String arrEmpAttributes[] = value.get(0).toString().split(",");
String arrDeptAttributes[] = value.get(1).toString().split(",");
txtValue.set(arrEmpAttributes[1].toString() + "\t"
+ arrEmpAttributes[2].toString() + "\t"
+ arrDeptAttributes[0].toString());
output.collect(txtKey, txtValue);
}
}
In the logs Map input records is 0.No output can be seen on hdfs .Someone please help me to understand this issue. Thanks
The problem lies in the Driver class where in InputFormat mentioned as KeyValueLongInputFormat.class in the strJoinStmt property which actually works for LongWritable key and Text value. Instead KeyValueTextInputFormat.class can be used when both the key and value are of Text type.As the input is comma separated file, also specify a custom delimiter as comma by setting a property in the job's configuration object as follows in the Driver class.conf.set("key.value.separator.in.input.line",",");
For Complete details please check the below example:
https://github.com/sudha-pn/CompositeInputFormat

How to custom a classloader

I'm trying to implementing this function with a customer classloader: I have some class files in a alternatives.jar file, they provide different implementation than normal implementation. i.e., each class in this jar, has another version which in other jar file -- also get loaded in the classpath.
I know it's better to use instrument API to achieve same purpose. But now my concern is I need to understand why I'm failing.
So this is my method:
1. define a AlternativeClassLoader.java, in this file, I override findClass method. So if the class name can be found from alternatives.jar, then use the version from alternatives.jar.
2. in constructor, I have called super(null) so all these class loading work will be performed by my classloader, rather that system's.
3. This (seems to be true) also requires me to load other classes (if they're not system one). So I have to parse classpath, find all classes which indicated by the classpath.
My problem is, I can load my alternative class, everything seems to be fine...However, I'm using slf4j which yells the following error:
Failed to auto configure default logger context
Reported exception:
ch.qos.logback.core.joran.spi.JoranException: Parser configuration error occurred
Failed to instantiate [ch.qos.logback.classic.LoggerContext]
Reported exception:
java.lang.ExceptionInInitializerError
at java.util.ResourceBundle.getLoader(ResourceBundle.java:431)
at java.util.ResourceBundle.getBundle(ResourceBundle.java:841)
I doubt this is caused by my bad classloader implementation. Would somebody help me out? Many thanks!
This is my classloader:
public class AlternativeClassLoader extends ClassLoader {
private static final String ALTERNATIVE_JAR_PROPERTY = "alternativejar";
private static final Logger logger = LoggerFactory
.getLogger(AlternativeClassLoader.class);
private Map<String, Class<?>> clzCache = new HashMap<String, Class<?>>();
private Map<String, String> others = new HashMap<String, String>();
private Set<String> alternativesRegistry;
private JarFile altjar;
public AlternativeClassLoader(ClassLoader parent) {
/*
* pass null so I can incept all class loading except system's. By doing
* this you'll need to override findClass
*/
super(null);
registerAlternatives();
registerOthers();
}
/**
* This method will parse classpath and get all non-system class name, and
* build classname - jar_file_path/file_system_path mappings
*/
private void registerOthers() {
String[] paths = System.getProperty("java.class.path").split(":");
URL[] urls = new URL[paths.length];
for (String path : paths) {
if (path.endsWith("*.jar")) {
registerClass(path, others);
} else {
File f = new File(path);
if (!f.isDirectory())
continue;
File[] classFiles = f.listFiles(new FileFilter() {
#Override
public boolean accept(File arg0) {
if (arg0.getName().endsWith(".class")) {
return true;
} else {
return false;
}
}
});
for (File file : classFiles) {
String fileName = file.getName();
String className = fileName.substring(0,
fileName.lastIndexOf("."));
others.put(className, file.getPath());
}
}
}
showRegistry(
"Me will also be responsible for loading the following classes:",
others);
}
private void registerClass(String path, Map<String, String> registry) {
try {
JarInputStream jis = new JarInputStream(new FileInputStream(path));
for (JarEntry entry = jis.getNextJarEntry(); entry != null; entry = jis
.getNextJarEntry()) {
if (entry.getName().endsWith(".class") && !entry.isDirectory()) {
StringBuilder className = new StringBuilder();
for (String part : entry.getName().split("/")) {
if (className.length() != 0)
className.append(".");
className.append(part);
if (part.endsWith(".class"))
className.setLength(className.length()
- ".class".length());
}
registry.put(className.toString(), path);
}
}
} catch (Exception e) {
e.printStackTrace(System.out);
logger.error(
"Failed when read/parse jar {}. Your class file may not been replaced by alternative implementation",
path, e);
}
}
/**
* Try to find alternative class implementation from jar file specified by
* ALTERNATIVE_JAR_PROPERTY. If it's not specified, then use same jar file
* where this classloader is loaded.
*/
private void registerAlternatives() {
String jarFilePath = System.getProperty(ALTERNATIVE_JAR_PROPERTY);
if (jarFilePath == null || jarFilePath.isEmpty()) {
URL url = getClass().getProtectionDomain().getCodeSource()
.getLocation();
System.out.println(url + ":" + url.toString());
jarFilePath = url.getPath();
}
try {
altjar = new JarFile(jarFilePath);
} catch (IOException e) {
logger.error("cannot read jar {}", jarFilePath);
return;
}
Map<String, String> registry = new HashMap<String, String>();
registerClass(jarFilePath, registry);
alternativesRegistry = registry.keySet();
showRegistry("===Found the following alternative class:===", registry);
}
private void showRegistry(String string, Map<String, String> registry) {
System.out.println(string);
for (String clzName : registry.keySet()) {
System.out.printf("Class:%30s ->%s\n", clzName,
registry.get(clzName));
}
}
private Class<?> myLoadClass(String name) throws IOException,
ClassFormatError {
logger.debug("myload class {}", name);
System.out.printf("myload class %s\n", name);
if (alternativesRegistry.contains(name) && altjar != null) {
JarEntry entry = altjar.getJarEntry(name + ".class");
InputStream is = altjar.getInputStream(entry);
return readClassData(name, is);
}
String path = others.get(name);
if (path == null || path.isEmpty()) {
return null;
}
if (path.endsWith(".jar")) {
JarFile jar = new JarFile(path);
JarEntry entry = jar.getJarEntry(name + ".class");
InputStream is = jar.getInputStream(entry);
return readClassData(name, is);
} else {// it's a folder, need to read clz from .class file
System.out.printf("file path for %s is %s\n", name, path);
InputStream is = new FileInputStream(new File(path));
return readClassData(name, is);
}
}
private Class<?> readClassData(String name, InputStream is)
throws IOException, ClassFormatError {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
int len = is.read(buffer);
while (len > 0) {
out.write(buffer, 0, len);
len = is.read(buffer);
}
Class<?> clz = defineClass(name, out.toByteArray(), 0, out.size());
if (clz != null) {
System.out.printf("loaded %s by me\n", name);
clzCache.put(name, clz);
}
return clz;
}
protected Class<?> findCachedClass(String name)
throws ClassNotFoundException {
Class<?> clz = clzCache.get(name);
if (clz == null) {
clz = findLoadedClass(name);
}
return clz;
}
#Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass: " + name);
Class<?> cls = findCachedClass(name);
if (cls == null) {
try {
cls = myLoadClass(name);
} catch (ClassFormatError | IOException e) {
logger.error("failed to load class {}", name, e);
System.out.printf("failed to load class %s\n", name);
e.printStackTrace();
}
}
return cls;
}
}
I have tried to override findResource(), but it's never called.
This is how I put my classloader into use:
java -Djava.system.class.loader=AlternativeClassLoader -classpath=.:./alternatives.jar:./slf4j-xxx.jar Test
OK, I solved the problem. The tricky is:
Never use any package other than java.*. Otherwise, it will cause recursively loading ...IllegalState error.
In your classloader constructor, load all the alternative class and cache them.
In your constructor, call super(parent) other than super(null), then you don't need to do all the class loading stuff, the parent classloader can do it for you.
in override findClass(), if the class can be found from cache (means they have alternative implementation), then return it, otherwise let super.findClass do the rest for you.
so the following is the source code:
public class AlternativeClassLoader extends ClassLoader {
private static final String ALTERNATIVE_JAR_PROPERTY = "alternativejar";
private Map<String, Class<?>> clzCache = new HashMap<String, Class<?>>();
public AlternativeClassLoader(ClassLoader parent) {
super(parent);
loadAlternativeClasses();
}
private void loadAlternativeClasses() {
String jarFilePath = System.getProperty(ALTERNATIVE_JAR_PROPERTY);
if (jarFilePath == null || jarFilePath.isEmpty()){
URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
System.out.println(url + ":" + url.toString());
jarFilePath = url.getPath();
}
JarInputStream jis;
try {
jis = new JarInputStream(new FileInputStream(jarFilePath));
JarEntry entry;
while ((entry = jis.getNextJarEntry()) != null){
String className = entry.getName();
className = className.substring(0, className.length() - ".class".length());
System.out.printf("loading class from %s: %s\n", jarFilePath, className);
readClassData(className, jis);
}
} catch (IOException e) {
e.printStackTrace();
}
} private Class<?> readClassData(String name, InputStream is) throws IOException,
ClassFormatError {
byte[] buffer = new byte[4096];
ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);
int len = is.read(buffer);
while (len > 0) {
out.write(buffer, 0, len);
len = is.read(buffer);
}
Class<?> clz = defineClass(name, out.toByteArray(), 0, out.size());
if (clz != null) {
System.out.printf("loaded %s by myself\n", name);
clzCache.put(name, clz);
}
return clz;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findClass: " + name);
Class<?> cls = clzCache.get(name);
if (cls == null)
cls = super.findClass(name);
return cls;
}
}

JavaFX - bind property to properties of every element in observable Collection

Does exist any method which bind BooleanProperty to conjunction of every element in ObservableList?
ObservableList<BooleanProperty> list;
list = FXCollections.observableList(new ArrayList<BooleanProperty>));
BooleanProperty emptyProperty = new SimpleBooleanProperty();
emptyProperty.bind(Bindings.conunction(list));`
Is there such a method as:
static BooleanBinding conjunction(ObservableList<BooleanProperty> op)
There is no conjunction api defined in the JavaFX 2.2 platform.
You could create a ConjunctionBooleanBinding (aka AllTrueBinding) by subclassing BooleanBinding.
Accept the ObservableList in the constructor of your new class, and use the low level binding api in an overridden computeValue method to set the binding value based upon logically anding together all of the boolean values in the list.
Here is a sample implementation. The sample could be further performance optimized and make use of WeakReferences, so it does not require manual disposition.
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.collections.*;
public class AllTrueBinding extends BooleanBinding {
private final ObservableList<BooleanProperty> boundList;
private final ListChangeListener<BooleanProperty> BOUND_LIST_CHANGE_LISTENER =
new ListChangeListener<BooleanProperty>() {
#Override public void onChanged(
ListChangeListener.Change<? extends BooleanProperty> change
) {
refreshBinding();
}
};
private BooleanProperty[] observedProperties = {};
AllTrueBinding(ObservableList<BooleanProperty> booleanList) {
booleanList.addListener(BOUND_LIST_CHANGE_LISTENER);
boundList = booleanList;
refreshBinding();
}
#Override protected boolean computeValue() {
for (BooleanProperty bp: observedProperties) {
if (!bp.get()) {
return false;
}
}
return true;
}
#Override public void dispose() {
boundList.removeListener(BOUND_LIST_CHANGE_LISTENER);
super.dispose();
}
private void refreshBinding() {
super.unbind(observedProperties);
observedProperties = boundList.toArray(new BooleanProperty[0]);
super.bind(observedProperties);
this.invalidate();
}
}
And here is a test harness to demonstrate how it works:
import java.util.*;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class ListBindingTest {
final BooleanProperty a = new SimpleBooleanProperty(true);
final BooleanProperty b = new SimpleBooleanProperty(true);
final BooleanProperty c = new SimpleBooleanProperty(true);
final BooleanProperty d = new SimpleBooleanProperty(true);
final ObservableList<BooleanProperty> booleanList =
FXCollections.observableArrayList(a, b, c, d);
public static void main(String[] args) {
new ListBindingTest().test();
}
private void test() {
AllTrueBinding at = new AllTrueBinding(booleanList);
System.out.println(at.get() + forArrayString(booleanList));
b.set(false);
System.out.println(at.get() + forArrayString(booleanList));
b.set(true);
System.out.println(at.get() + forArrayString(booleanList));
booleanList.add(new SimpleBooleanProperty(false));
System.out.println(at.get() + forArrayString(booleanList));
booleanList.remove(3, 5);
System.out.println(at.get() + forArrayString(booleanList));
at.dispose();
}
private String forArrayString(List list) {
return " for " + Arrays.toString(list.toArray());
}
}
You can easily implement the method as follows:
public static BooleanBinding conjunction(ObservableList<BooleanProperty> list){
BooleanBinding and = new SimpleBooleanProperty(true).and(list.get(0));
for(int i = 1; i < list.size(); i++){
and = and.and(list.get(i));
}
return and;
}