Prepare annotation processor
This commit is contained in:
parent
cd7aae7c9e
commit
ba85ea0e98
@ -1,4 +1,6 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
|
||||
@ -13,16 +15,6 @@ android {
|
||||
versionName version
|
||||
|
||||
resConfig 'en'
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = [
|
||||
// cannot cast GString...
|
||||
// cannot use `-`
|
||||
'markwon.samples.file': "\"${projectDir}/samples.json\"".toString()
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dexOptions {
|
||||
@ -40,11 +32,17 @@ android {
|
||||
java.srcDirs += '../sample-utils/annotations'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
kapt {
|
||||
arguments {
|
||||
arg('markwon.samples.file', "${projectDir}/samples.json".toString())
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
kapt project(':sample-utils:processor')
|
||||
|
||||
implementation 'io.noties:debug:5.1.0'
|
||||
annotationProcessor project(':sample-utils:processor')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
package io.noties.markwon.app;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import io.noties.debug.AndroidLogDebugOutput;
|
||||
import io.noties.debug.Debug;
|
||||
|
||||
public class App extends Application {
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
Debug.init(new AndroidLogDebugOutput(true));
|
||||
}
|
||||
}
|
14
app-sample/src/main/java/io/noties/markwon/app/App.kt
Normal file
14
app-sample/src/main/java/io/noties/markwon/app/App.kt
Normal file
@ -0,0 +1,14 @@
|
||||
package io.noties.markwon.app
|
||||
|
||||
import android.app.Application
|
||||
import io.noties.debug.AndroidLogDebugOutput
|
||||
import io.noties.debug.Debug
|
||||
|
||||
class App : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
Debug.init(AndroidLogDebugOutput(BuildConfig.DEBUG))
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package io.noties.markwon.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
public class MainActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package io.noties.markwon.app
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
|
||||
class MainActivity : Activity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
/**/
|
||||
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
package io.noties.markwon.app;
|
||||
|
||||
import io.noties.markwon.sample.annotations.MarkwonArtifact;
|
||||
import io.noties.markwon.sample.annotations.MarkwonSample;
|
||||
|
||||
@MarkwonSample(
|
||||
id = "202006163161416",
|
||||
title = "The first sample title",
|
||||
description = "This is description",
|
||||
artifacts = {MarkwonArtifact.CORE, MarkwonArtifact.EDITOR},
|
||||
tags = "none"
|
||||
)
|
||||
public class Test {
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.3.72'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
@ -7,6 +8,7 @@ buildscript {
|
||||
// on `3.5.3` tests are not run from CLI
|
||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,7 +82,10 @@ ext {
|
||||
'dagger' : "com.google.dagger:dagger:$daggerVersion",
|
||||
'picasso' : 'com.squareup.picasso:picasso:2.71828',
|
||||
'glide' : 'com.github.bumptech.glide:glide:4.9.0',
|
||||
'coil' : 'io.coil-kt:coil:0.10.1'
|
||||
'coil' : 'io.coil-kt:coil:0.10.1',
|
||||
'ix-java' : 'com.github.akarnokd:ixjava:1.0.0',
|
||||
'gson' : 'com.google.code.gson:gson:2.8.6',
|
||||
'commons-io' : 'commons-io:commons-io:2.6'
|
||||
]
|
||||
|
||||
deps['annotationProcessor'] = [
|
||||
@ -91,8 +96,6 @@ ext {
|
||||
deps['test'] = [
|
||||
'junit' : 'junit:junit:4.12',
|
||||
'robolectric' : 'org.robolectric:robolectric:3.8',
|
||||
'ix-java' : 'com.github.akarnokd:ixjava:1.0.0',
|
||||
'commons-io' : 'commons-io:commons-io:2.6',
|
||||
'mockito' : 'org.mockito:mockito-core:2.21.0',
|
||||
'commonmark-test-util': "com.atlassian.commonmark:commonmark-test-util:$commonMarkVersion",
|
||||
]
|
||||
|
@ -25,15 +25,14 @@ dependencies {
|
||||
compileOnly it['x-appcompat']
|
||||
}
|
||||
|
||||
deps['test'].with {
|
||||
testImplementation project(':markwon-test-span')
|
||||
testImplementation deps['commons-io']
|
||||
|
||||
testImplementation project(':markwon-test-span')
|
||||
deps['test'].with {
|
||||
|
||||
testImplementation it['junit']
|
||||
testImplementation it['robolectric']
|
||||
testImplementation it['mockito']
|
||||
|
||||
testImplementation it['commons-io']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,15 +17,14 @@ dependencies {
|
||||
|
||||
api project(':markwon-core')
|
||||
|
||||
deps['test'].with {
|
||||
testImplementation project(':markwon-test-span')
|
||||
testImplementation deps['commons-io']
|
||||
|
||||
testImplementation project(':markwon-test-span')
|
||||
deps['test'].with {
|
||||
|
||||
testImplementation it['junit']
|
||||
testImplementation it['robolectric']
|
||||
testImplementation it['mockito']
|
||||
|
||||
testImplementation it['commons-io']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,9 @@ dependencies {
|
||||
|
||||
deps.with {
|
||||
api it['commonmark-strikethrough']
|
||||
|
||||
// NB! ix-java dependency to be used in tests
|
||||
testImplementation it['ix-java']
|
||||
}
|
||||
|
||||
deps.test.with {
|
||||
@ -26,7 +29,6 @@ dependencies {
|
||||
testImplementation it['junit']
|
||||
testImplementation it['mockito']
|
||||
testImplementation it['robolectric']
|
||||
testImplementation it['ix-java']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,13 +17,13 @@ dependencies {
|
||||
|
||||
api project(':markwon-core')
|
||||
|
||||
deps['test'].with {
|
||||
testImplementation project(':markwon-test-span')
|
||||
testImplementation deps['commons-io']
|
||||
|
||||
testImplementation project(':markwon-test-span')
|
||||
deps['test'].with {
|
||||
|
||||
testImplementation it['junit']
|
||||
testImplementation it['robolectric']
|
||||
testImplementation it['commons-io']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,13 @@ dependencies {
|
||||
// it to be consistent with markdown (please note that we do not use markwon plugin
|
||||
// for that in case if different implementation is used)
|
||||
compileOnly it['commonmark-strikethrough']
|
||||
|
||||
testImplementation it['ix-java']
|
||||
}
|
||||
|
||||
deps.test.with {
|
||||
testImplementation it['junit']
|
||||
testImplementation it['robolectric']
|
||||
testImplementation it['ix-java']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,15 +23,14 @@ dependencies {
|
||||
compileOnly it['okhttp']
|
||||
}
|
||||
|
||||
deps['test'].with {
|
||||
testImplementation project(':markwon-test-span')
|
||||
testImplementation deps['commons-io']
|
||||
|
||||
testImplementation project(':markwon-test-span')
|
||||
deps['test'].with {
|
||||
|
||||
testImplementation it['junit']
|
||||
testImplementation it['robolectric']
|
||||
testImplementation it['mockito']
|
||||
|
||||
testImplementation it['commons-io']
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,9 +16,9 @@ android {
|
||||
dependencies {
|
||||
|
||||
api deps['x-annotations']
|
||||
api deps['ix-java']
|
||||
|
||||
deps['test'].with {
|
||||
api it['junit']
|
||||
api it['ix-java']
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface MarkwonSample {
|
||||
public @interface MarkwonSampleInfo {
|
||||
/**
|
||||
* Actual format is not important, but this key must be set in order to persist sample.
|
||||
* This key should not change during lifetime of sample
|
@ -8,5 +8,9 @@ sourceSets {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation deps['x-annotations']
|
||||
deps.with {
|
||||
implementation it['x-annotations']
|
||||
implementation it['gson']
|
||||
implementation it['commons-io']
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package io.noties.markwon.sample.processor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
class Logger {
|
||||
private final Messager messager;
|
||||
|
||||
Logger(@NonNull Messager messager) {
|
||||
this.messager = messager;
|
||||
}
|
||||
|
||||
void error(@NonNull String message, Object... args) {
|
||||
messager.printMessage(Diagnostic.Kind.ERROR, "\n[Markwon] " + String.format(message, args) + "\n\u00a0");
|
||||
}
|
||||
|
||||
void info(@NonNull String message, Object... args) {
|
||||
messager.printMessage(Diagnostic.Kind.NOTE, "\n[Markwon] " + String.format(message, args) + "\n\u00a0");
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package io.noties.markwon.sample.processor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import io.noties.markwon.sample.annotations.MarkwonArtifact;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
public class MarkwonSample {
|
||||
// represents full (package + class) name to be use in reflective lookup
|
||||
final String javaClassName;
|
||||
|
||||
final String id;
|
||||
final String title;
|
||||
final String description;
|
||||
final Set<MarkwonArtifact> artifacts;
|
||||
final Set<String> tags;
|
||||
|
||||
public MarkwonSample(
|
||||
@NonNull String javaClassName,
|
||||
@NonNull String id,
|
||||
@NonNull String title,
|
||||
@NonNull String description,
|
||||
@NonNull Set<MarkwonArtifact> artifacts,
|
||||
@NonNull Set<String> tags
|
||||
) {
|
||||
this.javaClassName = javaClassName;
|
||||
this.id = id;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.artifacts = artifacts;
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
MarkwonSample sample = (MarkwonSample) o;
|
||||
|
||||
if (!javaClassName.equals(sample.javaClassName)) return false;
|
||||
if (!id.equals(sample.id)) return false;
|
||||
if (!title.equals(sample.title)) return false;
|
||||
if (!description.equals(sample.description)) return false;
|
||||
if (!artifacts.equals(sample.artifacts)) return false;
|
||||
return tags.equals(sample.tags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = javaClassName.hashCode();
|
||||
result = 31 * result + id.hashCode();
|
||||
result = 31 * result + title.hashCode();
|
||||
result = 31 * result + description.hashCode();
|
||||
result = 31 * result + artifacts.hashCode();
|
||||
result = 31 * result + tags.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MarkwonSample{" +
|
||||
"javaClassName='" + javaClassName + '\'' +
|
||||
", id='" + id + '\'' +
|
||||
", title='" + title + '\'' +
|
||||
", description='" + description + '\'' +
|
||||
", artifacts=" + artifacts +
|
||||
", tags=" + tags +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -2,27 +2,50 @@ package io.noties.markwon.sample.processor;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import io.noties.markwon.sample.annotations.MarkwonSample;
|
||||
import io.noties.markwon.sample.annotations.MarkwonSampleInfo;
|
||||
|
||||
public class MarkwonSampleProcessor extends AbstractProcessor {
|
||||
|
||||
private static final String KEY_SAMPLES_FILE = "markwon.samples.file";
|
||||
private static final DateFormat ID_DATEFORMAT = new SimpleDateFormat("YYYYMMDDHHmmss", Locale.ROOT);
|
||||
|
||||
private Messager messager;
|
||||
private Logger logger;
|
||||
private String samplesFilePath;
|
||||
|
||||
private List<MarkwonSample> samples;
|
||||
private boolean samplesUpdated;
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedOptions() {
|
||||
return Collections.singleton(KEY_SAMPLES_FILE);
|
||||
@ -35,31 +58,174 @@ public class MarkwonSampleProcessor extends AbstractProcessor {
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedAnnotationTypes() {
|
||||
return Collections.singleton(MarkwonSample.class.getName());
|
||||
return Collections.singleton(MarkwonSampleInfo.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment processingEnvironment) {
|
||||
super.init(processingEnvironment);
|
||||
|
||||
messager = processingEnvironment.getMessager();
|
||||
logger = new Logger(processingEnvironment.getMessager());
|
||||
|
||||
samplesFilePath = processingEnvironment.getOptions().get(KEY_SAMPLES_FILE);
|
||||
|
||||
try {
|
||||
// create mutable copy
|
||||
samples = new ArrayList<>(readCurrentSamples(samplesFilePath));
|
||||
} catch (Throwable t) {
|
||||
logger.error(t.getMessage());
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
|
||||
if (!roundEnvironment.processingOver()) {
|
||||
final Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(MarkwonSample.class);
|
||||
final long begin = System.currentTimeMillis();
|
||||
final Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(MarkwonSampleInfo.class);
|
||||
if (elements != null) {
|
||||
|
||||
for (Element element : elements) {
|
||||
process(element);
|
||||
}
|
||||
|
||||
if (samplesUpdated) {
|
||||
logger.info("samples updated, writing at path: %s", samplesFilePath);
|
||||
try {
|
||||
writeSamples(samplesFilePath, samples);
|
||||
} catch (Throwable t) {
|
||||
logger.error(t.getMessage());
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
final long end = System.currentTimeMillis();
|
||||
logger.info("processing took: %d ms", end - begin);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void process(@NonNull Element element) {
|
||||
messager.printMessage(Diagnostic.Kind.WARNING, samplesFilePath, element);
|
||||
try {
|
||||
final MarkwonSample sample = parse((TypeElement) element);
|
||||
final boolean updated = updateSamples(samples, sample);
|
||||
if (updated) {
|
||||
logger.info("updated sample: '%s'", sample.javaClassName);
|
||||
}
|
||||
samplesUpdated = samplesUpdated || updated;
|
||||
} catch (Throwable t) {
|
||||
logger.error(t.getMessage());
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static List<MarkwonSample> readCurrentSamples(@NonNull String path) throws Throwable {
|
||||
|
||||
final File file = new File(path);
|
||||
if (!file.exists()) {
|
||||
// the very first one, no need to create file at this point, just return empty list
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try (
|
||||
InputStream inputStream = FileUtils.openInputStream(file);
|
||||
Reader reader = new InputStreamReader(inputStream)
|
||||
) {
|
||||
|
||||
return new Gson()
|
||||
.fromJson(reader, new TypeToken<List<MarkwonSample>>() {
|
||||
}.getType());
|
||||
} catch (IOException e) {
|
||||
throw new Throwable(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeSamples(@NonNull String path, @NonNull List<MarkwonSample> samples) throws Throwable {
|
||||
|
||||
final File file = new File(path);
|
||||
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
if (!file.createNewFile()) {
|
||||
throw new Throwable("Cannot create new file at: " + path);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new Throwable("Cannot create new file at: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
// sort based on id (it is date)
|
||||
// new items come first (DESC order)
|
||||
Collections.sort(samples, (lhs, rhs) -> rhs.id.compareTo(lhs.id));
|
||||
|
||||
final String json = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
.toJson(samples);
|
||||
|
||||
FileUtils.write(file, json, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private static MarkwonSample parse(@NonNull TypeElement element) throws Throwable {
|
||||
final MarkwonSampleInfo info = element.getAnnotation(MarkwonSampleInfo.class);
|
||||
if (info == null) {
|
||||
throw new Throwable("Cannot obtain `MarkwonSampleInfo` annotation");
|
||||
}
|
||||
|
||||
final String id = info.id();
|
||||
|
||||
final MarkwonSample sample = new MarkwonSample(
|
||||
element.getQualifiedName().toString(),
|
||||
id,
|
||||
info.title(),
|
||||
info.description(),
|
||||
new HashSet<>(Arrays.asList(info.artifacts())),
|
||||
new HashSet<>(Arrays.asList(info.tags()))
|
||||
);
|
||||
|
||||
try {
|
||||
ID_DATEFORMAT.parse(id);
|
||||
} catch (ParseException e) {
|
||||
throw new Throwable(String.format("sample: '%s', id does not match pattern: '%s'",
|
||||
sample.javaClassName,
|
||||
id)
|
||||
);
|
||||
}
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
// returns boolean indicating if samples were updated
|
||||
private static boolean updateSamples(@NonNull List<MarkwonSample> samples, @NonNull MarkwonSample sample) {
|
||||
|
||||
final ListIterator<MarkwonSample> iterator = samples.listIterator();
|
||||
|
||||
boolean found = false;
|
||||
boolean updated = false;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
final MarkwonSample existing = iterator.next();
|
||||
|
||||
// check for id
|
||||
if (existing.id.equals(sample.id)) {
|
||||
// if not the same -> replace
|
||||
if (!existing.equals(sample)) {
|
||||
iterator.set(sample);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
samples.add(sample);
|
||||
}
|
||||
|
||||
// if not found (inserted new) or updated (found and was different)
|
||||
return !found || updated;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1 @@
|
||||
io.noties.markwon.sample.processor.MarkwonSampleProcessor,isolating
|
Loading…
x
Reference in New Issue
Block a user