Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Genesys PGR
App Blocks
Commits
6e96b331
Commit
6e96b331
authored
Feb 14, 2020
by
Matija Obreza
Browse files
Merge branch 'added-super-model-filter' into 'master'
Introduce SuperModelFilter See merge request genesys-pgr/application-blocks!65
parents
1fdb381d
2edabb5b
Changes
2
Hide whitespace changes
Inline
Side-by-side
core/src/main/java/org/genesys/blocks/model/filters/BasicModelFilter.java
View file @
6e96b331
/*
* Copyright 20
19
Global Crop Diversity Trust
* Copyright 20
20
Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -15,41 +15,16 @@
*/
package
org.genesys.blocks.model.filters
;
import
java.io.IOException
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Modifier
;
import
java.util.ArrayList
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
com.fasterxml.jackson.core.JsonGenerator
;
import
com.fasterxml.jackson.core.JsonParser
;
import
com.fasterxml.jackson.databind.BeanProperty
;
import
com.fasterxml.jackson.databind.DeserializationContext
;
import
com.fasterxml.jackson.databind.JavaType
;
import
com.fasterxml.jackson.databind.JsonDeserializer
;
import
com.fasterxml.jackson.databind.JsonMappingException
;
import
com.fasterxml.jackson.databind.JsonSerializer
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.SerializerProvider
;
import
com.fasterxml.jackson.databind.annotation.JsonDeserialize
;
import
com.fasterxml.jackson.databind.annotation.JsonSerialize
;
import
com.fasterxml.jackson.databind.deser.ContextualDeserializer
;
import
org.apache.commons.collections4.CollectionUtils
;
import
org.genesys.blocks.model.BasicModel
;
import
org.genesys.blocks.model.QBasicModel
;
import
com.fasterxml.jackson.annotation.JsonIgnoreProperties
;
import
com.fasterxml.jackson.annotation.JsonInclude.Include
;
import
com.fasterxml.jackson.core.JsonProcessingException
;
import
com.querydsl.core.types.ExpressionUtils
;
import
com.querydsl.core.types.Predicate
;
import
com.querydsl.core.types.dsl.CollectionPathBase
;
import
com.querydsl.core.types.dsl.DslExpression
;
import
com.querydsl.core.types.dsl.EntityPathBase
;
import
com.querydsl.core.types.dsl.PathBuilder
;
import
com.querydsl.core.types.dsl.SimpleExpression
;
/**
* {@link BasicModel} match by sample filters.
...
...
@@ -57,45 +32,11 @@ import com.querydsl.core.types.dsl.SimpleExpression;
* @param <T> the generic type
* @param <R> the generic type
*/
public
abstract
class
BasicModelFilter
<
T
extends
BasicModelFilter
<
T
,
R
>,
R
extends
BasicModel
>
{
private
static
final
ObjectMapper
jsonizer
=
new
ObjectMapper
();
private
static
final
ObjectMapper
nonDefault
=
new
ObjectMapper
();
private
static
final
ObjectMapper
defaultMapper
=
new
ObjectMapper
();
static
{
// Any objectMapper configuration goes here
jsonizer
.
setSerializationInclusion
(
Include
.
NON_NULL
);
nonDefault
.
setSerializationInclusion
(
Include
.
NON_DEFAULT
);
defaultMapper
.
setSerializationInclusion
(
Include
.
USE_DEFAULTS
);
}
public
abstract
class
BasicModelFilter
<
T
extends
BasicModelFilter
<
T
,
R
>,
R
extends
BasicModel
>
extends
SuperModelFilter
<
T
,
R
>
{
/** The id. */
public
Set
<
Long
>
id
;
/** The negative filters, but don't de-/serialize it's own NOT-properties. */
@JsonIgnoreProperties
({
"NOT"
,
"NULL"
,
"NOTNULL"
})
@JsonSerialize
(
using
=
NoDefaultValuesSerializer
.
class
)
@JsonDeserialize
(
using
=
NonDefaultDeserializer
.
class
)
public
T
NOT
;
/** Names of properties to test with .isNull() */
public
Set
<
String
>
NULL
;
/** Names of properties to test with .isNotNull() */
public
Set
<
String
>
NOTNULL
;
public
abstract
List
<
Predicate
>
collectPredicates
();
/**
* Builds the DSL predicate.
*
* @return the predicate
*/
public
Predicate
buildPredicate
()
{
return
ExpressionUtils
.
allOf
(
collectPredicates
());
}
/**
* Collects list of filter predicates
*
...
...
@@ -104,154 +45,13 @@ public abstract class BasicModelFilter<T extends BasicModelFilter<T, R>, R exten
* @return list of predicates
*/
protected
List
<
Predicate
>
collectPredicates
(
final
EntityPathBase
<
R
>
instance
,
final
QBasicModel
basicModel
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<>(
);
List
<
Predicate
>
predicates
=
super
.
collectPredicates
(
instance
);
if
(
CollectionUtils
.
isNotEmpty
(
id
))
{
predicates
.
add
(
basicModel
.
id
.
in
(
id
));
}
if
(
NULL
!=
null
&&
!
NULL
.
isEmpty
())
{
final
Class
<?>
clazz
=
instance
.
getClass
();
NULL
.
forEach
(
nullProp
->
{
DslExpression
<?>
expression
=
getProperty
(
instance
,
clazz
,
nullProp
);
if
(
expression
instanceof
SimpleExpression
)
{
predicates
.
add
(((
SimpleExpression
<?>)
expression
).
isNull
());
}
else
if
(
expression
instanceof
CollectionPathBase
)
{
predicates
.
add
(((
CollectionPathBase
<?,
?,
?>)
expression
).
size
().
eq
(
0
));
}
});
}
if
(
NOTNULL
!=
null
&&
!
NOTNULL
.
isEmpty
())
{
final
Class
<?>
clazz
=
instance
.
getClass
();
NOTNULL
.
forEach
(
notNullProp
->
{
DslExpression
<?>
expression
=
getProperty
(
instance
,
clazz
,
notNullProp
);
if
(
expression
instanceof
SimpleExpression
)
{
predicates
.
add
(((
SimpleExpression
<?>)
expression
).
isNotNull
());
}
else
if
(
expression
instanceof
CollectionPathBase
)
{
predicates
.
add
(((
CollectionPathBase
<?,
?,
?>)
expression
).
size
().
gt
(
0
));
}
});
}
if
(
NOT
!=
null
)
{
predicates
.
add
(
ExpressionUtils
.
anyOf
(
NOT
.
collectPredicates
()).
not
());
}
return
predicates
;
}
public
void
clearFilter
(
String
jsonPath
)
throws
NoSuchFieldException
,
IllegalAccessException
{
this
.
clearFilter
(
jsonPath
,
true
);
}
public
void
clearFilter
(
String
jsonPath
,
boolean
clearNullAndNotNull
)
throws
NoSuchFieldException
,
IllegalAccessException
{
String
[]
paths
=
jsonPath
.
split
(
"\\."
);
Object
toClear
=
this
;
Class
<?>
clazz
=
this
.
getClass
();
Field
field
=
clazz
.
getField
(
paths
[
0
]);
for
(
int
i
=
1
;
i
<
paths
.
length
;
i
++)
{
clazz
=
field
.
getType
();
if
(!
BasicModelFilter
.
class
.
isAssignableFrom
(
clazz
))
break
;
toClear
=
field
.
get
(
toClear
);
if
(
toClear
==
null
)
return
;
field
=
clazz
.
getField
(
paths
[
i
]);
}
if
(
clearNullAndNotNull
)
{
if
(
this
.
NULL
!=
null
)
{
this
.
NULL
.
remove
(
jsonPath
);
}
if
(
this
.
NOTNULL
!=
null
)
{
this
.
NOTNULL
.
remove
(
jsonPath
);
}
}
field
.
set
(
toClear
,
null
);
}
/**
* Find the property of DSL-generated type.
*
* @param instance the DSL-generated type
* @param clazz type of instance
* @param nullProp property name
* @return
*/
private
DslExpression
<?>
getProperty
(
final
EntityPathBase
<
R
>
instance
,
final
Class
<?>
clazz
,
final
String
nullProp
)
{
try
{
// build path for nesting filters
if
(
nullProp
.
contains
(
"."
))
{
String
paths
[]
=
nullProp
.
split
(
"\\."
);
PathBuilder
<?>
pathBuilder
=
new
PathBuilder
<>(
instance
.
getType
(),
instance
.
getMetadata
());
Class
<?>
clazzToCheck
=
clazz
;
for
(
String
path
:
paths
)
{
Field
field
=
clazzToCheck
.
getField
(
path
);
if
(
CollectionPathBase
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
pathBuilder
.
getSet
(
path
,
field
.
getDeclaringClass
());
}
else
{
pathBuilder
.
getSimple
(
path
,
field
.
getDeclaringClass
());
}
clazzToCheck
=
field
.
getType
();
}
return
pathBuilder
.
getSimple
(
nullProp
,
clazzToCheck
);
}
else
{
final
Field
prop
=
clazz
.
getField
(
nullProp
);
if
(
SimpleExpression
.
class
.
isAssignableFrom
(
prop
.
getType
()))
{
return
(
SimpleExpression
<?>)
prop
.
get
(
instance
);
}
if
(
CollectionPathBase
.
class
.
isAssignableFrom
(
prop
.
getType
()))
{
return
(
CollectionPathBase
<?,
?,
?>)
prop
.
get
(
instance
);
}
else
{
throw
new
NoSuchFieldException
(
"Property "
+
nullProp
+
" is not a SimpleExpression"
);
}
}
}
catch
(
NoSuchFieldException
|
SecurityException
|
IllegalArgumentException
|
IllegalAccessException
e
)
{
// test if nested objects have required property
// will test only the first level of nesting
if
(
e
instanceof
NoSuchFieldException
)
{
Field
fields
[]
=
clazz
.
getDeclaredFields
();
for
(
Field
field
:
fields
)
{
Class
<?>
superClazz
=
field
.
getType
().
getSuperclass
();
if
(
superClazz
!=
null
&&
EntityPathBase
.
class
.
isAssignableFrom
(
superClazz
))
{
try
{
Field
result
=
field
.
getType
().
getField
(
nullProp
);
PathBuilder
<?>
pathBuilder
=
new
PathBuilder
<>(
instance
.
getType
(),
instance
.
getMetadata
());
if
(
CollectionPathBase
.
class
.
isAssignableFrom
(
result
.
getType
()))
{
return
pathBuilder
.
getSet
(
field
.
getName
()
+
"."
+
nullProp
,
result
.
getDeclaringClass
());
}
return
pathBuilder
.
getSimple
(
field
.
getName
()
+
"."
+
nullProp
,
result
.
getDeclaringClass
());
}
catch
(
NoSuchFieldException
e1
)
{
}
}
}
}
throw
new
RuntimeException
(
"Error accessing field "
+
nullProp
+
" for isNull() in "
+
instance
.
getClass
());
}
}
/**
* Copy by serializing to JSON and de-serializing to specified type.
*
* @param <X> the generic type
* @param targetType the target type
* @return the x
* @throws IOException Signals that an I/O exception has occurred.
*/
public
<
X
>
X
copy
(
final
Class
<
X
>
targetType
)
throws
IOException
{
return
defaultMapper
.
readValue
(
defaultMapper
.
writeValueAsString
(
this
),
targetType
);
}
@Override
public
String
toString
()
{
try
{
return
jsonizer
.
writeValueAsString
(
this
);
}
catch
(
final
JsonProcessingException
e
)
{
throw
new
RuntimeException
(
"Could not serialize to JSON: "
+
e
.
getMessage
(),
e
);
}
}
/**
* Id.
*
...
...
@@ -264,107 +64,4 @@ public abstract class BasicModelFilter<T extends BasicModelFilter<T, R>, R exten
return
id
;
}
/**
* Checks if is null.
*
* @return the sets the
*/
public
synchronized
Set
<
String
>
isNull
()
{
if
(
NULL
==
null
)
{
NULL
=
new
HashSet
<>();
}
return
NULL
;
}
/**
* Not null.
*
* @return the sets the
*/
public
synchronized
Set
<
String
>
notNull
()
{
if
(
NOTNULL
==
null
)
{
NOTNULL
=
new
HashSet
<>();
}
return
NOTNULL
;
}
/**
* Prepare filter for use. NULL and NOTNULLs will clear any actual values
* provided for those properties.
*
* @param <Q> any BasicModelFilter subtype
* @param filter the filter
* @return
*/
public
static
<
Q
extends
BasicModelFilter
<?,
?>>
Q
normalize
(
final
Q
filter
)
{
Set
<
String
>
toClear
=
new
HashSet
<>();
if
(
filter
.
NULL
!=
null
)
{
toClear
.
addAll
(
filter
.
NULL
);
}
if
(
filter
.
NOTNULL
!=
null
)
{
toClear
.
addAll
(
filter
.
NOTNULL
);
}
for
(
String
path
:
toClear
)
{
try
{
filter
.
clearFilter
(
path
,
false
);
}
catch
(
NoSuchFieldException
|
IllegalAccessException
e
)
{
System
.
err
.
println
(
"Error while clearing filter: "
+
e
.
getMessage
());
}
}
return
filter
;
}
/**
* Used to deserialize NOT filter without default values
*
* @param <Y> type of filter
*/
static
class
NonDefaultDeserializer
<
Y
extends
BasicModelFilter
<?,
?>>
extends
JsonDeserializer
<
Y
>
implements
ContextualDeserializer
{
private
Class
<
Y
>
targetClass
;
@Override
public
Y
deserialize
(
JsonParser
p
,
DeserializationContext
ctxt
)
throws
IOException
,
JsonProcessingException
{
Y
parsed
=
(
Y
)
p
.
getCodec
().
readValue
(
p
,
targetClass
);
Y
defaultFilter
=
null
;
try
{
defaultFilter
=
targetClass
.
newInstance
();
for
(
Field
f
:
targetClass
.
getDeclaredFields
())
{
if
(
Modifier
.
isPublic
(
f
.
getModifiers
())
&&
f
.
get
(
defaultFilter
)
!=
null
&&
f
.
get
(
defaultFilter
).
equals
(
f
.
get
(
parsed
)))
{
f
.
set
(
parsed
,
null
);
}
}
}
catch
(
InstantiationException
|
IllegalAccessException
e
)
{
throw
new
RuntimeException
(
"Parsing of filter failed, e: "
+
e
.
getMessage
());
}
return
parsed
;
}
@Override
public
JsonDeserializer
<?>
createContextual
(
DeserializationContext
deserializationContext
,
BeanProperty
beanProperty
)
throws
JsonMappingException
{
final
JavaType
type
;
if
(
beanProperty
!=
null
)
type
=
beanProperty
.
getType
();
else
{
type
=
deserializationContext
.
getContextualType
();
}
this
.
targetClass
=
(
Class
<
Y
>)
type
.
getRawClass
();
return
this
;
}
}
/**
* Used to serialize and ignore default values of NOT filter
*
* @param <Y> type of filter
*/
static
class
NoDefaultValuesSerializer
<
Y
extends
BasicModelFilter
<?,
?>>
extends
JsonSerializer
<
Y
>
{
@Override
public
void
serialize
(
Y
value
,
JsonGenerator
gen
,
SerializerProvider
provider
)
throws
IOException
{
gen
.
writeRawValue
(
BasicModelFilter
.
nonDefault
.
writeValueAsString
(
value
));
}
}
}
core/src/main/java/org/genesys/blocks/model/filters/SuperModelFilter.java
0 → 100644
View file @
6e96b331
/*
* Copyright 2020 Global Crop Diversity Trust
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
org.genesys.blocks.model.filters
;
import
java.io.IOException
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.Modifier
;
import
java.util.ArrayList
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
import
com.fasterxml.jackson.annotation.JsonIgnoreProperties
;
import
com.fasterxml.jackson.annotation.JsonInclude
;
import
com.fasterxml.jackson.core.JsonGenerator
;
import
com.fasterxml.jackson.core.JsonParser
;
import
com.fasterxml.jackson.core.JsonProcessingException
;
import
com.fasterxml.jackson.databind.BeanProperty
;
import
com.fasterxml.jackson.databind.DeserializationContext
;
import
com.fasterxml.jackson.databind.JavaType
;
import
com.fasterxml.jackson.databind.JsonDeserializer
;
import
com.fasterxml.jackson.databind.JsonMappingException
;
import
com.fasterxml.jackson.databind.JsonSerializer
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
com.fasterxml.jackson.databind.SerializerProvider
;
import
com.fasterxml.jackson.databind.annotation.JsonDeserialize
;
import
com.fasterxml.jackson.databind.annotation.JsonSerialize
;
import
com.fasterxml.jackson.databind.deser.ContextualDeserializer
;
import
com.querydsl.core.types.ExpressionUtils
;
import
com.querydsl.core.types.Predicate
;
import
com.querydsl.core.types.dsl.CollectionPathBase
;
import
com.querydsl.core.types.dsl.DslExpression
;
import
com.querydsl.core.types.dsl.EntityPathBase
;
import
com.querydsl.core.types.dsl.PathBuilder
;
import
com.querydsl.core.types.dsl.SimpleExpression
;
/**
* @param <T> the generic type
* @param <R> the generic type
*/
public
abstract
class
SuperModelFilter
<
T
extends
SuperModelFilter
<
T
,
R
>,
R
>
{
private
static
final
ObjectMapper
jsonizer
=
new
ObjectMapper
();
private
static
final
ObjectMapper
nonDefault
=
new
ObjectMapper
();
private
static
final
ObjectMapper
defaultMapper
=
new
ObjectMapper
();
static
{
// Any objectMapper configuration goes here
jsonizer
.
setSerializationInclusion
(
JsonInclude
.
Include
.
NON_NULL
);
nonDefault
.
setSerializationInclusion
(
JsonInclude
.
Include
.
NON_DEFAULT
);
defaultMapper
.
setSerializationInclusion
(
JsonInclude
.
Include
.
USE_DEFAULTS
);
}
/** The negative filters, but don't de-/serialize it's own NOT-properties. */
@JsonIgnoreProperties
({
"NOT"
,
"NULL"
,
"NOTNULL"
})
@JsonSerialize
(
using
=
SuperModelFilter
.
NoDefaultValuesSerializer
.
class
)
@JsonDeserialize
(
using
=
SuperModelFilter
.
NonDefaultDeserializer
.
class
)
public
T
NOT
;
/** Names of properties to test with .isNull() */
public
Set
<
String
>
NULL
;
/** Names of properties to test with .isNotNull() */
public
Set
<
String
>
NOTNULL
;
public
abstract
List
<
Predicate
>
collectPredicates
();
/**
* Builds the DSL predicate.
*
* @return the predicate
*/
public
Predicate
buildPredicate
()
{
return
ExpressionUtils
.
allOf
(
collectPredicates
());
}
/**
* Collects list of filter predicates
*
* @param instance the instance of Q-type of <em>R</em>
* @return list of predicates
*/
protected
List
<
Predicate
>
collectPredicates
(
final
EntityPathBase
<
R
>
instance
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<>();
if
(
NULL
!=
null
&&
!
NULL
.
isEmpty
())
{
final
Class
<?>
clazz
=
instance
.
getClass
();
NULL
.
forEach
(
nullProp
->
{
DslExpression
<?>
expression
=
getProperty
(
instance
,
clazz
,
nullProp
);
if
(
expression
instanceof
SimpleExpression
)
{
predicates
.
add
(((
SimpleExpression
<?>)
expression
).
isNull
());
}
else
if
(
expression
instanceof
CollectionPathBase
)
{
predicates
.
add
(((
CollectionPathBase
<?,
?,
?>)
expression
).
size
().
eq
(
0
));
}
});
}
if
(
NOTNULL
!=
null
&&
!
NOTNULL
.
isEmpty
())
{
final
Class
<?>
clazz
=
instance
.
getClass
();
NOTNULL
.
forEach
(
notNullProp
->
{
DslExpression
<?>
expression
=
getProperty
(
instance
,
clazz
,
notNullProp
);
if
(
expression
instanceof
SimpleExpression
)
{
predicates
.
add
(((
SimpleExpression
<?>)
expression
).
isNotNull
());
}
else
if
(
expression
instanceof
CollectionPathBase
)
{
predicates
.
add
(((
CollectionPathBase
<?,
?,
?>)
expression
).
size
().
gt
(
0
));
}
});
}
if
(
NOT
!=
null
)
{
predicates
.
add
(
ExpressionUtils
.
anyOf
(
NOT
.
collectPredicates
()).
not
());
}
return
predicates
;
}
public
void
clearFilter
(
String
jsonPath
)
throws
NoSuchFieldException
,
IllegalAccessException
{
this
.
clearFilter
(
jsonPath
,
true
);
}
public
void
clearFilter
(
String
jsonPath
,
boolean
clearNullAndNotNull
)
throws
NoSuchFieldException
,
IllegalAccessException
{
String
[]
paths
=
jsonPath
.
split
(
"\\."
);
Object
toClear
=
this
;
Class
<?>
clazz
=
this
.
getClass
();
Field
field
=
clazz
.
getField
(
paths
[
0
]);
for
(
int
i
=
1
;
i
<
paths
.
length
;
i
++)
{
clazz
=
field
.
getType
();
if
(!
SuperModelFilter
.
class
.
isAssignableFrom
(
clazz
))
break
;
toClear
=
field
.
get
(
toClear
);
if
(
toClear
==
null
)
return
;
field
=
clazz
.
getField
(
paths
[
i
]);
}
if
(
clearNullAndNotNull
)
{
if
(
this
.
NULL
!=
null
)
{
this
.
NULL
.
remove
(
jsonPath
);
}
if
(
this
.
NOTNULL
!=
null
)
{
this
.
NOTNULL
.
remove
(
jsonPath
);
}
}
field
.
set
(
toClear
,
null
);
}
/**
* Find the property of DSL-generated type.
*
* @param instance the DSL-generated type
* @param clazz type of instance
* @param nullProp property name
* @return
*/
private
DslExpression
<?>
getProperty
(
final
EntityPathBase
<
R
>
instance
,
final
Class
<?>
clazz
,
final
String
nullProp
)
{
try
{
// build path for nesting filters
if
(
nullProp
.
contains
(
"."
))
{
String
paths
[]
=
nullProp
.
split
(
"\\."
);
PathBuilder
<?>
pathBuilder
=
new
PathBuilder
<>(
instance
.
getType
(),
instance
.
getMetadata
());
Class
<?>
clazzToCheck
=
clazz
;
for
(
String
path
:
paths
)
{
Field
field
=
clazzToCheck
.
getField
(
path
);
if
(
CollectionPathBase
.
class
.
isAssignableFrom
(
field
.
getType
()))
{
pathBuilder
.
getSet
(
path
,
field
.
getDeclaringClass
());
}
else
{
pathBuilder
.
getSimple
(
path
,
field
.
getDeclaringClass
());
}
clazzToCheck
=
field
.
getType
();
}
return
pathBuilder
.
getSimple
(
nullProp
,
clazzToCheck
);
}
else
{
final
Field
prop
=
clazz
.
getField
(
nullProp
);
if
(
SimpleExpression
.
class
.
isAssignableFrom
(
prop
.
getType
()))
{
return
(
SimpleExpression
<?>)
prop
.
get
(
instance
);
}
if
(
CollectionPathBase
.
class
.
isAssignableFrom
(
prop
.
getType
()))
{
return
(
CollectionPathBase
<?,
?,
?>)
prop
.
get
(
instance
);
}
else
{
throw
new
NoSuchFieldException
(
"Property "
+
nullProp
+
" is not a SimpleExpression"
);
}
}
}
catch
(
NoSuchFieldException
|
SecurityException
|
IllegalArgumentException
|
IllegalAccessException
e
)
{
// test if nested objects have required property
// will test only the first level of nesting
if
(
e
instanceof
NoSuchFieldException
)
{
Field
fields
[]
=
clazz
.
getDeclaredFields
();
for
(
Field
field
:
fields
)
{
Class
<?>
superClazz
=
field
.
getType
().
getSuperclass
();
if
(
superClazz
!=
null
&&
EntityPathBase
.
class
.
isAssignableFrom
(
superClazz
))
{
try
{
Field
result
=
field
.
getType
().
getField
(
nullProp
);
PathBuilder
<?>
pathBuilder
=
new
PathBuilder
<>(
instance
.
getType
(),
instance
.
getMetadata
());
if
(
CollectionPathBase
.
class
.
isAssignableFrom
(
result
.
getType
()))
{
return
pathBuilder
.
getSet
(
field
.
getName
()
+
"."
+
nullProp
,
result
.
getDeclaringClass
());
}
return
pathBuilder
.
getSimple
(
field
.
getName
()
+
"."
+
nullProp
,
result
.
getDeclaringClass
());
}
catch
(
NoSuchFieldException
e1
)
{
}
}
}
}
throw
new
RuntimeException
(
"Error accessing field "
+
nullProp
+
" for isNull() in "
+
instance
.
getClass
());
}
}
/**
* Copy by serializing to JSON and de-serializing to specified type.