Merge pull request #23 from TouchInstinct/feature/validation-comments
Feature/validation comments
This commit is contained in:
commit
fbad7275ea
|
|
@ -0,0 +1,26 @@
|
|||
package ru.touchin.templates.validation;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 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}
|
||||
* @param <TInput> input type.
|
||||
* @param <TReturn> return type.
|
||||
*/
|
||||
public interface ValidationFunc<TInput, TReturn> extends Serializable {
|
||||
|
||||
/**
|
||||
* The method maps some {@link TInput} type argument into a {@link TReturn} type.
|
||||
* @param input data;
|
||||
* @return mapped data into a {@link TReturn} type.
|
||||
* @throws Throwable for catching conversion errors.
|
||||
*/
|
||||
@NonNull
|
||||
TReturn call(@NonNull final TInput input) throws Throwable;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Touch Instinct
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.validation;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* Basic class for validation states. If you need to have more states with more data in it -
|
||||
* create class that extends this class and don't forget to redefine {@link #equals(Object)} and {@link #hashCode()} methods.
|
||||
* Don't use same {@link #code} for different states.
|
||||
*/
|
||||
public class ValidationState implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Initial state of validation. It indicates that no validation rules applied yet.
|
||||
*/
|
||||
public static final ValidationState INITIAL = new ValidationState(-1);
|
||||
/**
|
||||
* Valid state.
|
||||
*/
|
||||
public static final ValidationState VALID = new ValidationState(-2);
|
||||
/**
|
||||
* Error shows when model (e.g. DateTime) is failing on conversion from raw data (e.g. from String) for validation.
|
||||
*/
|
||||
public static final ValidationState ERROR_CONVERSION = new ValidationState(-3);
|
||||
/**
|
||||
* Error shows when we don't need to show any description of error (e.g. just highlight input field with red color).
|
||||
*/
|
||||
public static final ValidationState ERROR_NO_DESCRIPTION = new ValidationState(-4);
|
||||
|
||||
private final int code;
|
||||
|
||||
public ValidationState(final int code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unique code of the {@link ValidationState}.
|
||||
* @return code or the ValidationState.
|
||||
*/
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't forget to override this method!
|
||||
* @param object that you want to compare.
|
||||
* @return true if objects equals and false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(final Object object) {
|
||||
return this == object
|
||||
|| !(object == null || getClass() != object.getClass()) && code == ((ValidationState) object).code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * code;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Touch Instinct
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.validation;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* Interface for views that must be validated. Have two states - show error or hide error.
|
||||
* You can provide your own Validation State to provide, eg string resource.
|
||||
* In this case use instanceOf to define what state do you have.
|
||||
*/
|
||||
public interface ViewWithError {
|
||||
|
||||
/**
|
||||
* Hides the error when validation passes successful.
|
||||
*/
|
||||
void hideError();
|
||||
|
||||
/**
|
||||
* Shows error
|
||||
* Pass here error state.
|
||||
* It is not correct to pass here {@link ValidationState#VALID} or {@link ValidationState#INITIAL}
|
||||
* @param validationState error state. Can be other than {@link ValidationState} if you have successor of base {@link ValidationState}.
|
||||
*/
|
||||
void showError(@NonNull final ValidationState validationState);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Touch Instinct
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.validation.validationcontrollers;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import ru.touchin.templates.validation.ValidationState;
|
||||
import ru.touchin.templates.validation.validators.SameTypeValidator;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* {@link ValidationController} for {@link Boolean} models. Eg if you have some flag that should be bounded to checkbox.
|
||||
*/
|
||||
public class BooleanValidationController extends ValidationController<Boolean, Boolean, SameTypeValidator<Boolean>> {
|
||||
|
||||
public BooleanValidationController(@NonNull final SameTypeValidator<Boolean> validator) {
|
||||
super(validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> validation(@NonNull final Observable<Boolean> activatedObservable) {
|
||||
return Observable.combineLatest(activatedObservable, getValidator().getWrapperModel().observe(),
|
||||
(activated, flag) -> {
|
||||
final boolean selected = flag == null ? false : flag;
|
||||
if (activated && !selected) {
|
||||
return ValidationState.ERROR_NO_DESCRIPTION;
|
||||
} else if (!activated && !selected) {
|
||||
return ValidationState.INITIAL;
|
||||
}
|
||||
return ValidationState.VALID;
|
||||
})
|
||||
.doOnNext(validationState -> getValidator().getValidationState().set(validationState));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Touch Instinct
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.validation.validationcontrollers;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import ru.touchin.roboswag.core.utils.pairs.NonNullPair;
|
||||
import ru.touchin.templates.validation.ValidationState;
|
||||
import ru.touchin.templates.validation.validators.EditTextValidator;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* ValidationController for {@link android.widget.EditText} views. It has method {@link #validation} that response
|
||||
* for validating view. To use this class properly, you need to subscribe to the {@link #modelAndViewUpdating}
|
||||
* and to the {@link #validation} methods.
|
||||
*/
|
||||
public class EditTextValidationController<TModel extends Serializable>
|
||||
extends ValidationController<String, TModel, EditTextValidator<TModel>> {
|
||||
|
||||
public EditTextValidationController(@NonNull final EditTextValidator<TModel> validationWrapper) {
|
||||
super(validationWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method validates bounded view.
|
||||
*
|
||||
* @param focusOutObservable that emits items when bounded view get focus in or out.
|
||||
* @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.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> validation(@NonNull final Observable<Boolean> focusOutObservable, @NonNull final Observable<Boolean> activatedObservable) {
|
||||
return Observable.combineLatest(activatedObservable,
|
||||
getValidator().getWrapperModel().observe(),
|
||||
focusOutObservable,
|
||||
getValidator().getShowFullCheck().observe(),
|
||||
this::getValidationPair)
|
||||
.switchMap(validationPair -> {
|
||||
if (validationPair == null) {
|
||||
return Observable.empty();
|
||||
}
|
||||
return validationPair.getSecond()
|
||||
.doOnNext(validationState -> {
|
||||
if (!validationPair.getFirst()) {
|
||||
getValidator().getShowFullCheck().set(validationState != ValidationState.VALID);
|
||||
}
|
||||
getValidator().getValidationState().set(validationState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
|
||||
private NonNullPair<Boolean, Observable<ValidationState>> getValidationPair(final boolean activated,
|
||||
@Nullable final String text,
|
||||
@Nullable final Boolean focusIn,
|
||||
final boolean showError) {
|
||||
if (focusIn == null && TextUtils.isEmpty(text) && !activated && !showError) {
|
||||
return null;
|
||||
}
|
||||
final boolean focus = focusIn == null ? false : focusIn;
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
return new NonNullPair<>(focus, (activated || showError)
|
||||
? getValidator().getValidationStateWhenEmpty().observe()
|
||||
: Observable.just(ValidationState.INITIAL));
|
||||
}
|
||||
if (!showError && focus) {
|
||||
return new NonNullPair<>(true, getValidator().primaryValidate(text));
|
||||
}
|
||||
return new NonNullPair<>(focus, getValidator().fullValidate(text));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package ru.touchin.templates.validation.validationcontrollers;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import ru.touchin.templates.validation.ValidationState;
|
||||
import ru.touchin.templates.validation.validators.Validator;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* 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 <TValidator> corresponding {@link Validator}
|
||||
*/
|
||||
public class SimpleValidationController<TModel extends Serializable, TValidator extends Validator<TModel, TModel>>
|
||||
extends ValidationController<TModel, TModel, TValidator> {
|
||||
|
||||
public SimpleValidationController(@NonNull final TValidator validator) {
|
||||
super(validator);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<?> validation(@NonNull final Observable<Boolean> activatedObservable) {
|
||||
return Observable.combineLatest(activatedObservable,
|
||||
getValidator().getWrapperModel().observe(), (activated, model) -> {
|
||||
if (model == null) {
|
||||
return activated ? ValidationState.ERROR_NO_DESCRIPTION : ValidationState.INITIAL;
|
||||
}
|
||||
return ValidationState.VALID;
|
||||
})
|
||||
.doOnNext(validationState -> getValidator().getValidationState().set(validationState));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Touch Instinct
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.validation.validationcontrollers;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import ru.touchin.templates.validation.ValidationState;
|
||||
import ru.touchin.templates.validation.ViewWithError;
|
||||
import ru.touchin.templates.validation.validators.Validator;
|
||||
import rx.Observable;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.functions.Action1;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* This class holds information about related {@link Validator} class and how to connect model with view.
|
||||
*/
|
||||
public class ValidationController
|
||||
<TWrapperModel extends Serializable, TModel extends Serializable, TValidator extends Validator<TWrapperModel, TModel>> {
|
||||
|
||||
@NonNull
|
||||
private final TValidator validator;
|
||||
|
||||
@NonNull
|
||||
public TValidator getValidator() {
|
||||
return validator;
|
||||
}
|
||||
|
||||
public ValidationController(@NonNull final TValidator validator) {
|
||||
this.validator = validator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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 Action1<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())
|
||||
.doOnNext(updateViewAction),
|
||||
getValidator().getValidationState().observe()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext(validationState -> {
|
||||
if (!showError(validationState)) {
|
||||
viewWithError.hideError();
|
||||
} else {
|
||||
viewWithError.showError(validationState);
|
||||
}
|
||||
}),
|
||||
stateObservable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
protected boolean showError(@NonNull final ValidationState validationState) {
|
||||
return !validationState.equals(ValidationState.VALID) && !validationState.equals(ValidationState.INITIAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Touch Instinct
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.validation.validators;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.Changeable;
|
||||
import ru.touchin.roboswag.core.observables.NonNullChangeable;
|
||||
import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair;
|
||||
import ru.touchin.templates.validation.ValidationFunc;
|
||||
import ru.touchin.templates.validation.ValidationState;
|
||||
import rx.Observable;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* Special class for {@link android.widget.EditText} validation. It holds information about how primary check on typing should be
|
||||
* ({@link #getPrimaryCheck()} - get and set check here) and how final check should be processed too ({@link #getFinalCheck()} - get and set check
|
||||
* here).
|
||||
*/
|
||||
public abstract class EditTextValidator<TModel extends Serializable> extends Validator<String, TModel> {
|
||||
|
||||
@NonNull
|
||||
private final NonNullChangeable<Boolean> showFullCheck = new NonNullChangeable<>(false);
|
||||
@NonNull
|
||||
private final Changeable<ValidationFunc<TModel, HalfNullablePair<ValidationState, TModel>>> finalCheck = new Changeable<>(null);
|
||||
@NonNull
|
||||
private final Changeable<ValidationFunc<String, HalfNullablePair<ValidationState, TModel>>> primaryCheck = new Changeable<>(null);
|
||||
|
||||
/**
|
||||
* 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
|
||||
public NonNullChangeable<Boolean> getShowFullCheck() {
|
||||
return showFullCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to get or set final check.
|
||||
* @return final check.
|
||||
*/
|
||||
@NonNull
|
||||
protected Changeable<ValidationFunc<TModel, HalfNullablePair<ValidationState, TModel>>> getFinalCheck() {
|
||||
return finalCheck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this method to get or set primary check.
|
||||
* @return primary check.
|
||||
*/
|
||||
@NonNull
|
||||
protected Changeable<ValidationFunc<String, HalfNullablePair<ValidationState, TModel>>> getPrimaryCheck() {
|
||||
return primaryCheck;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private HalfNullablePair<ValidationState, TModel> validateText(
|
||||
@Nullable final ValidationFunc<TModel, HalfNullablePair<ValidationState, TModel>> finalCheck,
|
||||
@Nullable final ValidationFunc<String, HalfNullablePair<ValidationState, TModel>> primaryCheck,
|
||||
@NonNull final String text, final boolean fullCheck)
|
||||
throws Throwable {
|
||||
if (primaryCheck == null && finalCheck == null) {
|
||||
return new HalfNullablePair<>(ValidationState.VALID, convertWrapperModelToModel(text));
|
||||
}
|
||||
if (primaryCheck != null) {
|
||||
final HalfNullablePair<ValidationState, TModel> primaryPair = primaryCheck.call(text);
|
||||
if (finalCheck == null || primaryPair.getFirst() != ValidationState.VALID || primaryPair.getSecond() == null || !fullCheck) {
|
||||
return primaryPair;
|
||||
}
|
||||
return finalCheck.call(primaryPair.getSecond());
|
||||
}
|
||||
return finalCheck.call(convertWrapperModelToModel(text));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@SuppressWarnings("PMD.AvoidCatchingThrowable")
|
||||
// It's intended
|
||||
private Observable<HalfNullablePair<ValidationState, TModel>> createValidationObservable(@NonNull final String text, final boolean fullCheck) {
|
||||
return Observable
|
||||
.combineLatest(finalCheck.observe().observeOn(Schedulers.computation()),
|
||||
primaryCheck.observe().observeOn(Schedulers.computation()),
|
||||
(finalCheck, primaryCheck) -> {
|
||||
try {
|
||||
return validateText(finalCheck, primaryCheck, 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.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<ValidationState> primaryValidate(@NonNull final String text) {
|
||||
return processChecks(text, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates text with final check.
|
||||
* @param text - input text.
|
||||
* @return {@link Observable} with the result of the final check.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<ValidationState> fullValidate(@NonNull final String text) {
|
||||
return processChecks(text, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<HalfNullablePair<ValidationState, TModel>> fullValidateAndGetModel(@NonNull final String text) {
|
||||
return createValidationObservable(text, true)
|
||||
.first();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package ru.touchin.templates.validation.validators;
|
||||
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@NonNull
|
||||
@Override
|
||||
protected TModel convertWrapperModelToModel(@NonNull final TModel wrapperModel)
|
||||
throws Throwable {
|
||||
return wrapperModel;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Touch Instinct
|
||||
*
|
||||
* This file is part of RoboSwag library.
|
||||
*
|
||||
* 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 ru.touchin.templates.validation.validators;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import ru.touchin.roboswag.core.observables.Changeable;
|
||||
import ru.touchin.roboswag.core.observables.NonNullChangeable;
|
||||
import ru.touchin.templates.validation.ValidationState;
|
||||
|
||||
/**
|
||||
* Created by Ilia Kurtov on 24/01/2017.
|
||||
* This class holds information about current state of the object - {@link #getWrapperModel()}(that should be connected to some view),
|
||||
* current error state {@link #getValidationState()}. Also you need to provide a {@link ValidationState} or class that extends it
|
||||
* as an empty state. Eg, if you need to show some text in your view to show user that this view shouldn't be empty - pass needed state
|
||||
* to the {@link #getValidationStateWhenEmpty()}
|
||||
* {@link TWrapperModel} is type of class that should be connected to its bounded view. {@link TModel} is type of class
|
||||
* that represent object that we need at the end. Eg, if we want to enter some digits to {@link android.widget.EditText}
|
||||
* and get {@link java.util.Date} as a result - {@link CharSequence} or {@link String} should be the {@link TWrapperModel}
|
||||
* and {@link java.util.Date} would be the {@link TModel} type.
|
||||
*/
|
||||
public abstract class Validator<TWrapperModel extends Serializable, TModel extends Serializable>
|
||||
implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@NonNull
|
||||
private final NonNullChangeable<ValidationState> validationState = new NonNullChangeable<>(ValidationState.INITIAL);
|
||||
@NonNull
|
||||
private final Changeable<TWrapperModel> wrapperModel = new Changeable<>(null);
|
||||
@NonNull
|
||||
private final NonNullChangeable<ValidationState> validationStateWhenEmpty = new NonNullChangeable<>(ValidationState.ERROR_NO_DESCRIPTION);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@NonNull
|
||||
protected abstract TModel convertWrapperModelToModel(@NonNull final TWrapperModel wrapperModel) throws Throwable;
|
||||
|
||||
/**
|
||||
* 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
|
||||
public Changeable<TWrapperModel> getWrapperModel() {
|
||||
return wrapperModel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current {@link ValidationState} or its successor. Needed to connect with bounded view and react to this state changes.
|
||||
* @return current validation state.
|
||||
*/
|
||||
@NonNull
|
||||
public NonNullChangeable<ValidationState> getValidationState() {
|
||||
return validationState;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
public NonNullChangeable<ValidationState> getValidationStateWhenEmpty() {
|
||||
return validationStateWhenEmpty;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue