validation initial logic added

This commit is contained in:
Gavriil Sitnikov 2017-01-24 20:32:01 +03:00
parent 549d9a7761
commit 069c2ceddf
11 changed files with 586 additions and 0 deletions

View File

@ -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<Boolean, Boolean, SameTypeValidator<Boolean>> {
public BooleanValidationController(@NonNull final SameTypeValidator<Boolean> validationWrapper) {
super(validationWrapper);
}
@NonNull
public Observable<?> validation(@NonNull final Observable<Boolean> 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));
}
}

View File

@ -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 {
}

View File

@ -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<TModel extends Serializable>
extends TwoWayValidationController<String, TModel, EditTextValidator<TModel>> {
public EditTextValidationController(@NonNull final EditTextValidator<TModel> validationWrapper) {
super(validationWrapper);
}
@NonNull
public Observable<?> validation(@NonNull final Observable<Boolean> focusOutObservable, @NonNull final Observable<Boolean> activatedObservable) {
return Observable
.<Boolean, String, Boolean, Boolean, NonNullPair<Boolean, Observable<ValidationState>>>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);
});
});
}
}

View File

@ -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<TModel extends Serializable> extends Validator<String, TModel> {
@NonNull
private final NonNullChangeable<Boolean> showFullCheck = new NonNullChangeable<>(false);
@NonNull
private final Changeable<Func1<TModel, HalfNullablePair<ValidationState, TModel>>> finalCheck = new Changeable<>(null);
@NonNull
private final Changeable<Func1<String, HalfNullablePair<ValidationState, TModel>>> primaryCheck = new Changeable<>(null);
@NonNull
public NonNullChangeable<Boolean> getShowFullCheck() {
return showFullCheck;
}
@Nullable
public Changeable<Func1<TModel, HalfNullablePair<ValidationState, TModel>>> getFinalCheck() {
return finalCheck;
}
@Nullable
public Changeable<Func1<String, HalfNullablePair<ValidationState, TModel>>> getPrimaryCheck() {
return primaryCheck;
}
@NonNull
private HalfNullablePair<ValidationState, TModel> validateText(
@Nullable final Func1<TModel, HalfNullablePair<ValidationState, TModel>> finalCheck,
@Nullable final Func1<String, HalfNullablePair<ValidationState, TModel>> 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<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
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 ConversionException exception) {
throw OnErrorThrowable.from(exception);
}
});
}
@NonNull
private Observable<ValidationState> 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<ValidationState> primaryValidate(@NonNull final String text) {
return processChecks(text, false);
}
@NonNull
public Observable<ValidationState> fullValidate(@NonNull final String text) {
return processChecks(text, true);
}
@NonNull
public Observable<HalfNullablePair<ValidationState, TModel>> fullValidateAndGetModel(@NonNull final String text) {
return createValidationObservable(text, true);
}
}

View File

@ -0,0 +1,17 @@
package ru.touchin.templates.validation;
import android.support.annotation.NonNull;
import java.io.Serializable;
public class SameTypeValidator<TModel extends Serializable> extends Validator<TModel, TModel> {
@NonNull
@Override
protected TModel convertWrapperModelToModel(@NonNull final TModel wrapperModel)
throws ConversionException {
return wrapperModel;
}
}

View File

@ -0,0 +1,28 @@
package ru.touchin.templates.validation;
import android.support.annotation.NonNull;
import java.io.Serializable;
import rx.Observable;
public class SimpleValidationController<TModel extends Serializable, TValidationWrapper extends Validator<TModel, TModel>>
extends TwoWayValidationController<TModel, TModel, TValidationWrapper> {
public SimpleValidationController(@NonNull final TValidationWrapper validationWrapper) {
super(validationWrapper);
}
@NonNull
public Observable<?> validation(@NonNull final Observable<Boolean> 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));
}
}

View File

@ -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
<TWrapperModel extends Serializable, TModel extends Serializable, TValidationWrapper extends Validator<TWrapperModel, TModel>>
extends ValidationController<TValidationWrapper> {
public TwoWayValidationController(@NonNull final TValidationWrapper validationWrapper) {
super(validationWrapper);
}
@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 -> 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;
}
}

View File

@ -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<TWrapper extends Validator> {
@NonNull
private final TWrapper validationWrapper;
public ValidationController(@NonNull final TWrapper validationWrapper) {
this.validationWrapper = validationWrapper;
}
@NonNull
public TWrapper getValidationWrapper() {
return validationWrapper;
}
}

View File

@ -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();
}

View File

@ -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<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 Changeable<ValidationState> validationStateWhenEmpty = new Changeable<>(null);
@NonNull
protected abstract TModel convertWrapperModelToModel(@NonNull final TWrapperModel wrapperModel) throws ConversionException;
@NonNull
public Changeable<TWrapperModel> getWrapperModel() {
return wrapperModel;
}
@NonNull
public NonNullChangeable<ValidationState> getValidationState() {
return validationState;
}
@NonNull
public Changeable<ValidationState> getValidationStateWhenEmpty() {
return validationStateWhenEmpty;
}
}

View File

@ -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);
}