I'm getting the results from the database into a List<Map<String, Object>>. The problem the values are not in order, so I thought to cast in a LinkedHashMap, but I get this exception:
javax.ejb.EJBException: java.lang.ClassCastException: class java.util.HashMap cannot be cast to class java.util.LinkedHashMap (java.util.HashMap and java.util.LinkedHashMap are in module java.base of loader 'bootstrap')
The method is this:
protected EntityManager em;
Query q = em.createNativeQuery("select * from City");
NativeQueryImpl nativeQuery = (NativeQueryImpl) q;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<LinkedHashMap<String, Object>> r = (List<LinkedHashMap<String, Object>>) nativeQuery.getResultList();
r.stream().forEach(System.out::println);
Does anybody knows how can I print results in order?
Create this class and call it instead of: AliasToEntityMapResultTransformer.INSTANCE
public class MyTransformer extends AliasedTupleSubsetResultTransformer {
public static final MyTransformer INSTANCE = new MyTransformer();
/**
* Disallow instantiation of AliasToEntityMapResultTransformer.
*/
private MyTransformer() {
}
#Override
public Object transformTuple(Object[] tuple, String[] aliases) {
Map result = new LinkedHashMap<>(tuple.length);
for (int i = 0; i < tuple.length; i++) {
String alias = aliases[i];
if (alias != null) {
result.put(alias, tuple[i]);
}
}
return result;
}
#Override
public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {
return false;
}
/**
* Serialization hook for ensuring singleton uniqueing.
*
* #return The singleton instance : {#link #INSTANCE}
*/
private Object readResolve() {
return INSTANCE;
}
}
Related
Currently I'm writing a program where I have the following statement.
List<BaseballStatistic> q = BaseballStatistic.FIND.where().eq("teamID", "CHN").query();
Here, it complains
Unchecked assignment: 'java.util.List' to 'java.util.List'. Reason: 'BaseballStatistic.FIND.where().eq("teamID", "CHN")' has raw type, so result of query is erased more...
I have an interface which looks like this
public interface Query<T> {
...
List<T> execute();
}
then an abstract class that implements this interface
public abstract class AbstractQuery<T> implements Query<T> {
Statement _statement = null;
String _tableName;
List<Clause> _clauses;
Class<T> _type;
AbstractQuery(Class<T> type) {
_type = type;
_clauses = new ArrayList<>();
_tableName = type.getAnnotation(Table.class).name();
}
...
public abstract List<T> execute();
}
and finally a concrete implementation:
public class SimpleQuery<T> extends AbstractQuery<T> {
public SimpleQuery(Class<T> type) {
super(type);
}
which houses the following .query function which looks like:
#Override
public List<T> execute() {
try {
JSONObject jsonObject = Peanut.getClient().listStatistics(buildQuery());
if (jsonObject == null || !jsonObject.has("results")) {
return Collections.emptyList();
}
JSONArray columnNames = jsonObject.getJSONArray("columns");
Map<String, Integer> columnNameMap = new HashMap<>();
for (int i = 0; i < columnNames.length(); i++) {
columnNameMap.put((String) columnNames.get(i), i);
}
JSONArray results = jsonObject.getJSONArray("results");
List<T> ts = new ArrayList<>();
for (int i = 0; i < results.length(); i++) {
JSONArray result = results.getJSONArray(i);
T t = _type.newInstance();
for (Field field : ObjectUtils.getFieldsUpTo(t.getClass(), PinotModel.class)) {
if (field.getAnnotation(Column.class) == null) {
continue;
}
Object obj = ObjectUtils.getDefaultValue(field.getType());
String columnName = field.getAnnotation(Column.class).name();
if (columnNameMap.containsKey(columnName)) {
int idx = columnNameMap.get(columnName);
field.setAccessible(true);
field.set(t, ObjectUtils.convertObject(obj, result.get(idx)));
}
}
ts.add(t);
}
return ts;
} catch (Exception e) {
// TODO: Throw Peanut specific error.
Peanut.LOG.error(e);
return Collections.emptyList();
}
}
It seems like here, at compilation, the returned list has lost it's type leading to the warning. If I change the original variable declaration to List the warning will leave, which makes sense.
Is there anyway around this or is there a larger fundamental issue at play?
EDIT:
Query Function that calls execute is here
public List<T> query() {
return _query.execute();
}
And the relationship between SimpleQuery and BaseballStatistic.Find is as follows.
#Table(name = "baseballStats")
public class BaseballStatistic extends PinotModel {
public static final Find FIND = new Find<BaseballStatistic (BaseballStatistic.class) { };
...
and PinotModel looks like
public class PinotModel {
public static class Find<T> {
private final Class<T> type;
protected Find(Class<T> type) {
this.type = type;
}
public Query select(String... s) {
return new SimpleQuery<T>(type).select(s);
}
public Clause where() {
return new SimpleQuery<T>(type).where();
}
public Clause limit(Integer n) {
return new SimpleQuery<T>(type).limit(n);
}
public Clause top(Integer n) {
return new SimpleQuery<T>(type).top(n);
}
public Clause orderBy(String columnName, Order o) {
return new SimpleQuery<T>(type).orderBy(columnName, o);
}
public String tableName() {
return new SimpleQuery<T>(type).getTableName();
}
}
}
There are 2 places that you're missing generic type parameters.
BaseballStatistic.FIND:
public static final Find<BaseballStatistic> FIND = new Find<BaseballStatistic> (BaseballStatistic.class) { };
PinotModel.select:
public Query<T> select(String... s) {
return new SimpleQuery<T>(type).select(s);
}
You're also missing type parameters on PinotModel.where(). Clause would also need a type parameter, including on the AbstractQuery._clauses field.
I want to have an Enum like this:
public enum Type {
STRING, INTEGER, BOOLEAN, LIST(Type);
Type t;
Type() { this.t = this; )
Type(Type t) { this.t = t; }
}
Such that I can enter various Types for LIST, like being able to call Type.LIST(STRING). Is this possible in Java?
enums are limited, you can't have an unknown amount of entries. So you can't have LIST(LIST(LIST(LIST(...))) as a separate Type enum. You'll need a class, but that doesn't mean you have to instantiate lots of objects necessarily:
It may be premature optimization, but you can use a flyweight pattern to ensure that you can't get more than one instance of a Type:
package com.example;
public final class Type {
public enum LeafType {
STRING,
INTEGER,
BOOLEAN
}
//Gives you the familiar enum syntax
public static final Type STRING = new Type(LeafType.STRING);
public static final Type INTEGER = new Type(LeafType.INTEGER);
public static final Type BOOLEAN = new Type(LeafType.BOOLEAN);
private final LeafType leafType;
private final Type listType;
private final Object lock = new Object();
// This is the cache that prevents creation of multiple instances
private Type listOfMeType;
private Type(LeafType leafType) {
if (leafType == null) throw new RuntimeException("X");
this.leafType = leafType;
listType = null;
}
private Type(Type type) {
leafType = null;
listType = type;
}
/**
* Get the type that represents a list of this type
*/
public Type list() {
synchronized (lock) {
if (listOfMeType == null) {
listOfMeType = new Type(this);
}
return listOfMeType;
}
}
public boolean isList() {
return listType != null;
}
/**
* If this type is a list, will return what type of list it is
*/
public Type getListType() {
if (!isList()) {
throw new RuntimeException("Not a list");
}
return listType;
}
/**
* If this type is a leaf, will return what type of leaf it is
*/
public LeafType getLeafType() {
if (isList()) {
throw new RuntimeException("Not a leaf");
}
return leafType;
}
#Override
public String toString() {
if (isList()) {
return "LIST(" + getListType() + ")";
}
return getLeafType().toString();
}
}
Usage:
Simple type:
Type string = Type.STRING;
List:
Type stringList = Type.STRING.list();
List of list:
Type stringListList = Type.STRING.list().list();
And you can never get in the situation where you have two instances of Type that describe the same type, e.g.:
Type t1 = Type.BOOLEAN.list().list().list();
Type t2 = Type.BOOLEAN.list().list().list();
System.out.println(t1 == t2 ? "Same instance" : "Not same instance");
I added toString for debugging:
Type listListListInt = Type.INTEGER.list().list().list();
System.out.println(listListListInt);
Gives:
LIST(LIST(LIST(INTEGER)))
Is there a way in Java to have a map where the type parameter of a value is tied to the type parameter of a key? What I want to write is something like the following:
public class Foo {
// This declaration won't compile - what should it be?
private static Map<Class<T>, T> defaultValues;
// These two methods are just fine
public static <T> void setDefaultValue(Class<T> clazz, T value) {
defaultValues.put(clazz, value);
}
public static <T> T getDefaultValue(Class<T> clazz) {
return defaultValues.get(clazz);
}
}
That is, I can store any default value against a Class object, provided the value's type matches that of the Class object. I don't see why this shouldn't be allowed since I can ensure when setting/getting values that the types are correct.
EDIT: Thanks to cletus for his answer. I don't actually need the type parameters on the map itself since I can ensure consistency in the methods which get/set values, even if it means using some slightly ugly casts.
You're not trying to implement Joshua Bloch's typesafe hetereogeneous container pattern are you? Basically:
public class Favorites {
private Map<Class<?>, Object> favorites =
new HashMap<Class<?>, Object>();
public <T> void setFavorite(Class<T> klass, T thing) {
favorites.put(klass, thing);
}
public <T> T getFavorite(Class<T> klass) {
return klass.cast(favorites.get(klass));
}
public static void main(String[] args) {
Favorites f = new Favorites();
f.setFavorite(String.class, "Java");
f.setFavorite(Integer.class, 0xcafebabe);
String s = f.getFavorite(String.class);
int i = f.getFavorite(Integer.class);
}
}
From Effective Java (2nd edition) and this presentation.
The question and the answers made me come up with this solution: Type-safe object map. Here is the code. Test case:
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class TypedMapTest {
private final static TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
private final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );
#Test
public void testGet() throws Exception {
TypedMap map = new TypedMap();
map.set( KEY1, null );
assertNull( map.get( KEY1 ) );
String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 );
assertEquals( expected, value );
map.set( KEY2, null );
assertNull( map.get( KEY2 ) );
List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 );
assertEquals( list, valueList );
}
}
This is the Key class. Note that the type T is never used in this class! It's purely for the purpose of type casting when reading the value out of the map. The field key only gives the key a name.
public class TypedMapKey<T> {
private String key;
public TypedMapKey( String key ) {
this.key = key;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( key == null ) ? 0 : key.hashCode() );
return result;
}
#Override
public boolean equals( Object obj ) {
if( this == obj ) {
return true;
}
if( obj == null ) {
return false;
}
if( getClass() != obj.getClass() ) {
return false;
}
TypedMapKey<?> other = (TypedMapKey<?>) obj;
if( key == null ) {
if( other.key != null ) {
return false;
}
} else if( !key.equals( other.key ) ) {
return false;
}
return true;
}
#Override
public String toString() {
return key;
}
}
TypedMap.java:
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class TypedMap implements Map<Object, Object> {
private Map<Object, Object> delegate;
public TypedMap( Map<Object, Object> delegate ) {
this.delegate = delegate;
}
public TypedMap() {
this.delegate = new HashMap<Object, Object>();
}
#SuppressWarnings( "unchecked" )
public <T> T get( TypedMapKey<T> key ) {
return (T) delegate.get( key );
}
#SuppressWarnings( "unchecked" )
public <T> T remove( TypedMapKey<T> key ) {
return (T) delegate.remove( key );
}
public <T> void set( TypedMapKey<T> key, T value ) {
delegate.put( key, value );
}
// --- Only calls to delegates below
public void clear() {
delegate.clear();
}
public boolean containsKey( Object key ) {
return delegate.containsKey( key );
}
public boolean containsValue( Object value ) {
return delegate.containsValue( value );
}
public Set<java.util.Map.Entry<Object, Object>> entrySet() {
return delegate.entrySet();
}
public boolean equals( Object o ) {
return delegate.equals( o );
}
public Object get( Object key ) {
return delegate.get( key );
}
public int hashCode() {
return delegate.hashCode();
}
public boolean isEmpty() {
return delegate.isEmpty();
}
public Set<Object> keySet() {
return delegate.keySet();
}
public Object put( Object key, Object value ) {
return delegate.put( key, value );
}
public void putAll( Map<? extends Object, ? extends Object> m ) {
delegate.putAll( m );
}
public Object remove( Object key ) {
return delegate.remove( key );
}
public int size() {
return delegate.size();
}
public Collection<Object> values() {
return delegate.values();
}
}
No, you can't do it directly. You'll need to write a wrapper class around Map<Class, Object> to enforce that Object will be instanceof Class.
It's possible to create a class which stores a map of type safe key to a value, and cast when necessary. The cast in get method is safe, as after using new Key<CharSequence>(), it's not possible to safely cast it to Key<String> or Key<Object>, so the type system enforces the correct usage of a class.
The Key class needs to be final, as otherwise an user could override equals and cause type-unsafety if two elements with different types were to be equal. Alternatively, it's possible to override equals to be final if you want to use inheritance despite the issues with it.
public final class TypeMap {
private final Map<Key<?>, Object> m = new HashMap<>();
public <T> T get(Key<? extends T> key) {
// Safe, as it's not possible to safely change the Key generic type,
// hash map cannot be accessed by an user, and this class being final
// to prevent serialization attacks.
#SuppressWarnings("unchecked")
T value = (T) m.get(key);
return value;
}
public <T> void put(Key<? super T> key, T value) {
m.put(key, value);
}
public static final class Key<T> {
}
}
You can use below 2 classes, Map class: GenericMap, Map-Key class: GenericKey
For example:
// Create a key includine Type definition
public static final GenericKey<HttpServletRequest> REQUEST = new GenericKey<>(HttpServletRequest.class, "HttpRequestKey");
public void example(HttpServletRequest requestToSave)
{
GenericMap map = new GenericMap();
// Saving value
map.put(REQUEST, requestToSave);
// Getting value
HttpServletRequest request = map.get(REQUEST);
}
Advantages
It forces the user to put and get correct types by compilation error
It's doing casing for you inside
Generic Key helps to avoid write the class type each time you calling put(..) or get
No typo mistakes, like if key is 'String' type
GenericMap
public class GenericMap
{
private Map<String, Object> storageMap;
protected GenericMap()
{
storageMap = new HashMap<String, Object>();
}
public <T> T get(GenericKey<T> key)
{
Object value = storageMap.get(key.getKey());
if (value == null)
{
return null;
}
return key.getClassType().cast(value);
}
/**
* #param key GenericKey object with generic type - T (it can be any type)
* #param object value to put in the map, the type of 'object' mast be - T
*/
public <T> void put(GenericKey<T> key, T object)
{
T castedObject = key.getClassType().cast(object);
storageMap.put(key.getKey(), castedObject);
}
#Override
public String toString()
{
return storageMap.toString();
}
}
GenericKey
public class GenericKey<T>
{
private Class<T> classType;
private String key;
#SuppressWarnings("unused")
private GenericKey()
{
}
public GenericKey(Class<T> iClassType, String iKey)
{
this.classType = iClassType;
this.key = iKey;
}
public Class<T> getClassType()
{
return classType;
}
public String getKey()
{
return key;
}
#Override
public String toString()
{
return "[classType=" + classType + ", key=" + key + "]";
}
}
T as a type must be defined generically in the class instance. The following example works:
public class Test<T> {
private Map<Class<T>, T> defaultValues;
public void setDefaultValue(Class<T> clazz, T value) {
defaultValues.put(clazz, value);
}
public T getDefaultValue(Class<T> clazz) {
return defaultValues.get(clazz);
}
}
Alternatively, you can use Paul Tomblin's answer, and wrap the Map with your own object which will enforce this type of generics.
I have a UserProfile class which contains user's data as shown below:
class UserProfile {
private String userId;
private String displayName;
private String loginId;
private String role;
private String orgId;
private String email;
private String contactNumber;
private Integer age;
private String address;
// few more fields ...
// getter and setter
}
I need to count non null fields to show how much percentage of the profile has been filled by the user. Also there are few fields which I do not want to consider in percentage calculation like: userId, loginId and displayName.
Simple way would be to use multiple If statements to get the non null field count but it would involve lot of boiler plate code and there is another class Organization for which I need to show completion percentage as well. So I created a utility function as show below:
public static <T, U> int getNotNullFieldCount(T t,
List<Function<? super T, ? extends U>> functionList) {
int count = 0;
for (Function<? super T, ? extends U> function : functionList) {
count += Optional.of(t).map(obj -> function.apply(t) != null ? 1 : 0).get();
}
return count;
}
And then I call this function as shown below:
List<Function<? super UserProfile, ? extends Object>> functionList = new ArrayList<>();
functionList.add(UserProfile::getAge);
functionList.add(UserProfile::getAddress);
functionList.add(UserProfile::getEmail);
functionList.add(UserProfile::getContactNumber);
System.out.println(getNotNullFieldCount(userProfile, functionList));
My question is, is this the best way I could count not null fields or I could improve it further. Please suggest.
You can simply a lot your code by creating a Stream over the given list of functions:
public static <T> long getNonNullFieldCount(T t, List<Function<? super T, ?>> functionList) {
return functionList.stream().map(f -> f.apply(t)).filter(Objects::nonNull).count();
}
This will return the count of non-null fields returned by each function. Each function is mapped to the result of applying it to the given object and null fields are filtered out with the predicate Objects::nonNull.
I wrote a utility class to get the total count of readable properties and the count of non null values in an object. The completion percentage can be calculated based on these.
It should work pretty well with inherited properties, nested properties, (multi-dimensional) iterables and maps.
I couldn't include the tests as well in here, because of the character limit, but here's the utility class:
import lombok.*;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class PropertyCountUtils {
/***
* See {#link #getReadablePropertyValueCount(Object, Set)}.
*/
public static PropertyValueCount getReadablePropertyValueCount(#NonNull Object object) {
return getReadablePropertyValueCount(object, null);
}
/**
* Counts the properties of the given object, including inherited and nested properties,
* returning the total property count and the count of properties with assigned values.
*
* <p>
* Properties with assigned values have a value meeting all conditions below:
* <ul>
* <li>different from null</li>
* <li>different from an empty iterable or an empty map</li>
* <li>different from an iterable containing only null values</li>
* <li>different from a map containing only null values.</li>
* </ul>
* For multidimensional Iterables and Maps, these conditions apply to each dimension.
* </p>
*
* #param object The object to inspect. It should not be null.
* #param ignoredProperties The properties to ignore or null.
* For nested properties, use dot as a separator: "property1.nestedProperty.nestedProperty2"
* #return A pair of `assignedValueCount` (properties with assigned value) and `totalCount` (total property count).
*/
public static PropertyValueCount getReadablePropertyValueCount(
#NonNull Object object, Set<String> ignoredProperties) {
PropertyValueCount countHolder = new PropertyValueCount();
processReadablePropertyValueCount(countHolder, object, ignoredProperties, null);
return countHolder;
}
/***
* #return true if the object had at least one non-null property value or no readable properties.
* <p>
* If the object is an instance of String, for example, it would have no readable nested properties.
* Also, if the object is an instance of some class for which all nested properties are ignored,
* the method would return true, since the object itself has a non-null value,
* but the caller decided to ignore all properties.
* </p>
*/
#SneakyThrows
private static boolean processReadablePropertyValueCount(
PropertyValueCount countHolder, #NonNull Object object, Set<String> ignoredProperties, String parentPath) {
boolean objectHasAssignedProperties = false;
boolean objectHasNoReadableProperties = true;
List<Field> fields = getAllDeclaredFields(object.getClass());
for (Field field : fields) {
String fieldPath = buildFieldPath(parentPath, field);
Method readMethod = getReadMethod(object.getClass(), ignoredProperties, field, fieldPath);
if (readMethod == null) {
continue;
}
countHolder.setTotalCount(countHolder.getTotalCount() + 1);
objectHasNoReadableProperties = false;
Object value = readMethod.invoke(object);
if (value == null || isCollectionWithoutAnyNonNullValue(value)) {
// no assigned value, so we'll just count the total available properties
int readablePropertyValueCount = getReadablePropertyCount(
readMethod.getGenericReturnType(), ignoredProperties, fieldPath);
countHolder.setTotalCount(countHolder.getTotalCount() + readablePropertyValueCount);
} else if (value instanceof Iterable<?> iterable) {
processPropertyValueCountInIterable(countHolder, ignoredProperties, fieldPath, iterable);
} else if (value instanceof Map<?, ?> map) {
processPropertyValueCountInIterable(countHolder, ignoredProperties, fieldPath, map.values());
} else {
countHolder.setAssignedValueCount(countHolder.getAssignedValueCount() + 1);
// process properties of nested object
processReadablePropertyValueCount(countHolder, value, ignoredProperties, fieldPath);
objectHasAssignedProperties = true;
}
}
return objectHasAssignedProperties || objectHasNoReadableProperties;
}
private static void processPropertyValueCountInIterable(
PropertyValueCount countHolder, Set<String> ignoredProperties, String fieldPath, Iterable<?> iterable) {
boolean iterableHasNonNullValues = false;
// process properties of each item in the iterable
for (Object value : iterable) {
if (value != null) {
// check if the current iterable item is also an iterable itself
Optional<Iterable<?>> nestedIterable = getProcessableCollection(value);
if (nestedIterable.isPresent()) {
processPropertyValueCountInIterable(countHolder, ignoredProperties, fieldPath, nestedIterable.get());
} else {
iterableHasNonNullValues = processReadablePropertyValueCount(
countHolder, value, ignoredProperties, fieldPath);
}
}
}
// consider the iterable as having an assigned value only if it contains at least one non-null value
if (iterableHasNonNullValues) {
countHolder.setAssignedValueCount(countHolder.getAssignedValueCount() + 1);
}
}
#SneakyThrows
private static int getReadablePropertyCount(
#NonNull Type inspectedType, Set<String> ignoredProperties, String parentPath) {
int totalReadablePropertyCount = 0;
Class<?> inspectedClass = getTargetClassFromGenericType(inspectedType);
List<Field> fields = getAllDeclaredFields(inspectedClass);
for (Field field : fields) {
String fieldPath = buildFieldPath(parentPath, field);
Method readMethod = getReadMethod(inspectedClass, ignoredProperties, field, fieldPath);
if (readMethod != null) {
totalReadablePropertyCount++;
Class<?> returnType = getTargetClassFromGenericType(readMethod.getGenericReturnType());
// process properties of nested class, avoiding infinite loops
if (!hasCircularTypeReference(inspectedClass, returnType)) {
int readablePropertyValueCount = getReadablePropertyCount(
returnType, ignoredProperties, fieldPath);
totalReadablePropertyCount += readablePropertyValueCount;
}
}
}
return totalReadablePropertyCount;
}
// In case the object being analyzed is of parameterized type,
// we want to count the properties in the class of the parameter, not of the container.
private static Class<?> getTargetClassFromGenericType(Type type) {
if (type instanceof ParameterizedType parameterizedType) {
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
if (actualTypeArguments.length > 0) {
// Inspect the last parameter type.
// For example, lists would only have one parameter type,
// but in the case of maps we would inspect the parameter representing the entry value, not the entry key.
Type inspectedTypeArgument = actualTypeArguments[actualTypeArguments.length - 1];
return inspectedTypeArgument instanceof ParameterizedType ?
getTargetClassFromGenericType(inspectedTypeArgument) :
(Class<?>) inspectedTypeArgument;
}
}
return type instanceof Class<?> ? (Class<?>) type : type.getClass();
}
private static List<Field> getAllDeclaredFields(#NonNull Class<?> inspectedClass) {
List<Field> fields = new ArrayList<>();
Collections.addAll(fields, inspectedClass.getDeclaredFields());
Class<?> superClass = inspectedClass.getSuperclass();
while (superClass != null) {
Collections.addAll(fields, superClass.getDeclaredFields());
superClass = superClass.getSuperclass();
}
return fields;
}
private static Method getReadMethod(#NonNull Class<?> inspectedClass, Set<String> ignoredProperties, Field field, String fieldPath) {
if (ignoredProperties != null && ignoredProperties.contains(fieldPath)) {
return null;
}
PropertyDescriptor propertyDescriptor;
try {
propertyDescriptor = new PropertyDescriptor(field.getName(), inspectedClass);
} catch (IntrospectionException e) {
// statement reached when the field doesn't have a getter
return null;
}
return propertyDescriptor.getReadMethod();
}
private static boolean hasCircularTypeReference(Class<?> propertyContainerClass, Class<?> propertyType) {
return propertyContainerClass.isAssignableFrom(propertyType);
}
private static String buildFieldPath(String parentPath, Field field) {
return parentPath == null ? field.getName() : parentPath + "." + field.getName();
}
private static boolean isCollectionWithoutAnyNonNullValue(Object value) {
Stream<?> stream = null;
if (value instanceof Iterable<?> iterable) {
stream = StreamSupport.stream(iterable.spliterator(), false);
} else if (value instanceof Map<?, ?> map) {
stream = map.values().stream();
}
return stream != null &&
stream.noneMatch(item -> item != null && !isCollectionWithoutAnyNonNullValue(item));
}
private static Optional<Iterable<?>> getProcessableCollection(Object value) {
if (value instanceof Iterable<?> iterable) {
return Optional.of(iterable);
} else if (value instanceof Map<?, ?> map) {
return Optional.of(map.values());
}
return Optional.empty();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public static class PropertyValueCount {
private int assignedValueCount;
private int totalCount;
}
}
The completion percentage can be calculated like this:
PropertyCountUtils.PropertyValueCount propertyValueCount = getReadablePropertyValueCount(profile);
BigDecimal profileCompletionPercentage = BigDecimal.valueOf(propertyValueCount.getNonNullValueCount())
.multiply(BigDecimal.valueOf(100))
.divide(BigDecimal.valueOf(propertyValueCount.getTotalCount()), 2, RoundingMode.UP)
.stripTrailingZeros();
I came across a class in our java project that is confusing for me to understand.
It implements the Iterable interface with a type parameter as it's self for that iterable. Could someone shed some light on to what this does and why it would be useful?
public class IndexRequest extends Request implements Cloneable, Iterable<IndexRequest> {
public enum OpType {insert, update};
protected Index index;
protected Type type;
protected boolean incomingAsArray;
protected long timeToLive;
protected String parent;
protected String route;
protected LinkedListMultimap<String, IndexRequest> iterable;
protected String errorMessage;
protected String stackTrace;
protected String warnMessage;
protected OpType opType = OpType.insert;
protected long versionId;
protected JsonNode previousDocument;
protected boolean ignored;
protected String resultCode;
protected int retries = 0;
protected boolean esUpsertFlag = false;
protected String id = UUID.randomUUID().toString();
/**
* Constructor
*/
public IndexRequest() {
super();
logger = LoggerFactory.getLogger(IndexRequest.class);
this.requestJson = JsonNodeFactory.instance.arrayNode();
}
/**
* Constructor
*/
public IndexRequest(String endpointPath, String requestId, String user, JsonNode requestJson, Map<String, String[]> requestParameters) {
super(endpointPath, requestId, user, requestJson, requestParameters);
}
/**
* Initialize our iterable version of this class
*/
protected void initIterable() {
LinkedListMultimap <String, IndexRequest> contents = LinkedListMultimap.create();
if (isArray()) {
ArrayNode docsArray = (ArrayNode) getDocument();
Iterator<JsonNode> docIterator = docsArray.iterator();
while (docIterator.hasNext()) {
IndexRequest is = this.clone(false);
// generate a new id since this is a new wrapper around a particular doc
is.id = UUID.randomUUID().toString();
is.setDocument(docIterator.next());
contents.put(is.getId(), is);
}
iterable = contents;
}
else {
iterable = LinkedListMultimap.create();
iterable.put(getId(), this);
}
}
/**
* Returns an iterator for this index set
* #return Iterator<IndexRequest> An iterator for this index set
*/
public Iterator<IndexRequest> iterator() {
if (iterable == null) {
initIterable();
}
return iterable.values().iterator();
}
}
Thank you in advance.
A class that implements the Iterable can be used with the new for-loop. Here is such an example:
List list = new ArrayList();
for(Object o : list){
//do something o;
}
The Iterable interface has only one method:
public interface Iterable<T> {
public Iterator<T> iterator();
}
It is possible to use our own collection type classes with the new for-loop. To do so, our class must implement the java.lang.Iterable interface. and provide the implementation of iterator method , which you can see in your class is provided here :
/**
* Returns an iterator for this index set
* #return Iterator<IndexRequest> An iterator for this index set
*/
public Iterator<IndexRequest> iterator() {
if (iterable == null) {
initIterable();
}
return iterable.values().iterator();
}
This class looks like it defines a node in a tree of such nodes. Instances hold a map from Strings to other instances of the same class:
protected LinkedListMultimap<String, IndexRequest> iterable;
They also inherit from their parent class some sense of being / holding an array of other instances. When they are iterated, they provide the values from their map or the elements of their array.