-
+
diff --git a/docs/contributors.md b/docs/contributors.md
index 1cdce0c..fd11291 100644
--- a/docs/contributors.md
+++ b/docs/contributors.md
@@ -6,11 +6,11 @@
### :zap: Contributors List :zap:
-
+
---
-### :pushpin: Teodor G. ([@TeodorHMX1](https://github.com/TeodorHMX1))
+### :pushpin: Teodor G. ([@teogor](https://github.com/teogor))
#### Contributions:
- :bust_in_silhouette::computer: Code
- :open_book::pencil2: Documentation
diff --git a/gradle.properties b/gradle.properties
index a1837af..8c076c7 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -12,7 +12,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app"s APK
+# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Enables namespacing of each library's R class so that its R class includes only the
@@ -20,11 +20,12 @@ android.useAndroidX=true
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
+
# Maven
POM_NAME=Memo
POM_PACKAGING=aar
-VERSION_NAME=1.2.0
-GROUP=com.zeoflow
+VERSION_NAME=1.3.0
+GROUP=com.zeoflow.memo
POM_DESCRIPTION=Android processing and secured library for managing SharedPreferences as key-value elements efficiently and structurally.
POM_URL=https://github.com/zeoflow/memo
POM_SCM_URL=https://github.com/zeoflow/memo
@@ -38,8 +39,4 @@ POM_DEVELOPER_NAME=ZeoFlow
POM_DEVELOPER_EMAIL=open-source@zeoflow.com
# sonatype elements
-mavenCentralUsername=
-mavenCentralPassword=
-signing.keyId=
-signing.password=
signing.secretKeyRingFile=../buildSrc/key.gpg
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 62d4c05..e708b1c 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index a0f7639..47964d2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,6 @@
+#Wed Jun 29 17:44:28 EEST 2022
distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-rc-2-bin.zip
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index fbd7c51..4f906e0 100644
--- a/gradlew
+++ b/gradlew
@@ -130,7 +130,7 @@ fi
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
+
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
diff --git a/gradlew.bat b/gradlew.bat
index 5093609..107acd3 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,21 +64,6 @@ echo location of your Java installation.
goto fail
-:init
-@rem Get command-line arguments, handling Windows variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-
:execute
@rem Setup the command line
@@ -86,7 +71,7 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
diff --git a/memo-annotation/.gitignore b/memo-annotation/.gitignore
deleted file mode 100644
index 796b96d..0000000
--- a/memo-annotation/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/memo-annotation/build.gradle b/memo-annotation/build.gradle
deleted file mode 100644
index d80ff61..0000000
--- a/memo-annotation/build.gradle
+++ /dev/null
@@ -1,22 +0,0 @@
-plugins {
- id("com.android.library")
- id("com.vanniktech.maven.publish")
-}
-
-android {
- compileSdk 31
- defaultConfig {
- minSdk 21
- targetSdk 31
- }
-
- compileOptions {
- // Sets Java compatibility to Java 8
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-}
-
-dependencies {
- api('androidx.annotation:annotation:1.4.0-alpha01')
-}
\ No newline at end of file
diff --git a/memo-annotation/gradle.properties b/memo-annotation/gradle.properties
deleted file mode 100644
index b7631a1..0000000
--- a/memo-annotation/gradle.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-# artifact id
-# com.zeoflow:memo-annotation
-POM_ARTIFACT_ID=memo-annotation
\ No newline at end of file
diff --git a/memo-annotation/src/main/AndroidManifest.xml b/memo-annotation/src/main/AndroidManifest.xml
deleted file mode 100644
index c654f69..0000000
--- a/memo-annotation/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/DefaultMemo.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/DefaultMemo.java
deleted file mode 100644
index 2668937..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/DefaultMemo.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * Marks a class as an default SharedPreference entity. This entity will be mapped the default
- * SharedPreference persistence data.
- */
-@Documented
-@Target(TYPE)
-@Retention(RUNTIME)
-public @interface DefaultMemo
-{
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Encoder.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Encoder.java
deleted file mode 100644
index 32e7be0..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Encoder.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import androidx.annotation.RequiresApi;
-
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-
-public class Encoder
-{
-
- @RequiresApi(19)
- public static String encodeUtf8(String string)
- {
- ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(string);
- return StandardCharsets.UTF_8.decode(byteBuffer).toString();
- }
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/EncryptEntity.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/EncryptEntity.java
deleted file mode 100644
index d6519c2..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/EncryptEntity.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-public @interface EncryptEntity
-{
-
- String value();
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/IMemoChangedListener.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/IMemoChangedListener.java
deleted file mode 100644
index 486bc67..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/IMemoChangedListener.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-/**
- * IMemoChangedListener is an interface of the listening for
- *
- * SharedPreference persistence's value changes.
- */
-public interface IMemoChangedListener
-{
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/IMemoStorageImpl.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/IMemoStorageImpl.java
deleted file mode 100644
index df99135..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/IMemoStorageImpl.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-public interface IMemoStorageImpl
-{
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/InjectPreference.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/InjectPreference.java
deleted file mode 100644
index 6a20edd..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/InjectPreference.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * Injects a dependency of the preference into a field.
- */
-@Documented
-@Target(FIELD)
-@Retention(RUNTIME)
-public @interface InjectPreference
-{
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/KeyName.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/KeyName.java
deleted file mode 100644
index 90e84ee..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/KeyName.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-/**
- * Marks a field as an SharedPreference key. This field will be mapped as the SharedPreference key
- * with Upper camel case.
- */
-@Documented
-@Target(FIELD)
-@Retention(CLASS)
-public @interface KeyName
-{
-
- /**
- * Preference Key name value in the SharedPreference. If not used, defaults to the field value
- * with Upper camel case.
- *
- * @return The preference key name value of the SharedPreference.
- */
- String value();
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Listener.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Listener.java
deleted file mode 100644
index c74aa86..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Listener.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 preference
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-/**
- * Marks a field as an SharedPreference key. This field will be mapped as the SharedPreference key
- * with Upper camel case.
- */
-@Documented
-@Target(FIELD)
-@Retention(CLASS)
-public @interface Listener
-{
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoComponent.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoComponent.java
deleted file mode 100644
index 38463f8..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoComponent.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * Marks a class as a component of PreferenceRooms.
- */
-@Documented
-@Target(TYPE)
-@Retention(RUNTIME)
-public @interface MemoComponent
-{
-
- /**
- * Declaring the entities for the component.
- *
- * @return entity classes.
- */
- Class>[] entities() default {};
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoCompoundFunction.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoCompoundFunction.java
deleted file mode 100644
index a0ca422..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoCompoundFunction.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-/**
- * Marks a class as a multiple field's getter or putter function.
- */
-@Documented
-@Target(METHOD)
-@Retention(CLASS)
-public @interface MemoCompoundFunction
-{
-
- /**
- * Set preference getter or putter function to key in the SharedPreference.
- *
- * @return The Preference keys name of the SharedPreference.
- */
- String[] values();
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoEntity.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoEntity.java
deleted file mode 100644
index cd7f38b..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoEntity.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-/**
- * Marks a class as an SharedPreference data. This class will have a mapping SharedPreference with
- * Upper camel case.
- */
-@Documented
-@Target(TYPE)
-@Retention(RUNTIME)
-public @interface MemoEntity
-{
-
- /**
- * Preference entity name of the SharedPreference persistence. w
- *
- *
If not used, the class generated entity class name will be upper camel case of the class
- * name.
- *
- * @return The Preference name of the SharedPreference entity.
- */
- String value() default "";
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoFunction.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoFunction.java
deleted file mode 100644
index 73cc803..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoFunction.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.METHOD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-/**
- * Marks a class as a field's getter or putter function.
- */
-@Documented
-@Target(METHOD)
-@Retention(CLASS)
-public @interface MemoFunction
-{
-
- /**
- * Set preference getter or putter function to key in the SharedPreference.
- *
- * @return The Preference key name of the SharedPreference.
- */
- String value();
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoStorage.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoStorage.java
deleted file mode 100644
index 1708477..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/MemoStorage.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2017 zeoflow
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-
-@SuppressWarnings("TryWithIdenticalCatches")
-public class MemoStorage
-{
-
- private static final String CLAZZ_PREFIX = "_Injector";
-
- public static void inject(Object object)
- {
- try
- {
- Class> clazz = object.getClass();
- Class> injector = clazz.getClassLoader().loadClass(clazz.getName() + CLAZZ_PREFIX);
- Constructor> constructor = injector.getConstructor(clazz);
- constructor.newInstance(object);
- } catch (IllegalAccessException e)
- {
- e.printStackTrace();
- } catch (InstantiationException e)
- {
- e.printStackTrace();
- } catch (InvocationTargetException e)
- {
- e.printStackTrace();
- } catch (ClassNotFoundException e)
- {
- e.printStackTrace();
- } catch (NoSuchMethodException e)
- {
- e.printStackTrace();
- }
- }
-
-}
diff --git a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Observable.java b/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Observable.java
deleted file mode 100644
index 3cb4ceb..0000000
--- a/memo-annotation/src/main/java/com/zeoflow/memo/annotation/Observable.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2017 preference
- *
- * 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 com.zeoflow.memo.annotation;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.RetentionPolicy.CLASS;
-
-/**
- * Marks a field as an SharedPreference key. This field will be mapped as the SharedPreference key
- * with Upper camel case.
- */
-@Documented
-@Target(FIELD)
-@Retention(CLASS)
-public @interface Observable
-{
-
-}
diff --git a/memo-common/.gitignore b/memo-common/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/memo-common/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/memo-common/build.gradle b/memo-common/build.gradle
new file mode 100644
index 0000000..ffa285e
--- /dev/null
+++ b/memo-common/build.gradle
@@ -0,0 +1,14 @@
+plugins {
+ id 'java-library'
+ id 'org.jetbrains.kotlin.jvm'
+ id 'com.vanniktech.maven.publish'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+}
+
+dependencies {
+ api("androidx.annotation:annotation:1.4.0")
+}
\ No newline at end of file
diff --git a/memo-common/gradle.properties b/memo-common/gradle.properties
new file mode 100644
index 0000000..f064c9f
--- /dev/null
+++ b/memo-common/gradle.properties
@@ -0,0 +1,3 @@
+# artifact id
+# com.zeoflow.memo:memo-common
+POM_ARTIFACT_ID=memo-common
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/CompoundFunction.kt b/memo-common/src/main/java/com/zeoflow/memo/common/CompoundFunction.kt
new file mode 100644
index 0000000..3ad6bee
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/CompoundFunction.kt
@@ -0,0 +1,10 @@
+package com.zeoflow.memo.common
+
+import kotlin.reflect.KClass
+
+@MustBeDocumented
+@Target(AnnotationTarget.FIELD)
+@Retention(AnnotationRetention.BINARY)
+public annotation class CompoundFunction(
+ val value: KClass<*>
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/CompoundFunctions.kt b/memo-common/src/main/java/com/zeoflow/memo/common/CompoundFunctions.kt
new file mode 100644
index 0000000..e959e39
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/CompoundFunctions.kt
@@ -0,0 +1,11 @@
+package com.zeoflow.memo.common
+
+open class CompoundFunctions {
+ open operator fun get(value: T): T {
+ return value
+ }
+
+ open fun set(value: T): T {
+ return value
+ }
+}
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/Default.kt b/memo-common/src/main/java/com/zeoflow/memo/common/Default.kt
new file mode 100644
index 0000000..36b5720
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/Default.kt
@@ -0,0 +1,10 @@
+package com.zeoflow.memo.common
+
+import kotlin.reflect.KClass
+
+@MustBeDocumented
+@Target(AnnotationTarget.FIELD)
+@Retention(AnnotationRetention.BINARY)
+public annotation class Default(
+ val value: KClass<*>
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/DefaultEntity.kt b/memo-common/src/main/java/com/zeoflow/memo/common/DefaultEntity.kt
new file mode 100644
index 0000000..6247157
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/DefaultEntity.kt
@@ -0,0 +1,10 @@
+package com.zeoflow.memo.common
+
+import kotlin.reflect.KClass
+
+@MustBeDocumented
+@Target(AnnotationTarget.CLASS)
+@Retention(AnnotationRetention.BINARY)
+public annotation class DefaultEntity(
+ val value: KClass<*>
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/DefaultMemo.kt b/memo-common/src/main/java/com/zeoflow/memo/common/DefaultMemo.kt
new file mode 100644
index 0000000..26741b4
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/DefaultMemo.kt
@@ -0,0 +1,10 @@
+package com.zeoflow.memo.common
+
+/**
+ * Marks a class as an default SharedPreference entity. This entity will be mapped the default
+ * SharedPreference persistence data.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+public annotation class DefaultMemo
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/DefaultType.kt b/memo-common/src/main/java/com/zeoflow/memo/common/DefaultType.kt
new file mode 100644
index 0000000..80b6ff9
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/DefaultType.kt
@@ -0,0 +1,5 @@
+package com.zeoflow.memo.common
+
+interface DefaultType {
+ fun value(): T
+}
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/Encoder.kt b/memo-common/src/main/java/com/zeoflow/memo/common/Encoder.kt
new file mode 100644
index 0000000..9427418
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/Encoder.kt
@@ -0,0 +1,13 @@
+package com.zeoflow.memo.common
+
+import androidx.annotation.RequiresApi
+import com.zeoflow.memo.common.MemoStorage
+import java.nio.charset.StandardCharsets
+
+public object Encoder {
+ @JvmStatic
+ fun encodeUtf8(string: String?): String {
+ val byteBuffer = StandardCharsets.UTF_8.encode(string.toString())
+ return StandardCharsets.UTF_8.decode(byteBuffer).toString()
+ }
+}
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/EncryptEntity.kt b/memo-common/src/main/java/com/zeoflow/memo/common/EncryptEntity.kt
new file mode 100644
index 0000000..3e6cb42
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/EncryptEntity.kt
@@ -0,0 +1,3 @@
+package com.zeoflow.memo.common
+
+public annotation class EncryptEntity(val value: String)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/IMemoChangedListener.kt b/memo-common/src/main/java/com/zeoflow/memo/common/IMemoChangedListener.kt
new file mode 100644
index 0000000..87bff6c
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/IMemoChangedListener.kt
@@ -0,0 +1,9 @@
+package com.zeoflow.memo.common
+
+/**
+ * IMemoChangedListener is an interface of the listening for
+ *
+ *
+ * SharedPreference persistence's value changes.
+ */
+public interface IMemoChangedListener
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/IMemoStorageImpl.kt b/memo-common/src/main/java/com/zeoflow/memo/common/IMemoStorageImpl.kt
new file mode 100644
index 0000000..a5be978
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/IMemoStorageImpl.kt
@@ -0,0 +1,3 @@
+package com.zeoflow.memo.common
+
+public interface IMemoStorageImpl
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/InjectPreference.kt b/memo-common/src/main/java/com/zeoflow/memo/common/InjectPreference.kt
new file mode 100644
index 0000000..57efb17
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/InjectPreference.kt
@@ -0,0 +1,9 @@
+package com.zeoflow.memo.common
+
+/**
+ * Injects a dependency of the preference into a field.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.FIELD)
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+public annotation class InjectPreference
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/KeyName.kt b/memo-common/src/main/java/com/zeoflow/memo/common/KeyName.kt
new file mode 100644
index 0000000..3618c30
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/KeyName.kt
@@ -0,0 +1,18 @@
+package com.zeoflow.memo.common
+
+/**
+ * Marks a field as an SharedPreference key. This field will be mapped as the SharedPreference key
+ * with Upper camel case.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.FIELD)
+@kotlin.annotation.Retention(AnnotationRetention.BINARY)
+public annotation class KeyName(
+ /**
+ * Preference Key name value in the SharedPreference. If not used, defaults to the field value
+ * with Upper camel case.
+ *
+ * @return The preference key name value of the SharedPreference.
+ */
+ public val value: String
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/Listener.kt b/memo-common/src/main/java/com/zeoflow/memo/common/Listener.kt
new file mode 100644
index 0000000..acaf7e1
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/Listener.kt
@@ -0,0 +1,10 @@
+package com.zeoflow.memo.common
+
+/**
+ * Marks a field as an SharedPreference key. This field will be mapped as the SharedPreference key
+ * with Upper camel case.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.FIELD)
+@kotlin.annotation.Retention(AnnotationRetention.BINARY)
+public annotation class Listener
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/MemoComponent.kt b/memo-common/src/main/java/com/zeoflow/memo/common/MemoComponent.kt
new file mode 100644
index 0000000..d3a0fa5
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/MemoComponent.kt
@@ -0,0 +1,18 @@
+package com.zeoflow.memo.common
+
+import kotlin.reflect.KClass
+
+/**
+ * Marks a class as a component of PreferenceRooms.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+public annotation class MemoComponent(
+ /**
+ * Declaring the entities for the component.
+ *
+ * @return entity classes.
+ */
+ val entities: Array> = []
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/MemoCompoundFunction.kt b/memo-common/src/main/java/com/zeoflow/memo/common/MemoCompoundFunction.kt
new file mode 100644
index 0000000..5cd751c
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/MemoCompoundFunction.kt
@@ -0,0 +1,20 @@
+package com.zeoflow.memo.common
+
+/**
+ * Marks a class as a multiple field's getter or putter function.
+ */
+@MustBeDocumented
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@kotlin.annotation.Retention(AnnotationRetention.BINARY)
+public annotation class MemoCompoundFunction(
+ /**
+ * Set preference getter or putter function to key in the SharedPreference.
+ *
+ * @return The Preference keys name of the SharedPreference.
+ */
+ val values: Array
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/MemoEntity.kt b/memo-common/src/main/java/com/zeoflow/memo/common/MemoEntity.kt
new file mode 100644
index 0000000..da5aeef
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/MemoEntity.kt
@@ -0,0 +1,21 @@
+package com.zeoflow.memo.common
+
+/**
+ * Marks a class as an SharedPreference data. This class will have a mapping SharedPreference with
+ * Upper camel case.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.CLASS)
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+public annotation class MemoEntity(
+ /**
+ * Preference entity name of the SharedPreference persistence. w
+ *
+ *
+ * If not used, the class generated entity class name will be upper camel case of the class
+ * name.
+ *
+ * @return The Preference name of the SharedPreference entity.
+ */
+ val value: String = ""
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/MemoFunction.kt b/memo-common/src/main/java/com/zeoflow/memo/common/MemoFunction.kt
new file mode 100644
index 0000000..2b357a3
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/MemoFunction.kt
@@ -0,0 +1,20 @@
+package com.zeoflow.memo.common
+
+/**
+ * Marks a class as a field's getter or putter function.
+ */
+@MustBeDocumented
+@Target(
+ AnnotationTarget.FUNCTION,
+ AnnotationTarget.PROPERTY_GETTER,
+ AnnotationTarget.PROPERTY_SETTER
+)
+@kotlin.annotation.Retention(AnnotationRetention.BINARY)
+public annotation class MemoFunction(
+ /**
+ * Set preference getter or putter function to key in the SharedPreference.
+ *
+ * @return The Preference key name of the SharedPreference.
+ */
+ val value: String
+)
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/MemoStorage.kt b/memo-common/src/main/java/com/zeoflow/memo/common/MemoStorage.kt
new file mode 100644
index 0000000..7c2bab0
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/MemoStorage.kt
@@ -0,0 +1,26 @@
+package com.zeoflow.memo.common
+
+import com.zeoflow.memo.common.MemoStorage
+import java.lang.reflect.InvocationTargetException
+
+public object MemoStorage {
+ private const val CLAZZ_PREFIX = "_Injector"
+ fun inject(`object`: Any) {
+ try {
+ val clazz: Class<*> = `object`.javaClass
+ val injector = clazz.classLoader.loadClass(clazz.name + CLAZZ_PREFIX)
+ val constructor = injector.getConstructor(clazz)
+ constructor.newInstance(`object`)
+ } catch (e: IllegalAccessException) {
+ e.printStackTrace()
+ } catch (e: InstantiationException) {
+ e.printStackTrace()
+ } catch (e: InvocationTargetException) {
+ e.printStackTrace()
+ } catch (e: ClassNotFoundException) {
+ e.printStackTrace()
+ } catch (e: NoSuchMethodException) {
+ e.printStackTrace()
+ }
+ }
+}
\ No newline at end of file
diff --git a/memo-common/src/main/java/com/zeoflow/memo/common/Observable.kt b/memo-common/src/main/java/com/zeoflow/memo/common/Observable.kt
new file mode 100644
index 0000000..fb1b638
--- /dev/null
+++ b/memo-common/src/main/java/com/zeoflow/memo/common/Observable.kt
@@ -0,0 +1,10 @@
+package com.zeoflow.memo.common
+
+/**
+ * Marks a field as an SharedPreference key. This field will be mapped as the SharedPreference key
+ * with Upper camel case.
+ */
+@MustBeDocumented
+@Target(AnnotationTarget.FIELD)
+@kotlin.annotation.Retention(AnnotationRetention.BINARY)
+public annotation class Observable
\ No newline at end of file
diff --git a/memo-compiler-ktx/.gitignore b/memo-compiler-ktx/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/memo-compiler-ktx/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/memo-compiler-ktx/build.gradle b/memo-compiler-ktx/build.gradle
new file mode 100644
index 0000000..8063969
--- /dev/null
+++ b/memo-compiler-ktx/build.gradle
@@ -0,0 +1,35 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+ id("kotlin")
+ id 'com.vanniktech.maven.publish'
+}
+
+dependencies {
+ implementation implementation(project(':memo-processor',))
+ implementation(project(":memo-compiler-processing"))
+
+ implementation("org.jetbrains.kotlin:kotlin-stdlib:1.7.0")
+ implementation("com.google.auto:auto-common:1.2")
+ implementation("com.google.auto.value:auto-value-annotations:1.6.3")
+ implementation("com.squareup:javapoet:1.13.0")
+ implementation("com.google.devtools.ksp:symbol-processing-api:1.5.0-1.0.0-alpha10")
+
+ implementation("org.xerial:sqlite-jdbc:3.34.0")
+ implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.3.0")
+ implementation("commons-codec:commons-codec:1.11")
+ implementation("com.intellij:annotations:12.0")
+}
+
+tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions {
+ freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn",
+ "-Xopt-in=kotlin.contracts.ExperimentalContracts",
+ "-Xopt-in=com.zeoflow.memo.compiler.processing.ExperimentalProcessingApi"]
+ }
+}
+
+tasks.withType(Test).configureEach {
+ // TODO: re-enable once b/177660733 is fixed.
+ it.systemProperty("com.zeoflow.memo.compiler.processing.strict", "false")
+}
\ No newline at end of file
diff --git a/memo-compiler-ktx/consumer-rules.pro b/memo-compiler-ktx/consumer-rules.pro
new file mode 100644
index 0000000..e69de29
diff --git a/memo-compiler-ktx/gradle.properties b/memo-compiler-ktx/gradle.properties
new file mode 100644
index 0000000..8efeddb
--- /dev/null
+++ b/memo-compiler-ktx/gradle.properties
@@ -0,0 +1,3 @@
+# artifact id
+# com.zeoflow.memo:memo-compiler-ktx
+POM_ARTIFACT_ID=memo-compiler-ktx
\ No newline at end of file
diff --git a/memo-compiler-ktx/proguard-rules.pro b/memo-compiler-ktx/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/memo-compiler-ktx/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/KotlinProcessingMachine.kt b/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/KotlinProcessingMachine.kt
new file mode 100644
index 0000000..99d1416
--- /dev/null
+++ b/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/KotlinProcessingMachine.kt
@@ -0,0 +1,70 @@
+package com.zeoflow.memo.compiler.ktx
+
+import com.squareup.kotlinpoet.ClassName
+import com.zeoflow.memo.compiler.processing.XProcessingEnv
+import com.zeoflow.memo.compiler.processing.XTypeElement
+import com.zeoflow.memo.processor.ClassTypes
+import com.zeoflow.memo.processor.Language
+import com.zeoflow.memo.processor.ProcessingMachine
+
+class KotlinProcessingMachine(processingEnv: XProcessingEnv) : ProcessingMachine(processingEnv) {
+
+ override fun process(functions: List) {
+
+ }
+
+ override fun language(): Language {
+ return Language.KOTLIN
+ }
+
+ override fun types(): ClassTypes {
+ return JavaClassTypes()
+ }
+
+}
+
+class JavaClassTypes : ClassTypes() {
+
+ override fun Memo(): Any {
+ return ClassName(
+ "com.zeoflow.memo",
+ "Memo"
+ )
+ }
+
+ override fun ConcealEncryption(): Any {
+ return ClassName(
+ "com.zeoflow.memo",
+ "ConcealEncryption"
+ )
+ }
+
+ override fun NoEncryption(): Any {
+ return ClassName(
+ "com.zeoflow.memo",
+ "NoEncryption"
+ )
+ }
+
+ override fun MutableLiveData(): Any {
+ return ClassName(
+ "androidx.lifecycle",
+ "MutableLiveData"
+ )
+ }
+
+ override fun Observer(): Any {
+ return ClassName(
+ "androidx.lifecycle",
+ "Observer"
+ )
+ }
+
+ override fun LifecycleOwner(): Any {
+ return ClassName(
+ "androidx.lifecycle",
+ "LifecycleOwner"
+ )
+ }
+
+}
\ No newline at end of file
diff --git a/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/LibKspProcessor.kt b/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/LibKspProcessor.kt
new file mode 100644
index 0000000..5d46010
--- /dev/null
+++ b/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/LibKspProcessor.kt
@@ -0,0 +1,48 @@
+package com.zeoflow.memo.compiler.ktx
+
+import com.zeoflow.memo.compiler.processing.XProcessingEnv
+import com.zeoflow.memo.compiler.processing.XProcessingStep.Companion.executeInKsp
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.processing.SymbolProcessor
+import com.google.devtools.ksp.processing.SymbolProcessorProvider
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.zeoflow.memo.processor.MemoProcessingStep
+
+/**
+ * Entry point for processing using KSP.
+ */
+class LibKspProcessor(
+ private val options: Map,
+ private val codeGenerator: CodeGenerator,
+ private val logger: KSPLogger
+) : SymbolProcessor {
+ override fun process(resolver: Resolver): List {
+ val processingEnv = XProcessingEnv.create(
+ options,
+ resolver,
+ codeGenerator,
+ logger
+ )
+
+ return MemoProcessingStep(KotlinProcessingMachine(processingEnv)).executeInKsp(
+ processingEnv
+ )
+ }
+
+ class Provider : SymbolProcessorProvider {
+ override fun create(
+ options: Map,
+ kotlinVersion: KotlinVersion,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ): SymbolProcessor {
+ return LibKspProcessor(
+ options = options,
+ codeGenerator = codeGenerator,
+ logger = logger
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/LibProcessor.kt b/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/LibProcessor.kt
new file mode 100644
index 0000000..9e2f924
--- /dev/null
+++ b/memo-compiler-ktx/src/main/java/com/zeoflow/memo/compiler/ktx/LibProcessor.kt
@@ -0,0 +1,125 @@
+package com.zeoflow.memo.compiler.ktx
+
+import com.zeoflow.memo.compiler.processing.XProcessingEnv
+import com.zeoflow.memo.compiler.processing.XProcessingStep.Companion.asAutoCommonProcessor
+import com.zeoflow.memo.compiler.util.SimpleJavaVersion
+import com.google.auto.common.BasicAnnotationProcessor
+import com.zeoflow.memo.processor.MemoProcessingStep
+import javax.lang.model.SourceVersion
+
+/**
+ * Annotation processor option to tell Gradle that Depot is an isolating annotation processor.
+ */
+private const val ISOLATING_ANNOTATION_PROCESSORS_INDICATOR =
+ "org.gradle.annotation.processing.isolating"
+
+/**
+ * The annotation processor for Depot.
+ */
+class LibProcessor : BasicAnnotationProcessor() {
+
+ /** Helper variable to avoid reporting the warning twice. */
+ private var jdkVersionHasBugReported = false
+
+ override fun steps(): MutableIterable {
+ return mutableListOf(
+ MemoProcessingStep(KotlinProcessingMachine(XProcessingEnv.create(
+ processingEnv
+ ))).asAutoCommonProcessor(processingEnv)
+ )
+ }
+
+ override fun getSupportedOptions(): MutableSet {
+ val supportedOptions = ARG_OPTIONS.toMutableSet()
+ // x processing is a cheap wrapper so it is fine to re-create.
+ val xProcessing = XProcessingEnv.create(processingEnv)
+ if (BooleanProcessorOptions.INCREMENTAL.getValue(xProcessing)) {
+ if (methodParametersVisibleInClassFiles()) {
+ // Depot can be incremental
+ supportedOptions.add(ISOLATING_ANNOTATION_PROCESSORS_INDICATOR)
+ } else {
+ if (!jdkVersionHasBugReported) {
+ jdkVersionHasBugReported = true
+ }
+ }
+ }
+
+ return supportedOptions
+ }
+
+ /**
+ * Returns `true` if the method parameters in class files can be accessed by Depot.
+ *
+ * Context: Depot requires access to the real parameter names of constructors (see
+ * PojoProcessor.getParamNames). Depot uses the ExecutableElement.getParemters() API on the
+ * constructor element to do this.
+ *
+ * When Depot is not yet incremental, the above API is working as expected. However, if we make
+ * Depot incremental, during an incremental compile Gradle may want to pass class files instead
+ * source files to annotation processors (to avoid recompiling the source files that haven't
+ * changed). Due to JDK bug https://bugs.openjdk.java.net/browse/JDK-8007720, the class files
+ * may lose the real parameter names of constructors, which would break Depot.
+ *
+ * The above JDK bug was fixed in JDK 11. The fix was also cherry-picked back into the
+ * embedded JDK that was shipped with Android Studio 3.5+.
+ *
+ * Therefore, for Depot to be incremental, we need to check whether the JDK being used has the
+ * fix: Either it is JDK 11+ or it is an embedded JDK that has the cherry-picked fix (version
+ * 1.8.0_202-release-1483-b39-5509098 or higher).
+ */
+ private fun methodParametersVisibleInClassFiles(): Boolean {
+ val currentJavaVersion = SimpleJavaVersion.getCurrentVersion() ?: return false
+
+ if (currentJavaVersion >= SimpleJavaVersion.VERSION_11_0_0) {
+ return true
+ }
+
+ val isEmbeddedJdk =
+ System.getProperty("java.vendor")?.contains("JetBrains", ignoreCase = true)
+ ?: false
+ // We are interested in 3 ranges of Android Studio (AS) versions:
+ // 1. AS 3.5.0-alpha09 and lower use JDK 1.8.0_152 or lower.
+ // 2. AS 3.5.0-alpha10 up to 3.5.0-beta01 use JDK 1.8.0_202-release-1483-b39-5396753.
+ // 3. AS 3.5.0-beta02 and higher use JDK 1.8.0_202-release-1483-b39-5509098 or higher,
+ // which have the cherry-picked JDK fix.
+ // Therefore, if the JDK version is 1.8.0_202, we need to filter out those in range #2.
+ return if (isEmbeddedJdk && (currentJavaVersion > SimpleJavaVersion.VERSION_1_8_0_202)) {
+ true
+ } else if (isEmbeddedJdk && (currentJavaVersion == SimpleJavaVersion.VERSION_1_8_0_202)) {
+ System.getProperty("java.runtime.version")
+ ?.let { it != "1.8.0_202-release-1483-b39-5396753" }
+ ?: false
+ } else {
+ false
+ }
+ }
+
+ override fun getSupportedSourceVersion(): SourceVersion {
+ return SourceVersion.latest()
+ }
+
+
+ companion object {
+ val ARG_OPTIONS by lazy {
+ BooleanProcessorOptions.values().map { it.argName }
+ }
+ }
+
+ enum class BooleanProcessorOptions(val argName: String, private val defaultValue: Boolean) {
+ INCREMENTAL("com.zeoflow.memo.compiler.incremental", true),
+ EXPAND_PROJECTION("com.zeoflow.memo.compiler.expandProjection", false);
+
+ /**
+ * Returns the value of this option passed through the [XProcessingEnv]. If the value
+ * is null or blank, it returns the default value instead.
+ */
+ fun getValue(processingEnv: XProcessingEnv): Boolean {
+ val value = processingEnv.options[argName]
+ return if (value.isNullOrBlank()) {
+ defaultValue
+ } else {
+ value.toBoolean()
+ }
+ }
+ }
+}
diff --git a/memo-compiler-ktx/src/main/resources/META-INF/gradle/incremental.annotation.processors b/memo-compiler-ktx/src/main/resources/META-INF/gradle/incremental.annotation.processors
new file mode 100644
index 0000000..f06ee3e
--- /dev/null
+++ b/memo-compiler-ktx/src/main/resources/META-INF/gradle/incremental.annotation.processors
@@ -0,0 +1 @@
+memo.compiler.ktx.LibProcessor,dynamic
diff --git a/memo-compiler-ktx/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/memo-compiler-ktx/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
new file mode 100644
index 0000000..f23f4a4
--- /dev/null
+++ b/memo-compiler-ktx/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider
@@ -0,0 +1 @@
+com.zeoflow.memo.compiler.ktx.LibKspProcessor$Provider
\ No newline at end of file
diff --git a/memo-compiler-ktx/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/memo-compiler-ktx/src/main/resources/META-INF/services/javax.annotation.processing.Processor
new file mode 100644
index 0000000..9feeb18
--- /dev/null
+++ b/memo-compiler-ktx/src/main/resources/META-INF/services/javax.annotation.processing.Processor
@@ -0,0 +1 @@
+com.zeoflow.memo.compiler.ktx.LibProcessor
diff --git a/memo-compiler-ktx/src/main/resources/NOTICE.txt b/memo-compiler-ktx/src/main/resources/NOTICE.txt
new file mode 100644
index 0000000..9f49260
--- /dev/null
+++ b/memo-compiler-ktx/src/main/resources/NOTICE.txt
@@ -0,0 +1,1812 @@
+List of 3rd party licenses:
+-----------------------------------------------------------------------------
+* commons-codec.jar (commons-codec:commons-codec:1.10)
+
+ ****** NOTICE:
+Apache Commons Codec
+Copyright 2002-2016 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java
+contains test data from http://aspell.net/test/orig/batch0.tab.
+Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org)
+
+===============================================================================
+
+The content of package org.apache.commons.codec.language.bm has been translated
+from the original php source code available at http://stevemorse.org/phoneticinfo.htm
+with permission from the original authors.
+Original source copyright:
+Copyright (c) 2008 Alexander Beider & Stephen P. Morse.
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* javapoet.jar (com.squareup:javapoet:1.8.0)
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* antlr4.jar (org.antlr:antlr4:4.5.3)
+
+ ****** LICENSE:
+[The "BSD 3-clause license"]
+Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holder nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+=====
+
+MIT License for codepointat.js from https://git.io/codepointat
+MIT License for fromcodepoint.js from https://git.io/vDW1m
+
+Copyright Mathias Bynens
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+
+
+-----------------------------------------------------------------------------
+* kotlin-stdlib.jar (org.jetbrains.kotlin:kotlin-stdlib:1.1.1)
+
+ ****** NOTICE:
+ =========================================================================
+ == NOTICE file corresponding to the section 4 d of ==
+ == the Apache License, Version 2.0, ==
+ == in this case for the Kotlin Compiler distribution. ==
+ =========================================================================
+
+ Kotlin Compiler
+ Copyright 2010-2015 JetBrains s.r.o and respective authors and developers
+
+ ****** LICENSE:
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * 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.
+ */
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* auto-common.jar (com.google.auto:auto-common:0.6)
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* annotations.jar (org.jetbrains:annotations:13.0)
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* gson.jar (com.google.code.gson:gson:2.8.0)
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
+
+-----------------------------------------------------------------------------
+* sqlite-jdbc.jar (org.xerial:sqlite-jdbc:3.16.1)
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+ ****** LICENSE:
+Copyright (c) 2006, David Crawshaw. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+
+
+
+
+-----------------------------------------------------------------------------
+* guava.jar (com.google.guava:guava:18.0)
+
+ ****** LICENSE:
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
+
+
+
diff --git a/memo-compiler-processing/.gitignore b/memo-compiler-processing/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/memo-compiler-processing/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/memo-compiler-processing/build.gradle b/memo-compiler-processing/build.gradle
new file mode 100644
index 0000000..79d1934
--- /dev/null
+++ b/memo-compiler-processing/build.gradle
@@ -0,0 +1,39 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+ id("kotlin")
+ id 'com.vanniktech.maven.publish'
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+ api("org.jetbrains.kotlin:kotlin-stdlib:1.7.0")
+ api("com.squareup:javapoet:1.13.0")
+ api("com.squareup:kotlinpoet:1.8.0")
+ implementation('androidx.annotation:annotation:1.4.0')
+ implementation("com.google.guava:guava:29.0-jre")
+ implementation("com.google.auto:auto-common:1.2")
+ implementation("com.google.auto.value:auto-value-annotations:1.6.3")
+
+ implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.3.0")
+ implementation("com.intellij:annotations:12.0")
+ implementation("com.google.devtools.ksp:symbol-processing-api:1.5.0-1.0.0-alpha10")
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0")
+}
+
+tasks.withType(KotlinCompile).configureEach {
+ kotlinOptions {
+ freeCompilerArgs += ["-Xopt-in=kotlin.RequiresOptIn",
+ "-Xopt-in=kotlin.contracts.ExperimentalContracts",
+ "-Xopt-in=com.zeoflow.memo.compiler.processing.ExperimentalProcessingApi"]
+ }
+}
+
+tasks.withType(Test).configureEach {
+ // TODO: re-enable once b/177660733 is fixed.
+ it.systemProperty("com.zeoflow.memo.compiler.processing.strict", "false")
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/gradle.properties b/memo-compiler-processing/gradle.properties
new file mode 100644
index 0000000..aae4623
--- /dev/null
+++ b/memo-compiler-processing/gradle.properties
@@ -0,0 +1,3 @@
+# artifact id
+# com.zeoflow.memo:memo-compiler-processing
+POM_ARTIFACT_ID=memo-compiler-processing
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ExperimentalProcessingApi.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ExperimentalProcessingApi.kt
new file mode 100644
index 0000000..554593e
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ExperimentalProcessingApi.kt
@@ -0,0 +1,10 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * These APIs are experimental and are not designed to be used by external artifacts.
+ *
+ * Make sure to jarjar them if you are using these APIs in your own processor.
+ */
+@RequiresOptIn
+@Retention(AnnotationRetention.BINARY)
+annotation class ExperimentalProcessingApi
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/InternalXAnnotated.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/InternalXAnnotated.kt
new file mode 100644
index 0000000..04ce38d
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/InternalXAnnotated.kt
@@ -0,0 +1,58 @@
+package com.zeoflow.memo.compiler.processing
+
+import kotlin.reflect.KClass
+
+/**
+ * Internal API for [XAnnotated] that handles repeated annotations.
+ */
+internal interface InternalXAnnotated : XAnnotated {
+ /**
+ * Repeated annotations show up differently between source and .class files.
+ *
+ * To avoid that inconsistency, [XAnnotated] only provides [XAnnotated.getAnnotations] and in
+ * this internal wrapper, we handle that inconsistency by finding the container class and
+ * asking implementers to implement the 2 arg version instead.
+ *
+ * see: https://github.com/google/ksp/issues/356
+ * see: https://github.com/google/ksp/issues/358
+ * see: https://youtrack.jetbrains.com/issue/KT-12794
+ *
+ * @param annotation The annotation to query
+ * @param containerAnnotation The container annotation of the [annotation] if it is a repeatable
+ * annotation.
+ *
+ * @see hasAnnotation
+ */
+ fun getAnnotations(
+ annotation: KClass,
+ containerAnnotation: KClass? = annotation.containerAnnotation
+ ): List>
+
+ override fun getAnnotations(annotation: KClass) = getAnnotations(
+ annotation = annotation,
+ containerAnnotation = annotation.containerAnnotation
+ )
+
+ override fun hasAnnotation(annotation: KClass) = hasAnnotation(
+ annotation = annotation,
+ containerAnnotation = annotation.containerAnnotation
+ )
+
+ /**
+ * Returns `true` if this element is annotated with the given [annotation].
+ *
+ * Note that this method should check for both [annotation] and [containerAnnotation] to
+ * support repeated annotations.
+ *
+ * @param annotation The annotation to query
+ * @param containerAnnotation The container annotation of the [annotation] if it is a repeatable
+ * annotation.
+ *
+ * @see [toAnnotationBox]
+ * @see [hasAnyOf]
+ */
+ fun hasAnnotation(
+ annotation: KClass,
+ containerAnnotation: KClass? = annotation.containerAnnotation
+ ): Boolean
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/JavaPoetExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/JavaPoetExt.kt
new file mode 100644
index 0000000..e5d903c
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/JavaPoetExt.kt
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2021 ZeoFlow SRL
+ *
+ * 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 com.zeoflow.memo.compiler.processing
+
+import com.zeoflow.memo.compiler.processing.javac.JavacExecutableElement
+import com.zeoflow.memo.compiler.processing.ksp.KspMethodElement
+import com.zeoflow.memo.compiler.processing.ksp.KspMethodType
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.MethodSpec
+import com.squareup.javapoet.ParameterSpec
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeSpec
+import javax.lang.model.element.Modifier
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Javapoet does not model NonType, unlike javac, which makes it hard to rely on TypeName for
+ * common functionality (e.g. ability to implement XType.isLong as typename() == TypeName.LONG
+ * instead of in the base class)
+ *
+ * For those cases, we have this hacky type so that we can always query TypeName on an XType.
+ *
+ * We should still strive to avoid these cases, maybe turn it to an error in tests.
+ */
+private val NONE_TYPE_NAME = ClassName.get("com.zeoflow.depot.compiler.processing.error", "NotAType")
+
+internal fun TypeMirror.safeTypeName(): TypeName = if (kind == TypeKind.NONE) {
+ NONE_TYPE_NAME
+} else {
+ TypeName.get(this)
+}
+
+/**
+ * Adds the given element as the originating element for compilation.
+ * see [TypeSpec.Builder.addOriginatingElement].
+ */
+fun TypeSpec.Builder.addOriginatingElement(element: XElement): TypeSpec.Builder {
+ element.originatingElementForPoet()?.let(this::addOriginatingElement)
+ return this
+}
+
+internal fun TypeName.rawTypeName(): TypeName {
+ return if (this is ParameterizedTypeName) {
+ this.rawType
+ } else {
+ this
+ }
+}
+
+/**
+ * Returns the unboxed TypeName for this if it can be unboxed, otherwise, returns this.
+ */
+internal fun TypeName.tryUnbox(): TypeName {
+ return if (isBoxedPrimitive) {
+ unbox()
+ } else {
+ this
+ }
+}
+
+/**
+ * Returns the boxed TypeName for this if it can be unboxed, otherwise, returns this.
+ */
+internal fun TypeName.tryBox(): TypeName {
+ return try {
+ box()
+ } catch (err: AssertionError) {
+ this
+ }
+}
+
+/**
+ * Helper class to create overrides for XExecutableElements with final parameters and correct
+ * parameter names read from Kotlin Metadata.
+ */
+object MethodSpecHelper {
+ /**
+ * Creates an overriding [MethodSpec] for the given [XExecutableElement] where:
+ * * all parameters are marked as final
+ * * parameter names are copied from KotlinMetadata when available
+ * * [Override] annotation is added and other annotations are dropped
+ * * thrown types are copied if the backing element is from java
+ */
+ @JvmStatic
+ fun overridingWithFinalParams(
+ elm: XMethodElement,
+ owner: XType
+ ): MethodSpec.Builder {
+ val asMember = elm.asMemberOf(owner)
+ return if (elm is KspMethodElement && asMember is KspMethodType) {
+ overridingWithFinalParams(
+ executableElement = elm,
+ resolvedType = asMember.inheritVarianceForOverride()
+ )
+ } else {
+ overridingWithFinalParams(
+ executableElement = elm,
+ resolvedType = asMember
+ )
+ }
+ }
+
+ private fun overridingWithFinalParams(
+ executableElement: XMethodElement,
+ resolvedType: XMethodType = executableElement.executableType
+ ): MethodSpec.Builder {
+ return MethodSpec.methodBuilder(executableElement.name).apply {
+ addTypeVariables(
+ resolvedType.typeVariableNames
+ )
+ resolvedType.parameterTypes.forEachIndexed { index, paramType ->
+ addParameter(
+ ParameterSpec.builder(
+ paramType.typeName,
+ executableElement.parameters[index].name,
+ Modifier.FINAL
+ ).build()
+ )
+ }
+ if (executableElement.isPublic()) {
+ addModifiers(Modifier.PUBLIC)
+ } else if (executableElement.isProtected()) {
+ addModifiers(Modifier.PROTECTED)
+ }
+ addAnnotation(Override::class.java)
+ varargs(executableElement.isVarArgs())
+ if (executableElement is JavacExecutableElement) {
+ // copy throws for java
+ executableElement.element.thrownTypes.forEach {
+ addException(TypeName.get(it))
+ }
+ }
+ returns(resolvedType.returnType.typeName)
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/JavacTestProcessor.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/JavacTestProcessor.kt
new file mode 100644
index 0000000..a10c0bb
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/JavacTestProcessor.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 ZeoFlow SRL
+ *
+ * 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 com.zeoflow.memo.compiler.processing
+
+import androidx.annotation.VisibleForTesting
+import com.zeoflow.memo.compiler.processing.javac.JavacProcessingEnv
+import com.zeoflow.memo.compiler.processing.javac.JavacRoundEnv
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.element.TypeElement
+
+/**
+ * Javac processor implementation that provides access to the round environment.
+ *
+ * This is only used in tests, the main processor uses an API similar to the processing step
+ * in Auto Common.
+ */
+@VisibleForTesting
+@ExperimentalProcessingApi
+abstract class JavacTestProcessor : AbstractProcessor() {
+ val xProcessingEnv by lazy {
+ // lazily create this as it is not available on construction time
+ XProcessingEnv.create(super.processingEnv)
+ }
+
+ final override fun process(
+ annotations: MutableSet,
+ roundEnv: RoundEnvironment
+ ): Boolean {
+ if (roundEnv.processingOver()) {
+ return true
+ }
+ val env = xProcessingEnv as JavacProcessingEnv
+ val javacRoundEnv = JavacRoundEnv(env, roundEnv)
+ val xAnnotations = annotations.mapTo(mutableSetOf()) {
+ env.wrapTypeElement(it)
+ }
+ return doProcess(xAnnotations, javacRoundEnv)
+ }
+
+ abstract fun doProcess(
+ annotations: Set,
+ roundEnv: XRoundEnv
+ ): Boolean
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/KClassExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/KClassExt.kt
new file mode 100644
index 0000000..5276f39
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/KClassExt.kt
@@ -0,0 +1,11 @@
+package com.zeoflow.memo.compiler.processing
+
+import kotlin.reflect.KClass
+
+private typealias JavaRepeatable = java.lang.annotation.Repeatable
+
+/**
+ * Returns the container annotation if `this` is a Repeatable annotation.
+ */
+internal val KClass.containerAnnotation: KClass?
+ get() = this.java.getAnnotation(JavaRepeatable::class.java)?.value
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/KotlinPoetExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/KotlinPoetExt.kt
new file mode 100644
index 0000000..3d71983
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/KotlinPoetExt.kt
@@ -0,0 +1,14 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.squareup.kotlinpoet.OriginatingElementsHolder
+
+/**
+ * Adds the given element as an originating element for compilation.
+ * see [OriginatingElementsHolder.Builder.addOriginatingElement].
+ */
+fun > T.addOriginatingElement(
+ element: XElement
+): T {
+ element.originatingElementForPoet()?.let(this::addOriginatingElement)
+ return this
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/MethodCollector.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/MethodCollector.kt
new file mode 100644
index 0000000..1eb9c80
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/MethodCollector.kt
@@ -0,0 +1,73 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Helper class to collect all methods of an [XTypeElement] to implement
+ * [XTypeElement.getAllMethods].
+ */
+private class MethodCollector(
+ val target: XTypeElement
+) {
+ // group methods by name for fast overrides check
+ private val selectionByName = mutableMapOf>()
+
+ // we keep a duplicate list to preserve declaration order, makes the generated code match
+ // user code
+ private val selection = mutableListOf()
+
+ fun collect() {
+ val selection = target.getDeclaredMethods().forEach(::addToSelection)
+
+ target.superType
+ ?.typeElement
+ ?.getAllMethods()
+ ?.forEach(::addIfNotOverridden)
+ target.getSuperInterfaceElements().forEach {
+ it.getAllMethods().forEach {
+ if (!it.isStatic()) {
+ addIfNotOverridden(it)
+ }
+ }
+ }
+ return selection
+ }
+
+ fun getResult(): List {
+ return selection
+ }
+
+ private fun addIfNotOverridden(candidate: XMethodElement) {
+ if (!target.canAccessSuperMethod(candidate)) {
+ return
+ }
+ val overridden = selectionByName[candidate.name]?.any { existing ->
+ existing.overrides(candidate, target)
+ } ?: false
+ if (!overridden) {
+ addToSelection(candidate.copyTo(target))
+ }
+ }
+
+ private fun addToSelection(method: XMethodElement) {
+ selectionByName.getOrPut(method.name) {
+ mutableListOf()
+ }.add(method)
+ selection.add(method)
+ }
+
+ private fun XTypeElement.canAccessSuperMethod(other: XMethodElement): Boolean {
+ if (other.isPublic() || other.isProtected()) {
+ return true
+ }
+ if (other.isPrivate()) {
+ return false
+ }
+ // check package
+ return packageName == other.enclosingElement.className.packageName()
+ }
+}
+
+internal fun XTypeElement.collectAllMethods(): List {
+ val collector = MethodCollector(this)
+ collector.collect()
+ return collector.getResult()
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XAnnotated.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XAnnotated.kt
new file mode 100644
index 0000000..abbacfa
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XAnnotated.kt
@@ -0,0 +1,64 @@
+package com.zeoflow.memo.compiler.processing
+
+import kotlin.reflect.KClass
+
+/**
+ * Common interface implemented by elements that might have annotations.
+ */
+interface XAnnotated {
+ /**
+ * Gets the list of annotations with the given type.
+ *
+ * For repeated annotations declared in Java code, please use the repeated annotation type,
+ * not the container. Calling this method with a container annotation will have inconsistent
+ * behaviour between Java AP and KSP.
+ *
+ * @see [hasAnnotation]
+ * @see [hasAnnotationWithPackage]
+ */
+ fun getAnnotations(
+ annotation: KClass
+ ): List>
+
+ /**
+ * Returns `true` if this element is annotated with the given [annotation].
+ *
+ * For repeated annotations declared in Java code, please use the repeated annotation type,
+ * not the container. Calling this method with a container annotation will have inconsistent
+ * behaviour between Java AP and KSP.
+ * @see [hasAnyOf]
+ */
+ fun hasAnnotation(
+ annotation: KClass
+ ): Boolean
+
+ /**
+ * Returns `true` if this element has an annotation that is declared in the given package.
+ */
+ // a very sad method but helps avoid abstraction annotation
+ fun hasAnnotationWithPackage(pkg: String): Boolean
+
+ /**
+ * Returns `true` if this element has one of the [annotations].
+ */
+ fun hasAnyOf(vararg annotations: KClass) = annotations.any(this::hasAnnotation)
+
+ @Deprecated(
+ replaceWith = ReplaceWith("getAnnotation(annotation)"),
+ message = "Use getAnnotation(not repeatable) or getAnnotations (repeatable)"
+ )
+ fun toAnnotationBox(annotation: KClass): XAnnotationBox? =
+ getAnnotation(annotation)
+
+ /**
+ * If the current element has an annotation with the given [annotation] class, a boxed instance
+ * of it will be returned where fields can be read. Otherwise, `null` value is returned.
+ *
+ * @see [hasAnnotation]
+ * @see [getAnnotations]
+ * @see [hasAnnotationWithPackage]
+ */
+ fun getAnnotation(annotation: KClass): XAnnotationBox? {
+ return getAnnotations(annotation).firstOrNull()
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XAnnotationBox.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XAnnotationBox.kt
new file mode 100644
index 0000000..31ee063
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XAnnotationBox.kt
@@ -0,0 +1,34 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * This wraps an annotation element that is both accessible from the processor and runtime.
+ *
+ * It won't scale to a general purpose processing APIs where an equivelant of the AnnotationMirror
+ * API needs to be provided but works well for Depot's case.
+ */
+interface XAnnotationBox {
+ /**
+ * The value field of the annotation
+ */
+ val value: T
+
+ /**
+ * Returns the value of the given [methodName] as a type reference.
+ */
+ fun getAsType(methodName: String): XType?
+
+ /**
+ * Returns the value of the given [methodName] as a list of type references.
+ */
+ fun getAsTypeList(methodName: String): List
+
+ /**
+ * Returns the value of the given [methodName] as another boxed annotation.
+ */
+ fun getAsAnnotationBox(methodName: String): XAnnotationBox
+
+ /**
+ * Returns the value of the given [methodName] as an array of boxed annotations.
+ */
+ fun getAsAnnotationBoxArray(methodName: String): Array>
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XArrayType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XArrayType.kt
new file mode 100644
index 0000000..4b50d30
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XArrayType.kt
@@ -0,0 +1,13 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Represents an Array type including Kotlin's [Array] type.
+ *
+ * @see [javax.lang.model.type.ArrayType]
+ */
+interface XArrayType : XType {
+ /**
+ * The type of elements in the Array
+ */
+ val componentType: XType
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XConstructorElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XConstructorElement.kt
new file mode 100644
index 0000000..b68fb0c
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XConstructorElement.kt
@@ -0,0 +1,24 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Represents a constructor of a class.
+ *
+ * @see XMethodElement
+ * @see XExecutableElement
+ */
+interface XConstructorElement : XExecutableElement {
+ override val enclosingElement: XTypeElement
+
+ override val fallbackLocationText: String
+ get() = buildString {
+ append(enclosingElement.qualifiedName)
+ append(".")
+ append("(")
+ append(
+ parameters.joinToString(", ") {
+ it.type.typeName.toString()
+ }
+ )
+ append(")")
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XElement.kt
new file mode 100644
index 0000000..aa4ef52
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XElement.kt
@@ -0,0 +1,86 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.zeoflow.memo.compiler.processing.javac.JavacElement
+import com.zeoflow.memo.compiler.processing.ksp.KSFileAsOriginatingElement
+import com.zeoflow.memo.compiler.processing.ksp.KspElement
+import javax.lang.model.element.Element
+import kotlin.contracts.contract
+
+/**
+ * Represents an element declared in code.
+ *
+ * @see [javax.lang.model.element.Element]
+ * @see XExecutableElement
+ * @see XVariableElement
+ * @see XTypeElement
+ */
+interface XElement : XAnnotated {
+ /**
+ * Returns the string representation of the Element's kind.
+ */
+ fun kindName(): String
+ /**
+ * When the location of an element is unknown, this String is appended to the diagnostic
+ * message. Without this information, developer gets no clue on where the error is.
+ */
+ val fallbackLocationText: String
+
+ /**
+ * The documentation comment of the element, or null if there is none.
+ */
+ val docComment: String?
+}
+
+/**
+ * Checks whether this element represents an [XTypeElement].
+ */
+// we keep these as extension methods to be able to use contracts
+fun XElement.isTypeElement(): Boolean {
+ contract {
+ returns(true) implies (this@isTypeElement is XTypeElement)
+ }
+ return this is XTypeElement
+}
+
+/**
+ * Checks whether this element represents an [XVariableElement].
+ */
+fun XElement.isVariableElement(): Boolean {
+ contract {
+ returns(true) implies (this@isVariableElement is XVariableElement)
+ }
+ return this is XVariableElement
+}
+
+/**
+ * Checks whether this element represents an [XMethodElement].
+ */
+fun XElement.isMethod(): Boolean {
+ contract {
+ returns(true) implies (this@isMethod is XMethodElement)
+ }
+ return this is XMethodElement
+}
+
+fun XElement.isConstructor(): Boolean {
+ contract {
+ returns(true) implies (this@isConstructor is XConstructorElement)
+ }
+ return this is XConstructorElement
+}
+
+/**
+ * Attempts to get a Javac [Element] representing the originating element for attribution
+ * when writing a file for incremental processing.
+ *
+ * In KSP a [KSFileAsOriginatingElement] will be returned, which is a synthetic javac element
+ * that allows us to pass originating elements to JavaPoet and KotlinPoet, and later extract
+ * the KSP file when writing with [XFiler].
+ */
+internal fun XElement.originatingElementForPoet(): Element? {
+ return when (this) {
+ is JavacElement -> element
+ is KspElement -> containingFileAsOriginatingElement()
+ else -> error("Originating element is not implemented for ${this.javaClass}")
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XEnumTypeElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XEnumTypeElement.kt
new file mode 100644
index 0000000..4e07fba
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XEnumTypeElement.kt
@@ -0,0 +1,17 @@
+package com.zeoflow.memo.compiler.processing
+
+import kotlin.contracts.contract
+
+/**
+ * Type elements that represent Enum declarations.
+ */
+interface XEnumTypeElement : XTypeElement {
+ val enumConstantNames: Set
+}
+
+fun XTypeElement.isEnum(): Boolean {
+ contract {
+ returns(true) implies (this@isEnum is XEnumTypeElement)
+ }
+ return this is XEnumTypeElement
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XEquality.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XEquality.kt
new file mode 100644
index 0000000..7ca64f5
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XEquality.kt
@@ -0,0 +1,40 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Helper interface to enforce implementing equality in wrappers so that we don't by mistake
+ * create wrappers that do not properly handle equality.
+ *
+ * Enforcement is done in JavacType and JavacElement
+ */
+internal interface XEquality {
+ /**
+ * The list of items that should participate in equality checks.
+ */
+ val equalityItems: Array
+
+ companion object {
+ fun hashCode(elements: Array): Int {
+ return elements.contentHashCode()
+ }
+
+ fun equals(first: Any?, second: Any?): Boolean {
+ if (first !is XEquality || second !is XEquality) {
+ return false
+ }
+ return equals(first.equalityItems, second.equalityItems)
+ }
+
+ fun equals(first: Array, second: Array): Boolean {
+ // TODO there is probably a better way to do this
+ if (first.size != second.size) {
+ return false
+ }
+ repeat(first.size) {
+ if (first[it] != second[it]) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XExecutableElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XExecutableElement.kt
new file mode 100644
index 0000000..9cf24d3
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XExecutableElement.kt
@@ -0,0 +1,31 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Represents a method, constructor or initializer.
+ *
+ * @see [javax.lang.model.element.ExecutableElement]
+ */
+interface XExecutableElement : XHasModifiers, XElement {
+ /**
+ * The element that declared this executable.
+ *
+ * For methods declared as top level functions in Kotlin:
+ * * When running with KAPT, the value will be an [XTypeElement].
+ * * When running with KSP, if this function is coming from the classpath, the value will
+ * be an [XTypeElement].
+ * * When running with KSP, if this function is in source, the value will **NOT** be an
+ * [XTypeElement]. If you need the generated synthetic java class name, you can use
+ * [XMemberContainer.className] property.
+ */
+ val enclosingElement: XMemberContainer
+ /**
+ * The list of parameters that should be passed into this method.
+ *
+ * @see [isVarArgs]
+ */
+ val parameters: List
+ /**
+ * Returns true if this method receives a vararg parameter.
+ */
+ fun isVarArgs(): Boolean
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XExecutableParameterElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XExecutableParameterElement.kt
new file mode 100644
index 0000000..6c71f44
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XExecutableParameterElement.kt
@@ -0,0 +1,6 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Parameter of a method.
+ */
+interface XExecutableParameterElement : XVariableElement
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XFieldElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XFieldElement.kt
new file mode 100644
index 0000000..a81ab9a
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XFieldElement.kt
@@ -0,0 +1,23 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Field in an [XTypeElement].
+ */
+interface XFieldElement : XVariableElement, XHasModifiers {
+ /**
+ * The element that declared this field.
+ * For fields declared in classes, this will be an [XTypeElement].
+ *
+ * For fields declared as top level properties in Kotlin:
+ * * When running with KAPT, the value will be an [XTypeElement].
+ * * When running with KSP, if this property is coming from the classpath, the value will
+ * be an [XTypeElement].
+ * * When running with KSP, if this property is in source, the value will **NOT** be an
+ * [XTypeElement]. If you need the generated synthetic java class name, you can use
+ * [XMemberContainer.className] property.
+ */
+ val enclosingElement: XMemberContainer
+
+ override val fallbackLocationText: String
+ get() = "$name in ${enclosingElement.fallbackLocationText}"
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XFiler.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XFiler.kt
new file mode 100644
index 0000000..1384555
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XFiler.kt
@@ -0,0 +1,33 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.squareup.javapoet.JavaFile
+import com.squareup.kotlinpoet.FileSpec
+
+/**
+ * Code generation interface for XProcessing.
+ */
+interface XFiler {
+
+ fun write(javaFile: JavaFile, mode: Mode = Mode.Isolating)
+
+ fun write(fileSpec: FileSpec, mode: Mode = Mode.Isolating)
+
+ /**
+ * Specifies whether a file represents aggregating or isolating inputs for incremental
+ * build purposes. This does not apply in Javac processing because aggregating vs isolating
+ * is set on the processor level. For more on KSP's definitions of isolating vs aggregating
+ * see the documentation at
+ * https://github.com/google/ksp/blob/master/docs/incremental.md
+ */
+ enum class Mode {
+ Aggregating, Isolating
+ }
+}
+
+fun JavaFile.writeTo(generator: XFiler, mode: XFiler.Mode = XFiler.Mode.Isolating) {
+ generator.write(this, mode)
+}
+
+fun FileSpec.writeTo(generator: XFiler, mode: XFiler.Mode = XFiler.Mode.Isolating) {
+ generator.write(this, mode)
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XHasModifiers.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XHasModifiers.kt
new file mode 100644
index 0000000..8dab880
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XHasModifiers.kt
@@ -0,0 +1,42 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Common interface for elements which might have modifiers (e.g. field, method, class)
+ */
+interface XHasModifiers {
+ /**
+ * Returns `true` if this element is public (has public modifier in Java or not marked as
+ * private / internal in Kotlin).
+ */
+ fun isPublic(): Boolean
+
+ /**
+ * Returns `true` if this element has protected modifier.
+ */
+ fun isProtected(): Boolean
+
+ /**
+ * Returns `true` if this element is declared as abstract.
+ */
+ fun isAbstract(): Boolean
+
+ /**
+ * Returns `true` if this element has private modifier.
+ */
+ fun isPrivate(): Boolean
+
+ /**
+ * Returns `true` if this element has static modifier.
+ */
+ fun isStatic(): Boolean
+
+ /**
+ * Returns `true` if this element has transient modifier.
+ */
+ fun isTransient(): Boolean
+
+ /**
+ * Returns `true` if this element is final and cannot be overridden.
+ */
+ fun isFinal(): Boolean
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMemberContainer.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMemberContainer.kt
new file mode 100644
index 0000000..b9146f6
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMemberContainer.kt
@@ -0,0 +1,23 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.squareup.javapoet.ClassName
+
+/**
+ * Common interface for elements that can contain methods and properties.
+ *
+ * This is especially important for handling top level methods / properties in KSP where the
+ * synthetic container class does not exist
+ */
+interface XMemberContainer : XElement {
+ /**
+ * The JVM ClassName for this container.
+ * For top level members of a Kotlin file, you can use this [className] for code generation.
+ */
+ val className: ClassName
+
+ /**
+ * The [XType] for the container if this is an [XTypeElement] otherwise `null` if a type
+ * representing this container does not exist (e.g. a top level Kotlin source file)
+ */
+ val type: XType?
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMessager.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMessager.kt
new file mode 100644
index 0000000..a6fe287
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMessager.kt
@@ -0,0 +1,37 @@
+package com.zeoflow.memo.compiler.processing
+
+import javax.tools.Diagnostic
+
+/**
+ * Logging interface for the processor
+ */
+abstract class XMessager {
+ private val watchers = mutableListOf()
+ /**
+ * Prints the given [msg] to the logs while also associating it with the given [element].
+ *
+ * @param kind Kind of the message
+ * @param msg The actual message to report to the compiler
+ * @param element The element with whom the message should be associated with
+ */
+ fun printMessage(kind: Diagnostic.Kind, msg: String, element: XElement? = null) {
+ watchers.forEach {
+ it.printMessage(kind, msg, element)
+ }
+ onPrintMessage(kind, msg, element)
+ }
+
+ protected abstract fun onPrintMessage(
+ kind: Diagnostic.Kind,
+ msg: String,
+ element: XElement? = null
+ )
+
+ fun addMessageWatcher(watcher: XMessager) {
+ watchers.add(watcher)
+ }
+
+ fun removeMessageWatcher(watcher: XMessager) {
+ watchers.remove(watcher)
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMethodElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMethodElement.kt
new file mode 100644
index 0000000..695d8b0
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMethodElement.kt
@@ -0,0 +1,91 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Represents a method in a class / interface.
+ *
+ * @see XConstructorElement
+ * @see XMethodElement
+ */
+interface XMethodElement : XExecutableElement {
+ /**
+ * The name of the method.
+ */
+ val name: String
+
+ /**
+ * The return type for the method. Note that it might be [XType.isNone] if it does not return or
+ * [XType.isError] if the return type cannot be resolved.
+ */
+ val returnType: XType
+
+ /**
+ * The type representation of the method where more type parameters might be resolved.
+ */
+ val executableType: XMethodType
+
+ override val fallbackLocationText: String
+ get() = buildString {
+ append(enclosingElement.fallbackLocationText)
+ append(".")
+ append(name)
+ append("(")
+ // don't report last parameter if it is a suspend function
+ append(
+ parameters.dropLast(
+ if (isSuspendFunction()) 1 else 0
+ ).joinToString(", ") {
+ it.type.typeName.toString()
+ }
+ )
+ append(")")
+ }
+
+ /**
+ * Returns true if this method has the default modifier.
+ *
+ * @see [hasKotlinDefaultImpl]
+ */
+ fun isJavaDefault(): Boolean
+
+ /**
+ * Returns the method as if it is declared in [other].
+ *
+ * This is specifically useful if you have a method that has type arguments and there is a
+ * subclass ([other]) where type arguments are specified to actual types.
+ */
+ fun asMemberOf(other: XType): XMethodType
+
+ /**
+ * Returns true if this method has a default implementation in Kotlin.
+ *
+ * To support default methods in interfaces, Kotlin generates a delegate implementation. In
+ * Java, we find the DefaultImpls class to delegate the call. In kotlin, we get this information
+ * from KSP.
+ */
+ fun hasKotlinDefaultImpl(): Boolean
+
+ /**
+ * Returns true if this is a suspend function.
+ *
+ * @see XMethodType.getSuspendFunctionReturnType
+ */
+ fun isSuspendFunction(): Boolean
+
+ /**
+ * Returns true if this method can be overridden without checking its enclosing [XElement].
+ */
+ fun isOverrideableIgnoringContainer(): Boolean {
+ return !isFinal() && !isPrivate() && !isStatic()
+ }
+
+ /**
+ * Returns `true` if this method overrides the [other] method when this method is viewed as
+ * member of the [owner].
+ */
+ fun overrides(other: XMethodElement, owner: XTypeElement): Boolean
+
+ /**
+ * Creates a new [XMethodElement] where containing element is replaced with [newContainer].
+ */
+ fun copyTo(newContainer: XTypeElement): XMethodElement
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMethodType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMethodType.kt
new file mode 100644
index 0000000..95d14dd
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XMethodType.kt
@@ -0,0 +1,36 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.squareup.javapoet.TypeVariableName
+import kotlin.contracts.contract
+
+/**
+ * Represents a type information for a method.
+ *
+ * It is not an XType as it does not represent a class or primitive.
+ */
+interface XMethodType {
+ /**
+ * The return type of the method
+ */
+ val returnType: XType
+
+ /**
+ * Parameter types of the method.
+ */
+ val parameterTypes: List
+
+ /**
+ * Returns the names of [TypeVariableName]s for this executable.
+ */
+ val typeVariableNames: List
+}
+
+/**
+ * Returns `true` if this method type represents a suspend function
+ */
+fun XMethodType.isSuspendFunction(): Boolean {
+ contract {
+ returns(true) implies (this@isSuspendFunction is XSuspendMethodType)
+ }
+ return this is XSuspendMethodType
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XNullability.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XNullability.kt
new file mode 100644
index 0000000..8623880
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XNullability.kt
@@ -0,0 +1,25 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Declares the nullability of a type or element.
+ */
+enum class XNullability {
+ /**
+ * The type is guaranteed to be nullable. This means it is either a Kotlin Type declared with a
+ * `?` at the end or it is a Java type that has one of the `nullable` annotations (e.g.
+ * [androidx.annotation.Nullable].
+ */
+ NULLABLE,
+ /**
+ * The type is guaranteed to be nonnull. This means it is either a Kotlin Type declared
+ * without a `?` at the end or it is a Java type that has one of the `non-null` annotations
+ * (e.g. [androidx.annotation.NonNull].
+ */
+ NONNULL,
+ /**
+ * The nullability of the type is unknown. This happens if this is a non-primitive Java type
+ * that does not have a nullability annotation or a Type in Kotlin where it is inferred from
+ * the platform.
+ */
+ UNKNOWN
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingConfig.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingConfig.kt
new file mode 100644
index 0000000..93b2cda
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingConfig.kt
@@ -0,0 +1,16 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Utility class to change some behavior in tests, like adding more strict tests.
+ */
+internal object XProcessingConfig {
+ /**
+ * When true, we do more strict checks and fail instead of workarounds or fallback
+ * behaviors. Set to true in depot's own tests.
+ */
+ val STRICT_MODE by lazy {
+ System.getProperty("$PROP_PREFIX.strict").toBoolean()
+ }
+
+ private const val PROP_PREFIX = "com.zeoflow.depot.compiler.processing"
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingEnv.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingEnv.kt
new file mode 100644
index 0000000..7d7d17e
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingEnv.kt
@@ -0,0 +1,150 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.zeoflow.memo.compiler.processing.javac.JavacProcessingEnv
+import com.zeoflow.memo.compiler.processing.ksp.KspProcessingEnv
+import com.google.devtools.ksp.processing.CodeGenerator
+import com.google.devtools.ksp.processing.KSPLogger
+import com.google.devtools.ksp.processing.Resolver
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.TypeName
+import javax.annotation.processing.ProcessingEnvironment
+import kotlin.reflect.KClass
+
+/**
+ * API for a Processor that is either backed by Java's Annotation Processing API or KSP.
+ */
+@ExperimentalProcessingApi
+interface XProcessingEnv {
+
+ val backend: Backend
+ /**
+ * The logger interface to log messages
+ */
+ val messager: XMessager
+
+ /**
+ * List of options passed into the annotation processor
+ */
+ val options: Map
+
+ /**
+ * The API to generate files
+ */
+ val filer: XFiler
+
+ /**
+ * Looks for the [XTypeElement] with the given qualified name and returns `null` if it does not
+ * exist.
+ */
+ fun findTypeElement(qName: String): XTypeElement?
+
+ /**
+ * Looks for the [XType] with the given qualified name and returns `null` if it does not exist.
+ */
+ fun findType(qName: String): XType?
+
+ /**
+ * Returns the [XType] with the given qualified name or throws an exception if it does not
+ * exist.
+ */
+ fun requireType(qName: String): XType = checkNotNull(findType(qName)) {
+ "cannot find required type $qName"
+ }
+
+ /**
+ * Returns the [XTypeElement] for the annotation that should be added to the generated code.
+ */
+ fun findGeneratedAnnotation(): XTypeElement?
+
+ /**
+ * Returns an [XType] for the given [type] element with the type arguments specified
+ * as in [types].
+ */
+ fun getDeclaredType(type: XTypeElement, vararg types: XType): XType
+
+ /**
+ * Return an [XArrayType] that has [type] as the [XArrayType.componentType].
+ */
+ fun getArrayType(type: XType): XArrayType
+
+ /**
+ * Returns the [XTypeElement] with the given qualified name or throws an exception if it does
+ * not exist.
+ */
+ fun requireTypeElement(qName: String): XTypeElement {
+ return checkNotNull(findTypeElement(qName)) {
+ "Cannot find required type element $qName"
+ }
+ }
+
+ // helpers for smooth migration, these could be extension methods
+ fun requireType(typeName: TypeName) = checkNotNull(findType(typeName)) {
+ "cannot find required type $typeName"
+ }
+
+ fun requireType(klass: KClass<*>) = requireType(klass.java.canonicalName!!)
+
+ fun findType(typeName: TypeName): XType? {
+ // TODO we probably need more complicated logic here but right now depot only has these
+ // usages.
+ if (typeName is ArrayTypeName) {
+ return findType(typeName.componentType)?.let {
+ getArrayType(it)
+ }
+ }
+ return findType(typeName.toString())
+ }
+
+ fun findType(klass: KClass<*>) = findType(klass.java.canonicalName!!)
+
+ fun requireTypeElement(typeName: TypeName) = requireTypeElement(typeName.toString())
+
+ fun requireTypeElement(klass: KClass<*>) = requireTypeElement(klass.java.canonicalName!!)
+
+ fun findTypeElement(typeName: TypeName) = findTypeElement(typeName.toString())
+
+ fun findTypeElement(klass: KClass<*>) = findTypeElement(klass.java.canonicalName!!)
+
+ fun getArrayType(typeName: TypeName) = getArrayType(requireType(typeName))
+
+ enum class Backend {
+ JAVAC,
+ KSP
+ }
+
+ companion object {
+ /**
+ * Creates a new [XProcessingEnv] implementation derived from the given Java [env].
+ */
+ @JvmStatic
+ fun create(env: ProcessingEnvironment): XProcessingEnv = JavacProcessingEnv(env)
+
+ /**
+ * Creates a new [XProcessingEnv] implementation derived from the given KSP environment.
+ */
+ @JvmStatic
+ fun create(
+ options: Map,
+ resolver: Resolver,
+ codeGenerator: CodeGenerator,
+ logger: KSPLogger
+ ): XProcessingEnv = KspProcessingEnv(
+ options = options,
+ codeGenerator = codeGenerator,
+ logger = logger,
+ resolver = resolver
+ )
+ }
+
+ /**
+ * Returns [XTypeElement]s with the given package name. Note that this call can be expensive.
+ *
+ * @param packageName the package name to look up.
+ *
+ * @return A list of [XTypeElement] with matching package name. This will return declarations
+ * from both dependencies and source.
+ */
+ fun getTypeElementsFromPackage(packageName: String): List
+
+ // TODO: Add support for getting top level members in a package
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingStep.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingStep.kt
new file mode 100644
index 0000000..431b167
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XProcessingStep.kt
@@ -0,0 +1,89 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.zeoflow.memo.compiler.processing.javac.JavacElement
+import com.zeoflow.memo.compiler.processing.javac.JavacProcessingEnv
+import com.zeoflow.memo.compiler.processing.ksp.KspElement
+import com.zeoflow.memo.compiler.processing.ksp.KspProcessingEnv
+import com.google.auto.common.BasicAnnotationProcessor
+import com.google.common.collect.ImmutableSetMultimap
+import com.google.devtools.ksp.symbol.KSAnnotated
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Element
+
+/**
+ * Processing step to simplify processing a set of annotations.
+ */
+interface XProcessingStep {
+ /**
+ * The implementation of processing logic for the step. It is guaranteed that the keys in
+ * [elementsByAnnotation] will be a subset of the set returned by [annotations].
+ *
+ * @return the elements (a subset of the values of [elementsByAnnotation]) that this step
+ * is unable to process, possibly until a later processing round. These elements will be
+ * passed back to this step at the next round of processing.
+ */
+ fun process(
+ env: XProcessingEnv,
+ elementsByAnnotation: Map>
+ ): Set
+
+ /**
+ * The set of annotation qualified names processed by this step.
+ */
+ fun annotations(): Set
+
+ companion object {
+
+ /**
+ * Wraps current [XProcessingStep] into an Auto Common
+ * [BasicAnnotationProcessor.ProcessingStep].
+ */
+ @JvmStatic
+ fun XProcessingStep.asAutoCommonProcessor(
+ env: ProcessingEnvironment
+ ): BasicAnnotationProcessor.Step {
+ return JavacProcessingStepDelegate(
+ env = env,
+ delegate = this
+ )
+ }
+
+ @JvmStatic
+ fun XProcessingStep.executeInKsp(env: XProcessingEnv): List {
+ check(env is KspProcessingEnv)
+ val round = XRoundEnv.create(env)
+ val args = annotations().associateWith { annotation ->
+ round.getElementsAnnotatedWith(annotation)
+ }
+ return process(env, args)
+ .map { (it as KspElement).declaration }
+ }
+ }
+}
+
+internal class JavacProcessingStepDelegate(
+ val env: ProcessingEnvironment,
+ val delegate: XProcessingStep
+) : BasicAnnotationProcessor.Step {
+ override fun annotations(): Set = delegate.annotations()
+
+ @Suppress("UnstableApiUsage")
+ override fun process(
+ elementsByAnnotation: ImmutableSetMultimap
+ ): Set {
+ val converted = mutableMapOf>()
+ // create a new x processing environment for each step to ensure it can freely cache
+ // whatever it wants and we don't keep elements references across rounds.
+ val xEnv = JavacProcessingEnv(env)
+ annotations().forEach { annotation ->
+ val elements = elementsByAnnotation[annotation].mapNotNull { element ->
+ xEnv.wrapAnnotatedElement(element, annotation)
+ }.toSet()
+ converted[annotation] = elements
+ }
+ val result = delegate.process(xEnv, converted)
+ return result.map {
+ (it as JavacElement).element
+ }.toSet()
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XRawType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XRawType.kt
new file mode 100644
index 0000000..b65eb42
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XRawType.kt
@@ -0,0 +1,28 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.squareup.javapoet.TypeName
+
+/**
+ * It is common for processors to check certain types against known types.
+ * e.g. you may want to check if an [XType] is a [List], or an [Iterable] or is assignable from
+ * an [Iterable].
+ *
+ * Kotlin does not model raw types, which makes it harder for our java compatibility.
+ * Instead, we model them as [XRawType], a special purpose class.
+ *
+ * Similar to how [XMethodType] is not an [XType], [XRawType] is not an [XType] either. It has a
+ * very specific use case to check against raw types and nothing else.
+ *
+ * Instances of XRawType implement equality.
+ */
+interface XRawType {
+ val typeName: TypeName
+ /**
+ * Returns `true` if this raw type can be assigned from [other].
+ */
+ fun isAssignableFrom(other: XRawType): Boolean
+ /**
+ * Returns `true` if this raw type can be assigned from [other].
+ */
+ fun isAssignableFrom(other: XType) = isAssignableFrom(other.rawType)
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XRoundEnv.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XRoundEnv.kt
new file mode 100644
index 0000000..7aa3b05
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XRoundEnv.kt
@@ -0,0 +1,49 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.zeoflow.memo.compiler.processing.javac.JavacProcessingEnv
+import com.zeoflow.memo.compiler.processing.javac.JavacRoundEnv
+import com.zeoflow.memo.compiler.processing.ksp.KspProcessingEnv
+import com.zeoflow.memo.compiler.processing.ksp.KspRoundEnv
+import javax.annotation.processing.RoundEnvironment
+import kotlin.reflect.KClass
+
+/**
+ * Representation of an annotation processing round.
+ *
+ * @see javax.annotation.processing.RoundEnvironment
+ */
+interface XRoundEnv {
+ /**
+ * The root elements in the round.
+ */
+ val rootElements: Set
+
+ /**
+ * Returns the set of [XElement]s that are annotated with the given annotation [klass].
+ */
+ fun getElementsAnnotatedWith(klass: KClass): Set
+
+ fun getElementsAnnotatedWith(annotationQualifiedName: String): Set
+
+ companion object {
+ /**
+ * Creates an [XRoundEnv] from the given Java processing parameters.
+ */
+ @JvmStatic
+ fun create(
+ processingEnv: XProcessingEnv,
+ roundEnvironment: RoundEnvironment? = null
+ ): XRoundEnv {
+ return when (processingEnv) {
+ is JavacProcessingEnv -> {
+ checkNotNull(roundEnvironment)
+ JavacRoundEnv(processingEnv, roundEnvironment)
+ }
+ is KspProcessingEnv -> {
+ KspRoundEnv(processingEnv)
+ }
+ else -> error("invalid processing environment type: $processingEnv")
+ }
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XSuspendMethodType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XSuspendMethodType.kt
new file mode 100644
index 0000000..0fca011
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XSuspendMethodType.kt
@@ -0,0 +1,8 @@
+package com.zeoflow.memo.compiler.processing
+
+interface XSuspendMethodType : XMethodType {
+ /**
+ * IfReturns the real return type as seen by Kotlin.
+ */
+ fun getSuspendFunctionReturnType(): XType
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XType.kt
new file mode 100644
index 0000000..1ed4e09
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XType.kt
@@ -0,0 +1,220 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import kotlin.contracts.contract
+import kotlin.reflect.KClass
+
+/**
+ * Represents a type reference
+ *
+ * @see javax.lang.model.type.TypeMirror
+ * @see [XArrayType]
+ */
+interface XType {
+ /**
+ * The Javapoet [TypeName] representation of the type
+ */
+ val typeName: TypeName
+
+ /**
+ * Returns the rawType of this type. (e.g. `List` to `List`.
+ */
+ val rawType: XRawType
+
+ /**
+ * Nullability declared in the code.
+ * For Kotlin types, it will be inferred from type declaration.
+ * For Java types, it will be inferred from annotations.
+ */
+ val nullability: XNullability
+
+ /**
+ * The [XTypeElement] that represents this type.
+ *
+ * Note that it might be null if the type is not backed by a type element (e.g. if it is a
+ * primitive, wildcard etc)
+ *
+ * @see isTypeElement
+ */
+ val typeElement: XTypeElement?
+
+ /**
+ * Type arguments for the element. Note that they might be either placeholders or real
+ * resolvable types depending on the usage.
+ *
+ * If the type is not declared (e.g. a primitive), the list is empty.
+ *
+ * @see [javax.lang.model.type.DeclaredType.getTypeArguments]
+ */
+ val typeArguments: List
+
+ /**
+ * Returns `true` if this type can be assigned from [other]
+ */
+ fun isAssignableFrom(other: XType): Boolean
+
+ /**
+ * Returns `true` if this type can be assigned from [other] while ignoring the type variance.
+ */
+ fun isAssignableFromWithoutVariance(other: XType): Boolean {
+ return isAssignableWithoutVariance(other, this)
+ }
+
+ // TODO these is checks may need to be moved into the implementation.
+ // It is not yet clear how we will model some types in Kotlin (e.g. primitives)
+ /**
+ * Returns `true` if this is an error type.
+ */
+ fun isError(): Boolean
+
+ /**
+ * Returns the string representation of a possible default value for this type.
+ * (e.g. `0` for `int`, `null` for `String`)
+ */
+ fun defaultValue(): String
+
+ /**
+ * Returns boxed version of this type if it is a primitive or itself if it is not a primitive
+ * type.
+ */
+ fun boxed(): XType
+
+ /**
+ * Returns `true` if this is a [List]
+ */
+ fun isList(): Boolean = isTypeOf(List::class)
+
+ /**
+ * Returns `true` if this is the None type.
+ */
+ fun isNone(): Boolean
+
+ /**
+ * Returns `true` if this is the same raw type as [other]
+ */
+ fun isTypeOf(other: KClass<*>): Boolean
+
+ /**
+ * Returns `true` if this represents the same type as [other].
+ * TODO: decide on how we want to handle nullability here.
+ */
+ fun isSameType(other: XType): Boolean
+
+ /**
+ * Returns the extends bound if this is a wildcard or self.
+ */
+ fun extendsBoundOrSelf(): XType = extendsBound() ?: this
+
+ /**
+ * Returns `true` if this can be assigned from an instance of [other] without checking for
+ * variance.
+ */
+ fun isAssignableWithoutVariance(other: XType): Boolean {
+ return isAssignableWithoutVariance(other, this)
+ }
+
+ /**
+ * If this is a wildcard with an extends bound, returns that bounded typed.
+ */
+ fun extendsBound(): XType?
+
+ /**
+ * Creates a type with nullability [XNullability.NULLABLE] or returns this if the nullability is
+ * already [XNullability.NULLABLE].
+ */
+ fun makeNullable(): XType
+
+ /**
+ * Creates a type with nullability [XNullability.NONNULL] or returns this if the nullability is
+ * already [XNullability.NONNULL].
+ */
+ fun makeNonNullable(): XType
+}
+
+/**
+ * Returns true if this is an [XArrayType].
+ */
+fun XType.isArray(): Boolean {
+ contract {
+ returns(true) implies (this@isArray is XArrayType)
+ }
+ return this is XArrayType
+}
+
+/**
+ * Returns true if this is a [List] or [Set].
+ */
+fun XType.isCollection(): Boolean {
+ return isTypeOf(List::class) || isTypeOf(Set::class)
+}
+
+private fun isAssignableWithoutVariance(from: XType, to: XType): Boolean {
+ val assignable = to.isAssignableFrom(from)
+ if (assignable) {
+ return true
+ }
+ val fromTypeArgs = from.typeArguments
+ val toTypeArgs = to.typeArguments
+ // no type arguments, we don't need extra checks
+ if (fromTypeArgs.isEmpty() || fromTypeArgs.size != toTypeArgs.size) {
+ return false
+ }
+ // check erasure version first, if it does not match, no reason to proceed
+ if (!to.rawType.isAssignableFrom(from)) {
+ return false
+ }
+ // convert from args to their upper bounds if it exists
+ val fromExtendsBounds = fromTypeArgs.map {
+ it.extendsBound()
+ }
+ // if there are no upper bound conversions, return.
+ if (fromExtendsBounds.all { it == null }) {
+ return false
+ }
+ // try to move the types of the from to their upper bounds. It does not matter for the "to"
+ // because Types.isAssignable handles it as it is valid java
+ return (fromTypeArgs.indices).all { index ->
+ isAssignableWithoutVariance(
+ from = fromExtendsBounds[index] ?: fromTypeArgs[index],
+ to = toTypeArgs[index]
+ )
+ }
+}
+
+/**
+ * Returns `true` if this is a primitive or boxed it
+ */
+fun XType.isInt(): Boolean = typeName == TypeName.INT || typeName == KnownTypeNames.BOXED_INT
+
+/**
+ * Returns `true` if this is a primitive or boxed long
+ */
+fun XType.isLong(): Boolean = typeName == TypeName.LONG || typeName == KnownTypeNames.BOXED_LONG
+/**
+ * Returns `true` if this is `void`
+ */
+fun XType.isVoid() = typeName == TypeName.VOID
+
+/**
+ * Returns `true` if this is a [Void]
+ */
+fun XType.isVoidObject(): Boolean = typeName == KnownTypeNames.BOXED_VOID
+
+/**
+ * Returns `true` if this is the kotlin [Unit] type.
+ */
+fun XType.isKotlinUnit(): Boolean = typeName == KnownTypeNames.KOTLIN_UNIT
+
+/**
+ * Returns `true` if this represents a `byte`.
+ */
+fun XType.isByte(): Boolean = typeName == TypeName.BYTE || typeName == KnownTypeNames.BOXED_BYTE
+
+internal object KnownTypeNames {
+ val BOXED_VOID = TypeName.VOID.box()
+ val BOXED_INT = TypeName.INT.box()
+ val BOXED_LONG = TypeName.LONG.box()
+ val BOXED_BYTE = TypeName.BYTE.box()
+ val KOTLIN_UNIT = ClassName.get("kotlin", "Unit")
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XTypeElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XTypeElement.kt
new file mode 100644
index 0000000..3609847
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XTypeElement.kt
@@ -0,0 +1,144 @@
+package com.zeoflow.memo.compiler.processing
+
+import com.squareup.javapoet.ClassName
+
+interface XTypeElement : XHasModifiers, XElement, XMemberContainer {
+ /**
+ * The qualified name of the Class/Interface.
+ */
+ val qualifiedName: String
+
+ /**
+ * The qualified name of the package that contains this element.
+ */
+ val packageName: String
+
+ /**
+ * SimpleName of the type converted to String.
+ *
+ * @see [javax.lang.model.element.Element.getSimpleName]
+ */
+ val name: String
+
+ /**
+ * The type represented by this [XTypeElement].
+ */
+ override val type: XType
+
+ /**
+ * The super type of this element if it represents a class.
+ */
+ val superType: XType?
+
+ /**
+ * Javapoet [ClassName] of the type.
+ */
+ override val className: ClassName
+
+ /**
+ * The [XTypeElement] that contains this [XTypeElement] if it is an inner class/interface.
+ */
+ val enclosingTypeElement: XTypeElement?
+
+ override val fallbackLocationText: String
+ get() = qualifiedName
+
+ /**
+ * Returns `true` if this [XTypeElement] represents an interface
+ */
+ fun isInterface(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a Kotlin functional interface,
+ * i.e. marked with the keyword `fun`
+ */
+ fun isFunctionalInterface(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents an ordinary class. ie not an enum, object,
+ * annotation, interface, or other type of specialty class.
+ */
+ fun isClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a Kotlin data class
+ */
+ fun isDataClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a Kotlin value class
+ */
+ fun isValueClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a class with the Kotlin `expect` keyword
+ */
+ fun isExpect(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] represents a Kotlin annotation class or a Java
+ * annotation type.
+ */
+ fun isAnnotationClass(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] is a non-companion `object` in Kotlin
+ */
+ fun isKotlinObject(): Boolean
+
+ /**
+ * Returns `true` if this [XTypeElement] is declared as a Kotlin `companion object`
+ */
+ fun isCompanionObject(): Boolean
+
+ /**
+ * All fields, including private supers.
+ * Depot only ever reads fields this way.
+ */
+ fun getAllFieldsIncludingPrivateSupers(): List
+
+ /**
+ * Returns the primary constructor for the type, if it exists.
+ *
+ * Note that this only exists for classes declared in Kotlin.
+ */
+ fun findPrimaryConstructor(): XConstructorElement?
+
+ /**
+ * methods declared in this type
+ * includes all instance/static methods in this
+ */
+ fun getDeclaredMethods(): List
+
+ /**
+ * Methods declared in this type and its parents
+ * includes all instance/static methods in this
+ * includes all instance/static methods in parent CLASS if they are accessible from this (e.g.
+ * not private).
+ * does not include static methods in parent interfaces
+ */
+ fun getAllMethods(): List {
+ return collectAllMethods()
+ }
+
+ /**
+ * Instance methods declared in this and supers
+ * include non private instance methods
+ * also includes non-private instance methods from supers
+ */
+ fun getAllNonPrivateInstanceMethods(): List {
+ return getAllMethods().filter {
+ !it.isPrivate() && !it.isStatic()
+ }
+ }
+
+ /**
+ * Returns the list of constructors in this type element
+ */
+ fun getConstructors(): List
+
+ /**
+ * List of interfaces implemented by this class
+ */
+ fun getSuperInterfaceElements(): List
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XVariableElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XVariableElement.kt
new file mode 100644
index 0000000..507fd9c
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/XVariableElement.kt
@@ -0,0 +1,29 @@
+package com.zeoflow.memo.compiler.processing
+
+/**
+ * Represents a variable element, that is either a method parameter or a field.
+ */
+interface XVariableElement : XElement {
+
+ /**
+ * The value for the variable element.
+ */
+ val constantValue: Any?
+
+ /**
+ * The name of the variable element.
+ */
+ val name: String
+
+ /**
+ * Returns the type of this field or parameter
+ */
+ val type: XType
+
+ /**
+ * Returns this type as a member of the [other] type.
+ * It is useful when this [XVariableElement] has a generic type declaration and its type is
+ * specified in [other]. (e.g. Bar vs Foo : Bar)
+ */
+ fun asMemberOf(other: XType): XType
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/DefaultJavacType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/DefaultJavacType.kt
new file mode 100644
index 0000000..51d3ee2
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/DefaultJavacType.kt
@@ -0,0 +1,60 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.XType
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import javax.lang.model.type.TypeMirror
+
+/**
+ * Catch-all class for XType implementation when we don't need/discover a sub-type
+ */
+internal class DefaultJavacType private constructor(
+ env: JavacProcessingEnv,
+ typeMirror: TypeMirror,
+ override val nullability: XNullability,
+ override val kotlinType: KmType?
+) : JavacType(
+ env, typeMirror
+) {
+ constructor(
+ env: JavacProcessingEnv,
+ typeMirror: TypeMirror,
+ kotlinType: KmType
+ ) : this(
+ env = env,
+ typeMirror = typeMirror,
+ nullability = kotlinType.nullability,
+ kotlinType = kotlinType
+ )
+
+ constructor(
+ env: JavacProcessingEnv,
+ typeMirror: TypeMirror,
+ nullability: XNullability
+ ) : this(
+ env = env,
+ typeMirror = typeMirror,
+ nullability = nullability,
+ kotlinType = null
+ )
+
+ override val equalityItems by lazy {
+ arrayOf(typeMirror)
+ }
+
+ override val typeArguments: List
+ /**
+ * This is always empty because if the type mirror is declared, we wrap it in a
+ * JavacDeclaredType.
+ */
+ get() = emptyList()
+
+ override fun copyWithNullability(nullability: XNullability): JavacType {
+ return DefaultJavacType(
+ env = env,
+ typeMirror = typeMirror,
+ kotlinType = kotlinType,
+ nullability = nullability
+ )
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/ElementExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/ElementExt.kt
new file mode 100644
index 0000000..2a57a19
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/ElementExt.kt
@@ -0,0 +1,81 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.util.ElementFilter
+import javax.lang.model.util.Elements
+
+private val NONNULL_ANNOTATIONS = arrayOf(
+ androidx.annotation.NonNull::class.java,
+ org.jetbrains.annotations.NotNull::class.java
+)
+
+private val NULLABLE_ANNOTATIONS = arrayOf(
+ androidx.annotation.Nullable::class.java,
+ org.jetbrains.annotations.Nullable::class.java
+)
+
+/**
+ * Returns all fields including private fields (including private fields in super). Removes
+ * duplicate fields if class has a field with the same name as the parent.
+ * Note that enum constants are not included in the list even thought they are fields in java.
+ * To access enum constants, use [JavacTypeElement.JavacEnumTypeElement].
+ */
+internal fun TypeElement.getAllFieldsIncludingPrivateSupers(
+ elementUtils: Elements
+): Set {
+ val selection = ElementFilter
+ .fieldsIn(elementUtils.getAllMembers(this))
+ .filterIsInstance()
+ .filterNot { it.kind == ElementKind.ENUM_CONSTANT }
+ .toMutableSet()
+ val selectionNames = selection.mapTo(mutableSetOf()) {
+ it.simpleName
+ }
+ if (superclass.kind != TypeKind.NONE) {
+ val superFields = MoreTypes.asTypeElement(superclass)
+ .getAllFieldsIncludingPrivateSupers(elementUtils)
+ // accept super fields only if the name does not conflict
+ superFields.forEach { superField ->
+ if (selectionNames.add(superField.simpleName)) {
+ selection.add(superField)
+ }
+ }
+ }
+ return selection
+}
+
+@Suppress("UnstableApiUsage")
+private fun Element.hasAnyOf(annotations: Array>) = annotations.any {
+ MoreElements.isAnnotationPresent(this, it)
+}
+
+internal val Element.nullability: XNullability
+ get() = if (asType().kind.isPrimitive || hasAnyOf(NONNULL_ANNOTATIONS)) {
+ XNullability.NONNULL
+ } else if (hasAnyOf(NULLABLE_ANNOTATIONS)) {
+ XNullability.NULLABLE
+ } else {
+ XNullability.UNKNOWN
+ }
+
+internal fun Element.requireEnclosingType(env: JavacProcessingEnv): JavacTypeElement {
+ return checkNotNull(enclosingType(env)) {
+ "Cannot find required enclosing type for $this"
+ }
+}
+
+@Suppress("UnstableApiUsage")
+internal fun Element.enclosingType(env: JavacProcessingEnv): JavacTypeElement? {
+ return if (MoreElements.isType(enclosingElement)) {
+ env.wrapTypeElement(MoreElements.asType(enclosingElement))
+ } else {
+ null
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacAnnotationBox.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacAnnotationBox.kt
new file mode 100644
index 0000000..6e0de1d
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacAnnotationBox.kt
@@ -0,0 +1,273 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XAnnotationBox
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.XType
+import com.google.auto.common.AnnotationMirrors
+import java.lang.reflect.Proxy
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.AnnotationValue
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.SimpleAnnotationValueVisitor6
+
+internal interface JavacClassGetter {
+ fun getAsType(methodName: String): XType?
+ fun getAsTypeList(methodName: String): List
+ fun getAsAnnotationBox(methodName: String): XAnnotationBox
+ fun getAsAnnotationBoxArray(methodName: String): Array>
+}
+
+/**
+ * Class that helps to read values from annotations. Simple types as string, int, lists can
+ * be read from [value]. If you need to read classes or another annotations from annotation use
+ * [getAsType], [getAsAnnotationBox] and [getAsAnnotationBoxArray] correspondingly.
+ */
+internal class JavacAnnotationBox(obj: Any) : XAnnotationBox {
+ private val classGetter = obj as JavacClassGetter
+
+ @Suppress("UNCHECKED_CAST")
+ override val value: T = obj as T
+ override fun getAsType(methodName: String): XType? = classGetter.getAsType(methodName)
+
+ override fun getAsTypeList(methodName: String): List =
+ classGetter.getAsTypeList(methodName)
+
+ override fun getAsAnnotationBox(methodName: String): XAnnotationBox {
+ return classGetter.getAsAnnotationBox(methodName)
+ }
+
+ override fun getAsAnnotationBoxArray(
+ methodName: String
+ ): Array> {
+ return classGetter.getAsAnnotationBoxArray(methodName)
+ }
+}
+
+internal fun AnnotationMirror.box(
+ env: JavacProcessingEnv,
+ cl: Class
+): JavacAnnotationBox {
+ if (!cl.isAnnotation) {
+ throw IllegalArgumentException("$cl is not annotation")
+ }
+ val map = cl.declaredMethods.associate { method ->
+ val value = AnnotationMirrors.getAnnotationValue(this, method.name)
+ val returnType = method.returnType
+ val defaultValue = method.defaultValue
+ val result: Any? = when {
+ returnType == Boolean::class.java -> value.getAsBoolean(defaultValue as Boolean)
+ returnType == String::class.java -> value.getAsString(defaultValue as String?)
+ returnType == Array::class.java -> value.getAsStringList().toTypedArray()
+ returnType == emptyArray>()::class.java -> value.toListOfClassTypes(env)
+ returnType == IntArray::class.java -> value.getAsIntList().toIntArray()
+ returnType == Class::class.java -> {
+ try {
+ value.toClassType(env)
+ } catch (notPresent: TypeNotPresentException) {
+ null
+ }
+ }
+ returnType == Int::class.java -> value.getAsInt(defaultValue as Int?)
+ returnType.isAnnotation -> {
+ @Suppress("UNCHECKED_CAST")
+ AnnotationClassVisitor(env, returnType as Class).visit(value)
+ }
+ returnType.isArray && returnType.componentType.isAnnotation -> {
+ @Suppress("UNCHECKED_CAST")
+ AnnotationListVisitor(env, returnType.componentType as Class)
+ .visit(value)
+ }
+ returnType.isArray && returnType.componentType.isEnum -> {
+ @Suppress("UNCHECKED_CAST")
+ EnumListVisitor(returnType.componentType as Class>).visit(value)
+ }
+ returnType.isEnum -> {
+ @Suppress("UNCHECKED_CAST")
+ value.getAsEnum(returnType as Class>)
+ }
+ else -> {
+ throw UnsupportedOperationException("$returnType isn't supported")
+ }
+ }
+ method.name to result
+ }
+ return JavacAnnotationBox(
+ Proxy.newProxyInstance(
+ JavacClassGetter::class.java.classLoader,
+ arrayOf(cl, JavacClassGetter::class.java)
+ ) { _, method, args ->
+ when (method.name) {
+ JavacClassGetter::getAsType.name -> map[args[0]]
+ JavacClassGetter::getAsTypeList.name -> map[args[0]]
+ "getAsAnnotationBox" -> map[args[0]]
+ "getAsAnnotationBoxArray" -> map[args[0]]
+ else -> map[method.name]
+ }
+ }
+ )
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_TO_INT_VISITOR = object : SimpleAnnotationValueVisitor6() {
+ override fun visitInt(i: Int, p: Void?): Int? {
+ return i
+ }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_TO_BOOLEAN_VISITOR = object :
+ SimpleAnnotationValueVisitor6() {
+ override fun visitBoolean(b: Boolean, p: Void?): Boolean? {
+ return b
+ }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_TO_STRING_VISITOR = object :
+ SimpleAnnotationValueVisitor6() {
+ override fun visitString(s: String?, p: Void?): String? {
+ return s
+ }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_STRING_ARR_VISITOR = object :
+ SimpleAnnotationValueVisitor6, Void>() {
+ override fun visitArray(vals: MutableList?, p: Void?): List {
+ return vals?.mapNotNull {
+ ANNOTATION_VALUE_TO_STRING_VISITOR.visit(it)
+ } ?: emptyList()
+ }
+}
+
+@Suppress("DEPRECATION")
+private val ANNOTATION_VALUE_INT_ARR_VISITOR = object :
+ SimpleAnnotationValueVisitor6, Void>() {
+ override fun visitArray(vals: MutableList?, p: Void?): List {
+ return vals?.mapNotNull {
+ ANNOTATION_VALUE_TO_INT_VISITOR.visit(it)
+ } ?: emptyList()
+ }
+}
+
+private fun AnnotationValue.getAsInt(def: Int? = null): Int? {
+ return ANNOTATION_VALUE_TO_INT_VISITOR.visit(this) ?: def
+}
+
+private fun AnnotationValue.getAsIntList(): List {
+ return ANNOTATION_VALUE_INT_ARR_VISITOR.visit(this)
+}
+
+private fun AnnotationValue.getAsString(def: String? = null): String? {
+ return ANNOTATION_VALUE_TO_STRING_VISITOR.visit(this) ?: def
+}
+
+private fun AnnotationValue.getAsBoolean(def: Boolean): Boolean {
+ return ANNOTATION_VALUE_TO_BOOLEAN_VISITOR.visit(this) ?: def
+}
+
+private fun AnnotationValue.getAsStringList(): List {
+ return ANNOTATION_VALUE_STRING_ARR_VISITOR.visit(this)
+}
+
+// code below taken from dagger2
+// compiler/src/main/java/dagger/internal/codegen/ConfigurationAnnotations.java
+@Suppress("DEPRECATION")
+private val TO_LIST_OF_TYPES = object :
+ SimpleAnnotationValueVisitor6, Void?>() {
+ override fun visitArray(values: MutableList?, p: Void?): List {
+ return values?.mapNotNull {
+ val tmp = TO_TYPE.visit(it)
+ tmp
+ } ?: emptyList()
+ }
+
+ override fun defaultAction(o: Any?, p: Void?): List? {
+ return emptyList()
+ }
+}
+
+@Suppress("DEPRECATION")
+private val TO_TYPE = object : SimpleAnnotationValueVisitor6() {
+
+ override fun visitType(t: TypeMirror, p: Void?): TypeMirror {
+ return t
+ }
+
+ override fun defaultAction(o: Any?, p: Void?): TypeMirror {
+ throw TypeNotPresentException(o!!.toString(), null)
+ }
+}
+
+private fun AnnotationValue.toListOfClassTypes(env: JavacProcessingEnv): List {
+ return TO_LIST_OF_TYPES.visit(this).map {
+ env.wrap(
+ typeMirror = it,
+ kotlinType = null,
+ elementNullability = XNullability.UNKNOWN
+ )
+ }
+}
+
+private fun AnnotationValue.toClassType(env: JavacProcessingEnv): XType? {
+ return TO_TYPE.visit(this)?.let {
+ env.wrap(
+ typeMirror = it,
+ kotlinType = null,
+ elementNullability = XNullability.UNKNOWN
+ )
+ }
+}
+
+@Suppress("DEPRECATION")
+private class AnnotationListVisitor(
+ private val env: JavacProcessingEnv,
+ private val annotationClass: Class
+) :
+ SimpleAnnotationValueVisitor6>, Void?>() {
+ override fun visitArray(
+ values: MutableList?,
+ void: Void?
+ ): Array> {
+ val visitor = AnnotationClassVisitor(env, annotationClass)
+ return values?.mapNotNull { visitor.visit(it) }?.toTypedArray() ?: emptyArray()
+ }
+}
+
+@Suppress("DEPRECATION")
+private class EnumListVisitor>(private val enumClass: Class) :
+ SimpleAnnotationValueVisitor6, Void?>() {
+ override fun visitArray(
+ values: MutableList?,
+ void: Void?
+ ): Array {
+ val result = values?.map { it.getAsEnum(enumClass) }
+ @Suppress("UNCHECKED_CAST")
+ val resultArray = java.lang.reflect.Array
+ .newInstance(enumClass, result?.size ?: 0) as Array
+ result?.forEachIndexed { index, value ->
+ resultArray[index] = value
+ }
+ return resultArray
+ }
+}
+
+@Suppress("DEPRECATION")
+private class AnnotationClassVisitor(
+ private val env: JavacProcessingEnv,
+ private val annotationClass: Class
+) :
+ SimpleAnnotationValueVisitor6?, Void?>() {
+ override fun visitAnnotation(a: AnnotationMirror?, v: Void?) = a?.box(env, annotationClass)
+}
+
+@Suppress("UNCHECKED_CAST", "DEPRECATION", "BanUncheckedReflection")
+private fun > AnnotationValue.getAsEnum(enumClass: Class): T {
+ return object : SimpleAnnotationValueVisitor6() {
+ override fun visitEnumConstant(value: VariableElement?, p: Void?): T {
+ return enumClass.getDeclaredMethod("valueOf", String::class.java)
+ .invoke(null, value!!.simpleName.toString()) as T
+ }
+ }.visit(this)
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacArrayType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacArrayType.kt
new file mode 100644
index 0000000..ee51d00
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacArrayType.kt
@@ -0,0 +1,76 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XArrayType
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.XType
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import javax.lang.model.type.ArrayType
+
+internal class JavacArrayType private constructor(
+ env: JavacProcessingEnv,
+ override val typeMirror: ArrayType,
+ override val nullability: XNullability,
+ private val knownComponentNullability: XNullability?,
+ override val kotlinType: KmType?
+) : JavacType(
+ env,
+ typeMirror
+),
+ XArrayType {
+ constructor(
+ env: JavacProcessingEnv,
+ typeMirror: ArrayType,
+ kotlinType: KmType
+ ) : this(
+ env = env,
+ typeMirror = typeMirror,
+ nullability = kotlinType.nullability,
+ knownComponentNullability = kotlinType.typeArguments.firstOrNull()?.nullability,
+ kotlinType = kotlinType
+ )
+
+ constructor(
+ env: JavacProcessingEnv,
+ typeMirror: ArrayType,
+ nullability: XNullability,
+ knownComponentNullability: XNullability?
+ ) : this(
+ env = env,
+ typeMirror = typeMirror,
+ nullability = nullability,
+ knownComponentNullability = knownComponentNullability,
+ kotlinType = null
+ )
+
+ override val equalityItems: Array by lazy {
+ arrayOf(typeMirror)
+ }
+
+ override val typeArguments: List
+ get() = emptyList()
+
+ override val componentType: XType by lazy {
+ val componentType = typeMirror.componentType
+ val componentTypeNullability =
+ knownComponentNullability ?: if (componentType.kind.isPrimitive) {
+ XNullability.NONNULL
+ } else {
+ XNullability.UNKNOWN
+ }
+ env.wrap(
+ typeMirror = componentType,
+ kotlinType = kotlinType?.typeArguments?.firstOrNull(),
+ elementNullability = componentTypeNullability
+ )
+ }
+
+ override fun copyWithNullability(nullability: XNullability): JavacType {
+ return JavacArrayType(
+ env = env,
+ typeMirror = typeMirror,
+ nullability = nullability,
+ knownComponentNullability = knownComponentNullability,
+ kotlinType = kotlinType
+ )
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacConstructorElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacConstructorElement.kt
new file mode 100644
index 0000000..e165a41
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacConstructorElement.kt
@@ -0,0 +1,32 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XConstructorElement
+import com.zeoflow.memo.compiler.processing.XTypeElement
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmConstructor
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+
+internal class JavacConstructorElement(
+ env: JavacProcessingEnv,
+ containing: JavacTypeElement,
+ element: ExecutableElement
+) : JavacExecutableElement(
+ env,
+ containing,
+ element
+),
+ XConstructorElement {
+ init {
+ check(element.kind == ElementKind.CONSTRUCTOR) {
+ "Constructor element is constructed with invalid type: $element"
+ }
+ }
+
+ override val enclosingElement: XTypeElement by lazy {
+ element.requireEnclosingType(env)
+ }
+
+ override val kotlinMetadata: KmConstructor? by lazy {
+ (enclosingElement as? JavacTypeElement)?.kotlinMetadata?.getConstructorMetadata(element)
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacDeclaredType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacDeclaredType.kt
new file mode 100644
index 0000000..a3c8e1d
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacDeclaredType.kt
@@ -0,0 +1,64 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import javax.lang.model.type.DeclaredType
+
+/**
+ * Declared types are different from non declared types in java (e.g. primitives, or wildcard
+ * types). Even thought XProcessing does not distinguish between these these, in the java
+ * implementation, it is handy to have a separate type for explicit typeMirror information.
+ */
+internal class JavacDeclaredType private constructor(
+ env: JavacProcessingEnv,
+ override val typeMirror: DeclaredType,
+ override val nullability: XNullability,
+ override val kotlinType: KmType?
+) : JavacType(
+ env, typeMirror
+) {
+ constructor(
+ env: JavacProcessingEnv,
+ typeMirror: DeclaredType,
+ kotlinType: KmType
+ ) : this(
+ env = env,
+ typeMirror = typeMirror,
+ nullability = kotlinType.nullability,
+ kotlinType = kotlinType
+ )
+
+ constructor(
+ env: JavacProcessingEnv,
+ typeMirror: DeclaredType,
+ nullability: XNullability
+ ) : this(
+ env = env,
+ typeMirror = typeMirror,
+ nullability = nullability,
+ kotlinType = null
+ )
+
+ override val equalityItems: Array by lazy {
+ arrayOf(typeMirror)
+ }
+
+ override val typeArguments: List by lazy {
+ typeMirror.typeArguments.mapIndexed { index, typeMirror ->
+ env.wrap(
+ typeMirror = typeMirror,
+ kotlinType = kotlinType?.typeArguments?.getOrNull(index),
+ elementNullability = XNullability.UNKNOWN
+ )
+ }
+ }
+
+ override fun copyWithNullability(nullability: XNullability): JavacDeclaredType {
+ return JavacDeclaredType(
+ env = env,
+ typeMirror = typeMirror,
+ kotlinType = kotlinType,
+ nullability = nullability
+ )
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacElement.kt
new file mode 100644
index 0000000..e86cac4
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacElement.kt
@@ -0,0 +1,77 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XAnnotationBox
+import com.zeoflow.memo.compiler.processing.XElement
+import com.zeoflow.memo.compiler.processing.XEquality
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreElements.isAnnotationPresent
+import com.zeoflow.memo.compiler.processing.InternalXAnnotated
+import java.util.Locale
+import javax.lang.model.element.Element
+import kotlin.reflect.KClass
+
+@Suppress("UnstableApiUsage")
+internal abstract class JavacElement(
+ protected val env: JavacProcessingEnv,
+ open val element: Element
+) : XElement, XEquality, InternalXAnnotated {
+ override fun getAnnotations(
+ annotation: KClass,
+ containerAnnotation: KClass?
+ ): List> {
+ // if there is a container annotation and annotation is repeated, we'll get the container.
+ if (containerAnnotation != null) {
+ MoreElements
+ .getAnnotationMirror(element, containerAnnotation.java)
+ .orNull()
+ ?.box(env, containerAnnotation.java)
+ ?.let { containerBox ->
+ // found a container, return
+ return containerBox.getAsAnnotationBoxArray("value").toList()
+ }
+ }
+ // if there is no container annotation or annotation is not repeated, we'll see the
+ // individual value
+ return MoreElements
+ .getAnnotationMirror(element, annotation.java)
+ .orNull()
+ ?.box(env, annotation.java)
+ ?.let {
+ listOf(it)
+ } ?: emptyList()
+ }
+
+ override fun hasAnnotation(
+ annotation: KClass,
+ containerAnnotation: KClass?
+ ): Boolean {
+ return isAnnotationPresent(element, annotation.java) ||
+ (containerAnnotation != null && isAnnotationPresent(element, containerAnnotation.java))
+ }
+
+ override fun toString(): String {
+ return element.toString()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return XEquality.equals(this, other)
+ }
+
+ override fun hashCode(): Int {
+ return XEquality.hashCode(equalityItems)
+ }
+
+ override fun kindName(): String {
+ return element.kind.name.lowercase(Locale.US)
+ }
+
+ override fun hasAnnotationWithPackage(pkg: String): Boolean {
+ return element.annotationMirrors.any {
+ MoreElements.getPackage(it.annotationType.asElement()).toString() == pkg
+ }
+ }
+
+ override val docComment: String? by lazy {
+ env.elementUtils.getDocComment(element)
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacExecutableElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacExecutableElement.kt
new file mode 100644
index 0000000..902d4cf
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacExecutableElement.kt
@@ -0,0 +1,48 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XExecutableElement
+import com.zeoflow.memo.compiler.processing.XHasModifiers
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmExecutable
+import com.zeoflow.memo.compiler.processing.javac.kotlin.descriptor
+import javax.lang.model.element.ExecutableElement
+
+internal abstract class JavacExecutableElement(
+ env: JavacProcessingEnv,
+ val containing: JavacTypeElement,
+ override val element: ExecutableElement
+) : JavacElement(
+ env,
+ element
+),
+ XExecutableElement,
+ XHasModifiers by JavacHasModifiers(element) {
+ abstract val kotlinMetadata: KmExecutable?
+
+ val descriptor by lazy {
+ element.descriptor()
+ }
+
+ override val parameters: List by lazy {
+ element.parameters.mapIndexed { index, variable ->
+ JavacMethodParameter(
+ env = env,
+ executable = this,
+ containing = containing,
+ element = variable,
+ kotlinMetadata = kotlinMetadata?.parameters?.getOrNull(index)
+ )
+ }
+ }
+
+ override val equalityItems: Array by lazy {
+ arrayOf(element, containing)
+ }
+
+ override fun isVarArgs(): Boolean {
+ return element.isVarArgs
+ }
+
+ companion object {
+ internal const val DEFAULT_IMPLS_CLASS_NAME = "DefaultImpls"
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacFieldElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacFieldElement.kt
new file mode 100644
index 0000000..9705f47
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacFieldElement.kt
@@ -0,0 +1,28 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XFieldElement
+import com.zeoflow.memo.compiler.processing.XHasModifiers
+import com.zeoflow.memo.compiler.processing.XTypeElement
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmProperty
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import javax.lang.model.element.VariableElement
+
+internal class JavacFieldElement(
+ env: JavacProcessingEnv,
+ containing: JavacTypeElement,
+ element: VariableElement
+) : JavacVariableElement(env, containing, element),
+ XFieldElement,
+ XHasModifiers by JavacHasModifiers(element) {
+
+ private val kotlinMetadata: KmProperty? by lazy {
+ (enclosingElement as? JavacTypeElement)?.kotlinMetadata?.getPropertyMetadata(name)
+ }
+
+ override val kotlinType: KmType?
+ get() = kotlinMetadata?.type
+
+ override val enclosingElement: XTypeElement by lazy {
+ element.requireEnclosingType(env)
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacFiler.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacFiler.kt
new file mode 100644
index 0000000..8bf0a42
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacFiler.kt
@@ -0,0 +1,23 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XFiler
+import com.squareup.javapoet.JavaFile
+import com.squareup.kotlinpoet.FileSpec
+import javax.annotation.processing.ProcessingEnvironment
+
+internal class JavacFiler(val processingEnv: ProcessingEnvironment) : XFiler {
+
+ // "mode" is ignored in javac, and only applicable in KSP
+ override fun write(javaFile: JavaFile, mode: XFiler.Mode) {
+ javaFile.writeTo(processingEnv.filer)
+ }
+
+ override fun write(fileSpec: FileSpec, mode: XFiler.Mode) {
+ require(processingEnv.options.containsKey("kapt.kotlin.generated")) {
+ val filePath = fileSpec.packageName.replace('.', '/')
+ "Could not generate kotlin file $filePath/${fileSpec.name}.kt. The " +
+ "annotation processing environment is not set to generate Kotlin files."
+ }
+ fileSpec.writeTo(processingEnv.filer)
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacHasModifiers.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacHasModifiers.kt
new file mode 100644
index 0000000..943efcf
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacHasModifiers.kt
@@ -0,0 +1,39 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XHasModifiers
+import javax.lang.model.element.Element
+import javax.lang.model.element.Modifier
+
+/**
+ * Implementation of [XHasModifiers] for java elements
+ */
+internal class JavacHasModifiers(private val element: Element) : XHasModifiers {
+
+ override fun isPublic(): Boolean {
+ return element.modifiers.contains(Modifier.PUBLIC)
+ }
+
+ override fun isProtected(): Boolean {
+ return element.modifiers.contains(Modifier.PROTECTED)
+ }
+
+ override fun isAbstract(): Boolean {
+ return element.modifiers.contains(Modifier.ABSTRACT)
+ }
+
+ override fun isPrivate(): Boolean {
+ return element.modifiers.contains(Modifier.PRIVATE)
+ }
+
+ override fun isStatic(): Boolean {
+ return element.modifiers.contains(Modifier.STATIC)
+ }
+
+ override fun isTransient(): Boolean {
+ return element.modifiers.contains(Modifier.TRANSIENT)
+ }
+
+ override fun isFinal(): Boolean {
+ return element.modifiers.contains(Modifier.FINAL)
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodElement.kt
new file mode 100644
index 0000000..48aa3e1
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodElement.kt
@@ -0,0 +1,136 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XMethodElement
+import com.zeoflow.memo.compiler.processing.XMethodType
+import com.zeoflow.memo.compiler.processing.XType
+import com.zeoflow.memo.compiler.processing.XTypeElement
+import com.zeoflow.memo.compiler.processing.XVariableElement
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmFunction
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+
+internal class JavacMethodElement(
+ env: JavacProcessingEnv,
+ containing: JavacTypeElement,
+ element: ExecutableElement
+) : JavacExecutableElement(
+ env,
+ containing,
+ element
+),
+ XMethodElement {
+ init {
+ check(element.kind == ElementKind.METHOD) {
+ "Method element is constructed with invalid type: $element"
+ }
+ }
+
+ override val name: String
+ get() = element.simpleName.toString()
+
+ override val enclosingElement: XTypeElement by lazy {
+ element.requireEnclosingType(env)
+ }
+
+ override val kotlinMetadata: KmFunction? by lazy {
+ (enclosingElement as? JavacTypeElement)?.kotlinMetadata?.getFunctionMetadata(element)
+ }
+
+ override val executableType: JavacMethodType by lazy {
+ val asMemberOf = env.typeUtils.asMemberOf(containing.type.typeMirror, element)
+ JavacMethodType.create(
+ env = env,
+ element = this,
+ executableType = MoreTypes.asExecutable(asMemberOf)
+ )
+ }
+
+ override val returnType: JavacType by lazy {
+ val asMember = env.typeUtils.asMemberOf(containing.type.typeMirror, element)
+ val asExec = MoreTypes.asExecutable(asMember)
+ env.wrap(
+ typeMirror = asExec.returnType,
+ kotlinType = if (isSuspendFunction()) {
+ // Don't use Kotlin metadata for suspend functions since we want the Java
+ // perspective. In Java, a suspend function returns Object and contains an extra
+ // parameter of type Continuation extends T> where T is the actual return type as
+ // declared in the Kotlin source.
+ null
+ } else {
+ kotlinMetadata?.returnType
+ },
+ elementNullability = element.nullability
+ )
+ }
+
+ override fun asMemberOf(other: XType): XMethodType {
+ return if (other !is JavacDeclaredType || containing.type.isSameType(other)) {
+ executableType
+ } else {
+ val asMemberOf = env.typeUtils.asMemberOf(other.typeMirror, element)
+ JavacMethodType.create(
+ env = env,
+ element = this,
+ executableType = MoreTypes.asExecutable(asMemberOf)
+ )
+ }
+ }
+
+ override fun isJavaDefault() = element.modifiers.contains(Modifier.DEFAULT)
+
+ override fun isSuspendFunction() = kotlinMetadata?.isSuspend() == true
+
+ override fun overrides(other: XMethodElement, owner: XTypeElement): Boolean {
+ check(other is JavacMethodElement)
+ check(owner is JavacTypeElement)
+ return env.elementUtils.overrides(element, other.element, owner.element)
+ }
+
+ override fun copyTo(newContainer: XTypeElement): XMethodElement {
+ check(newContainer is JavacTypeElement)
+ return JavacMethodElement(
+ env = env,
+ containing = newContainer,
+ element = element
+ )
+ }
+
+ override fun hasKotlinDefaultImpl(): Boolean {
+ fun paramsMatch(
+ ourParams: List,
+ theirParams: List
+ ): Boolean {
+ if (ourParams.size != theirParams.size - 1) {
+ return false
+ }
+ ourParams.forEachIndexed { i, variableElement ->
+ // Plus 1 to their index because their first param is a self object.
+ if (!theirParams[i + 1].type.isSameType(
+ variableElement.type
+ )
+ ) {
+ return false
+ }
+ }
+ return true
+ }
+ return kotlinDefaultImplClass?.getDeclaredMethods()?.any {
+ it.name == this.name && paramsMatch(parameters, it.parameters)
+ } ?: false
+ }
+
+ @Suppress("UnstableApiUsage")
+ private val kotlinDefaultImplClass by lazy {
+ val parent = element.enclosingElement as? TypeElement
+ val defaultImplElement = parent?.enclosedElements?.find {
+ MoreElements.isType(it) && it.simpleName.contentEquals(DEFAULT_IMPLS_CLASS_NAME)
+ } as? TypeElement
+ defaultImplElement?.let {
+ env.wrapTypeElement(it)
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodParameter.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodParameter.kt
new file mode 100644
index 0000000..9e84c1f
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodParameter.kt
@@ -0,0 +1,26 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmValueParameter
+import javax.lang.model.element.VariableElement
+
+internal class JavacMethodParameter(
+ env: JavacProcessingEnv,
+ private val executable: JavacExecutableElement,
+ containing: JavacTypeElement,
+ element: VariableElement,
+ val kotlinMetadata: KmValueParameter?
+) : JavacVariableElement(env, containing, element) {
+ override val name: String
+ get() = kotlinMetadata?.name ?: super.name
+ override val kotlinType: KmType?
+ get() = kotlinMetadata?.type
+ override val fallbackLocationText: String
+ get() = if (executable is JavacMethodElement && executable.isSuspendFunction() &&
+ this === executable.parameters.last()
+ ) {
+ "return type of ${executable.fallbackLocationText}"
+ } else {
+ "$name in ${executable.fallbackLocationText}"
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodType.kt
new file mode 100644
index 0000000..d2fbbd3
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacMethodType.kt
@@ -0,0 +1,107 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XMethodType
+import com.zeoflow.memo.compiler.processing.XSuspendMethodType
+import com.zeoflow.memo.compiler.processing.XType
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.TypeVariableName
+import javax.lang.model.type.ExecutableType
+
+internal sealed class JavacMethodType(
+ val env: JavacProcessingEnv,
+ val element: JavacMethodElement,
+ val executableType: ExecutableType
+) : XMethodType {
+ override val returnType: JavacType by lazy {
+ env.wrap(
+ typeMirror = executableType.returnType,
+ kotlinType = if (element.isSuspendFunction()) {
+ // don't use kotlin metadata for suspend return type since it needs to look like
+ // java perspective
+ null
+ } else {
+ element.kotlinMetadata?.returnType
+ },
+ elementNullability = element.element.nullability
+ )
+ }
+
+ override val typeVariableNames by lazy {
+ executableType.typeVariables.map {
+ TypeVariableName.get(it)
+ }
+ }
+
+ override val parameterTypes: List by lazy {
+ executableType.parameterTypes.mapIndexed { index, typeMirror ->
+ env.wrap(
+ typeMirror = typeMirror,
+ kotlinType = element.parameters[index].kotlinType,
+ elementNullability = element.parameters[index].element.nullability
+ )
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is JavacMethodType) return false
+ return executableType == other.executableType
+ }
+
+ override fun hashCode(): Int {
+ return executableType.hashCode()
+ }
+
+ override fun toString(): String {
+ return executableType.toString()
+ }
+
+ private class NormalMethodType(
+ env: JavacProcessingEnv,
+ element: JavacMethodElement,
+ executableType: ExecutableType
+ ) : JavacMethodType(
+ env = env,
+ element = element,
+ executableType = executableType
+ )
+
+ private class SuspendMethodType(
+ env: JavacProcessingEnv,
+ element: JavacMethodElement,
+ executableType: ExecutableType
+ ) : JavacMethodType(
+ env = env,
+ element = element,
+ executableType = executableType
+ ),
+ XSuspendMethodType {
+ override fun getSuspendFunctionReturnType(): XType {
+ // the continuation parameter is always the last parameter of a suspend function and it
+ // only has one type parameter, e.g Continuation super T>
+ val typeParam =
+ MoreTypes.asDeclared(executableType.parameterTypes.last()).typeArguments.first()
+ // kotlin generates ? extends Foo and we want Foo so get the extends bounds
+ val bounded = typeParam.extendsBound() ?: typeParam
+ return env.wrap(
+ typeMirror = bounded,
+ // use kotlin metadata here to get the real type information
+ kotlinType = element.kotlinMetadata?.returnType,
+ elementNullability = element.element.nullability
+ )
+ }
+ }
+
+ companion object {
+ fun create(
+ env: JavacProcessingEnv,
+ element: JavacMethodElement,
+ executableType: ExecutableType
+ ): JavacMethodType {
+ return if (element.isSuspendFunction()) {
+ SuspendMethodType(env, element, executableType)
+ } else {
+ NormalMethodType(env, element, executableType)
+ }
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacProcessingEnv.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacProcessingEnv.kt
new file mode 100644
index 0000000..1cda844
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacProcessingEnv.kt
@@ -0,0 +1,254 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XElement
+import com.zeoflow.memo.compiler.processing.XMessager
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.XProcessingEnv
+import com.zeoflow.memo.compiler.processing.XType
+import com.zeoflow.memo.compiler.processing.XTypeElement
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import com.google.auto.common.GeneratedAnnotations
+import com.google.auto.common.MoreTypes
+import java.util.Locale
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.PackageElement
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.util.Elements
+import javax.lang.model.util.Types
+
+internal class JavacProcessingEnv(
+ val delegate: ProcessingEnvironment
+) : XProcessingEnv {
+ override val backend: XProcessingEnv.Backend = XProcessingEnv.Backend.JAVAC
+
+ val elementUtils: Elements = delegate.elementUtils
+
+ val typeUtils: Types = delegate.typeUtils
+
+ private val typeElementStore =
+ XTypeElementStore(
+ findElement = { qName ->
+ delegate.elementUtils.getTypeElement(qName)
+ },
+ wrap = { typeElement ->
+ JavacTypeElement.create(this, typeElement)
+ },
+ getQName = {
+ it.qualifiedName.toString()
+ }
+ )
+
+ override val messager: XMessager by lazy {
+ JavacProcessingEnvMessager(delegate)
+ }
+
+ override val filer = JavacFiler(delegate)
+
+ override val options: Map
+ get() = delegate.options
+
+ override fun findTypeElement(qName: String): JavacTypeElement? {
+ return typeElementStore[qName]
+ }
+
+ override fun getTypeElementsFromPackage(packageName: String): List {
+ // Note, to support Java Modules we would need to use "getAllPackageElements",
+ // but that is only available in Java 9+.
+ val packageElement = delegate.elementUtils.getPackageElement(packageName)
+
+ return packageElement.enclosedElements
+ .filterIsInstance()
+ .map { wrapTypeElement(it) }
+ }
+
+ override fun findType(qName: String): XType? {
+ // check for primitives first
+ PRIMITIVE_TYPES[qName]?.let {
+ return wrap(
+ typeMirror = typeUtils.getPrimitiveType(it),
+ kotlinType = null,
+ elementNullability = XNullability.NONNULL
+ )
+ }
+ return findTypeElement(qName)?.type
+ }
+
+ override fun findGeneratedAnnotation(): XTypeElement? {
+ val element = GeneratedAnnotations.generatedAnnotation(elementUtils, delegate.sourceVersion)
+ return if (element.isPresent) {
+ wrapTypeElement(element.get())
+ } else {
+ null
+ }
+ }
+
+ override fun getArrayType(type: XType): JavacArrayType {
+ check(type is JavacType) {
+ "given type must be from java, $type is not"
+ }
+ return JavacArrayType(
+ env = this,
+ typeMirror = typeUtils.getArrayType(type.typeMirror),
+ nullability = XNullability.UNKNOWN,
+ knownComponentNullability = type.nullability
+ )
+ }
+
+ override fun getDeclaredType(type: XTypeElement, vararg types: XType): JavacType {
+ check(type is JavacTypeElement)
+ val args = types.map {
+ check(it is JavacType)
+ it.typeMirror
+ }.toTypedArray()
+ check(
+ types.all {
+ it is JavacType
+ }
+ )
+ return wrap(
+ typeMirror = typeUtils.getDeclaredType(type.element, *args),
+ // type elements cannot have nullability hence we don't synthesize anything here
+ kotlinType = null,
+ elementNullability = type.element.nullability
+ )
+ }
+
+ fun wrapTypeElement(element: TypeElement) = typeElementStore[element]
+
+ /**
+ * Wraps the given java processing type into an XType.
+ *
+ * @param typeMirror TypeMirror from java processor
+ * @param kotlinType If the type is derived from a kotlin source code, the KmType information
+ * parsed from kotlin metadata
+ * @param elementNullability The nullability information parsed from the code. This value is
+ * ignored if [kotlinType] is provided.
+ */
+ inline fun wrap(
+ typeMirror: TypeMirror,
+ kotlinType: KmType?,
+ elementNullability: XNullability
+ ): T {
+ return when (typeMirror.kind) {
+ TypeKind.ARRAY ->
+ if (kotlinType == null) {
+ JavacArrayType(
+ env = this,
+ typeMirror = MoreTypes.asArray(typeMirror),
+ nullability = elementNullability,
+ knownComponentNullability = null
+ )
+ } else {
+ JavacArrayType(
+ env = this,
+ typeMirror = MoreTypes.asArray(typeMirror),
+ kotlinType = kotlinType
+ )
+ }
+ TypeKind.DECLARED ->
+ if (kotlinType == null) {
+ JavacDeclaredType(
+ env = this,
+ typeMirror = MoreTypes.asDeclared(typeMirror),
+ nullability = elementNullability
+ )
+ } else {
+ JavacDeclaredType(
+ env = this,
+ typeMirror = MoreTypes.asDeclared(typeMirror),
+ kotlinType = kotlinType
+ )
+ }
+ else ->
+ if (kotlinType == null) {
+ DefaultJavacType(
+ env = this,
+ typeMirror = typeMirror,
+ nullability = elementNullability
+ )
+ } else {
+ DefaultJavacType(
+ env = this,
+ typeMirror = typeMirror,
+ kotlinType = kotlinType
+ )
+ }
+ } as T
+ }
+
+ internal fun wrapAnnotatedElement(
+ element: Element,
+ annotationName: String
+ ): XElement {
+ return when (element) {
+ is VariableElement -> {
+ wrapVariableElement(element)
+ }
+ is TypeElement -> {
+ wrapTypeElement(element)
+ }
+ is ExecutableElement -> {
+ wrapExecutableElement(element)
+ }
+ is PackageElement -> {
+ error(
+ "Cannot get elements with annotation $annotationName. Package " +
+ "elements are not supported by XProcessing."
+ )
+ }
+ else -> error("Unsupported element $element with annotation $annotationName")
+ }
+ }
+
+ fun wrapExecutableElement(element: ExecutableElement): JavacExecutableElement {
+ val enclosingType = element.requireEnclosingType(this)
+
+ return when (element.kind) {
+ ElementKind.CONSTRUCTOR -> {
+ JavacConstructorElement(
+ env = this,
+ containing = enclosingType,
+ element = element
+ )
+ }
+ ElementKind.METHOD -> {
+ JavacMethodElement(
+ env = this,
+ containing = enclosingType,
+ element = element
+ )
+ }
+ else -> error("Unsupported kind ${element.kind} of executable element $element")
+ }
+ }
+
+ fun wrapVariableElement(element: VariableElement): JavacVariableElement {
+ return when (val enclosingElement = element.enclosingElement) {
+ is ExecutableElement -> {
+ val executableElement = wrapExecutableElement(enclosingElement)
+
+ executableElement.parameters.find { param ->
+ param.element === element
+ } ?: error("Unable to create variable element for $element")
+ }
+ is TypeElement -> {
+ JavacFieldElement(this, wrapTypeElement(enclosingElement), element)
+ }
+ else -> error("Unsupported enclosing type $enclosingElement for $element")
+ }
+ }
+
+ companion object {
+ val PRIMITIVE_TYPES = TypeKind.values().filter {
+ it.isPrimitive
+ }.associateBy {
+ it.name.lowercase(Locale.US)
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacProcessingEnvMessager.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacProcessingEnvMessager.kt
new file mode 100644
index 0000000..46397eb
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacProcessingEnvMessager.kt
@@ -0,0 +1,52 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XElement
+import com.zeoflow.memo.compiler.processing.XMessager
+import javax.annotation.processing.ProcessingEnvironment
+import javax.lang.model.element.Element
+import javax.tools.Diagnostic
+
+internal class JavacProcessingEnvMessager(
+ private val processingEnv: ProcessingEnvironment
+) : XMessager() {
+ override fun onPrintMessage(kind: Diagnostic.Kind, msg: String, element: XElement?) {
+ val javacElement = (element as? JavacElement)?.element
+ processingEnv.messager.printMessage(
+ kind,
+ if (javacElement != null && javacElement.isFromCompiledClass()) {
+ "$msg - ${element.fallbackLocationText}"
+ } else {
+ msg
+ },
+ javacElement
+ )
+ }
+
+ companion object {
+ /**
+ * Indicates whether an element comes from a compiled class.
+ *
+ * If this method fails to identify if the element comes from a compiled class it will
+ * default to returning false. Note that this is a poor-man's method of identifying if
+ * the java source of the element is available without depending on compiler tools.
+ */
+ private fun Element.isFromCompiledClass(): Boolean {
+ fun getClassFileString(symbol: Any): String =
+ try {
+ symbol.javaClass.getDeclaredField("classfile").get(symbol).toString()
+ } catch (ex: NoSuchFieldException) {
+ getClassFileString(
+ symbol.javaClass.superclass.getDeclaredField("owner").get(symbol)
+ )
+ }
+
+ return try {
+ getClassFileString(this).let {
+ it.contains(".jar") || it.contains(".class")
+ }
+ } catch (ex: Throwable) {
+ false
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacRawType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacRawType.kt
new file mode 100644
index 0000000..6c7508d
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacRawType.kt
@@ -0,0 +1,31 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XRawType
+import com.zeoflow.memo.compiler.processing.safeTypeName
+import com.squareup.javapoet.TypeName
+
+internal class JavacRawType(
+ env: JavacProcessingEnv,
+ original: JavacType
+) : XRawType {
+ private val erased = env.typeUtils.erasure(original.typeMirror)
+ private val typeUtils = env.delegate.typeUtils
+
+ override val typeName: TypeName = erased.safeTypeName()
+
+ override fun isAssignableFrom(other: XRawType): Boolean {
+ return other is JavacRawType && typeUtils.isAssignable(other.erased, erased)
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return this === other || typeName == (other as? XRawType)?.typeName
+ }
+
+ override fun hashCode(): Int {
+ return typeName.hashCode()
+ }
+
+ override fun toString(): String {
+ return erased.toString()
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacRoundEnv.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacRoundEnv.kt
new file mode 100644
index 0000000..c4732f1
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacRoundEnv.kt
@@ -0,0 +1,42 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XElement
+import com.zeoflow.memo.compiler.processing.XRoundEnv
+import com.google.auto.common.MoreElements
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.element.Element
+import kotlin.reflect.KClass
+
+@Suppress("UnstableApiUsage")
+internal class JavacRoundEnv(
+ private val env: JavacProcessingEnv,
+ val delegate: RoundEnvironment
+) : XRoundEnv {
+ override val rootElements: Set by lazy {
+ delegate.rootElements.map {
+ check(MoreElements.isType(it))
+ env.wrapTypeElement(MoreElements.asType(it))
+ }.toSet()
+ }
+
+ override fun getElementsAnnotatedWith(klass: KClass): Set {
+ val elements = delegate.getElementsAnnotatedWith(klass.java)
+ return wrapAnnotatedElements(elements, klass.java.canonicalName)
+ }
+
+ override fun getElementsAnnotatedWith(annotationQualifiedName: String): Set {
+ val element = env.elementUtils.getTypeElement(annotationQualifiedName)
+ ?: error("Cannot find TypeElement: $annotationQualifiedName")
+
+ val elements = delegate.getElementsAnnotatedWith(element)
+
+ return wrapAnnotatedElements(elements, annotationQualifiedName)
+ }
+
+ private fun wrapAnnotatedElements(
+ elements: Set,
+ annotationName: String
+ ): Set {
+ return elements.map { env.wrapAnnotatedElement(it, annotationName) }.toSet()
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacType.kt
new file mode 100644
index 0000000..3e9dfe3
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacType.kt
@@ -0,0 +1,153 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XEquality
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.XRawType
+import com.zeoflow.memo.compiler.processing.XType
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import com.zeoflow.memo.compiler.processing.ksp.ERROR_TYPE_NAME
+import com.zeoflow.memo.compiler.processing.safeTypeName
+import com.google.auto.common.MoreTypes
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import kotlin.reflect.KClass
+
+internal abstract class JavacType(
+ protected val env: JavacProcessingEnv,
+ open val typeMirror: TypeMirror
+) : XType, XEquality {
+ // Kotlin type information about the type if this type is driven from kotlin code.
+ abstract val kotlinType: KmType?
+
+ override val rawType: XRawType by lazy {
+ JavacRawType(env, this)
+ }
+
+ override val typeElement by lazy {
+ val element = try {
+ MoreTypes.asTypeElement(typeMirror)
+ } catch (notAnElement: IllegalArgumentException) {
+ null
+ }
+ element?.let {
+ env.wrapTypeElement(it)
+ }
+ }
+
+ override fun isError(): Boolean {
+ return typeMirror.kind == TypeKind.ERROR ||
+ // https://kotlinlang.org/docs/reference/kapt.html#non-existent-type-correction
+ (kotlinType != null && typeName == ERROR_TYPE_NAME)
+ }
+
+ override val typeName by lazy {
+ typeMirror.safeTypeName()
+ }
+
+ override fun equals(other: Any?): Boolean {
+ return XEquality.equals(this, other)
+ }
+
+ override fun hashCode(): Int {
+ return XEquality.hashCode(equalityItems)
+ }
+
+ override fun defaultValue(): String {
+ return when (typeMirror.kind) {
+ TypeKind.BOOLEAN -> "false"
+ TypeKind.BYTE, TypeKind.SHORT, TypeKind.INT, TypeKind.LONG, TypeKind.CHAR -> "0"
+ TypeKind.FLOAT -> "0f"
+ TypeKind.DOUBLE -> "0.0"
+ else -> "null"
+ }
+ }
+
+ override fun boxed(): JavacType {
+ return when {
+ typeMirror.kind.isPrimitive -> {
+ env.wrap(
+ typeMirror = env.typeUtils.boxedClass(MoreTypes.asPrimitiveType(typeMirror))
+ .asType(),
+ kotlinType = kotlinType,
+ elementNullability = XNullability.NULLABLE
+ )
+ }
+ typeMirror.kind == TypeKind.VOID -> {
+ env.wrap(
+ typeMirror = env.elementUtils.getTypeElement("java.lang.Void").asType(),
+ kotlinType = kotlinType,
+ elementNullability = XNullability.NULLABLE
+ )
+ }
+ else -> {
+ this
+ }
+ }
+ }
+
+ override fun isNone() = typeMirror.kind == TypeKind.NONE
+
+ override fun toString(): String {
+ return typeMirror.toString()
+ }
+
+ override fun extendsBound(): XType? {
+ return typeMirror.extendsBound()?.let {
+ env.wrap(
+ typeMirror = it,
+ kotlinType = kotlinType?.extendsBound,
+ elementNullability = nullability
+ )
+ }
+ }
+
+ override fun isAssignableFrom(other: XType): Boolean {
+ return other is JavacType && env.typeUtils.isAssignable(
+ other.typeMirror,
+ typeMirror
+ )
+ }
+
+ override fun isTypeOf(other: KClass<*>): Boolean {
+ return try {
+ MoreTypes.isTypeOf(
+ other.java,
+ typeMirror
+ )
+ } catch (notAType: IllegalArgumentException) {
+ // `MoreTypes.isTypeOf` might throw if the current TypeMirror is not a type.
+ // for Depot, a `false` response is good enough.
+ false
+ }
+ }
+
+ override fun isSameType(other: XType): Boolean {
+ return other is JavacType && env.typeUtils.isSameType(typeMirror, other.typeMirror)
+ }
+
+ /**
+ * Create a copy of this type with the given nullability.
+ * This method is not called if the nullability of the type is already equal to the given
+ * nullability.
+ */
+ protected abstract fun copyWithNullability(nullability: XNullability): JavacType
+
+ final override fun makeNullable(): JavacType {
+ if (nullability == XNullability.NULLABLE) {
+ return this
+ }
+ if (typeMirror.kind.isPrimitive || typeMirror.kind == TypeKind.VOID) {
+ return boxed().makeNullable()
+ }
+ return copyWithNullability(XNullability.NULLABLE)
+ }
+
+ final override fun makeNonNullable(): JavacType {
+ if (nullability == XNullability.NONNULL) {
+ return this
+ }
+ // unlike makeNullable, we don't try to degrade to primitives here because it is valid for
+ // a boxed primitive to be marked as non-null.
+ return copyWithNullability(XNullability.NONNULL)
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacTypeElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacTypeElement.kt
new file mode 100644
index 0000000..4a63d54
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacTypeElement.kt
@@ -0,0 +1,180 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XEnumTypeElement
+import com.zeoflow.memo.compiler.processing.XFieldElement
+import com.zeoflow.memo.compiler.processing.XHasModifiers
+import com.zeoflow.memo.compiler.processing.XMethodElement
+import com.zeoflow.memo.compiler.processing.XTypeElement
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KotlinMetadataElement
+import com.google.auto.common.MoreElements
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.ClassName
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.util.ElementFilter
+
+internal sealed class JavacTypeElement(
+ env: JavacProcessingEnv,
+ override val element: TypeElement
+) : JavacElement(env, element), XTypeElement, XHasModifiers by JavacHasModifiers(element) {
+
+ override val name: String
+ get() = element.simpleName.toString()
+
+ @Suppress("UnstableApiUsage")
+ override val packageName: String
+ get() = MoreElements.getPackage(element).qualifiedName.toString()
+
+ val kotlinMetadata by lazy {
+ KotlinMetadataElement.createFor(element)
+ }
+
+ override val qualifiedName by lazy {
+ element.qualifiedName.toString()
+ }
+
+ override val className: ClassName by lazy {
+ ClassName.get(element)
+ }
+ override val enclosingTypeElement: XTypeElement? by lazy {
+ element.enclosingType(env)
+ }
+
+ private val _allFieldsIncludingPrivateSupers by lazy {
+ element.getAllFieldsIncludingPrivateSupers(
+ env.elementUtils
+ ).map {
+ JavacFieldElement(
+ env = env,
+ element = it,
+ containing = this
+ )
+ }
+ }
+
+ override fun getAllFieldsIncludingPrivateSupers(): List {
+ return _allFieldsIncludingPrivateSupers
+ }
+
+ override fun isKotlinObject() = kotlinMetadata?.isObject() == true
+ override fun isCompanionObject() = kotlinMetadata?.isCompanionObject() == true
+ override fun isDataClass() = kotlinMetadata?.isDataClass() == true
+ override fun isValueClass() = kotlinMetadata?.isValueClass() == true
+ override fun isFunctionalInterface() = kotlinMetadata?.isFunctionalInterface() == true
+ override fun isExpect() = kotlinMetadata?.isExpect() == true
+
+ override fun isAnnotationClass(): Boolean {
+ return kotlinMetadata?.isAnnotationClass()
+ ?: (element.kind == ElementKind.ANNOTATION_TYPE)
+ }
+
+ override fun isClass(): Boolean {
+ return kotlinMetadata?.isClass() ?: (element.kind == ElementKind.CLASS)
+ }
+
+ override fun isInterface(): Boolean {
+ return kotlinMetadata?.isInterface() ?: (element.kind == ElementKind.INTERFACE)
+ }
+
+ override fun findPrimaryConstructor(): JavacConstructorElement? {
+ val primarySignature = kotlinMetadata?.findPrimaryConstructorSignature() ?: return null
+ return getConstructors().firstOrNull {
+ primarySignature == it.descriptor
+ }
+ }
+
+ private val _declaredMethods by lazy {
+ ElementFilter.methodsIn(element.enclosedElements).map {
+ JavacMethodElement(
+ env = env,
+ containing = this,
+ element = it
+ )
+ }
+ }
+
+ override fun getDeclaredMethods(): List {
+ return _declaredMethods
+ }
+
+ override fun getConstructors(): List {
+ return ElementFilter.constructorsIn(element.enclosedElements).map {
+ JavacConstructorElement(
+ env = env,
+ containing = this,
+ element = it
+ )
+ }
+ }
+
+ override fun getSuperInterfaceElements(): List {
+ return element.interfaces.map {
+ env.wrapTypeElement(MoreTypes.asTypeElement(it))
+ }
+ }
+
+ override val type: JavacDeclaredType by lazy {
+ env.wrap(
+ typeMirror = element.asType(),
+ kotlinType = kotlinMetadata?.kmType,
+ elementNullability = element.nullability
+ )
+ }
+
+ override val superType: JavacType? by lazy {
+ // javac models non-existing types as TypeKind.NONE but we prefer to make it nullable.
+ // just makes more sense and safer as we don't need to check for none.
+
+ // The result value is a JavacType instead of JavacDeclaredType to gracefully handle
+ // cases where super is an error type.
+ val superClass = element.superclass
+ if (superClass.kind == TypeKind.NONE) {
+ null
+ } else {
+ env.wrap(
+ typeMirror = superClass,
+ kotlinType = kotlinMetadata?.superType,
+ elementNullability = element.nullability
+ )
+ }
+ }
+
+ override val equalityItems: Array by lazy {
+ arrayOf(element)
+ }
+
+ class DefaultJavacTypeElement(
+ env: JavacProcessingEnv,
+ element: TypeElement
+ ) : JavacTypeElement(env, element)
+
+ class JavacEnumTypeElement(
+ env: JavacProcessingEnv,
+ element: TypeElement
+ ) : JavacTypeElement(env, element), XEnumTypeElement {
+ init {
+ check(element.kind == ElementKind.ENUM)
+ }
+
+ override val enumConstantNames: Set by lazy {
+ element.enclosedElements.filter {
+ it.kind == ElementKind.ENUM_CONSTANT
+ }.mapTo(mutableSetOf()) {
+ it.simpleName.toString()
+ }
+ }
+ }
+
+ companion object {
+ fun create(
+ env: JavacProcessingEnv,
+ typeElement: TypeElement
+ ): JavacTypeElement {
+ return when (typeElement.kind) {
+ ElementKind.ENUM -> JavacEnumTypeElement(env, typeElement)
+ else -> DefaultJavacTypeElement(env, typeElement)
+ }
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacVariableElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacVariableElement.kt
new file mode 100644
index 0000000..8d051a7
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/JavacVariableElement.kt
@@ -0,0 +1,51 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XExecutableParameterElement
+import com.zeoflow.memo.compiler.processing.XType
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+import com.google.auto.common.MoreTypes
+import javax.lang.model.element.VariableElement
+
+internal abstract class JavacVariableElement(
+ env: JavacProcessingEnv,
+ val containing: JavacTypeElement,
+ override val element: VariableElement
+) : JavacElement(env, element), XExecutableParameterElement {
+
+ abstract val kotlinType: KmType?
+
+ // todo constantValueData
+ override val constantValue: Any?
+ get() = element.constantValue
+
+ override val name: String
+ get() = element.simpleName.toString()
+
+ override val type: JavacType by lazy {
+ MoreTypes.asMemberOf(env.typeUtils, containing.type.typeMirror, element).let {
+ env.wrap(
+ typeMirror = it,
+ kotlinType = kotlinType,
+ elementNullability = element.nullability
+ )
+ }
+ }
+
+ override fun asMemberOf(other: XType): JavacType {
+ return if (containing.type.isSameType(other)) {
+ type
+ } else {
+ check(other is JavacDeclaredType)
+ val asMember = MoreTypes.asMemberOf(env.typeUtils, other.typeMirror, element)
+ env.wrap(
+ typeMirror = asMember,
+ kotlinType = kotlinType,
+ elementNullability = element.nullability
+ )
+ }
+ }
+
+ override val equalityItems: Array by lazy {
+ arrayOf(element, containing)
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/KmTypeExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/KmTypeExt.kt
new file mode 100644
index 0000000..936c22a
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/KmTypeExt.kt
@@ -0,0 +1,12 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.javac.kotlin.KmType
+
+internal val KmType.nullability: XNullability
+ get() = if (isNullable()) {
+ com.zeoflow.memo.compiler.processing.XNullability.NULLABLE
+ } else {
+ // if there is an upper bound information, use its nullability (e.g. it might be T : Foo?)
+ extendsBound?.nullability ?: com.zeoflow.memo.compiler.processing.XNullability.NONNULL
+ }
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/TypeMirrorExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/TypeMirrorExt.kt
new file mode 100644
index 0000000..2acbbf8
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/TypeMirrorExt.kt
@@ -0,0 +1,16 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.type.WildcardType
+import javax.lang.model.util.SimpleTypeVisitor7
+
+internal fun TypeMirror.extendsBound(): TypeMirror? {
+ return this.accept(
+ object : SimpleTypeVisitor7() {
+ override fun visitWildcard(type: WildcardType, ignored: Void?): TypeMirror? {
+ return type.extendsBound ?: type.superBound
+ }
+ },
+ null
+ )
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/XTypeElementStore.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/XTypeElementStore.kt
new file mode 100644
index 0000000..1f54808
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/XTypeElementStore.kt
@@ -0,0 +1,46 @@
+package com.zeoflow.memo.compiler.processing.javac
+
+import com.zeoflow.memo.compiler.processing.XTypeElement
+import java.lang.ref.WeakReference
+
+/**
+ * Utility class to cache type element wrappers.
+ */
+internal class XTypeElementStore(
+ private val findElement: (qName: String) -> BackingType?,
+ private val getQName: (BackingType) -> String?,
+ private val wrap: (type: BackingType) -> T
+) {
+ // instead of something like a Guava cache, we use a map of weak references here because our
+ // main goal is avoiding to re-parse type elements as we go up & down in the hierarchy while
+ // not necessarily wanting to preserve type elements after we are done with them. Doing that
+ // could possibly hold a lot more information than we desire.
+ private val typeCache = mutableMapOf>()
+
+ operator fun get(backingType: BackingType): T {
+ val qName = getQName(backingType)
+ @Suppress("FoldInitializerAndIfToElvis")
+ if (qName == null) {
+ // just wrap without caching, likely an error or local type in kotlin
+ return wrap(backingType)
+ }
+ get(qName)?.let {
+ return it
+ }
+ val wrapped = wrap(backingType)
+ return cache(qName, wrapped)
+ }
+
+ operator fun get(qName: String): T? {
+ typeCache[qName]?.get()?.let {
+ return it
+ }
+ val result = findElement(qName)?.let(wrap) ?: return null
+ return cache(qName, result)
+ }
+
+ private fun cache(qName: String, element: T): T {
+ typeCache[qName] = WeakReference(element)
+ return element
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
new file mode 100644
index 0000000..7b4e0a9
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/JvmDescriptorUtils.kt
@@ -0,0 +1,190 @@
+package com.zeoflow.memo.compiler.processing.javac.kotlin
+
+import com.google.auto.common.MoreTypes
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.TypeName
+import javax.lang.model.element.Element
+import javax.lang.model.element.ExecutableElement
+import javax.lang.model.element.NestingKind
+import javax.lang.model.element.QualifiedNameable
+import javax.lang.model.element.TypeElement
+import javax.lang.model.element.VariableElement
+import javax.lang.model.type.ArrayType
+import javax.lang.model.type.DeclaredType
+import javax.lang.model.type.ErrorType
+import javax.lang.model.type.ExecutableType
+import javax.lang.model.type.IntersectionType
+import javax.lang.model.type.NoType
+import javax.lang.model.type.NullType
+import javax.lang.model.type.PrimitiveType
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.lang.model.type.TypeVariable
+import javax.lang.model.type.UnionType
+import javax.lang.model.type.WildcardType
+import javax.lang.model.util.AbstractTypeVisitor8
+
+/**
+ * Returns the method descriptor of this [ExecutableElement].
+ *
+ * For reference, see the [JVM specification, section 4.3.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2)
+ */
+internal fun VariableElement.descriptor() = "$simpleName:${asType().descriptor()}"
+
+/**
+ * Returns the method descriptor of this [ExecutableElement].
+ *
+ * For reference, see the [JVM specification, section 4.3.3](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.3)
+ */
+internal fun ExecutableElement.descriptor() =
+ "$simpleName${MoreTypes.asExecutable(asType()).descriptor()}"
+
+/**
+ * Returns the name of this [TypeElement] in its "internal form".
+ *
+ * For reference, see the [JVM specification, section 4.2](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.2).
+ */
+internal val Element.internalName: String
+ get() = when (this) {
+ is TypeElement ->
+ when (nestingKind) {
+ NestingKind.TOP_LEVEL ->
+ qualifiedName.toString().replace('.', '/')
+ NestingKind.MEMBER ->
+ enclosingElement.internalName + "$" + simpleName
+ NestingKind.LOCAL, NestingKind.ANONYMOUS ->
+ error("Unsupported nesting $nestingKind")
+ else ->
+ error("Unsupported, nestingKind == null")
+ }
+ is QualifiedNameable -> qualifiedName.toString().replace('.', '/')
+ else -> simpleName.toString()
+ }
+
+@Suppress("unused")
+internal val NoType.descriptor: String
+ get() = "V"
+
+internal val DeclaredType.descriptor: String
+ get() = "L" + asElement().internalName + ";"
+
+internal val PrimitiveType.descriptor: String
+ get() = when (this.kind) {
+ TypeKind.BYTE -> "B"
+ TypeKind.CHAR -> "C"
+ TypeKind.DOUBLE -> "D"
+ TypeKind.FLOAT -> "F"
+ TypeKind.INT -> "I"
+ TypeKind.LONG -> "J"
+ TypeKind.SHORT -> "S"
+ TypeKind.BOOLEAN -> "Z"
+ else -> error("Unknown primitive type $this")
+ }
+
+// see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.2-200
+internal fun String.typeNameFromJvmSignature(): TypeName {
+ check(isNotEmpty())
+ return when (this[0]) {
+ 'B' -> TypeName.BYTE
+ 'C' -> TypeName.CHAR
+ 'D' -> TypeName.DOUBLE
+ 'F' -> TypeName.FLOAT
+ 'I' -> TypeName.INT
+ 'J' -> TypeName.LONG
+ 'S' -> TypeName.SHORT
+ 'Z' -> TypeName.BOOLEAN
+ 'L' -> {
+ val end = lastIndexOf(";")
+ check(end > 0) {
+ "invalid input $this"
+ }
+ val simpleNamesSeparator = lastIndexOf('/')
+ val simpleNamesStart = if (simpleNamesSeparator < 0) {
+ 1 // first char is 'L'
+ } else {
+ simpleNamesSeparator + 1
+ }
+ val packageName = if (simpleNamesSeparator < 0) {
+ // no package name
+ ""
+ } else {
+ substring(1, simpleNamesSeparator).replace('/', '.')
+ }
+ val firstSimpleNameSeparator = indexOf('$', startIndex = simpleNamesStart)
+ return if (firstSimpleNameSeparator < 0) {
+ // not nested
+ ClassName.get(packageName, substring(simpleNamesStart, end))
+ } else {
+ // nested class
+ val firstSimpleName = substring(simpleNamesStart, firstSimpleNameSeparator)
+ val restOfSimpleNames = substring(firstSimpleNameSeparator + 1, end)
+ .split('$')
+ .toTypedArray()
+ ClassName.get(packageName, firstSimpleName, *restOfSimpleNames)
+ }
+ }
+ '[' -> ArrayTypeName.of(substring(1).typeNameFromJvmSignature())
+ else -> error("unexpected jvm signature $this")
+ }
+}
+
+internal fun TypeMirror.descriptor(): String = accept(JvmDescriptorTypeVisitor, Unit)
+
+@Suppress("unused")
+internal fun WildcardType.descriptor(): String = ""
+
+// The erasure of a type variable is the erasure of its leftmost bound. - JVM Spec Sec 4.6
+internal fun TypeVariable.descriptor(): String = this.upperBound.descriptor()
+
+// For a type variable with multiple bounds: "the erasure of a type variable is determined by
+// the first type in its bound" - JVM Spec Sec 4.4
+internal fun IntersectionType.descriptor(): String =
+ this.bounds[0].descriptor()
+
+internal fun ArrayType.descriptor(): String =
+ "[" + componentType.descriptor()
+
+internal fun ExecutableType.descriptor(): String {
+ val parameterDescriptors =
+ parameterTypes.joinToString(separator = "") { it.descriptor() }
+ val returnDescriptor = returnType.descriptor()
+ return "($parameterDescriptors)$returnDescriptor"
+}
+
+/**
+ * When applied over a type, it returns either:
+ * + a "field descriptor", for example: `Ljava/lang/Object;`
+ * + a "method descriptor", for example: `(Ljava/lang/Object;)Z`
+ *
+ * The easiest way to use this is through [TypeMirror.descriptor]
+ *
+ * For reference, see the [JVM specification, section 4.3](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3).
+ */
+@Suppress("DEPRECATION")
+internal object JvmDescriptorTypeVisitor : AbstractTypeVisitor8() {
+
+ override fun visitNoType(t: NoType, u: Unit): String = t.descriptor
+
+ override fun visitDeclared(t: DeclaredType, u: Unit): String = t.descriptor
+
+ override fun visitPrimitive(t: PrimitiveType, u: Unit): String = t.descriptor
+
+ override fun visitArray(t: ArrayType, u: Unit): String = t.descriptor()
+
+ override fun visitWildcard(t: WildcardType, u: Unit): String = t.descriptor()
+
+ override fun visitExecutable(t: ExecutableType, u: Unit): String = t.descriptor()
+
+ override fun visitTypeVariable(t: TypeVariable, u: Unit): String = t.descriptor()
+
+ override fun visitNull(t: NullType, u: Unit): String = visitUnknown(t, u)
+
+ override fun visitError(t: ErrorType, u: Unit): String = visitUnknown(t, u)
+
+ override fun visitIntersection(t: IntersectionType, u: Unit) = t.descriptor()
+
+ override fun visitUnion(t: UnionType, u: Unit) = visitUnknown(t, u)
+
+ override fun visitUnknown(t: TypeMirror, u: Unit): String = error("Unsupported type $t")
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
new file mode 100644
index 0000000..5719ae4
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/KotlinClassMetadataUtils.kt
@@ -0,0 +1,370 @@
+package com.zeoflow.memo.compiler.processing.javac.kotlin
+
+import kotlinx.metadata.ClassName
+import kotlinx.metadata.Flag
+import kotlinx.metadata.Flags
+import kotlinx.metadata.KmClassVisitor
+import kotlinx.metadata.KmConstructorExtensionVisitor
+import kotlinx.metadata.KmConstructorVisitor
+import kotlinx.metadata.KmExtensionType
+import kotlinx.metadata.KmFunctionExtensionVisitor
+import kotlinx.metadata.KmFunctionVisitor
+import kotlinx.metadata.KmPropertyVisitor
+import kotlinx.metadata.KmTypeParameterVisitor
+import kotlinx.metadata.KmTypeVisitor
+import kotlinx.metadata.KmValueParameterVisitor
+import kotlinx.metadata.KmVariance
+import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor
+import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor
+import kotlinx.metadata.jvm.JvmMethodSignature
+import kotlinx.metadata.jvm.KotlinClassMetadata
+
+// represents a function or constructor
+internal interface KmExecutable {
+ val parameters: List
+}
+
+/**
+ * Represents the kotlin metadata of a function
+ */
+internal data class KmFunction(
+ val descriptor: String,
+ private val flags: Flags,
+ override val parameters: List,
+ val returnType: KmType
+) : KmExecutable {
+ fun isSuspend() = Flag.Function.IS_SUSPEND(flags)
+}
+
+/**
+ * Represents the kotlin metadata of a constructor
+ */
+internal data class KmConstructor(
+ val descriptor: String,
+ private val flags: Flags,
+ override val parameters: List
+) : KmExecutable {
+ fun isPrimary() = !Flag.Constructor.IS_SECONDARY(flags)
+}
+
+internal data class KmProperty(
+ val name: String,
+ val type: KmType
+) {
+ val typeParameters
+ get() = type.typeArguments
+
+ fun isNullable() = Flag.Type.IS_NULLABLE(type.flags)
+}
+
+internal data class KmType(
+ val flags: Flags,
+ val typeArguments: List,
+ val extendsBound: KmType?
+) {
+ fun isNullable() = Flag.Type.IS_NULLABLE(flags)
+ fun erasure(): KmType = KmType(flags, emptyList(), extendsBound?.erasure())
+}
+
+private data class KmTypeParameter(
+ val name: String,
+ val flags: Flags,
+ val extendsBound: KmType?
+) {
+ fun asKmType() = KmType(
+ flags = flags,
+ typeArguments = emptyList(),
+ extendsBound = extendsBound
+ )
+}
+
+/**
+ * Represents the kotlin metadata of a parameter
+ */
+internal data class KmValueParameter(
+ val name: String,
+ val type: KmType
+) {
+ fun isNullable() = type.isNullable()
+}
+
+internal data class KmClassTypeInfo(
+ val kmType: KmType,
+ val superType: KmType?
+)
+
+internal fun KotlinClassMetadata.Class.readFunctions(): List =
+ mutableListOf().apply { accept(FunctionReader(this)) }
+
+private class FunctionReader(val result: MutableList) : KmClassVisitor() {
+ override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? {
+ return object : KmFunctionVisitor() {
+
+ lateinit var descriptor: String
+ val parameters = mutableListOf()
+ lateinit var returnType: KmType
+
+ override fun visitValueParameter(
+ flags: Flags,
+ name: String
+ ): KmValueParameterVisitor? {
+ return ValueParameterReader(name) {
+ parameters.add(it)
+ }
+ }
+
+ override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? {
+ if (type != JvmFunctionExtensionVisitor.TYPE) {
+ error("Unsupported extension type: $type")
+ }
+ return object : JvmFunctionExtensionVisitor() {
+ override fun visit(signature: JvmMethodSignature?) {
+ descriptor = signature!!.asString()
+ }
+ }
+ }
+
+ override fun visitReturnType(flags: Flags): KmTypeVisitor? {
+ return TypeReader(flags) {
+ returnType = it
+ }
+ }
+
+ override fun visitEnd() {
+ result.add(KmFunction(descriptor, flags, parameters, returnType))
+ }
+ }
+ }
+}
+
+internal fun KotlinClassMetadata.Class.readConstructors(): List =
+ mutableListOf().apply { accept(ConstructorReader(this)) }
+
+private class ConstructorReader(val result: MutableList) : KmClassVisitor() {
+ override fun visitConstructor(flags: Flags): KmConstructorVisitor? {
+ return object : KmConstructorVisitor() {
+
+ lateinit var descriptor: String
+ val parameters = mutableListOf()
+
+ override fun visitValueParameter(
+ flags: Flags,
+ name: String
+ ): KmValueParameterVisitor? {
+ return ValueParameterReader(name) {
+ parameters.add(it)
+ }
+ }
+
+ override fun visitExtensions(type: KmExtensionType): KmConstructorExtensionVisitor? {
+ if (type != JvmConstructorExtensionVisitor.TYPE) {
+ error("Unsupported extension type: $type")
+ }
+ return object : JvmConstructorExtensionVisitor() {
+ override fun visit(signature: JvmMethodSignature?) {
+ descriptor = signature!!.asString()
+ }
+ }
+ }
+
+ override fun visitEnd() {
+ result.add(KmConstructor(descriptor, flags, parameters))
+ }
+ }
+ }
+}
+
+internal class KotlinMetadataClassFlags(val classMetadata: KotlinClassMetadata.Class) {
+
+ private val flags: Flags by lazy {
+ var theFlags: Flags = 0
+ classMetadata.accept(object : KmClassVisitor() {
+ override fun visit(flags: Flags, name: ClassName) {
+ theFlags = flags
+ super.visit(flags, name)
+ }
+ })
+ return@lazy theFlags
+ }
+
+ fun isObject(): Boolean = Flag.Class.IS_OBJECT(flags)
+
+ fun isCompanionObject(): Boolean = Flag.Class.IS_COMPANION_OBJECT(flags)
+
+ fun isAnnotationClass(): Boolean = Flag.Class.IS_ANNOTATION_CLASS(flags)
+
+ fun isInterface(): Boolean = Flag.Class.IS_INTERFACE(flags)
+
+ fun isClass(): Boolean = Flag.Class.IS_CLASS(flags)
+
+ fun isDataClass(): Boolean = Flag.Class.IS_DATA(flags)
+
+ fun isValueClass(): Boolean = Flag.Class.IS_VALUE(flags)
+
+ fun isFunctionalInterface(): Boolean = Flag.Class.IS_FUN(flags)
+
+ fun isExpect(): Boolean = Flag.Class.IS_EXPECT(flags)
+}
+
+internal fun KotlinClassMetadata.Class.readProperties(): List =
+ mutableListOf().apply { accept(PropertyReader(this)) }
+
+/**
+ * Reads the properties of a class declaration
+ */
+private class PropertyReader(
+ val result: MutableList
+) : KmClassVisitor() {
+ override fun visitProperty(
+ flags: Flags,
+ name: String,
+ getterFlags: Flags,
+ setterFlags: Flags
+ ): KmPropertyVisitor? {
+ return object : KmPropertyVisitor() {
+ lateinit var returnType: KmType
+ override fun visitEnd() {
+ result.add(
+ KmProperty(
+ type = returnType,
+ name = name
+ )
+ )
+ }
+
+ override fun visitReturnType(flags: Flags): KmTypeVisitor? {
+ return TypeReader(flags) {
+ returnType = it
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Reads a type description and calls the output with the read value
+ */
+private class TypeReader(
+ private val flags: Flags,
+ private val output: (KmType) -> Unit
+) : KmTypeVisitor() {
+ private val typeArguments = mutableListOf()
+ private var extendsBound: KmType? = null
+ override fun visitArgument(flags: Flags, variance: KmVariance): KmTypeVisitor? {
+ return TypeReader(flags) {
+ typeArguments.add(it)
+ }
+ }
+
+ override fun visitFlexibleTypeUpperBound(
+ flags: Flags,
+ typeFlexibilityId: String?
+ ): KmTypeVisitor? {
+ return TypeReader(flags) {
+ extendsBound = it
+ }
+ }
+
+ override fun visitEnd() {
+ output(
+ KmType(
+ flags = flags,
+ typeArguments = typeArguments,
+ extendsBound = extendsBound
+ )
+ )
+ }
+}
+
+/**
+ * Reads the value parameter of a function or constructor and calls the output with the read value
+ */
+private class ValueParameterReader(
+ val name: String,
+ val output: (KmValueParameter) -> Unit
+) : KmValueParameterVisitor() {
+ lateinit var type: KmType
+ override fun visitType(flags: Flags): KmTypeVisitor? {
+ return TypeReader(flags) {
+ type = it
+ }
+ }
+
+ override fun visitEnd() {
+ output(
+ KmValueParameter(
+ name = name,
+ type = type
+ )
+ )
+ }
+}
+
+/**
+ * Reads a class declaration and turns it into a KmType for both itself and its super type
+ */
+internal class ClassAsKmTypeReader(
+ val output: (KmClassTypeInfo) -> Unit
+) : KmClassVisitor() {
+ private var flags: Flags = 0
+ private val typeParameters = mutableListOf()
+ private var superType: KmType? = null
+ override fun visit(flags: Flags, name: ClassName) {
+ this.flags = flags
+ }
+
+ override fun visitTypeParameter(
+ flags: Flags,
+ name: String,
+ id: Int,
+ variance: KmVariance
+ ): KmTypeParameterVisitor? {
+ return TypeParameterReader(name, flags) {
+ typeParameters.add(it)
+ }
+ }
+
+ override fun visitSupertype(flags: Flags): KmTypeVisitor? {
+ return TypeReader(flags) {
+ superType = it
+ }
+ }
+
+ override fun visitEnd() {
+ output(
+ KmClassTypeInfo(
+ kmType = KmType(
+ flags = flags,
+ typeArguments = typeParameters.map {
+ it.asKmType()
+ },
+ extendsBound = null
+ ),
+ superType = superType
+ )
+ )
+ }
+}
+
+private class TypeParameterReader(
+ private val name: String,
+ private val flags: Flags,
+ private val output: (KmTypeParameter) -> Unit
+) : KmTypeParameterVisitor() {
+ private var upperBound: KmType? = null
+ override fun visitEnd() {
+ output(
+ KmTypeParameter(
+ name = name,
+ flags = flags,
+ extendsBound = upperBound
+ )
+ )
+ }
+
+ override fun visitUpperBound(flags: Flags): KmTypeVisitor? {
+ return TypeReader(flags) {
+ upperBound = it
+ }
+ }
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/KotlinMetadataElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
new file mode 100644
index 0000000..bd7c699
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/javac/kotlin/KotlinMetadataElement.kt
@@ -0,0 +1,114 @@
+package com.zeoflow.memo.compiler.processing.javac.kotlin
+
+import kotlinx.metadata.jvm.KotlinClassHeader
+import kotlinx.metadata.jvm.KotlinClassMetadata
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ExecutableElement
+
+/**
+ * Utility class for processors that wants to run kotlin specific code.
+ */
+internal class KotlinMetadataElement(
+ val element: Element,
+ private val classMetadata: KotlinClassMetadata.Class
+) {
+ private val typeInfo: KmClassTypeInfo by lazy {
+ lateinit var result: KmClassTypeInfo
+ classMetadata.accept(
+ ClassAsKmTypeReader {
+ result = it
+ }
+ )
+ result
+ }
+ val kmType
+ get() = typeInfo.kmType
+ val superType
+ get() = typeInfo.superType
+ private val functionList: List by lazy { classMetadata.readFunctions() }
+ private val constructorList: List by lazy { classMetadata.readConstructors() }
+ private val propertyList: List by lazy { classMetadata.readProperties() }
+ private val classFlags: KotlinMetadataClassFlags by lazy {
+ KotlinMetadataClassFlags(classMetadata)
+ }
+
+ private val ExecutableElement.descriptor: String
+ get() = descriptor()
+
+ fun findPrimaryConstructorSignature() = constructorList.firstOrNull {
+ it.isPrimary()
+ }?.descriptor
+
+ fun isObject(): Boolean = classFlags.isObject()
+ fun isCompanionObject(): Boolean = classFlags.isCompanionObject()
+ fun isAnnotationClass(): Boolean = classFlags.isAnnotationClass()
+ fun isClass(): Boolean = classFlags.isClass()
+ fun isInterface(): Boolean = classFlags.isInterface()
+ fun isDataClass(): Boolean = classFlags.isDataClass()
+ fun isValueClass(): Boolean = classFlags.isValueClass()
+ fun isFunctionalInterface(): Boolean = classFlags.isFunctionalInterface()
+ fun isExpect(): Boolean = classFlags.isExpect()
+
+ fun getFunctionMetadata(method: ExecutableElement): KmFunction? {
+ check(method.kind == ElementKind.METHOD) {
+ "must pass an element type of method"
+ }
+ val methodSignature = method.descriptor
+ return functionList.firstOrNull { it.descriptor == methodSignature }
+ }
+
+ fun getConstructorMetadata(method: ExecutableElement): KmConstructor? {
+ check(method.kind == ElementKind.CONSTRUCTOR) {
+ "must pass an element type of constructor"
+ }
+ val methodSignature = method.descriptor
+ return constructorList.firstOrNull { it.descriptor == methodSignature }
+ }
+
+ fun getPropertyMetadata(propertyName: String) = propertyList.firstOrNull {
+ it.name == propertyName
+ }
+
+ companion object {
+ /**
+ * Creates a [KotlinMetadataElement] for the given element if it contains Kotlin metadata,
+ * otherwise this method returns null.
+ *
+ * Usually the [element] passed must represent a class. For example, if kotlin metadata is
+ * desired for a method, then the containing class should be used as parameter.
+ */
+ fun createFor(element: Element): KotlinMetadataElement? {
+ val metadata = getMetadataAnnotation(element)?.run {
+ KotlinClassHeader(
+ kind = kind,
+ metadataVersion = metadataVersion,
+ data1 = data1,
+ data2 = data2,
+ extraString = extraString,
+ packageName = packageName,
+ extraInt = extraInt
+ ).let {
+ // TODO: Support more metadata kind (file facade, synthetic class, etc...)
+ KotlinClassMetadata.read(it) as? KotlinClassMetadata.Class
+ }
+ }
+ return if (metadata != null) {
+ KotlinMetadataElement(element, metadata)
+ } else {
+ null
+ }
+ }
+
+ /**
+ * Search for Kotlin's Metadata annotation across the element's hierarchy.
+ */
+ private fun getMetadataAnnotation(element: Element?): Metadata? =
+ if (element != null) {
+ element.getAnnotation(Metadata::class.java)
+ ?: getMetadataAnnotation(element.enclosingElement)
+ } else {
+ null
+ }
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/DefaultKspType.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/DefaultKspType.kt
new file mode 100644
index 0000000..d8c7ff9
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/DefaultKspType.kt
@@ -0,0 +1,28 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.tryBox
+import com.google.devtools.ksp.symbol.KSType
+import com.squareup.javapoet.TypeName
+
+internal class DefaultKspType(
+ env: KspProcessingEnv,
+ ksType: KSType
+) : KspType(env, ksType) {
+ override val typeName: TypeName by lazy {
+ // always box these. For primitives, typeName might return the primitive type but if we
+ // wanted it to be a primitive, we would've resolved it to [KspPrimitiveType].
+ ksType.typeName(env.resolver).tryBox()
+ }
+
+ override fun boxed(): DefaultKspType {
+ return this
+ }
+
+ override fun copyWithNullability(nullability: XNullability): KspType {
+ return DefaultKspType(
+ env = env,
+ ksType = ksType.withNullability(nullability)
+ )
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSAnnotatedExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSAnnotatedExt.kt
new file mode 100644
index 0000000..91a7789
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSAnnotatedExt.kt
@@ -0,0 +1,16 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.google.devtools.ksp.symbol.KSAnnotated
+
+private fun KSAnnotated.hasAnnotationWithQName(qName: String) = annotations.any {
+ it.annotationType.resolve().declaration.qualifiedName?.asString() == qName
+}
+
+internal fun KSAnnotated.hasJvmStaticAnnotation() = hasAnnotationWithQName("kotlin.jvm.JvmStatic")
+
+internal fun KSAnnotated.hasJvmTransientAnnotation() =
+ hasAnnotationWithQName("kotlin.jvm.Transient")
+
+internal fun KSAnnotated.hasJvmFieldAnnotation() = hasAnnotationWithQName("kotlin.jvm.JvmField")
+
+internal fun KSAnnotated.hasJvmDefaultAnnotation() = hasAnnotationWithQName("kotlin.jvm.JvmDefault")
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSAsMemberOf.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSAsMemberOf.kt
new file mode 100644
index 0000000..089dde5
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSAsMemberOf.kt
@@ -0,0 +1,77 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSPropertyDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSValueParameter
+
+/**
+ * Returns the type of a property as if it is member of the given [ksType].
+ */
+internal fun KSPropertyDeclaration.typeAsMemberOf(ksType: KSType?): KSType {
+ val resolved = type.resolve()
+ if (isStatic()) {
+ // calling as member with a static would throw as it might be a member of the companion
+ // object
+ return resolved
+ }
+ if (ksType == null) {
+ return resolved
+ }
+ // see: https://github.com/google/ksp/issues/107
+ // as member of might lose the `isError` information hence we should check before calling
+ // asMemberOf.
+ if (resolved.isError) {
+ return resolved
+ }
+ return this.asMemberOf(
+ containing = ksType
+ )
+}
+
+internal fun KSValueParameter.typeAsMemberOf(
+ functionDeclaration: KSFunctionDeclaration,
+ ksType: KSType?
+): KSType {
+ val resolved = type.resolve()
+ if (functionDeclaration.isStatic()) {
+ // calling as member with a static would throw as it might be a member of the companion
+ // object
+ return resolved
+ }
+ if (resolved.isError) {
+ // see: https://github.com/google/ksp/issues/107
+ // as member of might lose the `isError` information hence we should check before calling
+ // asMemberOf.
+ return resolved
+ }
+ if (ksType == null) {
+ return resolved
+ }
+ val asMember = functionDeclaration.asMemberOf(
+ containing = ksType
+ )
+ // TODO b/173224718
+ // this is counter intuitive, we should remove asMemberOf from method parameters.
+ val myIndex = functionDeclaration.parameters.indexOf(this)
+ return asMember.parameterTypes[myIndex] ?: resolved
+}
+
+internal fun KSFunctionDeclaration.returnTypeAsMemberOf(
+ ksType: KSType?
+): KSType {
+ val resolved = returnType?.resolve()
+ return when {
+ resolved == null -> null
+ ksType == null -> resolved
+ resolved.isError -> resolved
+ isStatic() -> {
+ // calling as member with a static would throw as it might be a member of the companion
+ // object
+ resolved
+ }
+ else -> this.asMemberOf(
+ containing = ksType
+ ).returnType
+ } ?: error("cannot find return type for $this")
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSClassDeclarationExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSClassDeclarationExt.kt
new file mode 100644
index 0000000..8c447d7
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSClassDeclarationExt.kt
@@ -0,0 +1,9 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+
+internal fun KSClassDeclaration.findCompanionObject(): KSClassDeclaration? {
+ return declarations.firstOrNull {
+ it is KSClassDeclaration && it.isCompanionObject
+ } as? KSClassDeclaration
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSDeclarationExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSDeclarationExt.kt
new file mode 100644
index 0000000..d5b8bf4
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSDeclarationExt.kt
@@ -0,0 +1,59 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSDeclaration
+import com.google.devtools.ksp.symbol.KSFunctionDeclaration
+import com.google.devtools.ksp.symbol.KSPropertyAccessor
+import com.google.devtools.ksp.symbol.KSPropertyDeclaration
+import com.google.devtools.ksp.symbol.Modifier
+
+/**
+ * Finds the class that contains this declaration and throws [IllegalStateException] if it cannot
+ * be found.
+ * @see [findEnclosingAncestorClassDeclaration]
+ */
+internal fun KSDeclaration.requireEnclosingMemberContainer(
+ env: KspProcessingEnv
+): KspMemberContainer {
+ return checkNotNull(findEnclosingMemberContainer(env)) {
+ "Cannot find required enclosing type for $this"
+ }
+}
+
+/**
+ * Find the class that contains this declaration.
+ *
+ * Node that this is not necessarily the parent declaration. e.g. when a property is declared in
+ * a constructor, its containing type is actual two levels up.
+ */
+internal fun KSDeclaration.findEnclosingMemberContainer(
+ env: KspProcessingEnv
+): KspMemberContainer? {
+ return findEnclosingAncestorClassDeclaration()?.let {
+ env.wrapClassDeclaration(it)
+ } ?: this.containingFile?.let {
+ env.wrapKSFile(it)
+ }
+}
+
+private fun KSDeclaration.findEnclosingAncestorClassDeclaration(): KSClassDeclaration? {
+ var parent = parentDeclaration
+ while (parent != null && parent !is KSClassDeclaration) {
+ parent = parent.parentDeclaration
+ }
+ return parent as? KSClassDeclaration
+}
+
+internal fun KSDeclaration.isStatic(): Boolean {
+ return modifiers.contains(Modifier.JAVA_STATIC) || hasJvmStaticAnnotation() ||
+ when (this) {
+ is KSPropertyAccessor -> this.receiver.findEnclosingAncestorClassDeclaration() == null
+ is KSPropertyDeclaration -> this.findEnclosingAncestorClassDeclaration() == null
+ is KSFunctionDeclaration -> this.findEnclosingAncestorClassDeclaration() == null
+ else -> false
+ }
+}
+
+internal fun KSDeclaration.isTransient(): Boolean {
+ return modifiers.contains(Modifier.JAVA_TRANSIENT) || hasJvmTransientAnnotation()
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSFileAsOriginatingElement.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSFileAsOriginatingElement.kt
new file mode 100644
index 0000000..cb80390
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSFileAsOriginatingElement.kt
@@ -0,0 +1,68 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.google.devtools.ksp.symbol.KSFile
+import javax.lang.model.element.AnnotationMirror
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.ElementVisitor
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.Name
+import javax.lang.model.type.TypeMirror
+
+/**
+ * When generating java code, JavaPoet only provides an API that receives Element.
+ * This wrapper class helps us wrap a KSFile as an originating element and KspFiler unwraps it to
+ * get the actual KSFile out of it.
+ */
+internal data class KSFileAsOriginatingElement(
+ val ksFile: KSFile
+) : Element {
+ override fun getAnnotationMirrors(): List {
+ return emptyList()
+ }
+
+ override fun getAnnotation(annotationType: Class?): A? {
+ return null
+ }
+
+ override fun getAnnotationsByType(annotationType: Class): Array {
+ @Suppress("UNCHECKED_CAST")
+ return arrayOfNulls(size = 0) as Array
+ }
+
+ override fun asType(): TypeMirror {
+ throw UnsupportedOperationException(
+ "KSFileAsOriginatingElement cannot be converted to a type"
+ )
+ }
+
+ override fun getKind(): ElementKind {
+ return ElementKind.OTHER
+ }
+
+ override fun getModifiers(): Set {
+ return emptySet()
+ }
+
+ override fun getSimpleName(): Name {
+ return NameImpl(ksFile.fileName)
+ }
+
+ override fun getEnclosingElement(): Element? {
+ return null
+ }
+
+ override fun getEnclosedElements(): List {
+ return emptyList()
+ }
+
+ override fun accept(v: ElementVisitor?, p: P): R? {
+ return null
+ }
+
+ private class NameImpl(private val str: String) : Name, CharSequence by str {
+ override fun contentEquals(cs: CharSequence): Boolean {
+ return str == cs.toString()
+ }
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSTypeExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSTypeExt.kt
new file mode 100644
index 0000000..adada60
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSTypeExt.kt
@@ -0,0 +1,150 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.zeoflow.memo.compiler.processing.XNullability
+import com.zeoflow.memo.compiler.processing.javac.kotlin.typeNameFromJvmSignature
+import com.zeoflow.memo.compiler.processing.tryBox
+import com.google.devtools.ksp.KspExperimental
+import com.google.devtools.ksp.processing.Resolver
+import com.google.devtools.ksp.symbol.KSDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSTypeAlias
+import com.google.devtools.ksp.symbol.KSTypeArgument
+import com.google.devtools.ksp.symbol.KSTypeParameter
+import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.Modifier
+import com.google.devtools.ksp.symbol.Variance
+import com.squareup.javapoet.ArrayTypeName
+import com.squareup.javapoet.ClassName
+import com.squareup.javapoet.ParameterizedTypeName
+import com.squareup.javapoet.TypeName
+import com.squareup.javapoet.TypeVariableName
+import com.squareup.javapoet.WildcardTypeName
+
+// Catch-all type name when we cannot resolve to anything. This is what KAPT uses as error type
+// and we use the same type in KSP for consistency.
+// https://kotlinlang.org/docs/reference/kapt.html#non-existent-type-correction
+internal val ERROR_TYPE_NAME = ClassName.get("error", "NonExistentClass")
+
+/**
+ * Turns a KSTypeReference into a TypeName in java's type system.
+ */
+internal fun KSTypeReference?.typeName(resolver: Resolver): TypeName {
+ return if (this == null) {
+ ERROR_TYPE_NAME
+ } else {
+ resolve().typeName(resolver)
+ }
+}
+
+/**
+ * Turns a KSDeclaration into a TypeName in java's type system.
+ */
+@OptIn(KspExperimental::class)
+internal fun KSDeclaration.typeName(resolver: Resolver): TypeName {
+ if (this is KSTypeAlias) {
+ return this.type.typeName(resolver)
+ }
+ // if there is no qualified name, it is a resolution error so just return shared instance
+ // KSP may improve that later and if not, we can improve it in Depot
+ // TODO: https://issuetracker.google.com/issues/168639183
+ val qualified = qualifiedName?.asString() ?: return ERROR_TYPE_NAME
+ val jvmSignature = resolver.mapToJvmSignature(this)
+ if (jvmSignature != null && jvmSignature.isNotBlank()) {
+ return jvmSignature.typeNameFromJvmSignature()
+ }
+ if (this is KSTypeParameter) {
+ return TypeVariableName.get(name.asString())
+ }
+ // fallback to custom generation, it is very likely that this is an unresolved type
+ // get the package name first, it might throw for invalid types, hence we use
+ // safeGetPackageName
+ val pkg = getNormalizedPackageName()
+ // using qualified name and pkg, figure out the short names.
+ val shortNames = if (pkg == "") {
+ qualified
+ } else {
+ qualified.substring(pkg.length + 1)
+ }.split('.')
+ return ClassName.get(pkg, shortNames.first(), *(shortNames.drop(1).toTypedArray()))
+}
+
+/**
+ * Turns a KSTypeArgument into a TypeName in java's type system.
+ */
+internal fun KSTypeArgument.typeName(
+ param: KSTypeParameter,
+ resolver: Resolver
+): TypeName {
+ return when (variance) {
+ Variance.CONTRAVARIANT -> WildcardTypeName.supertypeOf(type.typeName(resolver).tryBox())
+ Variance.COVARIANT -> WildcardTypeName.subtypeOf(type.typeName(resolver).tryBox())
+ Variance.STAR -> {
+ // for star projected types, JavaPoet uses the name from the declaration if
+ // * is not given explicitly
+ if (type == null) {
+ // explicit *
+ WildcardTypeName.subtypeOf(TypeName.OBJECT)
+ } else {
+ TypeVariableName.get(param.name.asString(), type.typeName(resolver).tryBox())
+ }
+ }
+ else -> type.typeName(resolver).tryBox()
+ }
+}
+
+/**
+ * Turns a KSType into a TypeName in java's type system.
+ */
+internal fun KSType.typeName(resolver: Resolver): TypeName {
+ return if (this.arguments.isNotEmpty()) {
+ val args: Array = this.arguments.mapIndexed { index, typeArg ->
+ typeArg.typeName(
+ this.declaration.typeParameters[index],
+ resolver
+ )
+ }.map {
+ it.tryBox()
+ }.toTypedArray()
+ when (val typeName = declaration.typeName(resolver).tryBox()) {
+ is ArrayTypeName -> ArrayTypeName.of(args.single())
+ is ClassName -> ParameterizedTypeName.get(
+ typeName,
+ *args
+ )
+ else -> error("Unexpected type name for KSType: $typeName")
+ }
+ } else {
+ this.declaration.typeName(resolver)
+ }
+}
+
+/**
+ * Root package comes as instead of "" so we work around it here.
+ */
+internal fun KSDeclaration.getNormalizedPackageName(): String {
+ return packageName.asString().let {
+ if (it == "") {
+ ""
+ } else {
+ it
+ }
+ }
+}
+
+internal fun KSTypeArgument.requireType(): KSType {
+ return checkNotNull(type?.resolve()) {
+ "KSTypeArgument.type should not have been null, please file a bug. $this"
+ }
+}
+
+internal fun KSTypeReference.isTypeParameterReference(): Boolean {
+ return this.resolve().declaration is KSTypeParameter
+}
+
+fun KSType.isInline() = declaration.modifiers.contains(Modifier.INLINE)
+
+internal fun KSType.withNullability(nullability: XNullability) = when (nullability) {
+ XNullability.NULLABLE -> makeNullable()
+ XNullability.NONNULL -> makeNotNullable()
+ else -> throw IllegalArgumentException("Cannot set KSType nullability to platform")
+}
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSTypeReferenceExt.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSTypeReferenceExt.kt
new file mode 100644
index 0000000..0cde33e
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KSTypeReferenceExt.kt
@@ -0,0 +1,57 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSReferenceElement
+import com.google.devtools.ksp.symbol.KSType
+import com.google.devtools.ksp.symbol.KSTypeReference
+import com.google.devtools.ksp.symbol.KSVisitor
+import com.google.devtools.ksp.symbol.Location
+import com.google.devtools.ksp.symbol.Modifier
+import com.google.devtools.ksp.symbol.NonExistLocation
+import com.google.devtools.ksp.symbol.Origin
+
+/**
+ * Creates a new TypeReference from [this] where the resolved type [replacement] but everything
+ * else is the same (e.g. location).
+ */
+internal fun KSTypeReference.swapResolvedType(replacement: KSType): KSTypeReference {
+ return DelegatingTypeReference(
+ original = this,
+ resolved = replacement
+ )
+}
+
+/**
+ * Creates a [NonExistLocation] type reference for [this].
+ */
+internal fun KSType.createTypeReference(): KSTypeReference {
+ return NoLocationTypeReference(this)
+}
+
+private class DelegatingTypeReference(
+ val original: KSTypeReference,
+ val resolved: KSType
+) : KSTypeReference by original {
+ override fun resolve() = resolved
+}
+
+private class NoLocationTypeReference(
+ val resolved: KSType
+) : KSTypeReference {
+ override val annotations: Sequence
+ get() = emptySequence()
+ override val element: KSReferenceElement?
+ get() = null
+ override val location: Location
+ get() = NonExistLocation
+ override val modifiers: Set
+ get() = emptySet()
+ override val origin: Origin
+ get() = Origin.SYNTHETIC
+
+ override fun accept(visitor: KSVisitor, data: D): R {
+ return visitor.visitTypeReference(this, data)
+ }
+
+ override fun resolve(): KSType = resolved
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KspAnnotated.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KspAnnotated.kt
new file mode 100644
index 0000000..c204c0f
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KspAnnotated.kt
@@ -0,0 +1,142 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.zeoflow.memo.compiler.processing.XAnnotationBox
+import com.google.devtools.ksp.symbol.AnnotationUseSiteTarget
+import com.google.devtools.ksp.symbol.KSAnnotated
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.zeoflow.memo.compiler.processing.InternalXAnnotated
+import kotlin.reflect.KClass
+
+internal sealed class KspAnnotated(
+ val env: KspProcessingEnv
+) : InternalXAnnotated {
+ abstract fun annotations(): Sequence
+
+ private fun findAnnotations(annotation: KClass): Sequence {
+ return annotations().filter {
+ val qName = it.annotationType.resolve().declaration.qualifiedName?.asString()
+ qName == annotation.qualifiedName
+ }
+ }
+
+ override fun getAnnotations(
+ annotation: KClass,
+ containerAnnotation: KClass?
+ ): List> {
+ // we'll try both because it can be the container or the annotation itself.
+ // try container first
+ if (containerAnnotation != null) {
+ // if container also repeats, this won't work but we don't have that use case
+ findAnnotations(containerAnnotation).firstOrNull()?.let {
+ return KspAnnotationBox(
+ env = env,
+ annotation = it,
+ annotationClass = containerAnnotation.java,
+ ).getAsAnnotationBoxArray("value").toList()
+ }
+ }
+ // didn't find anything with the container, try the annotation class
+ return findAnnotations(annotation).map {
+ KspAnnotationBox(
+ env = env,
+ annotationClass = annotation.java,
+ annotation = it
+ )
+ }.toList()
+ }
+
+ override fun hasAnnotationWithPackage(pkg: String): Boolean {
+ return annotations().any {
+ it.annotationType.resolve().declaration.qualifiedName?.getQualifier() == pkg
+ }
+ }
+
+ override fun hasAnnotation(
+ annotation: KClass,
+ containerAnnotation: KClass?
+ ): Boolean {
+ return annotations().any {
+ val qName = it.annotationType.resolve().declaration.qualifiedName?.asString()
+ qName == annotation.qualifiedName ||
+ (containerAnnotation != null && qName == containerAnnotation.qualifiedName)
+ }
+ }
+
+ private class KSAnnotatedDelegate(
+ env: KspProcessingEnv,
+ private val delegate: KSAnnotated,
+ private val useSiteFilter: UseSiteFilter
+ ) : KspAnnotated(env) {
+ override fun annotations(): Sequence {
+ return delegate.annotations.asSequence().filter {
+ useSiteFilter.accept(it)
+ }
+ }
+ }
+
+ private class NotAnnotated(env: KspProcessingEnv) : KspAnnotated(env) {
+ override fun annotations(): Sequence {
+ return emptySequence()
+ }
+ }
+
+ /**
+ * TODO: The implementation of UseSiteFilter is not 100% correct until
+ * https://github.com/google/ksp/issues/96 is fixed.
+ * https://kotlinlang.org/docs/reference/annotations.html
+ *
+ * More specifically, when a use site is not defined in an annotation, we need to find the
+ * declaration of the annotation and decide on the use site based on that.
+ * Unfortunately, due to KSP issue #96, we cannot yet read values from a `@Target` annotation
+ * which prevents implementing it correctly.
+ *
+ * Current implementation just approximates it which should work for Depot.
+ */
+ interface UseSiteFilter {
+ fun accept(annotation: KSAnnotation): Boolean
+
+ private class Impl(
+ val acceptedTarget: AnnotationUseSiteTarget,
+ private val acceptNoTarget: Boolean = true,
+ ) : UseSiteFilter {
+ override fun accept(annotation: KSAnnotation): Boolean {
+ val target = annotation.useSiteTarget
+ return if (target == null) {
+ acceptNoTarget
+ } else {
+ acceptedTarget == target
+ }
+ }
+ }
+
+ companion object {
+ val NO_USE_SITE = object : UseSiteFilter {
+ override fun accept(annotation: KSAnnotation): Boolean {
+ return annotation.useSiteTarget == null
+ }
+ }
+ val NO_USE_SITE_OR_FIELD: UseSiteFilter = Impl(AnnotationUseSiteTarget.FIELD)
+ val NO_USE_SITE_OR_METHOD_PARAMETER: UseSiteFilter =
+ Impl(AnnotationUseSiteTarget.PARAM)
+ val NO_USE_SITE_OR_GETTER: UseSiteFilter = Impl(AnnotationUseSiteTarget.GET)
+ val NO_USE_SITE_OR_SETTER: UseSiteFilter = Impl(AnnotationUseSiteTarget.SET)
+ val NO_USE_SITE_OR_SET_PARAM: UseSiteFilter = Impl(AnnotationUseSiteTarget.SETPARAM)
+ val FILE: UseSiteFilter = Impl(
+ acceptedTarget = AnnotationUseSiteTarget.FILE,
+ acceptNoTarget = false
+ )
+ }
+ }
+
+ companion object {
+ fun create(
+ env: KspProcessingEnv,
+ delegate: KSAnnotated?,
+ filter: UseSiteFilter
+ ): KspAnnotated {
+ return delegate?.let {
+ KSAnnotatedDelegate(env, it, filter)
+ } ?: NotAnnotated(env)
+ }
+ }
+}
\ No newline at end of file
diff --git a/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KspAnnotationBox.kt b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KspAnnotationBox.kt
new file mode 100644
index 0000000..d0c7aa4
--- /dev/null
+++ b/memo-compiler-processing/src/main/java/com/zeoflow/memo/compiler/processing/ksp/KspAnnotationBox.kt
@@ -0,0 +1,163 @@
+package com.zeoflow.memo.compiler.processing.ksp
+
+import com.zeoflow.memo.compiler.processing.XAnnotationBox
+import com.zeoflow.memo.compiler.processing.XType
+import com.google.devtools.ksp.symbol.KSAnnotation
+import com.google.devtools.ksp.symbol.KSClassDeclaration
+import com.google.devtools.ksp.symbol.KSType
+import java.lang.reflect.Proxy
+
+@Suppress("UNCHECKED_CAST")
+internal class KspAnnotationBox(
+ private val env: KspProcessingEnv,
+ private val annotationClass: Class,
+ private val annotation: KSAnnotation
+) : XAnnotationBox