From 069c2ceddf886135e7f86edff3673535c16f6dd9 Mon Sep 17 00:00:00 2001 From: Gavriil Sitnikov Date: Tue, 24 Jan 2017 20:32:01 +0300 Subject: [PATCH] validation initial logic added --- .../BooleanValidationController.java | 51 +++++++ .../validation/ConversionException.java | 27 ++++ .../EditTextValidationController.java | 78 +++++++++++ .../validation/EditTextValidator.java | 126 ++++++++++++++++++ .../validation/SameTypeValidator.java | 17 +++ .../SimpleValidationController.java | 28 ++++ .../TwoWayValidationController.java | 70 ++++++++++ .../validation/ValidationController.java | 42 ++++++ .../templates/validation/ValidationState.java | 50 +++++++ .../templates/validation/Validator.java | 63 +++++++++ .../templates/validation/ViewWithError.java | 34 +++++ 11 files changed, 586 insertions(+) create mode 100644 src/main/java/ru/touchin/templates/validation/BooleanValidationController.java create mode 100644 src/main/java/ru/touchin/templates/validation/ConversionException.java create mode 100644 src/main/java/ru/touchin/templates/validation/EditTextValidationController.java create mode 100644 src/main/java/ru/touchin/templates/validation/EditTextValidator.java create mode 100644 src/main/java/ru/touchin/templates/validation/SameTypeValidator.java create mode 100644 src/main/java/ru/touchin/templates/validation/SimpleValidationController.java create mode 100644 src/main/java/ru/touchin/templates/validation/TwoWayValidationController.java create mode 100644 src/main/java/ru/touchin/templates/validation/ValidationController.java create mode 100644 src/main/java/ru/touchin/templates/validation/ValidationState.java create mode 100644 src/main/java/ru/touchin/templates/validation/Validator.java create mode 100644 src/main/java/ru/touchin/templates/validation/ViewWithError.java diff --git a/src/main/java/ru/touchin/templates/validation/BooleanValidationController.java b/src/main/java/ru/touchin/templates/validation/BooleanValidationController.java new file mode 100644 index 0000000..903484c --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/BooleanValidationController.java @@ -0,0 +1,51 @@ +/* + * 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; + +import rx.Observable; + +/** + * Created by Ilia Kurtov on 24/01/2017. + * TODO: fill + */ +public class BooleanValidationController extends TwoWayValidationController> { + + public BooleanValidationController(@NonNull final SameTypeValidator validationWrapper) { + super(validationWrapper); + } + + @NonNull + public Observable validation(@NonNull final Observable activatedObservable) { + return Observable.combineLatest(activatedObservable, getValidationWrapper().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 -> getValidationWrapper().getValidationState().set(validationState)); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/ConversionException.java b/src/main/java/ru/touchin/templates/validation/ConversionException.java new file mode 100644 index 0000000..e705851 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/ConversionException.java @@ -0,0 +1,27 @@ +/* + * 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; + +/** + * Created by Ilia Kurtov on 24/01/2017. + * TODO: fill + */ +public class ConversionException extends Exception { +} diff --git a/src/main/java/ru/touchin/templates/validation/EditTextValidationController.java b/src/main/java/ru/touchin/templates/validation/EditTextValidationController.java new file mode 100644 index 0000000..62ea68b --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/EditTextValidationController.java @@ -0,0 +1,78 @@ +/* + * 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; +import android.text.TextUtils; + +import java.io.Serializable; + +import ru.touchin.roboswag.core.utils.pairs.NonNullPair; +import rx.Observable; + +/** + * Created by Ilia Kurtov on 24/01/2017. + * TODO: fill + */ +public class EditTextValidationController + extends TwoWayValidationController> { + + public EditTextValidationController(@NonNull final EditTextValidator validationWrapper) { + super(validationWrapper); + } + + @NonNull + public Observable validation(@NonNull final Observable focusOutObservable, @NonNull final Observable activatedObservable) { + return Observable + .>>combineLatest(activatedObservable, + getValidationWrapper().getWrapperModel().observe(), + focusOutObservable, + getValidationWrapper().getShowFullCheck().observe(), + (activated, text, focusIn, 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) + ? getValidationWrapper().getValidationStateWhenEmpty().observe() + .map(state -> state != null ? state : ValidationState.ERROR_NO_DESCRIPTION) + : Observable.just(ValidationState.INITIAL)); + } + if (!showError && focus) { + return new NonNullPair<>(true, getValidationWrapper().primaryValidate(text)); + } + return new NonNullPair<>(focus, getValidationWrapper().fullValidate(text)); + }) + .switchMap(validationPair -> { + if (validationPair == null) { + return Observable.empty(); + } + return validationPair.getSecond() + .doOnNext(validationState -> { + if (!validationPair.getFirst()) { + getValidationWrapper().getShowFullCheck().set(validationState != ValidationState.VALID); + } + getValidationWrapper().getValidationState().set(validationState); + }); + }); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/EditTextValidator.java b/src/main/java/ru/touchin/templates/validation/EditTextValidator.java new file mode 100644 index 0000000..20425a8 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/EditTextValidator.java @@ -0,0 +1,126 @@ +/* + * 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; +import android.support.annotation.Nullable; + +import java.io.Serializable; + +import ru.touchin.roboswag.core.log.Lc; +import ru.touchin.roboswag.core.observables.Changeable; +import ru.touchin.roboswag.core.observables.NonNullChangeable; +import ru.touchin.roboswag.core.utils.pairs.HalfNullablePair; +import rx.Observable; +import rx.exceptions.OnErrorThrowable; +import rx.functions.Func1; +import rx.schedulers.Schedulers; + +/** + * Created by Ilia Kurtov on 24/01/2017. + * TODO: fill + */ +public abstract class EditTextValidator extends Validator { + + @NonNull + private final NonNullChangeable showFullCheck = new NonNullChangeable<>(false); + @NonNull + private final Changeable>> finalCheck = new Changeable<>(null); + @NonNull + private final Changeable>> primaryCheck = new Changeable<>(null); + + @NonNull + public NonNullChangeable getShowFullCheck() { + return showFullCheck; + } + + @Nullable + public Changeable>> getFinalCheck() { + return finalCheck; + } + + @Nullable + public Changeable>> getPrimaryCheck() { + return primaryCheck; + } + + @NonNull + private HalfNullablePair validateText( + @Nullable final Func1> finalCheck, + @Nullable final Func1> primaryCheck, + @NonNull final String text, final boolean fullCheck) + throws ConversionException { + if (primaryCheck == null && finalCheck == null) { + return new HalfNullablePair<>(ValidationState.VALID, convertWrapperModelToModel(text)); + } + + if (primaryCheck != null) { + final HalfNullablePair 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 + private Observable> 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 ConversionException exception) { + throw OnErrorThrowable.from(exception); + } + }); + } + + @NonNull + private Observable processChecks(@NonNull final String text, final boolean fullCheck) { + return createValidationObservable(text, fullCheck) + .map(HalfNullablePair::getFirst) + .onErrorResumeNext(throwable -> { + if (throwable instanceof ConversionException || throwable.getCause() instanceof ConversionException) { + Lc.assertion(throwable); + return Observable.just(ValidationState.ERROR_CONVERSION); + } + return Observable.error(throwable); + }); + } + + @NonNull + public Observable primaryValidate(@NonNull final String text) { + return processChecks(text, false); + } + + @NonNull + public Observable fullValidate(@NonNull final String text) { + return processChecks(text, true); + } + + @NonNull + public Observable> fullValidateAndGetModel(@NonNull final String text) { + return createValidationObservable(text, true); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/SameTypeValidator.java b/src/main/java/ru/touchin/templates/validation/SameTypeValidator.java new file mode 100644 index 0000000..f2af062 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/SameTypeValidator.java @@ -0,0 +1,17 @@ +package ru.touchin.templates.validation; + + +import android.support.annotation.NonNull; + +import java.io.Serializable; + +public class SameTypeValidator extends Validator { + + @NonNull + @Override + protected TModel convertWrapperModelToModel(@NonNull final TModel wrapperModel) + throws ConversionException { + return wrapperModel; + } + +} diff --git a/src/main/java/ru/touchin/templates/validation/SimpleValidationController.java b/src/main/java/ru/touchin/templates/validation/SimpleValidationController.java new file mode 100644 index 0000000..9262e11 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/SimpleValidationController.java @@ -0,0 +1,28 @@ +package ru.touchin.templates.validation; + +import android.support.annotation.NonNull; + +import java.io.Serializable; + +import rx.Observable; + +public class SimpleValidationController> + extends TwoWayValidationController { + + public SimpleValidationController(@NonNull final TValidationWrapper validationWrapper) { + super(validationWrapper); + } + + @NonNull + public Observable validation(@NonNull final Observable activatedObservable) { + return Observable.combineLatest(activatedObservable, + getValidationWrapper().getWrapperModel().observe(), (activated, model) -> { + if (model == null) { + return activated ? ValidationState.ERROR_NO_DESCRIPTION : ValidationState.INITIAL; + } + return ValidationState.VALID; + }) + .doOnNext(validationState -> getValidationWrapper().getValidationState().set(validationState)); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/TwoWayValidationController.java b/src/main/java/ru/touchin/templates/validation/TwoWayValidationController.java new file mode 100644 index 0000000..cc5a735 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/TwoWayValidationController.java @@ -0,0 +1,70 @@ +/* + * 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; +import android.support.annotation.Nullable; + +import java.io.Serializable; + +import rx.Observable; +import rx.android.schedulers.AndroidSchedulers; +import rx.functions.Action1; + +/** + * Created by Ilia Kurtov on 24/01/2017. + * TODO: fill + */ +public abstract class TwoWayValidationController + > + extends ValidationController { + + public TwoWayValidationController(@NonNull final TValidationWrapper validationWrapper) { + super(validationWrapper); + } + + @NonNull + public Observable modelAndViewUpdating(@Nullable final Observable viewStateObservable, + @NonNull final Action1 updateViewAction, + @NonNull final ViewWithError viewWithError) { + final Observable stateObservable = viewStateObservable != null + ? viewStateObservable.doOnNext(flag -> getValidationWrapper().getWrapperModel().set(flag)) + : Observable.empty(); + return Observable + .merge(getValidationWrapper().getWrapperModel().observe() + .observeOn(AndroidSchedulers.mainThread()) + .doOnNext(updateViewAction), + getValidationWrapper().getValidationState().observe() + .observeOn(AndroidSchedulers.mainThread()) + .doOnNext(validationState -> { + if (!showError(validationState)) { + viewWithError.hideError(); + } else { + viewWithError.showError(validationState); + } + }), + stateObservable); + } + + protected boolean showError(@NonNull final ValidationState validationState) { + return validationState != ValidationState.VALID && validationState != ValidationState.INITIAL; + } + +} diff --git a/src/main/java/ru/touchin/templates/validation/ValidationController.java b/src/main/java/ru/touchin/templates/validation/ValidationController.java new file mode 100644 index 0000000..c543813 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/ValidationController.java @@ -0,0 +1,42 @@ +/* + * 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. + * TODO: fill + */ +public abstract class ValidationController { + + @NonNull + private final TWrapper validationWrapper; + + public ValidationController(@NonNull final TWrapper validationWrapper) { + this.validationWrapper = validationWrapper; + } + + @NonNull + public TWrapper getValidationWrapper() { + return validationWrapper; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/ValidationState.java b/src/main/java/ru/touchin/templates/validation/ValidationState.java new file mode 100644 index 0000000..6cb01a9 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/ValidationState.java @@ -0,0 +1,50 @@ +/* + * 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. + * TODO: fill + */ +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(); + /** + * Valid state. + */ + public static final ValidationState VALID = new ValidationState(); + /** + * 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(); + /** + * 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(); + +} diff --git a/src/main/java/ru/touchin/templates/validation/Validator.java b/src/main/java/ru/touchin/templates/validation/Validator.java new file mode 100644 index 0000000..59e7f4d --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/Validator.java @@ -0,0 +1,63 @@ +/* + * 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; + +import java.io.Serializable; + +import ru.touchin.roboswag.core.observables.Changeable; +import ru.touchin.roboswag.core.observables.NonNullChangeable; + +/** + * Created by Ilia Kurtov on 24/01/2017. + * TODO: fill + */ +public abstract class Validator + implements Serializable { + + private static final long serialVersionUID = 1L; + + @NonNull + private final NonNullChangeable validationState = new NonNullChangeable<>(ValidationState.INITIAL); + @NonNull + private final Changeable wrapperModel = new Changeable<>(null); + @NonNull + private final Changeable validationStateWhenEmpty = new Changeable<>(null); + + @NonNull + protected abstract TModel convertWrapperModelToModel(@NonNull final TWrapperModel wrapperModel) throws ConversionException; + + @NonNull + public Changeable getWrapperModel() { + return wrapperModel; + } + + @NonNull + public NonNullChangeable getValidationState() { + return validationState; + } + + @NonNull + public Changeable getValidationStateWhenEmpty() { + return validationStateWhenEmpty; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/ViewWithError.java b/src/main/java/ru/touchin/templates/validation/ViewWithError.java new file mode 100644 index 0000000..937e6a6 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/ViewWithError.java @@ -0,0 +1,34 @@ +/* + * 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. + * TODO: fill + */ +public interface ViewWithError { + + void hideError(); + + void showError(@NonNull ValidationState validationState); + +}