Skip to content

Commit

Permalink
Merge pull request #456 from eclipse/value-null
Browse files Browse the repository at this point in the history
Update Value to work with Null as value
  • Loading branch information
otaviojava authored Nov 19, 2023
2 parents dcf8ba9 + 929d712 commit 3165c5b
Show file tree
Hide file tree
Showing 33 changed files with 391 additions and 119 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ and this project adheres to https://semver.org/spec/v2.0.0.html[Semantic Version
=== Changed

- Add support to boolean values at the queries declaration

- Make null a valid value

== [1.0.2] - 2023-10-01

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,6 @@
* A {@link ColumnEntity} has one or more Columns.
*/
public interface Column extends Entry {



/**
* Alias to {@link Value#get(Class)}
*
Expand Down Expand Up @@ -67,18 +64,17 @@ public interface Column extends Entry {
* @param value - column's value
* @param <V> the value type
* @return a column instance
* @throws NullPointerException when there is any null parameter
* @throws NullPointerException when name is null
* @see Columns
*/
static <V> Column of(String name, V value) {
Objects.requireNonNull(name, "name is required");
Objects.requireNonNull(value, "value is required");
return new DefaultColumn(name, getValue(value));
}

private static Value getValue(Object value) {
if (value instanceof Value) {
return Value.class.cast(value);
return (Value) value;
} else {
return Value.of(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,10 @@ public void add(Column column) {
* @param name a name of the column
* @param value the information of the column
* @throws UnsupportedOperationException when this method is not supported
* @throws NullPointerException when either name or value are null
* @throws NullPointerException when either name is null
*/
public void add(String name, Object value) {
requireNonNull(name, "name is required");
requireNonNull(value, "value is required");
this.columns.put(name, Column.of(name, Value.of(value)));
}

Expand All @@ -93,14 +92,24 @@ public void add(String name, Object value) {
* @param name a name of the column
* @param value the information of the column
* @throws UnsupportedOperationException when this method is not supported
* @throws NullPointerException when either name or value are null
* @throws NullPointerException when either name is null
*/
public void add(String name, Value value) {
requireNonNull(name, "name is required");
requireNonNull(value, "value is required");
this.columns.put(name, Column.of(name, value));
}

/**
* Adds a column with a null value to the collection of columns.
*
* @param name the name of the column to add; must not be {@code null}
* @throws NullPointerException if the provided {@code name} is {@code null}
*/
public void addNull(String name){
requireNonNull(name, "name is required");
this.columns.put(name, Column.of(name, Value.ofNull()));
}

/**
* Converts the columns to a Map where:
* the key is the name the column
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ private Columns() {
* @param name column's name
* @param value column's value
* @return a column's instance
* @throws NullPointerException when either name or value are null
* @throws NullPointerException when name is null
*/
public static Column of(String name, Object value) {
return Column.of(name, Value.of(value));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ class ColumnDeleteQueryTest {

@Test
void shouldBuilderThrownException() {
assertThrows(NullPointerException.class, () -> {
ColumnDeleteQuery.builder(new String[]{null});
});
assertThrows(NullPointerException.class, () -> ColumnDeleteQuery.builder(new String[]{null}));
}

@ParameterizedTest(name = "{0} passed to build method then a valid builder should be returned")
Expand All @@ -46,9 +44,7 @@ void shouldBuildReturnsAValidBuilder(String scenario, String[] documents) {

@Test
void shouldDeleteThrownException() {
assertThrows(NullPointerException.class, () -> {
ColumnDeleteQuery.delete(new String[]{null});
});
assertThrows(NullPointerException.class, () -> ColumnDeleteQuery.delete(new String[]{null}));
}

@ParameterizedTest(name = "{0} passed to delete method then a valid builder should be returned")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.eclipse.jnosql.communication.column;

import org.assertj.core.api.SoftAssertions;
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.Value;
import org.junit.jupiter.api.Assertions;
Expand All @@ -33,7 +34,6 @@
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotSame;
Expand Down Expand Up @@ -310,10 +310,14 @@ void shouldAddColumnAsNameAndValue() {
}

@Test
void shouldReturnErrorWhenAddColumnsObjectWhenHasNullObject() {
Assertions.assertThrows(NullPointerException.class, () -> {
ColumnEntity entity = new ColumnEntity("columnFamily");
entity.add("name", null);
void shouldReturnWhenAddColumnsObjectWhenHasNullObject() {
ColumnEntity entity = new ColumnEntity("columnFamily");
entity.add("name", null);
assertEquals(1, entity.size());
Column name = entity.find("name").orElseThrow();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(name.name()).isEqualTo("name");
softly.assertThat(name.get()).isNull();
});
}

Expand Down Expand Up @@ -415,10 +419,20 @@ void shouldRemoveAllElementsWhenUseClearMethod() {

ColumnEntity columnFamily = ColumnEntity.of("columnFamily", columns);


assertFalse(columnFamily.isEmpty());
columnFamily.clear();
assertTrue(columnFamily.isEmpty());
}

@Test
void shouldCreateNull(){
ColumnEntity entity = ColumnEntity.of("entity");
entity.addNull("name");
Column name = entity.find("name").orElseThrow();
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(name.name()).isEqualTo("name");
softly.assertThat(name.get()).isNull();
});
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.eclipse.jnosql.communication.column;


import org.assertj.core.api.SoftAssertions;
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.Value;
import org.junit.jupiter.api.Assertions;
Expand All @@ -33,15 +34,15 @@ class ColumnTest {

@Test
void shouldReturnNameWhenNameIsNull() {
Assertions.assertThrows(NullPointerException.class, () -> {
Column.of(null, DEFAULT_VALUE);
});
Assertions.assertThrows(NullPointerException.class, () -> Column.of(null, DEFAULT_VALUE));
}

@Test
void shouldReturnNameWhenValueIsNull() {
Assertions.assertThrows(NullPointerException.class, () -> {
Column.of("Name", null);
Column column = Column.of("Name", null);
SoftAssertions.assertSoftly(softly -> {
softly.assertThat(column.name()).isEqualTo("Name");
softly.assertThat(column.value().isNull()).isTrue();
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
*/
record DefaultValue(Object value) implements Value {

/**
* A constant {@link Value} instance representing a null value.
* This instance is often used to signify the absence of a meaningful value.
* It is commonly employed in scenarios where a valid value is expected but none is available.
* The {@code NULL} instance is immutable and can be used to compare against other {@link Value} instances
* to determine if they encapsulate a null value.
*/
public static final Value NULL = NullValue.INSTANCE;
private static final ValueReader SERVICE_PROVIDER = ValueReaderDecorator.getInstance();

private static final TypeReferenceReader REFERENCE_READER = TypeReferenceReaderDecorator.getInstance();
Expand All @@ -42,6 +50,7 @@ public <T> T get(Class<T> type) {

@Override
public <T> T get(TypeSupplier<T> supplier) {
Objects.requireNonNull(supplier, "supplier is required");
if (REFERENCE_READER.test(Objects.requireNonNull(supplier, "supplier is required"))) {
return REFERENCE_READER.convert(supplier, value);
}
Expand All @@ -54,6 +63,11 @@ public boolean isInstanceOf(Class<?> typeClass) {
return typeClass.isInstance(value);
}

@Override
public boolean isNull() {
return false;
}


@Override
public boolean equals(Object o) {
Expand All @@ -70,4 +84,5 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hashCode(value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
*
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
*
* Otavio Santana
*
*/
package org.eclipse.jnosql.communication;

/**
* An enum implementing the {@link Value} interface to represent a null value.
* This enum provides a standardized way to handle null values within the context of {@link Value}.
* It is often used in scenarios where the absence of a meaningful value needs to be explicitly represented.
* The methods in this enum always return null or false, depending on the context.
*/
enum NullValue implements Value {
INSTANCE;

@Override
public Object get() {
return null;
}

@Override
public <T> T get(Class<T> type) {
return null;
}

@Override
public <T> T get(TypeSupplier<T> supplier) {
return null;
}

@Override
public boolean isInstanceOf(Class<?> typeClass) {
return false;
}

@Override
public boolean isNull() {
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public boolean isInstanceOf(Class<?> typeClass) {
return typeClass.isInstance(value);
}

@Override
public boolean isNull() {
return false;
}

public boolean isEmpty() {
return Objects.isNull(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package org.eclipse.jnosql.communication;


import java.util.Objects;

/**
* It represents an information unit that is to/from a database.
Expand All @@ -32,53 +31,76 @@ public interface Value {
/**
* Returns the value without conversion.
*
* @return the instance inside {@link Value}
* @return the instance inside {@link Value}, or {@code null} if the value is null
*/
Object get();

/**
* Converts {@link Value#get()} to specified class
* Converts {@link Value#get()} to the specified class.
* When the value is {@code null}, it will return null.
*
* @param type the class type
* @param <T> the new instance type
* @return a new instance converted to informed class
* @return a new instance converted to the informed class, or {@code null} if the value is null
* @throws NullPointerException when the class is null
* @throws UnsupportedOperationException when the type is unsupported
* @see ValueReader
*/
<T> T get(Class<T> type);


/**
* Converts {@link Value#get()} to specified class
* Converts {@link Value#get()} to the specified class.
*
* @param supplier the type supplier
* @param <T> the new instance type
* @return a new instance converted to informed class
* @return a new instance converted to the informed class, or {@code null} if the value is null
* @throws NullPointerException when the class is null
* @throws UnsupportedOperationException when the type is unsupported
* @see ValueReader
*/
<T> T get(TypeSupplier<T> supplier);

/**
* A wrapper of {@link Class#isInstance(Object)} to check the value instance within the {@link Value}
* A wrapper of {@link Class#isInstance(Object)} to check the value instance within the {@link Value}.
*
* @param type the type
* @return {@link Class#isInstance(Object)}
* @throws NullPointerException when type is null
* @throws NullPointerException when the type is null
*/
boolean isInstanceOf(Class<?> type);

/**
* Checks whether the current instance represents a null value.
*
* @return {@code true} if the value encapsulated by this instance is null,
* {@code false} otherwise.
*/
boolean isNull();


/**
* Creates a new {@link Value} instance
* Creates a new {@link Value} instance.
*
* @param value - the information to {@link Value}
* @return a {@link Value} instance within a value informed
* @throws NullPointerException when the parameter is null
* @return a {@link Value} instance within a value informed, or {@link DefaultValue#NULL} if the value is null
*/
static Value of(Object value) {
Objects.requireNonNull(value, "value is required");
if(value == null) {
return DefaultValue.NULL;
}
return new DefaultValue(value);
}

/**
* Creates and returns a {@link Value} instance representing a null value.
* This method provides a convenient way to obtain a {@link Value} instance that encapsulates a null value.
* The returned instance is often used to signify the absence of a meaningful value in scenarios
* where a valid value is expected but none is available.
*
* @return a {@link Value} instance representing a null value, typically {@link DefaultValue#NULL}.
*/
static Value ofNull() {
return DefaultValue.NULL;
}
}
Loading

0 comments on commit 3165c5b

Please sign in to comment.