diff --git a/src/main/java/ru/touchin/templates/validation/ValidationFunc.java b/src/main/java/ru/touchin/templates/validation/ValidationFunc.java new file mode 100644 index 0000000..02a1e82 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/ValidationFunc.java @@ -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 input type. + * @param return type. + */ +public interface ValidationFunc 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; + +} \ 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..2adb29e --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/ValidationState.java @@ -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; + } + +} \ 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..a4bc14a --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/ViewWithError.java @@ -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); + +} diff --git a/src/main/java/ru/touchin/templates/validation/validationcontrollers/BooleanValidationController.java b/src/main/java/ru/touchin/templates/validation/validationcontrollers/BooleanValidationController.java new file mode 100644 index 0000000..5d27370 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/validationcontrollers/BooleanValidationController.java @@ -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> { + + public BooleanValidationController(@NonNull final SameTypeValidator 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 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)); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/validationcontrollers/EditTextValidationController.java b/src/main/java/ru/touchin/templates/validation/validationcontrollers/EditTextValidationController.java new file mode 100644 index 0000000..c8da94b --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/validationcontrollers/EditTextValidationController.java @@ -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 + extends ValidationController> { + + public EditTextValidationController(@NonNull final EditTextValidator 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 focusOutObservable, @NonNull final Observable 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> 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)); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/validationcontrollers/SimpleValidationController.java b/src/main/java/ru/touchin/templates/validation/validationcontrollers/SimpleValidationController.java new file mode 100644 index 0000000..d8e9da9 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/validationcontrollers/SimpleValidationController.java @@ -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 type of the model. + * @param corresponding {@link Validator} + */ +public class SimpleValidationController> + extends ValidationController { + + 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 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)); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/validationcontrollers/ValidationController.java b/src/main/java/ru/touchin/templates/validation/validationcontrollers/ValidationController.java new file mode 100644 index 0000000..7f209fb --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/validationcontrollers/ValidationController.java @@ -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 + > { + + @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 viewStateObservable, + @NonNull final Action1 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); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/validators/EditTextValidator.java b/src/main/java/ru/touchin/templates/validation/validators/EditTextValidator.java new file mode 100644 index 0000000..46fc435 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/validators/EditTextValidator.java @@ -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 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); + + /** + * 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 getShowFullCheck() { + return showFullCheck; + } + + /** + * Use this method to get or set final check. + * @return final check. + */ + @NonNull + protected Changeable>> getFinalCheck() { + return finalCheck; + } + + /** + * Use this method to get or set primary check. + * @return primary check. + */ + @NonNull + protected Changeable>> getPrimaryCheck() { + return primaryCheck; + } + + @NonNull + private HalfNullablePair validateText( + @Nullable final ValidationFunc> finalCheck, + @Nullable final ValidationFunc> 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 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> 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 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 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 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> fullValidateAndGetModel(@NonNull final String text) { + return createValidationObservable(text, true) + .first(); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/touchin/templates/validation/validators/SameTypeValidator.java b/src/main/java/ru/touchin/templates/validation/validators/SameTypeValidator.java new file mode 100644 index 0000000..6921c07 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/validators/SameTypeValidator.java @@ -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 model that should be bounded with a view. + */ +public class SameTypeValidator extends Validator { + + /** + * 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; + } + +} diff --git a/src/main/java/ru/touchin/templates/validation/validators/Validator.java b/src/main/java/ru/touchin/templates/validation/validators/Validator.java new file mode 100644 index 0000000..f255f36 --- /dev/null +++ b/src/main/java/ru/touchin/templates/validation/validators/Validator.java @@ -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 + 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 NonNullChangeable 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 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 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 getValidationStateWhenEmpty() { + return validationStateWhenEmpty; + } + +} \ No newline at end of file