// Path: processor/src/main/java/net/yslibrary/simplepreferences/processor/exception/ProcessingException.java
// public class ProcessingException extends Exception {
// 
//   public final Element element;
// 
//   public ProcessingException(Element element, String message, Object... args) {
//     super(String.format(message, args));
// 
//     this.element = element;
//   }
// }
// 
// Path: processor/src/main/java/net/yslibrary/simplepreferences/processor/writer/PreferenceWriter.java
// public class PreferenceWriter {
// 
//   private final PreferenceAnnotatedClass annotatedClass;
// 
//   public PreferenceWriter(PreferenceAnnotatedClass annotatedClass) {
//     this.annotatedClass = annotatedClass;
//   }
// 
//   public TypeSpec write() {
//     boolean useDefaultPreferences = annotatedClass.useDefaultPreferences();
//     String preferenceName = annotatedClass.preferenceName;
//     String preferenceClassName = annotatedClass.preferenceClassName;
//     String packageName = annotatedClass.packageName;
//     TypeElement annotatedElement = annotatedClass.annotatedElement;
// 
//     TypeSpec.Builder classBuilder = TypeSpec.classBuilder(preferenceClassName)
//         .superclass(ClassName.get(annotatedElement));
// 
//     if (annotatedClass.shouldBeExposed) {
//       classBuilder.addModifiers(Modifier.PUBLIC);
//     }
// 
//     TypeName generatingClass = ClassName.get(packageName, preferenceClassName);
// 
//     // SharedPreferences field ---
//     FieldSpec prefsField =
//         FieldSpec.builder(SharedPreferences.class, "prefs", Modifier.PRIVATE, Modifier.FINAL)
//             .build();
//     classBuilder.addField(prefsField);
// 
//     // constructor
//     classBuilder.addMethod(writeConstructor(preferenceName, useDefaultPreferences));
// 
//     // create method
//     classBuilder.addMethod(writeFactory(generatingClass));
// 
//     // clear method
//     classBuilder.addMethod(writeClear());
// 
//     if (annotatedClass.needCommitMethodForClear) {
//       classBuilder.addMethod(writeClearWithCommit());
//     }
// 
//     // keys
//     annotatedClass.keys.forEach(keyAnnotatedField -> {
//       List<MethodSpec> methods =
//           TypeWriter.create(generatingClass, keyAnnotatedField).writeMethods(prefsField);
// 
//       classBuilder.addMethods(methods);
//     });
// 
//     return classBuilder.build();
//   }
// 
// 
//   private MethodSpec writeConstructor(String preferenceName, boolean useDefaultPreferences) {
//     MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
//         .addModifiers(Modifier.PRIVATE)
//         .addParameter(
//             ParameterSpec.builder(Context.class, "context").addAnnotation(NonNull.class).build());
// 
//     if (useDefaultPreferences) {
//       constructorBuilder.addStatement(
//           "prefs = $T.getDefaultSharedPreferences(context.getApplicationContext())",
//           PreferenceManager.class);
//     } else {
//       constructorBuilder.addStatement(
//           "prefs = context.getApplicationContext().getSharedPreferences($S, Context.MODE_PRIVATE)",
//           preferenceName);
//     }
// 
//     return constructorBuilder.build();
//   }
// 
//   private MethodSpec writeFactory(TypeName generatingClass) {
//     MethodSpec.Builder createMethod = MethodSpec.methodBuilder("create")
//         .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
//         .addParameter(
//             ParameterSpec.builder(Context.class, "context").addAnnotation(NonNull.class).build())
//         .returns(generatingClass);
// 
//     createMethod.beginControlFlow("if (context == null)")
//         .addStatement("throw new NullPointerException($S)", "Context is Null!")
//         .endControlFlow();
//     createMethod.addStatement("return new $T(context)", generatingClass);
// 
//     return createMethod.build();
//   }
// 
//   private MethodSpec writeClear() {
//     MethodSpec.Builder clearMethod = MethodSpec.methodBuilder("clear")
//         .addModifiers(Modifier.PUBLIC);
//     clearMethod.addStatement("prefs.edit().clear().apply()");
// 
//     return clearMethod.build();
//   }
// 
//   private MethodSpec writeClearWithCommit() {
//     MethodSpec.Builder clearWithCommitMethod = MethodSpec.methodBuilder("clearWithCommit")
//         .addModifiers(Modifier.PUBLIC)
//         .addStatement("prefs.edit().clear().commit()");
// 
//     return clearWithCommitMethod.build();
//   }
// }


import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import net.yslibrary.simplepreferences.annotation.Preferences;
import net.yslibrary.simplepreferences.processor.exception.ProcessingException;
import net.yslibrary.simplepreferences.processor.writer.PreferenceWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
        return;
      }

      try {
        TypeElement typeElement = (TypeElement) element;

        PreferenceAnnotatedClass model = new PreferenceAnnotatedClass(typeElement, elementUtils);

        checkValid(model);

        PreferenceAnnotatedClass existing = items.get(model.preferenceName);

        if (existing != null) {
          throw new ProcessingException(typeElement,
              "Conflict: Class %s is annotated with @%s with value='%s', but %s already uses same value",
              model.annotatedElement, Preferences.class.getSimpleName(), model.preferenceName,
              existing.annotatedElement.getQualifiedName().toString());
        }

        items.put(model.preferenceName, model);
      } catch (ProcessingException e) {
        error(e.element, e.getMessage());
      } catch (IllegalStateException e) {
        error(null, e.getMessage());
      }
    });

    // generate
    items.forEach((key, preferenceAnnotatedClass) -> {
      try {
// Path: processor/src/main/java/net/yslibrary/simplepreferences/processor/exception/ProcessingException.java
// public class ProcessingException extends Exception {
// 
//   public final Element element;
// 
//   public ProcessingException(Element element, String message, Object... args) {
//     super(String.format(message, args));
// 
//     this.element = element;
//   }
// }
// 
// Path: processor/src/main/java/net/yslibrary/simplepreferences/processor/writer/PreferenceWriter.java
// public class PreferenceWriter {
// 
//   private final PreferenceAnnotatedClass annotatedClass;
// 
//   public PreferenceWriter(PreferenceAnnotatedClass annotatedClass) {
//     this.annotatedClass = annotatedClass;
//   }
// 
//   public TypeSpec write() {
//     boolean useDefaultPreferences = annotatedClass.useDefaultPreferences();
//     String preferenceName = annotatedClass.preferenceName;
//     String preferenceClassName = annotatedClass.preferenceClassName;
//     String packageName = annotatedClass.packageName;
//     TypeElement annotatedElement = annotatedClass.annotatedElement;
// 
//     TypeSpec.Builder classBuilder = TypeSpec.classBuilder(preferenceClassName)
//         .superclass(ClassName.get(annotatedElement));
// 
//     if (annotatedClass.shouldBeExposed) {
//       classBuilder.addModifiers(Modifier.PUBLIC);
//     }
// 
//     TypeName generatingClass = ClassName.get(packageName, preferenceClassName);
// 
//     // SharedPreferences field ---
//     FieldSpec prefsField =
//         FieldSpec.builder(SharedPreferences.class, "prefs", Modifier.PRIVATE, Modifier.FINAL)
//             .build();
//     classBuilder.addField(prefsField);
// 
//     // constructor
//     classBuilder.addMethod(writeConstructor(preferenceName, useDefaultPreferences));
// 
//     // create method
//     classBuilder.addMethod(writeFactory(generatingClass));
// 
//     // clear method
//     classBuilder.addMethod(writeClear());
// 
//     if (annotatedClass.needCommitMethodForClear) {
//       classBuilder.addMethod(writeClearWithCommit());
//     }
// 
//     // keys
//     annotatedClass.keys.forEach(keyAnnotatedField -> {
//       List<MethodSpec> methods =
//           TypeWriter.create(generatingClass, keyAnnotatedField).writeMethods(prefsField);
// 
//       classBuilder.addMethods(methods);
//     });
// 
//     return classBuilder.build();
//   }
// 
// 
//   private MethodSpec writeConstructor(String preferenceName, boolean useDefaultPreferences) {
//     MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
//         .addModifiers(Modifier.PRIVATE)
//         .addParameter(
//             ParameterSpec.builder(Context.class, "context").addAnnotation(NonNull.class).build());
// 
//     if (useDefaultPreferences) {
//       constructorBuilder.addStatement(
//           "prefs = $T.getDefaultSharedPreferences(context.getApplicationContext())",
//           PreferenceManager.class);
//     } else {
//       constructorBuilder.addStatement(
//           "prefs = context.getApplicationContext().getSharedPreferences($S, Context.MODE_PRIVATE)",
//           preferenceName);
//     }
// 
//     return constructorBuilder.build();
//   }
// 
//   private MethodSpec writeFactory(TypeName generatingClass) {
//     MethodSpec.Builder createMethod = MethodSpec.methodBuilder("create")
//         .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
//         .addParameter(
//             ParameterSpec.builder(Context.class, "context").addAnnotation(NonNull.class).build())
//         .returns(generatingClass);
// 
//     createMethod.beginControlFlow("if (context == null)")
//         .addStatement("throw new NullPointerException($S)", "Context is Null!")
//         .endControlFlow();
//     createMethod.addStatement("return new $T(context)", generatingClass);
// 
//     return createMethod.build();
//   }
// 
//   private MethodSpec writeClear() {
//     MethodSpec.Builder clearMethod = MethodSpec.methodBuilder("clear")
//         .addModifiers(Modifier.PUBLIC);
//     clearMethod.addStatement("prefs.edit().clear().apply()");
// 
//     return clearMethod.build();
//   }
// 
//   private MethodSpec writeClearWithCommit() {
//     MethodSpec.Builder clearWithCommitMethod = MethodSpec.methodBuilder("clearWithCommit")
//         .addModifiers(Modifier.PUBLIC)
//         .addStatement("prefs.edit().clear().commit()");
// 
//     return clearWithCommitMethod.build();
//   }
// }


// Path: processor/src/main/java/net/yslibrary/simplepreferences/processor/SimplePreferencesProcessor.java
import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.TypeSpec;
import net.yslibrary.simplepreferences.annotation.Preferences;
import net.yslibrary.simplepreferences.processor.exception.ProcessingException;
import net.yslibrary.simplepreferences.processor.writer.PreferenceWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;

        return;
      }

      try {
        TypeElement typeElement = (TypeElement) element;

        PreferenceAnnotatedClass model = new PreferenceAnnotatedClass(typeElement, elementUtils);

        checkValid(model);

        PreferenceAnnotatedClass existing = items.get(model.preferenceName);

        if (existing != null) {
          throw new ProcessingException(typeElement,
              "Conflict: Class %s is annotated with @%s with value='%s', but %s already uses same value",
              model.annotatedElement, Preferences.class.getSimpleName(), model.preferenceName,
              existing.annotatedElement.getQualifiedName().toString());
        }

        items.put(model.preferenceName, model);
      } catch (ProcessingException e) {
        error(e.element, e.getMessage());
      } catch (IllegalStateException e) {
        error(null, e.getMessage());
      }
    });

    // generate
    items.forEach((key, preferenceAnnotatedClass) -> {
      try {
        TypeSpec clazz = (new PreferenceWriter(preferenceAnnotatedClass)).write();