├── .gitignore ├── README.md ├── android_app.gradle ├── android_lib.gradle ├── build.gradle ├── demos └── test-java │ ├── .gitignore │ ├── build.gradle │ └── src │ └── test │ └── java │ └── com │ └── fashare │ └── javasugar │ └── test_java │ ├── designpattern │ ├── MyEventBus.java │ ├── TestBuilder.java │ ├── TestSingleton.java │ ├── TestSubject.java │ ├── TestTitleBar.java │ ├── TitleBar.java │ ├── UserManager.java │ └── UserManager2.java │ ├── lang │ ├── TestGetter.java │ ├── TestSetter.java │ ├── User.java │ ├── User2.java │ └── User3.java │ └── util │ └── AssertUtil.java ├── ext.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── java_lib.gradle ├── libs ├── annotation │ ├── .gitignore │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── fashare │ │ └── javasugar │ │ └── annotation │ │ ├── designpattern │ │ ├── Builder.java │ │ ├── ISubject.java │ │ ├── Observer.java │ │ ├── Singleton.java │ │ ├── Singletons.java │ │ └── Subject.java │ │ └── lang │ │ ├── Getter.java │ │ ├── Instances.java │ │ └── Setter.java └── apt │ ├── .gitignore │ ├── build.gradle │ └── src │ └── main │ └── kotlin │ └── com │ └── fashare │ └── javasugar │ └── apt │ ├── base │ ├── AllFileProcessor.kt │ ├── BaseProcessor.kt │ └── SingleAnnotationProcessor.kt │ ├── processors │ ├── designpattern │ │ ├── BuilderProcessor.kt │ │ ├── SingletonProcessor.kt │ │ └── SubjectProcessor.kt │ ├── lang │ │ ├── GetterProcessor.kt │ │ └── SetterProcessor.kt │ └── util │ │ └── CheckClassDefProcessor.kt │ └── util │ ├── AnnotationUtil.kt │ ├── EnvUtil.kt │ ├── JCTreeUtil.kt │ ├── LogUtil.kt │ └── NameUtil.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/* 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # java-sugar 2 | -------------------------------------------------------------------------------- /android_app.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion _android.compileSdkVersion 5 | buildToolsVersion _android.buildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId _android.applicationId 9 | 10 | minSdkVersion _android.minSdkVersion 11 | targetSdkVersion _android.targetSdkVersion 12 | versionCode _android.versionCode 13 | versionName _android.versionName 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | 24 | compileOptions { 25 | targetCompatibility JavaVersion.VERSION_1_8 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | } 28 | } 29 | 30 | dependencies { 31 | api fileTree(dir: 'libs', include: ['*.jar']) 32 | 33 | // rxjava 34 | api rx.rxjava2 35 | api rx.rxandroid2 36 | } 37 | -------------------------------------------------------------------------------- /android_lib.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | apply plugin: 'kotlin-android' 4 | apply plugin: 'kotlin-android-extensions' 5 | 6 | android { 7 | compileSdkVersion _android.compileSdkVersion 8 | buildToolsVersion _android.buildToolsVersion 9 | 10 | defaultConfig { 11 | // applicationId _android.applicationId 12 | 13 | minSdkVersion _android.minSdkVersion 14 | targetSdkVersion _android.targetSdkVersion 15 | versionCode _android.versionCode 16 | versionName _android.versionName 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | } 19 | 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | 27 | compileOptions { 28 | targetCompatibility JavaVersion.VERSION_1_8 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | } 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/kotlin' 34 | } 35 | } 36 | 37 | dependencies { 38 | api fileTree(dir: 'libs', include: ['*.jar']) 39 | 40 | // kotlin 41 | compile _kotlin.kotlin_stdlib 42 | 43 | // rxjava 44 | api rx.rxjava2 45 | api rx.rxandroid2 46 | } 47 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | 2 | buildscript { 3 | apply from: "ext.gradle" 4 | 5 | repositories { 6 | google() 7 | jcenter() 8 | // maven { 9 | // url uri('./repo') 10 | // } 11 | } 12 | dependencies { 13 | classpath _android.plugin 14 | 15 | classpath _kotlin.kotlin_gradle_plugin 16 | classpath _kotlin.kotlin_android_extensions 17 | } 18 | } 19 | 20 | allprojects { 21 | repositories { 22 | google() 23 | jcenter() 24 | // maven { 25 | // url uri('./repo') 26 | // } 27 | } 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /demos/test-java/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demos/test-java/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../../java_lib.gradle' 2 | 3 | buildscript { 4 | repositories { 5 | maven { 6 | url "https://plugins.gradle.org/m2/" 7 | } 8 | } 9 | dependencies { 10 | classpath "net.ltgt.gradle:gradle-apt-plugin:0.20" 11 | } 12 | } 13 | 14 | sourceSets { 15 | main.java.srcDirs += 'src/main/kotlin' 16 | test.java.srcDirs += 'build/generated/sources/annotationProcessor/java/test' 17 | } 18 | 19 | apply plugin: "net.ltgt.apt" 20 | 21 | dependencies { 22 | testCompile 'junit:junit:4.12' 23 | 24 | compile project(':libs:annotation') 25 | annotationProcessor project(":libs:apt") 26 | testAnnotationProcessor project(":libs:apt") 27 | } 28 | 29 | /** 30 | * java.lang.IllegalStateException: endPosTable already set 31 | * 32 | * javac bug 增量编译报错,删去 apt 生成的文件,绕过该 bug 33 | * https://github.com/immutables/immutables/issues/134 34 | * https://github.com/google/dagger/issues/147 35 | */ 36 | def generatedSrcRoot = file("${buildDir}/generated/sources/annotationProcessor/java/test") 37 | 38 | task deleteGeneratedSources(type: Delete) { 39 | delete generatedSrcRoot 40 | } 41 | 42 | tasks.withType(JavaCompile) { it.dependsOn('deleteGeneratedSources') } 43 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/MyEventBus.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Observer; 4 | import com.fashare.javasugar.annotation.designpattern.Singleton; 5 | import com.fashare.javasugar.annotation.designpattern.Subject; 6 | 7 | /** 8 | * Created by apple on 2019/1/16. 9 | */ 10 | 11 | @Singleton 12 | @Subject({ 13 | @Observer(value = MyEventBus.Listener.class, name = "Event"), 14 | }) 15 | abstract class MyEventBus implements MyEventBus$$ISubject { 16 | public interface Listener { 17 | void onEvent(T event); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/TestBuilder.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.test_java.lang.User; 4 | import com.fashare.javasugar.test_java.lang.User$$Builder; 5 | import com.fashare.javasugar.test_java.lang.User$$Phone$$Builder; 6 | 7 | import org.junit.Test; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | import static org.junit.Assert.assertNull; 11 | 12 | /** 13 | * Created by apple on 2019/1/15. 14 | */ 15 | 16 | public class TestBuilder { 17 | 18 | @Test 19 | public void builder() { 20 | // User user = new User(); 21 | User user = new User$$Builder() 22 | .build(); 23 | assertNull(user.getName()); 24 | assertEquals(user.getId(), 0); 25 | assertNull(user.getPhoneNums()); 26 | assertNull(user.getMap()); 27 | 28 | user = new User$$Builder() 29 | .setName("name by builder") 30 | .setId(55) 31 | .build(); 32 | assertEquals(user.getName(), "name by builder"); 33 | assertEquals(user.getId(), 55); 34 | } 35 | 36 | @Test 37 | public void inner_builder() { 38 | // User.Phone phone = new User.Phone(); 39 | User.Phone phone = new User$$Phone$$Builder() 40 | .build(); 41 | assertNull(phone.getOs()); 42 | assertEquals(phone.getPrice(), 0); 43 | 44 | phone = new User$$Phone$$Builder() 45 | .setOs("ios") 46 | .setPrice(9999) 47 | .build(); 48 | assertEquals(phone.getOs(), "ios"); 49 | assertEquals(phone.getPrice(), 9999); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/TestSingleton.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Singletons; 4 | import com.fashare.javasugar.annotation.lang.Instances; 5 | import com.fashare.javasugar.test_java.lang.User; 6 | import com.fashare.javasugar.test_java.util.AssertUtil; 7 | 8 | import org.junit.Test; 9 | 10 | import static com.fashare.javasugar.test_java.designpattern.UserManager.NoAnnotationManager; 11 | import static com.fashare.javasugar.test_java.designpattern.UserManager.PhoneManager; 12 | import static org.junit.Assert.assertEquals; 13 | 14 | /** 15 | * Created by apple on 2019/1/16. 16 | */ 17 | 18 | public class TestSingleton { 19 | 20 | @Test 21 | public void getter_exist() { 22 | AssertUtil.assertMethodExist(true, UserManager.class, "getInstance"); 23 | 24 | User user = Singletons.get(UserManager.class).mUser; 25 | assertEquals(user.getName(), "fashare"); 26 | assertEquals(Singletons.get(UserManager.class) == Singletons.get(UserManager.class), true); 27 | } 28 | 29 | @Test 30 | public void inner_getter_exist() { 31 | AssertUtil.assertMethodExist(true, PhoneManager.class, "getInstance"); 32 | 33 | User.Phone phone = Singletons.get(PhoneManager.class).mPhone; 34 | assertEquals(phone.getOs(), "Android"); 35 | assertEquals(Singletons.get(PhoneManager.class) == Singletons.get(PhoneManager.class), true); 36 | } 37 | 38 | @Test(expected = IllegalArgumentException.class) 39 | public void inner_getter_not_exist() { 40 | AssertUtil.assertMethodExist(false, NoAnnotationManager.class, "getInstance"); 41 | 42 | Singletons.get(NoAnnotationManager.class); 43 | } 44 | 45 | @Test 46 | public void UserManager2_getter_exist() { 47 | AssertUtil.assertMethodExist(true, UserManager2.class, "getInstance"); 48 | 49 | User user = Singletons.get(UserManager2.class).mUser; 50 | assertEquals(user.getName(), "fashare"); 51 | assertEquals(Singletons.get(UserManager2.class) == Singletons.get(UserManager2.class), true); 52 | } 53 | 54 | @Test 55 | public void UserManager3_getter_exist() { 56 | AssertUtil.assertMethodExist(true, UserManager3.class, "getInstance", User.class); 57 | 58 | User outUser = Instances.get(User.class); 59 | User user = Singletons.get(UserManager3.class, outUser).mUser; 60 | 61 | assertEquals(user == outUser, true); 62 | assertEquals(user.getName(), "fashare"); 63 | assertEquals(Singletons.get(UserManager3.class, outUser) == Singletons.get(UserManager3.class, outUser), true); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/TestSubject.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Singletons; 4 | 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | import static org.junit.Assert.assertNotEquals; 9 | 10 | /** 11 | * Created by apple on 2019/1/16. 12 | */ 13 | 14 | public class TestSubject { 15 | 16 | private String data = ""; 17 | private MyEventBus mEventBus = Singletons.get(MyEventBus.class); 18 | private MyEventBus.Listener mListener = event -> data = event; 19 | 20 | @Test 21 | public void register_and_notify() { 22 | mEventBus.getEventSubject().clear(); 23 | mEventBus.getEventSubject().add(mListener); 24 | mEventBus.getEventSubject().asNotifier().onEvent("data"); 25 | assertEquals(data, "data"); 26 | 27 | mEventBus.getEventSubject().asNotifier().onEvent("data 2"); 28 | assertEquals(data, "data 2"); 29 | } 30 | 31 | @Test 32 | public void unRegister_and_notify() { 33 | String curData = data; 34 | mEventBus.getEventSubject().clear(); 35 | mEventBus.getEventSubject().add(mListener); 36 | mEventBus.getEventSubject().remove(mListener); 37 | mEventBus.getEventSubject().asNotifier().onEvent("data changed"); 38 | assertNotEquals(data, "data changed"); 39 | assertEquals(data, curData); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/TestTitleBar.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.annotation.lang.Instances; 4 | 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | public class TestTitleBar { 10 | 11 | private boolean onClickCalled; 12 | private TitleBar titleBar = Instances.get(TitleBar.class); 13 | private TitleBar.OnClickListener onClickListener = self -> onClickCalled = true; 14 | 15 | @Test 16 | public void register_and_notify() { 17 | titleBar.getClickSubject().clear(); 18 | titleBar.getClickSubject().add(onClickListener); 19 | 20 | onClickCalled = false; 21 | titleBar.getClickSubject().asNotifier().onClick(titleBar); 22 | assertEquals(onClickCalled, true); 23 | } 24 | 25 | @Test 26 | public void unRegister_and_notify() { 27 | titleBar.getClickSubject().remove(onClickListener); 28 | 29 | onClickCalled = false; 30 | titleBar.getClickSubject().asNotifier().onClick(titleBar); 31 | assertEquals(onClickCalled, false); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/TitleBar.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Observer; 4 | import com.fashare.javasugar.annotation.designpattern.Subject; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.Callable; 8 | 9 | import io.reactivex.functions.Function9; 10 | 11 | /** 12 | * Created by apple on 2019/1/16. 13 | */ 14 | 15 | @Subject({ 16 | @Observer(value = TitleBar.OnClickListener.class, name = "Click"), 17 | @Observer(value = TitleBar.OnScrollListener.class, name = "Scroll"), 18 | @Observer(value = Runnable.class, name = "Runnable"), 19 | @Observer(value = Callable.class, name = "Callable"), 20 | @Observer(value = Map.class, name = "Map"), 21 | @Observer(value = Function9.class, name = "Function9"), 22 | }) 23 | abstract class TitleBar implements TitleBar$$ISubject { 24 | public interface OnClickListener { 25 | void onClick(TitleBar self); 26 | } 27 | 28 | @SuppressWarnings("unused") 29 | public interface OnScrollListener { 30 | void onScrolled(TitleBar self, int dy); 31 | 32 | void onScrollChange(TitleBar self, int scrollState); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/UserManager.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Singleton; 4 | import com.fashare.javasugar.annotation.lang.Instances; 5 | import com.fashare.javasugar.test_java.lang.User; 6 | 7 | /** 8 | * Created by apple on 2019/1/16. 9 | */ 10 | 11 | @Singleton 12 | class UserManager { 13 | User mUser = Instances.get(User.class); 14 | 15 | @Singleton 16 | static class PhoneManager { 17 | User.Phone mPhone = Instances.get(User.Phone.class); 18 | } 19 | 20 | @SuppressWarnings("unused") 21 | static class NoAnnotationManager { 22 | User.NoAnnotation mNoAnnotation = new User.NoAnnotation(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/designpattern/UserManager2.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.designpattern; 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Singleton; 4 | import com.fashare.javasugar.annotation.lang.Instances; 5 | import com.fashare.javasugar.test_java.lang.User; 6 | 7 | /** 8 | * Created by apple on 2019/1/16. 9 | */ 10 | 11 | @Singleton 12 | class UserManager2 { 13 | User mUser = Instances.get(User.class); 14 | 15 | private static final UserManager2 sInstance = new UserManager2(); 16 | 17 | /** 18 | * test case: getInstance() 已存在 19 | */ 20 | @SuppressWarnings("unused") 21 | public static UserManager2 getInstance() { 22 | return sInstance; 23 | } 24 | } 25 | 26 | /** 27 | * test case: getInstance(User) 带参数 28 | */ 29 | @Singleton 30 | class UserManager3 { 31 | User mUser; 32 | 33 | @Singleton.Main 34 | private UserManager3(User user) { 35 | mUser = user; 36 | } 37 | } 38 | 39 | /** 40 | * test case: getInstance(a, b, c ..) 多参数 41 | */ 42 | @SuppressWarnings("unused") 43 | @Singleton 44 | class UserManager4 { 45 | @SuppressWarnings("FieldCanBeLocal") 46 | private User mUser; 47 | 48 | @Singleton.Main 49 | private UserManager4(User user, 50 | String a1, 51 | String a2, 52 | String a3, 53 | String a4, 54 | String a5, 55 | String a6) { 56 | mUser = user; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/lang/TestGetter.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.lang; 2 | 3 | import com.fashare.javasugar.annotation.lang.Instances; 4 | import com.fashare.javasugar.test_java.util.AssertUtil; 5 | 6 | import org.junit.Test; 7 | 8 | import java.util.Arrays; 9 | 10 | import static org.junit.Assert.assertArrayEquals; 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertNotEquals; 13 | 14 | /** 15 | * Created by apple on 2019/1/15. 16 | */ 17 | 18 | public class TestGetter { 19 | 20 | @Test 21 | public void getter_exist() { 22 | AssertUtil.assertMethodExist(true, User.class, "getName"); 23 | AssertUtil.assertMethodExist(true, User.class, "getId"); 24 | AssertUtil.assertMethodExist(true, User.class, "getPhoneNums"); 25 | 26 | // User user = new User(); 27 | User user = Instances.get(User.class); 28 | assertEquals(user.getName(), "fashare"); 29 | assertEquals(user.getId(), 5); 30 | assertArrayEquals(user.getPhoneNums().toArray(new String[0]), 31 | Arrays.asList("10010", "10086").toArray(new String[0])); 32 | } 33 | 34 | @Test 35 | public void inner_getter_exist() { 36 | AssertUtil.assertMethodExist(true, User.Phone.class, "getOs"); 37 | AssertUtil.assertMethodExist(true, User.Phone.class, "getPrice"); 38 | 39 | // User.Phone phone = new User.Phone(); 40 | User.Phone phone = Instances.get(User.Phone.class); 41 | assertEquals(phone.getOs(), "Android"); 42 | assertEquals(phone.getPrice(), 1234); 43 | } 44 | 45 | @Test 46 | public void inner_getter_not_exist() { 47 | AssertUtil.assertMethodExist(false, User.NoAnnotation.class, "getName"); 48 | } 49 | 50 | // User2 51 | @Test 52 | public void User2_getter_exist() { 53 | AssertUtil.assertMethodExist(true, User2.class, "getName"); 54 | AssertUtil.assertMethodExist(true, User2.class, "getId"); 55 | 56 | User2 user = Instances.get(User2.class); 57 | assertNotEquals(user.getName(), "fashare"); 58 | assertEquals(user.getName(), "getName existed"); 59 | assertEquals(user.getId(), 5); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/lang/TestSetter.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.lang; 2 | 3 | import com.fashare.javasugar.annotation.lang.Instances; 4 | import com.fashare.javasugar.test_java.util.AssertUtil; 5 | 6 | import org.junit.Test; 7 | 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | import static org.junit.Assert.assertArrayEquals; 12 | import static org.junit.Assert.assertEquals; 13 | import static org.junit.Assert.assertNotEquals; 14 | 15 | /** 16 | * Created by apple on 2019/1/15. 17 | */ 18 | 19 | public class TestSetter { 20 | 21 | @Test 22 | public void setter_exist() { 23 | AssertUtil.assertMethodExist(true, User.class, "setName", String.class); 24 | AssertUtil.assertMethodExist(true, User.class, "setId", int.class); 25 | AssertUtil.assertMethodExist(true, User.class, "setPhoneNums", List.class); 26 | 27 | // User user = new User() 28 | User user = Instances.get(User.class) 29 | .setName("google") 30 | .setId(50) 31 | .setPhoneNums(Collections.singletonList("10000")); 32 | 33 | assertEquals(user.getName(), "google"); 34 | assertEquals(user.getId(), 50); 35 | assertArrayEquals(user.getPhoneNums().toArray(new String[0]), 36 | Collections.singletonList("10000").toArray(new String[0])); 37 | } 38 | 39 | @Test 40 | public void inner_setter_exist() { 41 | AssertUtil.assertMethodExist(true, User.Phone.class, "setOs", String.class); 42 | AssertUtil.assertMethodExist(true, User.Phone.class, "setPrice", int.class); 43 | 44 | // User.Phone phone = new User.Phone() 45 | User.Phone phone = Instances.get(User.Phone.class); 46 | phone.setOs("ios"); 47 | phone.setPrice(9999); 48 | 49 | assertEquals(phone.getOs(), "ios"); 50 | assertEquals(phone.getPrice(), 9999); 51 | } 52 | 53 | @Test 54 | public void inner_setter_not_exist() { 55 | AssertUtil.assertMethodExist(false, User.NoAnnotation.class, "setName", String.class); 56 | } 57 | 58 | // User2 59 | @Test 60 | public void User2_setter_exist() { 61 | AssertUtil.assertMethodExist(true, User2.class, "setName", String.class); 62 | AssertUtil.assertMethodExist(true, User2.class, "setId", int.class); 63 | 64 | User2 user = Instances.get(User2.class) 65 | .setName("google") 66 | .setId(50); 67 | 68 | assertNotEquals(user.getId(), 50); 69 | assertEquals(user.getId(), 999); 70 | } 71 | 72 | // User3 73 | @Test 74 | public void User3_setter_exist() { 75 | AssertUtil.assertMethodExist(true, User3.class, "setName", String.class); 76 | AssertUtil.assertMethodExist(true, User3.class, "setId", int.class); 77 | 78 | User3 user = Instances.get(User4.class) 79 | .setName("google") 80 | .setId(50); 81 | 82 | assertEquals(user.getId(), 50); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/lang/User.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.lang; 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Builder; 4 | import com.fashare.javasugar.annotation.lang.Getter; 5 | import com.fashare.javasugar.annotation.lang.Setter; 6 | 7 | import java.util.Arrays; 8 | import java.util.HashMap; 9 | import java.util.List; 10 | import java.util.Map; 11 | 12 | /** 13 | * Created by apple on 2019/1/15. 14 | */ 15 | 16 | @SuppressWarnings("unused") 17 | @Builder 18 | @Getter 19 | @Setter 20 | public abstract class User implements User$$IGetter, User$$ISetter { 21 | private String name = "fashare"; 22 | private int id = 5; 23 | private List phoneNums = Arrays.asList("10010", "10086"); 24 | private Map map = new HashMap<>(); 25 | 26 | @Builder 27 | @Getter 28 | @Setter(returnThis = false) 29 | public static abstract class Phone implements User$$Phone$$IGetter, User$$Phone$$ISetter { 30 | private String os = "Android"; 31 | private int price = 1234; 32 | } 33 | 34 | public static class NoAnnotation { 35 | private String name = "aaa"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/lang/User2.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.lang; 2 | 3 | import com.fashare.javasugar.annotation.lang.Getter; 4 | import com.fashare.javasugar.annotation.lang.Setter; 5 | 6 | /** 7 | * 测试异常情况 8 | */ 9 | @SuppressWarnings("unused") 10 | @Getter 11 | @Setter 12 | public abstract class User2 implements User2$$IGetter, User2$$ISetter { 13 | private String name = "fashare"; 14 | @SuppressWarnings("FieldCanBeLocal") 15 | private int id = 5; 16 | 17 | /** 18 | * 已存在 getName() 19 | */ 20 | @Override 21 | public String getName() { 22 | return "getName existed"; 23 | } 24 | 25 | /** 26 | * 已存在 setId() 27 | */ 28 | @Override 29 | public User2 setId(int id) { 30 | this.id = 999; 31 | return this; 32 | } 33 | 34 | // 测试生成的类 User2$$IGetter 不会和内部类解糖后命名冲突 35 | public interface IGetter { 36 | String getName(); 37 | } 38 | 39 | public interface ISetter { 40 | String getName(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/lang/User3.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.lang; 2 | 3 | import com.fashare.javasugar.annotation.lang.Getter; 4 | import com.fashare.javasugar.annotation.lang.Setter; 5 | 6 | /** 7 | * 测试异常情况, 有 Runnable, 不去掉 abstract 8 | */ 9 | @SuppressWarnings("unused") 10 | @Getter 11 | @Setter 12 | abstract class User3 implements User3$$IGetter, User3$$ISetter, Runnable { 13 | private String name = "fashare"; 14 | private int id = 5; 15 | } 16 | 17 | /** 18 | * 子类实现了 Runnable, 去掉 abstract 19 | */ 20 | @SuppressWarnings("unused") 21 | abstract class User4 extends User3 { 22 | @Override 23 | public void run() { 24 | // ignore 25 | } 26 | } 27 | 28 | /** 29 | * 子类实现了 Runnable, 但是自身有抽象方法, 不去掉 abstract 30 | */ 31 | @SuppressWarnings("unused") 32 | abstract class User5 extends User3 { 33 | @Override 34 | public void run() { 35 | // ignore 36 | } 37 | 38 | abstract void test(); 39 | } 40 | -------------------------------------------------------------------------------- /demos/test-java/src/test/java/com/fashare/javasugar/test_java/util/AssertUtil.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.test_java.util; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | /** 6 | * Created by apple on 2019/1/15. 7 | */ 8 | 9 | public class AssertUtil { 10 | public static void assertMethodExist(boolean isExist, Class clazz, String methodName, Class... parameterTypes) { 11 | boolean isMethodExist = true; 12 | try { 13 | clazz.getDeclaredMethod(methodName, parameterTypes); 14 | } catch (NoSuchMethodException ignored) { 15 | isMethodExist = false; 16 | } 17 | assertEquals(isExist, isMethodExist); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ext.gradle: -------------------------------------------------------------------------------- 1 | project.ext { 2 | def androidPluginVersion = "3.0.0" 3 | _android = [ 4 | plugin : "com.android.tools.build:gradle:$androidPluginVersion", 5 | 6 | compileSdkVersion: 27, 7 | buildToolsVersion: '27.0.1', 8 | 9 | minSdkVersion : 15, 10 | targetSdkVersion : 24, 11 | versionCode : 1, 12 | versionName : "1.0", 13 | 14 | applicationId : "com.fashare.javasugar" 15 | ] 16 | 17 | // kotlin 18 | def kotlinVersion = "1.1.3-2" 19 | _kotlin = [ 20 | // plugins 21 | kotlin_gradle_plugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion", 22 | kotlin_android_extensions: "org.jetbrains.kotlin:kotlin-android-extensions:$kotlinVersion", 23 | 24 | // libs 25 | kotlin_stdlib : "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion" 26 | ] 27 | 28 | // UI 29 | def supportLibraryVersion = "25.3.1" 30 | support = [ 31 | support_v4 : "com.android.support:support-v4:$supportLibraryVersion", 32 | appcompat : "com.android.support:appcompat-v7:$supportLibraryVersion", 33 | recyclerview: "com.android.support:recyclerview-v7:$supportLibraryVersion", 34 | design : "com.android.support:design:$supportLibraryVersion", 35 | cardview : "com.android.support:cardview-v7:$supportLibraryVersion" 36 | ] 37 | springview = "com.liaoinstan.springview:library:1.3.0" 38 | swipebackhelper = "com.jude:swipebackhelper:3.1.2" 39 | 40 | // rx 41 | def rxjavaVersion = "2.1.0" 42 | def rxandroidVersion = "2.0.1" 43 | rx = [ 44 | rxjava2 : "io.reactivex.rxjava2:rxjava:$rxjavaVersion", 45 | rxandroid2 : "io.reactivex.rxjava2:rxandroid:$rxandroidVersion", 46 | rxlifecycle : "com.trello.rxlifecycle2:rxlifecycle:$rxjavaVersion", 47 | rxlifecycle_android : "com.trello.rxlifecycle2:rxlifecycle-android:$rxjavaVersion", 48 | rxlifecycle_components: "com.trello.rxlifecycle2:rxlifecycle-components:$rxjavaVersion", 49 | 50 | rxpermissions : 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar' 51 | ] 52 | 53 | // net 54 | def okhttp3Version = "3.8.0" 55 | okhttp3 = [ 56 | logging_interceptor: "com.squareup.okhttp3:logging-interceptor:$okhttp3Version", 57 | ] 58 | def retrofitVersion = "2.3.0" 59 | retrofit2 = [ 60 | retrofit : "com.squareup.retrofit2:retrofit:$retrofitVersion", 61 | adapter_rxjava2: "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion", 62 | converter_gson : "com.squareup.retrofit2:converter-gson:$retrofitVersion" 63 | ] 64 | gson = "com.google.code.gson:gson:2.7" 65 | klog = "com.github.zhaokaiqiang.klog:library:1.6.0" 66 | 67 | def glideVersion = "3.7.0" 68 | glide = [ 69 | glide : "com.github.bumptech.glide:glide:$glideVersion", 70 | glide_transformations: "jp.wasabeef:glide-transformations:2.0.1", 71 | okhttp3_integration : "com.github.bumptech.glide:okhttp3-integration:1.4.0@aar" 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | #org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | #### debug apt 时,打开下边的开关 #### 16 | #org.gradle.daemon=true 17 | #org.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fashare2015/java-sugar/22e13a4d9b913912f869828c2b9ddf973a574c3b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jan 09 13:36:10 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /java_lib.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | apply plugin: 'kotlin' 3 | 4 | sourceSets { 5 | main.java.srcDirs += 'src/main/kotlin' 6 | } 7 | 8 | dependencies { 9 | compile fileTree(dir: 'libs', include: ['*.jar']) 10 | 11 | // kotlin 12 | compile _kotlin.kotlin_stdlib 13 | 14 | // rxjava 15 | compile rx.rxjava2 16 | } 17 | -------------------------------------------------------------------------------- /libs/annotation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /libs/annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply from: '../../java_lib.gradle' 2 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/designpattern/Builder.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.designpattern; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE) 10 | public @interface Builder { 11 | } 12 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/designpattern/ISubject.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.designpattern; 2 | 3 | import java.lang.reflect.InvocationHandler; 4 | import java.lang.reflect.Method; 5 | import java.lang.reflect.ParameterizedType; 6 | import java.lang.reflect.Proxy; 7 | import java.lang.reflect.Type; 8 | import java.lang.reflect.WildcardType; 9 | import java.util.ArrayList; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public interface ISubject { 14 | void add(Object observer); 15 | 16 | void remove(Object observer); 17 | 18 | void clear(); 19 | 20 | T asNotifier(); 21 | 22 | @SuppressWarnings("unused") 23 | class Stub implements ISubject { 24 | @SuppressWarnings("WeakerAccess") 25 | protected final ArrayList mObservers = new ArrayList<>(); 26 | @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") 27 | private Map, ?> mNotifierCache = new HashMap<>(); 28 | 29 | @SuppressWarnings({"unchecked"}) 30 | @Override 31 | public void add(Object observer) { 32 | //noinspection RedundantCast 33 | if (!mObservers.contains((T) observer)) { 34 | mObservers.add((T) observer); 35 | } 36 | } 37 | 38 | @SuppressWarnings({"unchecked"}) 39 | @Override 40 | public void remove(Object observer) { 41 | //noinspection RedundantCast 42 | if (mObservers.contains((T) observer)) { 43 | mObservers.remove(observer); 44 | } 45 | } 46 | 47 | @Override 48 | public void clear() { 49 | mObservers.clear(); 50 | } 51 | 52 | @SuppressWarnings({"unchecked"}) 53 | @Override 54 | public T asNotifier() { 55 | Class clazz = getTypeParam(); 56 | 57 | if (mNotifierCache.get(clazz) != null) { 58 | return (T) mNotifierCache.get(clazz); 59 | } 60 | //noinspection Convert2Lambda 61 | T notifier = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() { 62 | @Override 63 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 64 | for (T observer : mObservers) { 65 | method.invoke(observer, args); 66 | } 67 | return null; 68 | } 69 | }); 70 | 71 | ((Map) mNotifierCache).put(clazz, notifier); 72 | return notifier; 73 | } 74 | 75 | @SuppressWarnings({"unchecked"}) 76 | private Class getTypeParam() { 77 | return (Class) getParameterUpperBound(0, ((ParameterizedType) this.getClass().getGenericSuperclass())); 78 | } 79 | 80 | /** 81 | * copy from retrofit.Utils 82 | */ 83 | @SuppressWarnings("SameParameterValue") 84 | private static Type getParameterUpperBound(int index, ParameterizedType type) { 85 | Type[] types = type.getActualTypeArguments(); 86 | if (index < 0 || index >= types.length) { 87 | throw new IllegalArgumentException( 88 | "Index " + index + " not in range [0," + types.length + ") for " + type); 89 | } 90 | Type paramType = types[index]; 91 | if (paramType instanceof WildcardType) { 92 | return ((WildcardType) paramType).getUpperBounds()[0]; 93 | } 94 | return paramType; 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/designpattern/Observer.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.designpattern; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE) 10 | public @interface Observer { 11 | Class value(); 12 | 13 | String name(); 14 | } 15 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/designpattern/Singleton.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.designpattern; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE) 10 | public @interface Singleton { 11 | 12 | @Retention(RetentionPolicy.CLASS) 13 | @Target(ElementType.CONSTRUCTOR) 14 | @interface Main { 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/designpattern/Singletons.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.designpattern; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | 7 | public class Singletons { 8 | private static HashMap sMethodCache = new HashMap<>(); 9 | 10 | @SuppressWarnings({"unchecked"}) 11 | public static T get(Class singletonClazz, Object... params) { 12 | try { 13 | Method method = getMethod(singletonClazz, "getInstance", params); 14 | if (method != null) { 15 | return (T) method.invoke(null, params); 16 | } 17 | } catch (Exception ignored) { 18 | } 19 | // return null; 20 | throw new IllegalArgumentException(singletonClazz.getSimpleName() + String.format(".getInstance(%s) not found", Arrays.toString(params))); 21 | } 22 | 23 | @SuppressWarnings("SameParameterValue") 24 | private static Method getMethod(Class singletonClazz, String methodName, Object... params) { 25 | StringBuilder sb = new StringBuilder(singletonClazz.getCanonicalName() + methodName); 26 | Class[] classes = new Class[params.length]; 27 | for (int i = 0; i < params.length; i++) { 28 | Class clazz = params[i].getClass(); 29 | sb.append(clazz.getCanonicalName()); 30 | classes[i] = clazz; 31 | } 32 | String key = sb.toString(); 33 | 34 | Method method = sMethodCache.get(key); 35 | if (method == null) { 36 | try { 37 | method = singletonClazz.getDeclaredMethod(methodName, classes); 38 | method.setAccessible(true); 39 | } catch (NoSuchMethodException ignored) { 40 | } 41 | 42 | if (method != null) { 43 | sMethodCache.put(key, method); 44 | } 45 | } 46 | 47 | return method; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/designpattern/Subject.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.designpattern; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE) 10 | public @interface Subject { 11 | /** 12 | * class of observer 13 | */ 14 | Observer[] value(); 15 | } 16 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/lang/Getter.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.lang; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE) 10 | public @interface Getter { 11 | } 12 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/lang/Instances.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.lang; 2 | 3 | import java.lang.reflect.Constructor; 4 | 5 | public class Instances { 6 | 7 | @SuppressWarnings({"unchecked"}) 8 | public static T get(Class singletonClazz) { 9 | try { 10 | Constructor constructor = singletonClazz.getDeclaredConstructor(); 11 | constructor.setAccessible(true); // 防止 package/privete 调用 newInstance() 失败 12 | return constructor.newInstance(); 13 | } catch (Exception e) { 14 | throw new IllegalArgumentException(singletonClazz.getSimpleName() + ".() not found", e); 15 | } 16 | // return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /libs/annotation/src/main/java/com/fashare/javasugar/annotation/lang/Setter.java: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.annotation.lang; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | @Retention(RetentionPolicy.CLASS) 9 | @Target(ElementType.TYPE) 10 | public @interface Setter { 11 | boolean returnThis() default true; 12 | } 13 | -------------------------------------------------------------------------------- /libs/apt/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /libs/apt/build.gradle: -------------------------------------------------------------------------------- 1 | import org.gradle.internal.jvm.Jvm 2 | 3 | apply from: '../../java_lib.gradle' 4 | 5 | dependencies { 6 | // javac tools.jar 7 | compile files(Jvm.current().toolsJar) 8 | 9 | // AutoService: 由 google 开发, 10 | // 用于生成 META-INF/services/javax.annotation.processing.Processor 11 | compile 'com.google.auto.service:auto-service:1.0-rc2' 12 | kapt 'com.google.auto.service:auto-service:1.0-rc2' 13 | 14 | // java 代码生成器: java-1.7, 由 square 开发 15 | compile 'com.squareup:javapoet:1.7.0' 16 | 17 | compile project(':libs:annotation') 18 | } 19 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/base/AllFileProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.base 2 | 3 | import com.fashare.javasugar.apt.util.logd 4 | import com.sun.tools.javac.tree.JCTree 5 | import java.io.IOException 6 | import javax.annotation.processing.RoundEnvironment 7 | import javax.lang.model.element.TypeElement 8 | 9 | abstract class AllFileProcessor : BaseProcessor() { 10 | private var mIsFirstRound = true 11 | 12 | protected var rootTree: JCTree.JCCompilationUnit? = null 13 | 14 | final override fun getSupportedAnnotationTypes(): Set { 15 | return setOf("*") 16 | } 17 | 18 | final override fun process(set: Set, roundEnvironment: RoundEnvironment): Boolean { 19 | if (!mIsFirstRound) { 20 | return false 21 | } 22 | mIsFirstRound = false 23 | 24 | logd("process begin !!! set = $set") 25 | 26 | roundEnvironment.getRootElements() 27 | .filter { it is TypeElement } 28 | .map { it as TypeElement } 29 | .forEach { 30 | val treePath = trees.getPath(it) 31 | val cu = treePath.compilationUnit as JCTree.JCCompilationUnit 32 | rootTree = cu 33 | logd("process find class = $it, jcTree = ${cu.javaClass.simpleName}") 34 | translate(it, trees.getTree(it) as JCTree) 35 | 36 | try { 37 | generateJavaFile(it, trees.getTree(it) as JCTree) 38 | } catch (e: IOException) { 39 | e.printStackTrace() 40 | } 41 | } 42 | 43 | logd("process end !!!") 44 | return true 45 | } 46 | 47 | 48 | abstract fun translate(curElement: TypeElement, curTree: JCTree) 49 | 50 | @Throws(IOException::class) 51 | open fun generateJavaFile(curElement: TypeElement, curTree: JCTree){} 52 | } 53 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/base/BaseProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.base 2 | 3 | import com.fashare.javasugar.apt.util.EnvUtil 4 | import com.sun.source.util.Trees 5 | import com.sun.tools.javac.tree.TreeMaker 6 | import com.sun.tools.javac.util.Names 7 | import javax.annotation.processing.AbstractProcessor 8 | import javax.annotation.processing.Filer 9 | import javax.annotation.processing.Messager 10 | import javax.annotation.processing.ProcessingEnvironment 11 | import javax.lang.model.SourceVersion 12 | import javax.lang.model.util.Elements 13 | 14 | 15 | abstract class BaseProcessor : AbstractProcessor() { 16 | // apt 相关类 17 | protected val filer: Filer get() = EnvUtil.filer 18 | protected val elements: Elements get() = EnvUtil.elements 19 | protected val messager: Messager get() = EnvUtil.messager 20 | 21 | // javac 编译器相关类 22 | protected val trees: Trees get() = EnvUtil.trees 23 | protected val treeMaker: TreeMaker get() = EnvUtil.treeMaker 24 | protected val names: Names get() = EnvUtil.names 25 | 26 | override fun init(env: ProcessingEnvironment) { 27 | super.init(env) 28 | EnvUtil.init(env) 29 | } 30 | 31 | override fun getSupportedSourceVersion(): SourceVersion { 32 | return SourceVersion.latestSupported() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/base/SingleAnnotationProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.base 2 | 3 | import com.fashare.javasugar.apt.util.logd 4 | import com.sun.tools.javac.tree.JCTree 5 | import java.io.IOException 6 | import javax.annotation.processing.RoundEnvironment 7 | import javax.lang.model.element.TypeElement 8 | 9 | abstract class SingleAnnotationProcessor : BaseProcessor() { 10 | private var mIsFirstRound = true 11 | 12 | abstract val mAnnotation: Class 13 | 14 | protected var rootTree: JCTree.JCCompilationUnit? = null 15 | 16 | final override fun getSupportedAnnotationTypes(): Set { 17 | return setOf(mAnnotation.canonicalName) 18 | } 19 | 20 | final override fun process(set: Set, roundEnvironment: RoundEnvironment): Boolean { 21 | if (!mIsFirstRound) { 22 | return false 23 | } 24 | mIsFirstRound = false 25 | 26 | logd("process begin !!! set = $set") 27 | 28 | roundEnvironment.getElementsAnnotatedWith(mAnnotation) 29 | .filter { it is TypeElement } 30 | .map { it as TypeElement } 31 | .forEach { 32 | val treePath = trees.getPath(it) 33 | val cu = treePath.compilationUnit as JCTree.JCCompilationUnit 34 | rootTree = cu 35 | logd("process find class = $it, jcTree = ${cu.javaClass.simpleName}") 36 | translate(it, trees.getTree(it) as JCTree) 37 | 38 | try { 39 | generateJavaFile(it, trees.getTree(it) as JCTree) 40 | } catch (e: IOException) { 41 | e.printStackTrace() 42 | } 43 | } 44 | 45 | logd("process end !!!") 46 | return true 47 | } 48 | 49 | 50 | abstract fun translate(curElement: TypeElement, curTree: JCTree) 51 | 52 | @Throws(IOException::class) 53 | open fun generateJavaFile(curElement: TypeElement, curTree: JCTree){} 54 | } 55 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/processors/designpattern/BuilderProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.processors.designpattern 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Builder 4 | import com.fashare.javasugar.apt.base.SingleAnnotationProcessor 5 | import com.fashare.javasugar.apt.util.asSetter 6 | import com.fashare.javasugar.apt.util.lowerFirst 7 | import com.google.auto.service.AutoService 8 | import com.squareup.javapoet.* 9 | import com.sun.source.tree.Tree 10 | import com.sun.tools.javac.code.Flags 11 | import com.sun.tools.javac.tree.JCTree 12 | import com.sun.tools.javac.tree.JCTree.JCClassDecl 13 | import com.sun.tools.javac.tree.JCTree.JCVariableDecl 14 | import com.sun.tools.javac.tree.TreeTranslator 15 | import com.sun.tools.javac.util.List 16 | import com.sun.tools.javac.util.Name 17 | import javax.annotation.processing.Processor 18 | import javax.lang.model.element.Modifier 19 | import javax.lang.model.element.TypeElement 20 | 21 | @Suppress("unused") 22 | @AutoService(Processor::class) 23 | internal class BuilderProcessor : SingleAnnotationProcessor() { 24 | override val mAnnotation = Builder::class.java 25 | 26 | private var mFields: List = List.nil() 27 | 28 | private var returnThis = true 29 | 30 | override fun translate(curElement: TypeElement, curTree: JCTree) { 31 | curTree.accept(MyTreeTranslator(curElement.simpleName as Name)) 32 | } 33 | 34 | override fun generateJavaFile(curElement: TypeElement, curTree: JCTree) { 35 | getJavaFile(curElement).writeTo(filer) 36 | } 37 | 38 | private fun getJavaFile(curElement: TypeElement): JavaFile { 39 | val packageName = rootTree?.packageName?.toString() ?: "" 40 | val rawTargetName = curElement.qualifiedName.substring(packageName.length + 1) 41 | val targetName = rawTargetName.replace(".", "$$") 42 | val builderName = "$targetName\$\$Builder" 43 | 44 | val curBuilder = TypeSpec.classBuilder(builderName) 45 | .addModifiers(Modifier.PUBLIC) 46 | .apply { 47 | mFields.forEach { 48 | this.addField(FieldSpec.builder(ClassName.get(it.sym.asType()), it.name.toString(), Modifier.PRIVATE).build()) 49 | 50 | this.addMethod(MethodSpec.methodBuilder(it.name.toString().asSetter()) 51 | .addModifiers(Modifier.PUBLIC) 52 | .apply { 53 | if (returnThis) 54 | this.returns(ClassName.get(packageName, builderName)) 55 | else 56 | this.returns(Void.TYPE) 57 | } 58 | .addParameter(ClassName.get(it.sym.asType()), it.name.toString()) 59 | .addStatement("this.\$L = \$L", it.name.toString(), it.name.toString()) 60 | .apply { 61 | if(returnThis) 62 | this.addStatement("return this") 63 | } 64 | .build() 65 | ) 66 | } 67 | 68 | @Suppress("UnnecessaryVariable") 69 | val typeUser = rawTargetName 70 | val varUser = targetName.lowerFirst() 71 | this.addMethod(MethodSpec.methodBuilder("build") 72 | .addModifiers(Modifier.PUBLIC) 73 | .returns(ClassName.get(packageName, typeUser)) 74 | .apply { 75 | this.addStatement("\$L \$L = new \$L()", typeUser, varUser, typeUser) 76 | .apply { 77 | mFields.forEach { 78 | this.addStatement("\$L.\$L = this.\$L", varUser, it.name.toString(), it.name.toString()) 79 | } 80 | } 81 | .addStatement("return \$L", varUser) 82 | } 83 | .build() 84 | ) 85 | } 86 | .build() 87 | 88 | return JavaFile.builder(packageName, curBuilder) 89 | .build() 90 | } 91 | 92 | inner class MyTreeTranslator(private val rootClazzName: Name) : TreeTranslator() { 93 | 94 | override fun visitClassDef(jcClassDecl: JCClassDecl) { 95 | if (jcClassDecl.name == rootClazzName) { 96 | treeMaker.at(jcClassDecl.pos) 97 | mFields = List.nil() 98 | jcClassDecl.defs 99 | .filter { it.kind == Tree.Kind.VARIABLE } 100 | .map { it as JCVariableDecl } 101 | .forEach { 102 | mFields = mFields.append(it) 103 | 104 | // Modifier 变为 package 105 | it.mods.flags = it.mods.flags and (Flags.PRIVATE.toLong().inv()) 106 | } 107 | } 108 | super.visitClassDef(jcClassDecl) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/processors/designpattern/SingletonProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.processors.designpattern 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Singleton 4 | import com.fashare.javasugar.apt.base.SingleAnnotationProcessor 5 | import com.fashare.javasugar.apt.util.contains 6 | import com.google.auto.service.AutoService 7 | import com.sun.tools.javac.code.Flags 8 | import com.sun.tools.javac.code.Symbol 9 | import com.sun.tools.javac.code.TypeTag 10 | import com.sun.tools.javac.tree.JCTree 11 | import com.sun.tools.javac.tree.JCTree.JCClassDecl 12 | import com.sun.tools.javac.tree.JCTree.JCMethodDecl 13 | import com.sun.tools.javac.tree.TreeTranslator 14 | import com.sun.tools.javac.util.List 15 | import com.sun.tools.javac.util.ListBuffer 16 | import com.sun.tools.javac.util.Name 17 | import javax.annotation.processing.Processor 18 | import javax.lang.model.element.ExecutableElement 19 | import javax.lang.model.element.TypeElement 20 | 21 | @Suppress("unused") 22 | @AutoService(Processor::class) 23 | internal class SingletonProcessor : SingleAnnotationProcessor() { 24 | override val mAnnotation = Singleton::class.java 25 | 26 | override fun translate(curElement: TypeElement, curTree: JCTree) { 27 | var useDCL = false 28 | var executableElement: ExecutableElement? = null 29 | curElement.enclosedElements 30 | .filter { it is ExecutableElement } 31 | .map { it as ExecutableElement } 32 | .forEach { 33 | if (it.getAnnotation(Singleton.Main::class.java) != null && !it.parameters.isEmpty()) { 34 | useDCL = true 35 | executableElement = it 36 | return@forEach 37 | } 38 | } 39 | if (useDCL) { // DCL 40 | curTree.accept(DCLTreeTranslator(curElement.simpleName as Name, executableElement as? Symbol.MethodSymbol)) 41 | } else { // Instance Holder 42 | curTree.accept(InstanceHolderTreeTranslator(curElement.simpleName as Name)) 43 | } 44 | } 45 | 46 | inner class DCLTreeTranslator(private val rootClazzName: Name, private val constructor: Symbol.MethodSymbol?) : TreeTranslator() { 47 | 48 | override fun visitClassDef(jcClassDecl: JCClassDecl) { 49 | if (jcClassDecl.name == rootClazzName) { // 防止重复访问生成的 _InstanceHolder 50 | if (constructor == null) 51 | return 52 | treeMaker.at(jcClassDecl.pos) 53 | val getInstanceMethod = makeGetInstanceMethodDecl(jcClassDecl) 54 | if (!jcClassDecl.contains(getInstanceMethod)) { 55 | jcClassDecl.defs = jcClassDecl.defs 56 | .prepend(getInstanceMethod) 57 | .prepend(makeInstanceFieldDecl(jcClassDecl)) 58 | } 59 | } 60 | super.visitClassDef(jcClassDecl) 61 | } 62 | 63 | /** 64 | * public static UserManager getInstance(User user) { 65 | * if (_sInstance == null) { 66 | * synchronized (UserManager.class) { 67 | * if (_sInstance == null) { 68 | * _sInstance = new UserManager(user); 69 | * } 70 | * } 71 | * } 72 | * return _sInstance; 73 | * } 74 | */ 75 | private fun makeGetInstanceMethodDecl(jcClassDecl: JCClassDecl): JCMethodDecl { 76 | val body = ListBuffer() 77 | .append(makeCheckNullIfDecl(jcClassDecl)) 78 | .append(treeMaker.Return(treeMaker.Ident(names.fromString("_sInstance")))) 79 | .toList() 80 | .let { treeMaker.Block(0, it) } 81 | 82 | return treeMaker.MethodDef( 83 | treeMaker.Modifiers(Flags.PUBLIC.toLong() or Flags.STATIC.toLong()), 84 | names.fromString("getInstance"), 85 | treeMaker.Ident(jcClassDecl.name), 86 | List.nil(), 87 | constructor?.parameters.let { paramSyms -> 88 | var params = List.nil() 89 | paramSyms?.forEach { 90 | params = params.append(treeMaker.VarDef(it, null)) 91 | } 92 | params 93 | }, 94 | List.nil(), body, null) 95 | } 96 | 97 | private fun makeCheckNullIfDecl(jcClassDecl: JCClassDecl): JCTree.JCIf { 98 | return treeMaker.If(treeMaker.Parens(treeMaker.Binary(JCTree.Tag.EQ, treeMaker.Ident(names.fromString("_sInstance")), treeMaker.Literal(TypeTag.BOT, null))), 99 | ListBuffer() 100 | .append(makeSyncDecl(jcClassDecl)) 101 | .toList() 102 | .let { treeMaker.Block(0, it) }, 103 | null) 104 | } 105 | 106 | private fun makeSyncDecl(jcClassDecl: JCClassDecl): JCTree.JCSynchronized? { 107 | return treeMaker.Synchronized(treeMaker.Parens(treeMaker.Select(treeMaker.Ident(jcClassDecl.name), names._class)), 108 | ListBuffer() 109 | .append(makeDoubleCheckNullIfDecl(jcClassDecl)) 110 | .toList() 111 | .let { treeMaker.Block(0, it) }) 112 | } 113 | 114 | private fun makeDoubleCheckNullIfDecl(jcClassDecl: JCClassDecl): JCTree.JCIf? { 115 | return treeMaker.If(treeMaker.Parens(treeMaker.Binary(JCTree.Tag.EQ, treeMaker.Ident(names.fromString("_sInstance")), treeMaker.Literal(TypeTag.BOT, null))), 116 | ListBuffer() 117 | .append(makeAssignDecl(jcClassDecl)) 118 | .toList() 119 | .let { treeMaker.Block(0, it) }, 120 | null) 121 | } 122 | 123 | private fun makeAssignDecl(jcClassDecl: JCClassDecl): JCTree.JCExpressionStatement? { 124 | return treeMaker.Exec(treeMaker.Assign( 125 | treeMaker.Ident(names.fromString("_sInstance")), 126 | treeMaker.NewClass(null, List.nil(), treeMaker.Ident(jcClassDecl.name), constructor?.parameters.let { paramSyms -> 127 | var params = List.nil() 128 | paramSyms?.forEach { 129 | params = params.append(treeMaker.Ident(it.name)) 130 | } 131 | params 132 | }, null) 133 | )) 134 | } 135 | 136 | /** 137 | * private static volatile UserManager _sInstance; 138 | */ 139 | private fun makeInstanceFieldDecl(jcClassDecl: JCClassDecl): JCTree { 140 | return treeMaker.VarDef( 141 | treeMaker.Modifiers(Flags.PRIVATE.toLong() or Flags.STATIC.toLong() or Flags.VOLATILE.toLong()), 142 | names.fromString("_sInstance"), 143 | treeMaker.Ident(jcClassDecl.name), 144 | null) 145 | } 146 | } 147 | 148 | inner class InstanceHolderTreeTranslator(private val rootClazzName: Name) : TreeTranslator() { 149 | 150 | override fun visitClassDef(jcClassDecl: JCClassDecl) { 151 | if (jcClassDecl.name == rootClazzName) { // 防止重复访问生成的 _InstanceHolder 152 | val getInstanceMethod = makeGetInstanceMethodDecl(jcClassDecl) 153 | if (!jcClassDecl.contains(getInstanceMethod)) { 154 | jcClassDecl.defs = jcClassDecl.defs 155 | .prepend(getInstanceMethod) 156 | .prepend(makeInstanceHolderDecl(jcClassDecl)) 157 | } 158 | } 159 | super.visitClassDef(jcClassDecl) 160 | } 161 | 162 | /** 163 | * public static UserManager getInstance() { 164 | * return UserManager._InstanceHolder._sInstance; 165 | * } 166 | */ 167 | private fun makeGetInstanceMethodDecl(jcClassDecl: JCClassDecl): JCMethodDecl { 168 | val body = ListBuffer() 169 | .append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names.fromString("_InstanceHolder")), names.fromString("_sInstance")))) 170 | .toList() 171 | .let { treeMaker.Block(0, it) } 172 | 173 | return treeMaker.MethodDef( 174 | treeMaker.Modifiers(Flags.PUBLIC.toLong() or Flags.STATIC.toLong()), 175 | names.fromString("getInstance"), 176 | treeMaker.Ident(jcClassDecl.name), 177 | List.nil(), List.nil(), List.nil(), body, null) 178 | } 179 | 180 | /** 181 | * private static class _InstanceHolder { 182 | * private static final UserManager _sInstance = new UserManager(); 183 | * } 184 | */ 185 | private fun makeInstanceHolderDecl(jcClassDecl: JCClassDecl): JCTree { 186 | return treeMaker.ClassDef( 187 | treeMaker.Modifiers(Flags.PRIVATE.toLong() or Flags.STATIC.toLong()), 188 | names.fromString("_InstanceHolder"), 189 | List.nil(), null, List.nil(), 190 | List.of(makeInstanceFieldDecl(jcClassDecl))) 191 | } 192 | 193 | /** 194 | * private static final UserManager _sInstance = new UserManager(); 195 | */ 196 | private fun makeInstanceFieldDecl(jcClassDecl: JCClassDecl): JCTree { 197 | return treeMaker.VarDef( 198 | treeMaker.Modifiers(Flags.PRIVATE.toLong() or Flags.STATIC.toLong() or Flags.FINAL.toLong()), 199 | names.fromString("_sInstance"), 200 | treeMaker.Ident(jcClassDecl.name), 201 | treeMaker.NewClass(null, List.nil(), treeMaker.Ident(jcClassDecl.name), List.nil(), null)) 202 | } 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/processors/designpattern/SubjectProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.processors.designpattern 2 | 3 | import com.fashare.javasugar.annotation.designpattern.ISubject 4 | import com.fashare.javasugar.annotation.designpattern.Observer 5 | import com.fashare.javasugar.annotation.designpattern.Subject 6 | import com.fashare.javasugar.apt.base.SingleAnnotationProcessor 7 | import com.fashare.javasugar.apt.util.asField 8 | import com.fashare.javasugar.apt.util.asGetter 9 | import com.fashare.javasugar.apt.util.getValue 10 | import com.google.auto.service.AutoService 11 | import com.squareup.javapoet.* 12 | import com.sun.tools.javac.code.Flags 13 | import com.sun.tools.javac.code.Type 14 | import com.sun.tools.javac.tree.JCTree 15 | import com.sun.tools.javac.tree.JCTree.JCClassDecl 16 | import com.sun.tools.javac.tree.JCTree.JCStatement 17 | import com.sun.tools.javac.tree.TreeTranslator 18 | import com.sun.tools.javac.util.List 19 | import com.sun.tools.javac.util.ListBuffer 20 | import com.sun.tools.javac.util.Name 21 | import javax.annotation.processing.Processor 22 | import javax.lang.model.element.Modifier 23 | import javax.lang.model.element.TypeElement 24 | 25 | @Suppress("unused") 26 | @AutoService(Processor::class) 27 | internal class SubjectProcessor : SingleAnnotationProcessor() { 28 | override val mAnnotation = Subject::class.java 29 | 30 | var mObservers = emptyArray() 31 | 32 | override fun translate(curElement: TypeElement, curTree: JCTree) { 33 | mObservers = curElement.getAnnotation(mAnnotation).value 34 | 35 | rootTree?.accept(MyTreeTranslator(curElement.simpleName as Name)) 36 | } 37 | 38 | override fun generateJavaFile(curElement: TypeElement, curTree: JCTree) { 39 | getJavaFile(curElement).writeTo(filer) 40 | } 41 | 42 | private fun getJavaFile(curElement: TypeElement): JavaFile { 43 | val packageName = rootTree?.packageName?.toString() ?: "" 44 | 45 | val curISubject = TypeSpec.interfaceBuilder("${curElement.qualifiedName.substring(packageName.length + 1).replace(".", "$$")}\$\$ISubject") 46 | .addModifiers(Modifier.PUBLIC) 47 | .apply { 48 | val typeISubject = ClassName.get(ISubject::class.java) 49 | 50 | mObservers.forEach { observer -> 51 | val typeFull: Type.ClassType = (observer.getValue() as Type.ClassType) // Observer 52 | .let { it.tsym.type as Type.ClassType } // Observer 53 | val typeObserver = ClassName.get(observer.getValue()) as ClassName 54 | val typeTs = typeFull.typeArguments.indices.map { 55 | TypeVariableName.get("${typeFull.typeArguments[it]}") 56 | } 57 | 58 | val hasTypeParam = typeFull.typeArguments.isNotEmpty() 59 | val typeReturn = if (hasTypeParam) { // ISubject> 60 | val typeObserverT = ParameterizedTypeName.get(typeObserver, *typeTs.toTypedArray()) 61 | ParameterizedTypeName.get(typeISubject, typeObserverT) 62 | } else { // ISubject 63 | ParameterizedTypeName.get(typeISubject, typeObserver) 64 | } 65 | this.addMethod(MethodSpec.methodBuilder("${observer.name}Subject".asGetter()) 66 | .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) 67 | .apply { 68 | if (hasTypeParam) 69 | this.addTypeVariables(typeTs) 70 | } 71 | .returns(typeReturn) 72 | .build() 73 | ) 74 | } 75 | } 76 | .build() 77 | 78 | return JavaFile.builder(packageName, curISubject) 79 | .build() 80 | } 81 | 82 | inner class MyTreeTranslator(private val rootClazzName: Name) : TreeTranslator() { 83 | 84 | override fun visitTopLevel(cu: JCTree.JCCompilationUnit) { 85 | cu.defs = cu.defs 86 | .prepend(treeMaker.Import(treeMaker.Select(treeMaker.Ident(names.fromString("java.util")), names.fromString("*")), false)) 87 | .prepend(treeMaker.Import(treeMaker.Select(treeMaker.Ident(names.fromString("com.fashare.javasugar.annotation.designpattern")), names.fromString("ISubject")), false)) 88 | super.visitTopLevel(cu) 89 | } 90 | 91 | override fun visitClassDef(jcClassDecl: JCClassDecl) { 92 | if (jcClassDecl.name == rootClazzName) { 93 | treeMaker.at(jcClassDecl.pos) 94 | 95 | makeISubjectFieldAndGetter(jcClassDecl) 96 | } 97 | super.visitClassDef(jcClassDecl) 98 | } 99 | 100 | private fun makeISubjectFieldAndGetter(jcClassDecl: JCClassDecl) { 101 | mObservers.forEach { observer -> 102 | val iSubject = treeMaker.Ident(names.fromString("ISubject")) 103 | val iSubjectStub = treeMaker.Select(treeMaker.Ident(names.fromString("ISubject")), names.fromString("Stub")) 104 | val typeParam = treeMaker.Ident((observer.getValue() as? Type)?.asElement()?.simpleName) 105 | val fieldName = names.fromString("_${observer.name.asField()}") 106 | 107 | val iSubjectField = treeMaker.VarDef( 108 | treeMaker.Modifiers(Flags.PRIVATE.toLong()), 109 | fieldName, 110 | treeMaker.TypeApply(iSubject, List.of(typeParam)), // 泛型 ISubject 111 | treeMaker.NewClass(null, List.nil(), 112 | treeMaker.TypeApply(iSubjectStub, List.of(typeParam)), 113 | List.nil(), 114 | treeMaker.AnonymousClassDef(treeMaker.Modifiers(0), List.nil()))) 115 | 116 | jcClassDecl.defs = jcClassDecl.defs.prepend(iSubjectField) 117 | 118 | // getter method 119 | val methodName = names.fromString("${observer.name}Subject".asGetter()) 120 | val body = ListBuffer() 121 | .append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names._this), fieldName))) 122 | .toList() 123 | .let { treeMaker.Block(0, it) } 124 | 125 | val iSubjectGetter = treeMaker.MethodDef( 126 | treeMaker.Modifiers(Flags.PUBLIC.toLong()), 127 | methodName, 128 | iSubjectField.vartype, 129 | List.nil(), List.nil(), List.nil(), body, null) 130 | 131 | jcClassDecl.defs = jcClassDecl.defs.prepend(iSubjectGetter) 132 | } 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/processors/lang/GetterProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.processors.lang 2 | 3 | import com.fashare.javasugar.annotation.lang.Getter 4 | import com.fashare.javasugar.apt.base.SingleAnnotationProcessor 5 | import com.fashare.javasugar.apt.util.appendIf 6 | import com.fashare.javasugar.apt.util.asGetter 7 | import com.fashare.javasugar.apt.util.contains 8 | import com.google.auto.service.AutoService 9 | import com.squareup.javapoet.ClassName 10 | import com.squareup.javapoet.JavaFile 11 | import com.squareup.javapoet.MethodSpec 12 | import com.squareup.javapoet.TypeSpec 13 | import com.sun.source.tree.Tree 14 | import com.sun.tools.javac.code.Flags 15 | import com.sun.tools.javac.tree.JCTree 16 | import com.sun.tools.javac.tree.JCTree.* 17 | import com.sun.tools.javac.tree.TreeTranslator 18 | import com.sun.tools.javac.util.List 19 | import com.sun.tools.javac.util.ListBuffer 20 | import com.sun.tools.javac.util.Name 21 | import javax.annotation.processing.Processor 22 | import javax.lang.model.element.Modifier 23 | import javax.lang.model.element.TypeElement 24 | 25 | @Suppress("unused") 26 | @AutoService(Processor::class) 27 | internal class GetterProcessor : SingleAnnotationProcessor() { 28 | override val mAnnotation = Getter::class.java 29 | 30 | private var mFields: List = List.nil() 31 | 32 | override fun translate(curElement: TypeElement, curTree: JCTree) { 33 | curTree.accept(MyTreeTranslator(curElement.simpleName as Name)) 34 | } 35 | 36 | override fun generateJavaFile(curElement: TypeElement, curTree: JCTree) { 37 | getJavaFile(curElement).writeTo(filer) 38 | } 39 | 40 | private fun getJavaFile(curElement: TypeElement): JavaFile { 41 | val packageName = rootTree?.packageName?.toString() ?: "" 42 | 43 | val curIGetter = TypeSpec.interfaceBuilder("${curElement.qualifiedName.substring(packageName.length + 1).replace(".", "$$")}\$\$IGetter") 44 | .addModifiers(Modifier.PUBLIC) 45 | .apply { 46 | mFields.forEach { 47 | this.addMethod(MethodSpec.methodBuilder(it.name.toString().asGetter()) 48 | .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) 49 | .returns(ClassName.get(it.sym.asType())) 50 | .build() 51 | ) 52 | } 53 | } 54 | .build() 55 | 56 | return JavaFile.builder(packageName, curIGetter) 57 | .build() 58 | } 59 | 60 | inner class MyTreeTranslator(private val rootClazzName: Name) : TreeTranslator() { 61 | 62 | override fun visitClassDef(jcClassDecl: JCClassDecl) { 63 | if (jcClassDecl.name == rootClazzName) { 64 | mFields = List.nil() 65 | jcClassDecl.defs 66 | .filter { it.kind == Tree.Kind.VARIABLE } 67 | .map { it as JCVariableDecl } 68 | .forEach { 69 | mFields = mFields.append(it) 70 | 71 | jcClassDecl.defs = jcClassDecl.defs 72 | .appendIf(makeGetterMethodDecl(it)) { 73 | !jcClassDecl.contains(it as JCMethodDecl) 74 | } 75 | } 76 | } 77 | super.visitClassDef(jcClassDecl) 78 | } 79 | 80 | /** 81 | * public String getName() { 82 | * return this.name; 83 | * } 84 | */ 85 | private fun makeGetterMethodDecl(jcVariableDecl: JCVariableDecl): JCMethodDecl { 86 | val body = ListBuffer() 87 | .append(treeMaker.Return(treeMaker.Select(treeMaker.Ident(names._this), jcVariableDecl.getName()))) 88 | .toList() 89 | .let { treeMaker.Block(0, it) } 90 | 91 | return treeMaker.MethodDef( 92 | treeMaker.Modifiers(Flags.PUBLIC.toLong()), 93 | names.fromString(jcVariableDecl.getName().toString().asGetter()), 94 | jcVariableDecl.vartype, 95 | List.nil(), List.nil(), List.nil(), body, null) 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/processors/lang/SetterProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.processors.lang 2 | 3 | import com.fashare.javasugar.annotation.lang.Setter 4 | import com.fashare.javasugar.apt.base.SingleAnnotationProcessor 5 | import com.fashare.javasugar.apt.util.appendIf 6 | import com.fashare.javasugar.apt.util.asSetter 7 | import com.fashare.javasugar.apt.util.contains 8 | import com.google.auto.service.AutoService 9 | import com.squareup.javapoet.ClassName 10 | import com.squareup.javapoet.JavaFile 11 | import com.squareup.javapoet.MethodSpec 12 | import com.squareup.javapoet.TypeSpec 13 | import com.sun.source.tree.Tree 14 | import com.sun.tools.javac.code.Flags 15 | import com.sun.tools.javac.code.TypeTag 16 | import com.sun.tools.javac.tree.JCTree 17 | import com.sun.tools.javac.tree.JCTree.* 18 | import com.sun.tools.javac.tree.TreeTranslator 19 | import com.sun.tools.javac.util.List 20 | import com.sun.tools.javac.util.ListBuffer 21 | import com.sun.tools.javac.util.Name 22 | import javax.annotation.processing.Processor 23 | import javax.lang.model.element.Modifier 24 | import javax.lang.model.element.TypeElement 25 | 26 | @Suppress("unused") 27 | @AutoService(Processor::class) 28 | internal class SetterProcessor : SingleAnnotationProcessor() { 29 | override val mAnnotation = Setter::class.java 30 | 31 | private var mFields: List = List.nil() 32 | 33 | private var returnThis = true 34 | 35 | override fun translate(curElement: TypeElement, curTree: JCTree) { 36 | returnThis = curElement.getAnnotation(mAnnotation).returnThis 37 | 38 | curTree.accept(MyTreeTranslator(curElement.simpleName as Name)) 39 | } 40 | 41 | override fun generateJavaFile(curElement: TypeElement, curTree: JCTree) { 42 | getJavaFile(curElement).writeTo(filer) 43 | } 44 | 45 | private fun getJavaFile(curElement: TypeElement): JavaFile { 46 | val packageName = rootTree?.packageName?.toString() ?: "" 47 | 48 | val curISetter = TypeSpec.interfaceBuilder("${curElement.qualifiedName.substring(packageName.length + 1).replace(".", "$$")}\$\$ISetter") 49 | .addModifiers(Modifier.PUBLIC) 50 | .apply { 51 | mFields.forEach { 52 | this.addMethod(MethodSpec.methodBuilder(it.name.toString().asSetter()) 53 | .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) 54 | .apply { 55 | if (returnThis) 56 | this.returns(ClassName.get(curElement.asType())) 57 | else 58 | this.returns(Void.TYPE) 59 | } 60 | .addParameter(ClassName.get(it.sym.asType()), it.name.toString()) 61 | .build() 62 | ) 63 | } 64 | } 65 | .build() 66 | 67 | return JavaFile.builder(packageName, curISetter) 68 | .build() 69 | } 70 | 71 | inner class MyTreeTranslator(private val rootClazzName: Name) : TreeTranslator() { 72 | 73 | override fun visitClassDef(jcClassDecl: JCClassDecl) { 74 | if (jcClassDecl.name == rootClazzName) { 75 | treeMaker.at(jcClassDecl.pos) 76 | mFields = List.nil() 77 | jcClassDecl.defs 78 | .filter { it.kind == Tree.Kind.VARIABLE } 79 | .map { it as JCVariableDecl } 80 | .forEach { 81 | mFields = mFields.append(it) 82 | 83 | jcClassDecl.defs = jcClassDecl.defs 84 | .appendIf(makeSetterMethodDecl(it, jcClassDecl)) { 85 | !jcClassDecl.contains(it as JCMethodDecl) 86 | } 87 | } 88 | } 89 | super.visitClassDef(jcClassDecl) 90 | } 91 | 92 | /** 93 | * public User setName(String name) { 94 | * this.name = name; 95 | * return this; 96 | * } 97 | */ 98 | private fun makeSetterMethodDecl(jcVariableDecl: JCVariableDecl, jcClassDecl: JCClassDecl): JCMethodDecl { 99 | val body = ListBuffer() 100 | .append(treeMaker.Exec(treeMaker.Assign( 101 | treeMaker.Select(treeMaker.Ident(names._this), jcVariableDecl.getName()), 102 | treeMaker.Ident(jcVariableDecl.name) 103 | ))) 104 | .apply { 105 | if (returnThis) { 106 | this.append(treeMaker.Return(treeMaker.Ident(names._this))) 107 | } 108 | } 109 | .toList() 110 | .let { treeMaker.Block(0, it) } 111 | 112 | return treeMaker.MethodDef( 113 | treeMaker.Modifiers(Flags.PUBLIC.toLong()), 114 | names.fromString(jcVariableDecl.getName().toString().asSetter()), 115 | if (returnThis) { 116 | treeMaker.Ident(jcClassDecl.name) 117 | } else { 118 | treeMaker.TypeIdent(TypeTag.VOID) 119 | }, 120 | List.nil(), 121 | List.of(treeMaker.VarDef(treeMaker.Modifiers(Flags.PARAMETER), 122 | jcVariableDecl.name, 123 | jcVariableDecl.vartype, 124 | null) 125 | ), 126 | List.nil(), 127 | body, null) 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/processors/util/CheckClassDefProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.processors.util 2 | 3 | import com.fashare.javasugar.apt.base.AllFileProcessor 4 | import com.fashare.javasugar.apt.util.removeAbstractIfNeed 5 | import com.google.auto.service.AutoService 6 | import com.sun.tools.javac.tree.JCTree 7 | import com.sun.tools.javac.tree.JCTree.JCClassDecl 8 | import com.sun.tools.javac.tree.TreeTranslator 9 | import com.sun.tools.javac.util.Name 10 | import javax.annotation.processing.Processor 11 | import javax.lang.model.element.TypeElement 12 | 13 | @Suppress("unused") 14 | @AutoService(Processor::class) 15 | internal class CheckClassDefProcessor : AllFileProcessor() { 16 | 17 | override fun translate(curElement: TypeElement, curTree: JCTree) { 18 | curTree.accept(MyTreeTranslator(curElement.simpleName as Name)) 19 | } 20 | 21 | inner class MyTreeTranslator(private val rootClazzName: Name) : TreeTranslator() { 22 | 23 | override fun visitClassDef(jcClassDecl: JCClassDecl) { 24 | // if (jcClassDecl.name == rootClazzName) { 25 | // 去掉 abstract 26 | jcClassDecl.removeAbstractIfNeed() 27 | // } 28 | super.visitClassDef(jcClassDecl) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/util/AnnotationUtil.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.util 2 | 3 | import com.fashare.javasugar.annotation.designpattern.Observer 4 | import javax.lang.model.type.MirroredTypeException 5 | import javax.lang.model.type.TypeMirror 6 | 7 | 8 | /** 9 | * Created by apple on 2019/1/17. 10 | */ 11 | fun Observer.getValue(): TypeMirror? { 12 | try { 13 | this.value 14 | } catch (mte: MirroredTypeException) { 15 | return mte.typeMirror 16 | } 17 | return null 18 | } -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/util/EnvUtil.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.util 2 | 3 | import com.sun.source.util.Trees 4 | import com.sun.tools.javac.code.Types 5 | import com.sun.tools.javac.processing.JavacProcessingEnvironment 6 | import com.sun.tools.javac.tree.TreeMaker 7 | import com.sun.tools.javac.util.Names 8 | import javax.annotation.processing.Filer 9 | import javax.annotation.processing.Messager 10 | import javax.annotation.processing.ProcessingEnvironment 11 | import javax.lang.model.util.Elements 12 | 13 | /** 14 | * Created by apple on 2019/1/10. 15 | */ 16 | internal object EnvUtil { 17 | private var env: ProcessingEnvironment? = null 18 | 19 | // apt 相关类 20 | lateinit var filer: Filer //文件相关的辅助类 21 | lateinit var elements: Elements //元素相关的辅助类 22 | lateinit var messager: Messager //日志相关的辅助类 23 | 24 | // javac 编译器相关类 25 | lateinit var trees: Trees 26 | lateinit var treeMaker: TreeMaker 27 | lateinit var names: Names 28 | lateinit var types: Types 29 | 30 | fun init(env: ProcessingEnvironment) { 31 | if (EnvUtil.env != null) { 32 | return 33 | } 34 | EnvUtil.env = env 35 | 36 | filer = env.filer 37 | elements = env.elementUtils 38 | messager = env.messager 39 | 40 | trees = Trees.instance(env) 41 | val context = (env as JavacProcessingEnvironment).context 42 | treeMaker = TreeMaker.instance(context) 43 | names = Names.instance(context) 44 | types = Types.instance(context) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/util/JCTreeUtil.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.util 2 | 3 | import com.fashare.javasugar.apt.util.EnvUtil.types 4 | import com.sun.tools.javac.code.Flags 5 | import com.sun.tools.javac.tree.JCTree.JCClassDecl 6 | import com.sun.tools.javac.tree.JCTree.JCMethodDecl 7 | import com.sun.tools.javac.util.List 8 | 9 | fun JCClassDecl.removeAbstractIfNeed() { 10 | if (this.sym == null) 11 | return 12 | val undef = types.firstUnimplementedAbstract(this.sym) 13 | if (undef == null) { 14 | // 去掉 abstract 15 | this.mods.flags = this.mods.flags and (Flags.ABSTRACT.toLong().inv()) 16 | } 17 | } 18 | 19 | fun JCClassDecl.contains(method: JCMethodDecl): Boolean { 20 | this.defs.forEach { 21 | if (it is JCMethodDecl && it.conflict(method)) { 22 | return true 23 | } 24 | } 25 | return false 26 | } 27 | 28 | fun JCMethodDecl.conflict(that: JCMethodDecl): Boolean { 29 | return this.name == that.name && this.let { 30 | if (this.params.length() != that.params.length()) 31 | return false 32 | 33 | for (i in this.params.indices) { 34 | if (this.params[i].vartype.toString() != that.params[i].vartype.toString()) 35 | return false 36 | } 37 | 38 | return true 39 | } 40 | } 41 | 42 | fun List.appendIf(item: T, validate: (item: T) -> Boolean): List { 43 | var result = this 44 | if (validate.invoke(item)) { 45 | result = this.append(item) 46 | } 47 | return result 48 | } 49 | 50 | @Suppress("unused") 51 | fun List.prependIf(item: T, validate: (item: T) -> Boolean): List { 52 | var result = this 53 | if (validate.invoke(item)) { 54 | result = this.prepend(item) 55 | } 56 | return result 57 | } 58 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/util/LogUtil.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.util 2 | 3 | import javax.tools.Diagnostic 4 | 5 | /** 6 | * Created by apple on 2019/1/10. 7 | */ 8 | fun Any.logd(msg: String) { 9 | EnvUtil.messager.printMessage(Diagnostic.Kind.NOTE, "${this.javaClass.simpleName}: $msg") 10 | } 11 | 12 | @Suppress("unused") 13 | fun Any.loge(msg: String) { 14 | EnvUtil.messager.printMessage(Diagnostic.Kind.ERROR, "${this.javaClass.simpleName}: $msg") 15 | } 16 | -------------------------------------------------------------------------------- /libs/apt/src/main/kotlin/com/fashare/javasugar/apt/util/NameUtil.kt: -------------------------------------------------------------------------------- 1 | package com.fashare.javasugar.apt.util 2 | 3 | /** 4 | * Created by apple on 2019/1/17. 5 | */ 6 | fun String.upperFirst(): String { 7 | val str = this 8 | return if (!str.isEmpty()) 9 | str.substring(0, 1).toUpperCase() + str.substring(1, str.length) 10 | else 11 | str 12 | } 13 | 14 | fun String.lowerFirst(): String { 15 | val str = this 16 | return if (!str.isEmpty()) 17 | str.substring(0, 1).toLowerCase() + str.substring(1, str.length) 18 | else 19 | str 20 | } 21 | 22 | @Suppress("unused") 23 | fun String.asClass(): String { 24 | return this.upperFirst() 25 | } 26 | 27 | fun String.asField(): String { 28 | return this.lowerFirst() 29 | } 30 | 31 | @Suppress("unused") 32 | fun String.asMethod(): String { 33 | return this.lowerFirst() 34 | } 35 | 36 | fun String.asGetter(): String { 37 | return "get" + this.upperFirst() 38 | } 39 | 40 | fun String.asSetter(): String { 41 | return "set" + this.upperFirst() 42 | } 43 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':libs:apt', ':libs:annotation', ':demos:test-java' 2 | --------------------------------------------------------------------------------