// Path: droidux/src/main/java/info/izumin/android/droidux/UndoableState.java
// public interface UndoableState<T> extends Cloneable {
//     T clone();
// }
// 
// Path: droidux-processor/src/main/java/info/izumin/android/droidux/processor/exception/InvalidReducerDeclarationException.java
// public class InvalidReducerDeclarationException extends RuntimeException {
//     public InvalidReducerDeclarationException(String message) {
//         super(message);
//     }
// }
// 
// Path: droidux-processor/src/main/java/info/izumin/android/droidux/processor/model/ReducerModel.java
// public class ReducerModel {
//     public static final String TAG = ReducerModel.class.getSimpleName();
// 
//     public static final String CLASS_NAME_SUFFIX = "Reducer";
// 
//     private final TypeElement element;
//     private final TypeElement stateElement;
//     private final ClassName state;
//     private final ClassName className;
// 
//     private final String qualifiedName;
//     private final String packageName;
//     private final String variableName;
//     private String stateName;
//     private String stateVariableName;
// 
//     private final boolean isUndoable;
// 
//     private List<DispatchableModel> dispatchableModels;
// 
//     public ReducerModel(TypeElement element) {
//         this.element = element;
//         this.stateElement = asTypeElement(getTypeFromAnnotation(element, Reducer.class, "value"));
//         this.state = ClassName.get(stateElement);
//         this.stateName = state.simpleName();
//         this.stateVariableName = StringUtils.getLowerCamelFromUpperCamel(stateName);
// 
//         this.isUndoable = element.getAnnotation(Undoable.class) != null;
// 
//         this.className = ClassName.get(element);
// 
//         this.qualifiedName = element.getQualifiedName().toString();
//         this.packageName = StringUtils.getPackageName(qualifiedName);
//         this.variableName = StringUtils.getLowerCamelFromUpperCamel(className.simpleName());
// 
//         this.dispatchableModels = new ArrayList<>();
//         for (ExecutableElement el : findMethodsByAnnotation(element, Dispatchable.class)) {
//             dispatchableModels.add(new DispatchableModel(el, this));
//         }
// 
//         ReducerValidator.validate(this);
//     }
// 
//     public TypeElement getElement() {
//         return element;
//     }
// 
//     public TypeElement getStateElement() {
//         return stateElement;
//     }
// 
//     public ClassName getState() {
//         return state;
//     }
// 
//     public ClassName getClassName() {
//         return className;
//     }
// 
//     public String getQualifiedName() {
//         return qualifiedName;
//     }
// 
//     public String getPackageName() {
//         return packageName;
//     }
// 
//     public String getVariableName() {
//         return variableName;
//     }
// 
//     public String getStateName() {
//         return stateName;
//     }
// 
//     public String getStateVariableName() {
//         return stateVariableName;
//     }
// 
//     public List<DispatchableModel> getDispatchableModels() {
//         return dispatchableModels;
//     }
// 
//     public boolean isUndoable() {
//         return isUndoable;
//     }
// }


import com.google.auto.common.MoreTypes;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import info.izumin.android.droidux.UndoableState;
import info.izumin.android.droidux.processor.exception.InvalidReducerDeclarationException;
import info.izumin.android.droidux.processor.model.ReducerModel;
package info.izumin.android.droidux.processor.validator;




/**
 * Created by izumin on 11/29/15.
 */
public final class ReducerValidator {
    private ReducerValidator() {
        throw new AssertionError("constructor of the utility class should not be called");
    }

    public static void validate(ReducerModel model) {
        if (!isValidClassName(model)) {
            throw new InvalidReducerDeclarationException(
                    "@Reducer class name must end with \"" + ReducerModel.CLASS_NAME_SUFFIX + "\". "
                            + "\"" + model.getClassName().simpleName() + "\" has invalid class name."
            );
        }

        if (model.isUndoable() && !hasUndoableState(model.getStateElement())) {
            throw new InvalidReducerDeclarationException(
                    "@Reducer class annotated with @Undoable must have the state implements \"UndoableState<T>\". "
                            + model.getState().simpleName() + " state of " + model.getClassName().simpleName() + " does not implement it."
            );
        }
    }

// Path: droidux/src/main/java/info/izumin/android/droidux/UndoableState.java
// public interface UndoableState<T> extends Cloneable {
//     T clone();
// }
// 
// Path: droidux-processor/src/main/java/info/izumin/android/droidux/processor/exception/InvalidReducerDeclarationException.java
// public class InvalidReducerDeclarationException extends RuntimeException {
//     public InvalidReducerDeclarationException(String message) {
//         super(message);
//     }
// }
// 
// Path: droidux-processor/src/main/java/info/izumin/android/droidux/processor/model/ReducerModel.java
// public class ReducerModel {
//     public static final String TAG = ReducerModel.class.getSimpleName();
// 
//     public static final String CLASS_NAME_SUFFIX = "Reducer";
// 
//     private final TypeElement element;
//     private final TypeElement stateElement;
//     private final ClassName state;
//     private final ClassName className;
// 
//     private final String qualifiedName;
//     private final String packageName;
//     private final String variableName;
//     private String stateName;
//     private String stateVariableName;
// 
//     private final boolean isUndoable;
// 
//     private List<DispatchableModel> dispatchableModels;
// 
//     public ReducerModel(TypeElement element) {
//         this.element = element;
//         this.stateElement = asTypeElement(getTypeFromAnnotation(element, Reducer.class, "value"));
//         this.state = ClassName.get(stateElement);
//         this.stateName = state.simpleName();
//         this.stateVariableName = StringUtils.getLowerCamelFromUpperCamel(stateName);
// 
//         this.isUndoable = element.getAnnotation(Undoable.class) != null;
// 
//         this.className = ClassName.get(element);
// 
//         this.qualifiedName = element.getQualifiedName().toString();
//         this.packageName = StringUtils.getPackageName(qualifiedName);
//         this.variableName = StringUtils.getLowerCamelFromUpperCamel(className.simpleName());
// 
//         this.dispatchableModels = new ArrayList<>();
//         for (ExecutableElement el : findMethodsByAnnotation(element, Dispatchable.class)) {
//             dispatchableModels.add(new DispatchableModel(el, this));
//         }
// 
//         ReducerValidator.validate(this);
//     }
// 
//     public TypeElement getElement() {
//         return element;
//     }
// 
//     public TypeElement getStateElement() {
//         return stateElement;
//     }
// 
//     public ClassName getState() {
//         return state;
//     }
// 
//     public ClassName getClassName() {
//         return className;
//     }
// 
//     public String getQualifiedName() {
//         return qualifiedName;
//     }
// 
//     public String getPackageName() {
//         return packageName;
//     }
// 
//     public String getVariableName() {
//         return variableName;
//     }
// 
//     public String getStateName() {
//         return stateName;
//     }
// 
//     public String getStateVariableName() {
//         return stateVariableName;
//     }
// 
//     public List<DispatchableModel> getDispatchableModels() {
//         return dispatchableModels;
//     }
// 
//     public boolean isUndoable() {
//         return isUndoable;
//     }
// }


// Path: droidux-processor/src/main/java/info/izumin/android/droidux/processor/validator/ReducerValidator.java
import com.google.auto.common.MoreTypes;
import com.google.common.base.Function;
import com.google.common.collect.FluentIterable;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeMirror;
import info.izumin.android.droidux.UndoableState;
import info.izumin.android.droidux.processor.exception.InvalidReducerDeclarationException;
import info.izumin.android.droidux.processor.model.ReducerModel;

package info.izumin.android.droidux.processor.validator;




/**
 * Created by izumin on 11/29/15.
 */
public final class ReducerValidator {
    private ReducerValidator() {
        throw new AssertionError("constructor of the utility class should not be called");
    }

    public static void validate(ReducerModel model) {
        if (!isValidClassName(model)) {
            throw new InvalidReducerDeclarationException(
                    "@Reducer class name must end with \"" + ReducerModel.CLASS_NAME_SUFFIX + "\". "
                            + "\"" + model.getClassName().simpleName() + "\" has invalid class name."
            );
        }

        if (model.isUndoable() && !hasUndoableState(model.getStateElement())) {
            throw new InvalidReducerDeclarationException(
                    "@Reducer class annotated with @Undoable must have the state implements \"UndoableState<T>\". "
                            + model.getState().simpleName() + " state of " + model.getClassName().simpleName() + " does not implement it."
            );
        }
    }

    private static boolean isValidClassName(ReducerModel model) {