Merge pull request #48 from TouchInstinct/rxjava2/validation

validation
This commit is contained in:
Gavriil 2017-04-18 14:40:52 +03:00 committed by GitHub
commit cf47237e49
8 changed files with 49 additions and 24 deletions

View File

@ -4,11 +4,13 @@ import android.support.annotation.NonNull;
import java.io.Serializable;
import io.reactivex.functions.Function;
/**
* Created by Ilia Kurtov on 30/01/2017.
* Simple interface that gets one parameter with {@link TInput} type as input and returns other type {@link TReturn} as a result.
* Interface extends {@link Serializable} to survive after {@link ru.touchin.roboswag.components.navigation.AbstractState} recreation.
* Created as a replace for {@link rx.functions.Func1} because it needed to be {@link Serializable}
* Created as a replace for {@link Function} because it needed to be {@link Serializable}
*
* @param <TInput> input type.
* @param <TReturn> return type.

View File

@ -37,6 +37,7 @@ public class BooleanValidationController extends ValidationController<Boolean, B
/**
* This method validates bounded view.
*
* @param activatedObservable emits true when we need to show error on empty fields. Eg when user clicks on Done button but he missed some
* necessary fields to fill.
* @return observable without any concrete type. Simply subscribe to this method to make it works.
@ -45,7 +46,7 @@ public class BooleanValidationController extends ValidationController<Boolean, B
public Observable<?> validation(@NonNull final Observable<Boolean> activatedObservable) {
return Observable.combineLatest(activatedObservable, getValidator().getWrapperModel().observe(),
(activated, flag) -> {
final boolean selected = flag == null ? false : flag;
final boolean selected = flag.get() == null ? false : flag.get();
if (activated && !selected) {
return ValidationState.ERROR_NO_DESCRIPTION;
} else if (!activated && !selected) {

View File

@ -26,6 +26,7 @@ import android.text.TextUtils;
import java.io.Serializable;
import io.reactivex.Observable;
import ru.touchin.roboswag.core.utils.Optional;
import ru.touchin.roboswag.core.utils.pairs.NonNullPair;
import ru.touchin.templates.validation.ValidationState;
import ru.touchin.templates.validation.validators.EditTextValidator;
@ -77,9 +78,10 @@ public class EditTextValidationController<TModel extends Serializable>
@Nullable
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
private NonNullPair<Boolean, Observable<ValidationState>> getValidationPair(final boolean activated,
@Nullable final String text,
@NonNull Optional<String> optionalText,
@Nullable final Boolean focusIn,
final boolean showError) {
final String text = optionalText.get();
if (focusIn == null && TextUtils.isEmpty(text) && !activated && !showError) {
return null;
}
@ -98,8 +100,7 @@ public class EditTextValidationController<TModel extends Serializable>
/**
* If we don't want to show error when focus is lost.
*
* @param showErrorOnFocusOut show an error or don't show an error.
*
* @param showErrorOnFocusOut show an error or don't show an error.
*/
public void setShowErrorOnFocusOut(final boolean showErrorOnFocusOut) {
this.showErrorOnFocusOut = showErrorOnFocusOut;

View File

@ -12,7 +12,8 @@ import ru.touchin.templates.validation.validators.Validator;
* Created by Ilia Kurtov on 24/01/2017.
* {@link ValidationController} for models that have the same modal as wrapper model. You can use it when you simply need to be sure
* that user have selected some item and it is not null.
* @param <TModel> type of the model.
*
* @param <TModel> type of the model.
* @param <TValidator> corresponding {@link Validator}
*/
public class SimpleValidationController<TModel extends Serializable, TValidator extends Validator<TModel, TModel>>
@ -24,6 +25,7 @@ public class SimpleValidationController<TModel extends Serializable, TValidator
/**
* This method validates bounded view.
*
* @param activatedObservable emits true when we need to show error on empty fields. Eg when user clicks on Done button but he missed some
* necessary fields to fill.
* @return observable without any concrete type. Simply subscribe to this method to make it works.

View File

@ -27,6 +27,7 @@ import java.io.Serializable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import ru.touchin.roboswag.core.utils.Optional;
import ru.touchin.templates.validation.ValidationState;
import ru.touchin.templates.validation.ViewWithError;
import ru.touchin.templates.validation.validators.Validator;
@ -53,22 +54,24 @@ public class ValidationController
/**
* Bind to this observable to connect view and model. If you provide first argument (viewStateObservable) - the connection would be two-way.
* If not - one-way. This method changes updates view with current {@link ValidationState}.
*
* @param viewStateObservable input view state {@link Observable}.
* Eg it can be observable with input text from the {@link android.widget.EditText}
* @param updateViewAction action that updates current state of the bounded view.
* @param viewWithError view that implements {@link ViewWithError} interface and could reacts to the validation errors.
* @param updateViewAction action that updates current state of the bounded view.
* @param viewWithError view that implements {@link ViewWithError} interface and could reacts to the validation errors.
* @return observable without any concrete type. Simply subscribe to this method to make it works.
*/
@NonNull
public Observable<?> modelAndViewUpdating(@Nullable final Observable<TWrapperModel> viewStateObservable,
@NonNull final Consumer<TWrapperModel> updateViewAction,
@NonNull final Consumer<Optional<TWrapperModel>> updateViewAction,
@NonNull final ViewWithError viewWithError) {
final Observable<?> stateObservable = viewStateObservable != null
? viewStateObservable.doOnNext(flag -> getValidator().getWrapperModel().set(flag))
: Observable.empty();
return Observable
.merge(getValidator().getWrapperModel().observe()
.observeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(updateViewAction),
getValidator().getValidationState().observe()
.observeOn(AndroidSchedulers.mainThread())
@ -84,6 +87,7 @@ public class ValidationController
/**
* Helper function to check if validation state in error state ot not
*
* @param validationState the state you want to check for the errors.
* @return true if validation state is in error and false otherwise.
*/

View File

@ -25,6 +25,7 @@ import android.support.annotation.Nullable;
import java.io.Serializable;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
import ru.touchin.roboswag.core.observables.Changeable;
import ru.touchin.roboswag.core.observables.NonNullChangeable;
@ -51,6 +52,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
* This flag needed to force showing errors. You don't want to show final error when you start to enter data in some field at first time.
* But if user leaves this view and final check not passed - you need to force to show an error till user not enters correct data and leaves
* the view.
*
* @return {@link NonNullChangeable} with current state of the flag - do we need to show errors from final checks while user types.
*/
@NonNull
@ -60,6 +62,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
/**
* Use this method to get or set final check.
*
* @return final check.
*/
@NonNull
@ -69,6 +72,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
/**
* Use this method to get or set primary check.
*
* @return primary check.
*/
@NonNull
@ -104,21 +108,16 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
primaryCheck.observe().observeOn(Schedulers.computation()),
(finalCheck, primaryCheck) -> {
try {
return validateText(finalCheck, primaryCheck, text, fullCheck);
return validateText(finalCheck.get(), primaryCheck.get(), text, fullCheck);
} catch (final Throwable exception) {
return new HalfNullablePair<>(ValidationState.ERROR_CONVERSION, null);
}
});
}
@NonNull
private Observable<ValidationState> processChecks(@NonNull final String text, final boolean fullCheck) {
return createValidationObservable(text, fullCheck)
.map(HalfNullablePair::getFirst);
}
/**
* Validates text with primary check.
*
* @param text - input text.
* @return {@link Observable} with the result of the primary check.
*/
@ -129,6 +128,7 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
/**
* Validates text with final check.
*
* @param text - input text.
* @return {@link Observable} with the result of the final check.
*/
@ -140,15 +140,21 @@ public abstract class EditTextValidator<TModel extends Serializable> extends Val
/**
* Validates text with primary and final check consequentially and returns {@link Observable} with {@link HalfNullablePair} of final state
* and resulting model.
*
* @param text - input text.
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the text.
* Model can be null if validation fails on primary or final checks.
* Model can be null if validation fails on primary or final checks.
*/
@NonNull
@Override
public Observable<HalfNullablePair<ValidationState, TModel>> fullValidateAndGetModel(@NonNull final String text) {
return createValidationObservable(text, true)
.first();
return createValidationObservable(text, true);
}
@NonNull
private Observable<ValidationState> processChecks(@NonNull final String text, final boolean fullCheck) {
return createValidationObservable(text, fullCheck)
.map(HalfNullablePair::getFirst);
}
}

View File

@ -5,19 +5,21 @@ import android.support.annotation.NonNull;
import java.io.Serializable;
import io.reactivex.Observable;
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
import ru.touchin.templates.validation.ValidationState;
import rx.Observable;
/**
* Created by Ilia Kurtov on 24/01/2017.
* Class that simplifies work with {@link Validator}'s that have the same wrapper model and model type.
*
* @param <TModel> model that should be bounded with a view.
*/
public class SameTypeValidator<TModel extends Serializable> extends Validator<TModel, TModel> {
/**
* Simply returns the same model without any converting.
*
* @param wrapperModel input model.
* @return the same model as input parameter.
* @throws Throwable - in this case no throwable would be thrown.
@ -31,9 +33,10 @@ public class SameTypeValidator<TModel extends Serializable> extends Validator<TM
/**
* Validates {@link TModel} and returns {@link Observable} with {@link HalfNullablePair} of final state and resulting model.
*
* @param wrapperModel - not null value that should be validated.
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the {@link TModel}.
* Model can be null if validation fails.
* Model can be null if validation fails.
*/
@NonNull
@Override

View File

@ -24,6 +24,7 @@ import android.support.annotation.NonNull;
import java.io.Serializable;
import io.reactivex.Observable;
import io.reactivex.Single;
import ru.touchin.roboswag.core.observables.Changeable;
import ru.touchin.roboswag.core.observables.NonNullChangeable;
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
@ -54,6 +55,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
/**
* This method converts {@link TWrapperModel} into a {@link TModel}.
*
* @param wrapperModel - not null value that should be converted into a {@link TModel} object.
* @return converted wrapperModel into a {@link TModel}.
* @throws Throwable for the cases when converting cannot be processed.
@ -63,6 +65,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
/**
* Call this method to get {@link Changeable} with {@link TWrapperModel} inside it that should be connected to its bounded view.
*
* @return {@link Changeable} with {@link TWrapperModel}.
*/
@NonNull
@ -72,6 +75,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
/**
* Returns current {@link ValidationState} or its successor. Needed to connect with bounded view and react to this state changes.
*
* @return current validation state.
*/
@NonNull
@ -82,6 +86,7 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
/**
* This method needed to get {@link ValidationState} that needed to be shown when bounded view is empty and you need to show to user reminder,
* that he or she needs to fill this view.
*
* @return {@link ValidationState} that should be shown for an empty field.
*/
@NonNull
@ -90,10 +95,11 @@ public abstract class Validator<TWrapperModel extends Serializable, TModel exten
}
/**
* Validates {@link TWrapperModel} and returns {@link Observable} with {@link HalfNullablePair} of final state and resulting model.
* Validates {@link TWrapperModel} and returns {@link Single} with {@link HalfNullablePair} of final state and resulting model.
*
* @param wrapperModel - not null value that should be validated.
* @return pair with final {@link ValidationState} that is always not null and a model that we get after converting the {@link TWrapperModel}.
* Model can be null if validation fails.
* Model can be null if validation fails.
*/
@NonNull
public abstract Observable<HalfNullablePair<ValidationState, TModel>> fullValidateAndGetModel(@NonNull final TWrapperModel wrapperModel);