001package io.avaje.jsonb.generator; 002 003import io.avaje.jsonb.Json; 004 005import javax.annotation.processing.AbstractProcessor; 006import javax.annotation.processing.ProcessingEnvironment; 007import javax.annotation.processing.RoundEnvironment; 008import javax.lang.model.SourceVersion; 009import javax.lang.model.element.Element; 010import javax.lang.model.element.ElementKind; 011import javax.lang.model.element.TypeElement; 012import java.io.IOException; 013import java.util.*; 014 015public class Processor extends AbstractProcessor { 016 017 private final ComponentMetaData metaData = new ComponentMetaData(); 018 private final ImportReader importReader = new ImportReader(); 019 private final List<BeanReader> allReaders = new ArrayList<>(); 020 private final Set<String> sourceTypes = new HashSet<>(); 021 022 private ProcessingContext context; 023 private SimpleComponentWriter componentWriter; 024 private boolean readModuleInfo; 025 026 public Processor() { 027 } 028 029 @Override 030 public SourceVersion getSupportedSourceVersion() { 031 return SourceVersion.latest(); 032 } 033 034 @Override 035 public synchronized void init(ProcessingEnvironment processingEnv) { 036 super.init(processingEnv); 037 this.context = new ProcessingContext(processingEnv); 038 this.componentWriter = new SimpleComponentWriter(context, metaData); 039 } 040 041 @Override 042 public Set<String> getSupportedAnnotationTypes() { 043 Set<String> annotations = new LinkedHashSet<>(); 044 annotations.add(Json.class.getCanonicalName()); 045 annotations.add(Json.Import.class.getCanonicalName()); 046 return annotations; 047 } 048 049 /** 050 * Read the existing metadata from the generated component (if exists). 051 */ 052 private void readModule() { 053 if (readModuleInfo) { 054 return; 055 } 056 readModuleInfo = true; 057 new ComponentReader(context, metaData).read(); 058 } 059 060 @Override 061 public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment round) { 062 readModule(); 063 writeAdapters(round.getElementsAnnotatedWith(Json.class)); 064 writeAdaptersForImported(round.getElementsAnnotatedWith(Json.Import.class)); 065 initialiseComponent(); 066 cascadeTypes(); 067 writeComponent(round.processingOver()); 068 return false; 069 } 070 071 private void cascadeTypes() { 072 while (!allReaders.isEmpty()) { 073 cascadeTypesInner(); 074 } 075 } 076 077 private void cascadeTypesInner() { 078 ArrayList<BeanReader> copy = new ArrayList<>(allReaders); 079 allReaders.clear(); 080 081 Set<String> extraTypes = new TreeSet<>(); 082 for (BeanReader reader : copy) { 083 reader.cascadeTypes(extraTypes); 084 } 085 for (String type : extraTypes) { 086 if (!ignoreType(type)) { 087 TypeElement element = context.element(type); 088 if (cascadeElement(element)) { 089 writeAdapterForType(element); 090 } 091 } 092 } 093 } 094 095 private boolean cascadeElement(TypeElement element) { 096 return element.getKind() != ElementKind.ENUM 097 && !metaData.contains(adapterName(element)); 098 } 099 100 private String adapterName(TypeElement element) { 101 return new AdapterName(element).fullName(); 102 } 103 104 private boolean ignoreType(String type) { 105 return type.indexOf('.') == -1 106 || type.startsWith("java.") 107 || type.startsWith("javax.") 108 || sourceTypes.contains(type); 109 } 110 111 /** 112 * Elements that have a {@code @Json.Import} annotation. 113 */ 114 private void writeAdaptersForImported(Set<? extends Element> importedElements) { 115 for (Element importedElement : importedElements) { 116 for (String importType : importReader.read(importedElement)) { 117 TypeElement element = context.element(importType); 118 if (element == null) { 119 context.logError("Unable to find imported element " + importType); 120 } else { 121 writeAdapterForType(element); 122 } 123 } 124 } 125 } 126 127 private void initialiseComponent() { 128 metaData.initialiseFullName(); 129 try { 130 componentWriter.initialise(); 131 } catch (IOException e) { 132 context.logError("Error creating writer for JsonbComponent", e); 133 } 134 } 135 136 private void writeComponent(boolean processingOver) { 137 if (processingOver) { 138 try { 139 componentWriter.write(); 140 componentWriter.writeMetaInf(); 141 } catch (IOException e) { 142 context.logError("Error writing component", e); 143 } 144 } 145 } 146 147 /** 148 * Read the beans that have changed. 149 */ 150 private void writeAdapters(Set<? extends Element> beans) { 151 for (Element element : beans) { 152 if (!(element instanceof TypeElement)) { 153 context.logError("unexpected type [" + element + "]"); 154 } else { 155 writeAdapterForType((TypeElement) element); 156 } 157 } 158 } 159 160 private void writeAdapterForType(TypeElement typeElement) { 161 BeanReader beanReader = new BeanReader(typeElement, context); 162 beanReader.read(); 163 try { 164 SimpleAdapterWriter beanWriter = new SimpleAdapterWriter(beanReader, context); 165 metaData.add(beanWriter.fullName()); 166 beanWriter.write(); 167 allReaders.add(beanReader); 168 sourceTypes.add(typeElement.getSimpleName().toString()); 169 } catch (IOException e) { 170 context.logError("Error writing JsonAdapter for %s %s", beanReader, e); 171 } 172 } 173 174}