1 package com.android.codegen 2 3 import com.github.javaparser.JavaParser 4 import java.io.File 5 6 7 const val THIS_SCRIPT_LOCATION = "" 8 const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME" 9 const val GENERATED_END = "// End of generated code" 10 const val INDENT_SINGLE = " " 11 12 val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean") 13 val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character" 14 15 val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern") 16 17 const val FLAG_BUILDER_PROTECTED_SETTERS = "--builder-protected-setters" 18 const val FLAG_NO_FULL_QUALIFIERS = "--no-full-qualifiers" 19 20 val JAVA_PARSER = JavaParser() 21 22 /** @see [FeatureFlag] */ 23 val USAGE = """ 24 Usage: $CODEGEN_NAME [--[PREFIX-]FEATURE...] JAVAFILE 25 26 Generates boilerplade parcelable/data class code at the bottom of JAVAFILE, based o fields' declaration in the given JAVAFILE's top-level class 27 28 FEATURE represents some generatable code, and can be among: 29 ${FeatureFlag.values().map { feature -> 30 " ${feature.kebabCase}" to feature.desc 31 }.columnize(" - ")} 32 33 And PREFIX can be: 34 <empty> - request to generate the feature 35 no - suppress generation of the feature 36 hidden - request to generate the feature with @hide 37 38 Extra options: 39 --help - view this help 40 --update-only - auto-detect flags from the previously auto-generated comment within the file 41 $FLAG_NO_FULL_QUALIFIERS 42 - when referring to classes don't use package name prefix; handy with IDE auto-import 43 $FLAG_BUILDER_PROTECTED_SETTERS 44 - make builder's setters protected to expose them as public in a subclass on a whitelist basis 45 46 47 Special field modifiers and annotations: 48 transient - ignore the field completely 49 @Nullable - support null value when parcelling, and never throw on null input 50 @NonNull - throw on null input and don't parcel the nullness bit for the field 51 @DataClass.Enum - parcel field as an enum value by ordinal 52 @DataClass.PluralOf(..) - provide a singular version of a collection field name to be used in the builder's 'addFoo(..)' 53 @DataClass.ParcelWith(..) - provide a custom Parcelling class, specifying the custom (un)parcelling logic for this field 54 = <initializer>; - provide default value and never throw if this field was not provided e.g. when using builder 55 /** ... */ - copy given javadoc on field's getters/setters/constructor params/builder setters etc. 56 @hide (in javadoc) - force field's getters/setters/withers/builder setters to be @hide-den if generated 57 58 59 Special methods/etc. you can define: 60 61 <any auto-generatable method> 62 For any method to be generated, if a method with same name and argument types is already 63 defined, than that method will not be generated. 64 This allows you to override certain details on granular basis. 65 66 void onConstructed() 67 Will be called in constructor, after all the fields have been initialized. 68 This is a good place to put any custom validation logic that you may have 69 70 static class $CANONICAL_BUILDER_CLASS extends $BASE_BUILDER_CLASS 71 If a class extending $BASE_BUILDER_CLASS is specified, generated builder's setters will 72 return the provided $CANONICAL_BUILDER_CLASS type. 73 $BASE_BUILDER_CLASS's constructor(s) will be package-private to encourage using $CANONICAL_BUILDER_CLASS instead 74 This allows you to extend the generated builder, adding or overriding any methods you may want 75 76 77 In addition, for any field mMyField(or myField) of type FieldType you can define the following methods: 78 79 void parcelMyField(Parcel dest, int flags) 80 Allows you to provide custom logic for storing mMyField into a Parcel 81 82 static FieldType unparcelMyField(Parcel in) 83 Allows you to provide custom logic to deserialize the value of mMyField from a Parcel 84 85 String myFieldToString() 86 Allows you to provide a custom toString representation of mMyField's value 87 88 FieldType lazyInitMyField() 89 Requests a lazy initialization in getMyField(), with the provided method being the constructor 90 You may additionally mark the fields as volatile to cause this to generate a thread-safe 91 double-check locking lazy initialization 92 93 FieldType defaultMyField() 94 Allows you to provide a default value to initialize the field to, in case an explicit one 95 was not provided. 96 This is an alternative to providing a field initializer that, unlike the initializer, 97 you can use with final fields. 98 99 Version: $CODEGEN_VERSION 100 101 Questions? Feedback? 102 Contact: eugenesusla@ 103 Bug/feature request: http://go/codegen-bug 104 105 Slides: http://go/android-codegen 106 In-depth example: http://go/SampleDataClass 107 """ 108 109 fun main(args: Array<String>) { 110 if (args.contains("--help")) { 111 println(USAGE) 112 System.exit(0) 113 } 114 if (args.contains("--version")) { 115 println(CODEGEN_VERSION) 116 System.exit(0) 117 } 118 val file = File(args.last()).absoluteFile 119 val sourceLisnesOriginal = file.readLines() 120 val sourceLinesNoClosingBrace = sourceLisnesOriginal.dropLastWhile { 121 it.startsWith("}") || it.all(Char::isWhitespace) 122 } 123 val cliArgs = handleUpdateFlag(args, sourceLinesNoClosingBrace) 124 125 val fileInfo = FileInfo(sourceLisnesOriginal, cliArgs, file) 126 fileInfo.main() 127 file.writeText(fileInfo.stringBuilder.toString().mapLines { trimEnd() }) 128 } 129 130 private fun handleUpdateFlag(cliArgs: Array<String>, sourceLines: List<String>): Array<String> { 131 if ("--update-only" in cliArgs 132 && sourceLines.none { GENERATED_WARNING_PREFIX in it || it.startsWith("@DataClass") }) { 133 System.exit(0) 134 } 135 return cliArgs - "--update-only" 136 }