diff --git a/README.md b/README.md index 4250fc0..5f4612c 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,31 @@ Notes: - Methods with default implementations are used in the generation unless they are annotated with `@IgnoreDefaultMethod` - If you do not want a record builder generated, annotate your interface as `@RecordInterface(addRecordBuilder = false)` +## Generation Via Includes + +An alternate method of generation is to use the Include variants of the annotations. These variants +act on lists of specified classes. This allows the source classes to be pristine or even come from +libraries where you are not able to annotate the source. + +E.g. + +``` +import some.library.code.ImportedRecord +import some.library.code.ImportedInterface + +@RecordBuilder.Include({ + ImportedRecord.class // generates a record builder for ImportedRecord +}) +@RecordInterface.Include({ + ImportedInterface.class // generates a record interface for ImportedInterface +}) +public void Placeholder { +} +``` + +The target package for generation is the same as the package that contains the "Include" +annotation. Use `packagePattern` to change this (see Javadoc for details). + ## Usage ### Maven diff --git a/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordBuilder.java b/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordBuilder.java index faf1454..15cb9f1 100644 --- a/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordBuilder.java +++ b/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordBuilder.java @@ -23,4 +23,19 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE) public @interface RecordBuilder { + @Target({ElementType.TYPE, ElementType.PACKAGE}) + @Retention(RetentionPolicy.SOURCE) + @interface Include { + Class[] value(); + + /** + * Pattern used to generate the package for the generated class. The value + * is the literal package name however two replacement values can be used. '@' + * is replaced with the package of the Include annotation. '*' is replaced with + * the package of the included class. + * + * @return package pattern + */ + String packagePattern() default "@"; + } } diff --git a/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordInterface.java b/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordInterface.java index f98bce0..2f2f5aa 100644 --- a/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordInterface.java +++ b/record-builder-core/src/main/java/io/soabase/recordbuilder/core/RecordInterface.java @@ -24,4 +24,22 @@ import java.lang.annotation.Target; @Target(ElementType.TYPE) public @interface RecordInterface { boolean addRecordBuilder() default true; + + @Target({ElementType.TYPE, ElementType.PACKAGE}) + @Retention(RetentionPolicy.SOURCE) + @interface Include { + Class[] value(); + + boolean addRecordBuilder() default true; + + /** + * Pattern used to generate the package for the generated class. The value + * is the literal package name however two replacement values can be used. '@' + * is replaced with the package of the Include annotation. '*' is replaced with + * the package of the included class. + * + * @return package pattern + */ + String packagePattern() default "@"; + } } diff --git a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/ElementUtils.java b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/ElementUtils.java index cc46bc3..8a3cc73 100644 --- a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/ElementUtils.java +++ b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/ElementUtils.java @@ -20,14 +20,63 @@ import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeVariableName; import io.soabase.recordbuilder.core.RecordBuilderMetaData; - +import javax.annotation.processing.ProcessingEnvironment; +import javax.lang.model.element.AnnotationMirror; +import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.RecordComponentElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeParameterElement; +import javax.lang.model.type.TypeMirror; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; public class ElementUtils { + public static Optional findAnnotationMirror(ProcessingEnvironment processingEnv, Element element, String annotationClass) { + return processingEnv.getElementUtils().getAllAnnotationMirrors(element).stream() + .filter(e -> e.getAnnotationType().toString().equals(annotationClass)) + .findFirst(); + } + + public static Optional getAnnotationValue(Map values, String name) { + return values.entrySet() + .stream() + .filter(e -> e.getKey().getSimpleName().toString().equals(name)) + .map(Map.Entry::getValue) + .findFirst(); + } + + @SuppressWarnings("unchecked") + public static List getClassesAttribute(AnnotationValue attribute) + { + List values = (attribute != null) ? (List)attribute.getValue() : Collections.emptyList(); + return values.stream().map(v -> (TypeMirror)v.getValue()).collect(Collectors.toList()); + } + + public static boolean getBooleanAttribute(AnnotationValue attribute) + { + Object value = (attribute != null) ? attribute.getValue() : null; + if ( value != null ) + { + return Boolean.parseBoolean(String.valueOf(value)); + } + return false; + } + + public static String getStringAttribute(AnnotationValue attribute, String defaultValue) + { + Object value = (attribute != null) ? attribute.getValue() : null; + if ( value != null ) + { + return String.valueOf(value); + } + return defaultValue; + } + public static String getPackageName(TypeElement typeElement) { while (typeElement.getNestingKind().isNested()) { Element enclosingElement = typeElement.getEnclosingElement(); diff --git a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordBuilderProcessor.java b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordBuilderProcessor.java index 06e6fb1..d17f1fd 100644 --- a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordBuilderProcessor.java +++ b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordBuilderProcessor.java @@ -33,6 +33,7 @@ import java.util.AbstractMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -53,11 +54,11 @@ class InternalRecordBuilderProcessor private final TypeSpec builderType; private final TypeSpec.Builder builder; - InternalRecordBuilderProcessor(TypeElement record, RecordBuilderMetaData metaData) + InternalRecordBuilderProcessor(TypeElement record, RecordBuilderMetaData metaData, Optional packageNameOpt) { this.metaData = metaData; recordClassType = ElementUtils.getClassType(record, record.getTypeParameters()); - packageName = ElementUtils.getPackageName(record); + packageName = packageNameOpt.orElseGet(() -> ElementUtils.getPackageName(record)); builderClassType = ElementUtils.getClassType(packageName, getBuilderName(record, metaData, recordClassType, metaData.suffix()), record.getTypeParameters()); typeVariables = record.getTypeParameters().stream().map(TypeVariableName::get).collect(Collectors.toList()); recordComponents = record.getRecordComponents().stream().map(ElementUtils::getClassType).collect(Collectors.toList()); diff --git a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java index 52ee2fa..9cc6fa7 100644 --- a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java +++ b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/InternalRecordInterfaceProcessor.java @@ -15,12 +15,14 @@ */ package io.soabase.recordbuilder.processor; -import com.squareup.javapoet.*; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.TypeSpec; +import com.squareup.javapoet.TypeVariableName; +import io.soabase.recordbuilder.core.IgnoreDefaultMethod; import io.soabase.recordbuilder.core.RecordBuilder; import io.soabase.recordbuilder.core.RecordBuilderMetaData; -import io.soabase.recordbuilder.core.RecordInterface; -import io.soabase.recordbuilder.core.IgnoreDefaultMethod; - import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; @@ -28,7 +30,13 @@ import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.tools.Diagnostic; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -46,9 +54,9 @@ class InternalRecordInterfaceProcessor { private static final String FAKE_METHOD_NAME = "__FAKE__"; - InternalRecordInterfaceProcessor(ProcessingEnvironment processingEnv, TypeElement iface, RecordInterface recordInterface, RecordBuilderMetaData metaData) { + InternalRecordInterfaceProcessor(ProcessingEnvironment processingEnv, TypeElement iface, boolean addRecordBuilder, RecordBuilderMetaData metaData, Optional packageNameOpt) { this.processingEnv = processingEnv; - packageName = ElementUtils.getPackageName(iface); + packageName = packageNameOpt.orElseGet(() -> ElementUtils.getPackageName(iface)); recordComponents = getRecordComponents(iface); this.iface = iface; @@ -65,7 +73,7 @@ class InternalRecordInterfaceProcessor { .addAnnotation(generatedRecordInterfaceAnnotation) .addTypeVariables(typeVariables); - if (recordInterface.addRecordBuilder()) { + if (addRecordBuilder) { ClassType builderClassType = ElementUtils.getClassType(packageName, getBuilderName(iface, metaData, recordClassType, metaData.suffix()) + "." + metaData.withClassName(), iface.getTypeParameters()); builder.addAnnotation(RecordBuilder.class); builder.addSuperinterface(builderClassType.typeName()); diff --git a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java index ddc6d42..2e4b06a 100644 --- a/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java +++ b/record-builder-processor/src/main/java/io/soabase/recordbuilder/processor/RecordBuilderProcessor.java @@ -18,39 +18,46 @@ package io.soabase.recordbuilder.processor; import com.squareup.javapoet.AnnotationSpec; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.TypeSpec; +import io.soabase.recordbuilder.core.RecordBuilder; import io.soabase.recordbuilder.core.RecordBuilderMetaData; import io.soabase.recordbuilder.core.RecordInterface; - -import javax.annotation.processing.*; +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; +import javax.annotation.processing.Generated; +import javax.annotation.processing.RoundEnvironment; import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; +import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; +import javax.lang.model.type.TypeMirror; import javax.tools.Diagnostic; import javax.tools.JavaFileObject; import java.io.IOException; import java.io.Writer; +import java.util.Optional; import java.util.Set; import java.util.function.Function; -import static io.soabase.recordbuilder.processor.RecordBuilderProcessor.RECORD_BUILDER; -import static io.soabase.recordbuilder.processor.RecordBuilderProcessor.RECORD_INTERFACE; - -@SupportedAnnotationTypes({RECORD_BUILDER, RECORD_INTERFACE}) public class RecordBuilderProcessor extends AbstractProcessor { - public static final String RECORD_BUILDER = "io.soabase.recordbuilder.core.RecordBuilder"; - public static final String RECORD_INTERFACE = "io.soabase.recordbuilder.core.RecordInterface"; + private static final String RECORD_BUILDER = RecordBuilder.class.getName(); + private static final String RECORD_BUILDER_INCLUDE = RecordBuilder.Include.class.getName().replace('$', '.'); + private static final String RECORD_INTERFACE = RecordInterface.class.getName(); + private static final String RECORD_INTERFACE_INCLUDE = RecordInterface.Include.class.getName().replace('$', '.'); - static final AnnotationSpec generatedRecordBuilderAnnotation = AnnotationSpec.builder(Generated.class).addMember("value", "$S", RECORD_BUILDER).build(); - static final AnnotationSpec generatedRecordInterfaceAnnotation = AnnotationSpec.builder(Generated.class).addMember("value", "$S", RECORD_INTERFACE).build(); + static final AnnotationSpec generatedRecordBuilderAnnotation = AnnotationSpec.builder(Generated.class).addMember("value", "$S", RecordBuilder.class.getName()).build(); + static final AnnotationSpec generatedRecordInterfaceAnnotation = AnnotationSpec.builder(Generated.class).addMember("value", "$S", RecordInterface.class.getName()).build(); @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { - annotations.forEach(annotation -> roundEnv.getElementsAnnotatedWith(annotation) - .forEach(element -> process(annotation, element)) - ); + annotations.forEach(annotation -> roundEnv.getElementsAnnotatedWith(annotation).forEach(element -> process(annotation, element))); return true; } - + + @Override + public Set getSupportedAnnotationTypes() { + return Set.of(RECORD_BUILDER, RECORD_BUILDER_INCLUDE, RECORD_INTERFACE, RECORD_INTERFACE_INCLUDE); + } + @Override public SourceVersion getSupportedSourceVersion() { // we don't directly return RELEASE_14 as that may @@ -63,37 +70,102 @@ public class RecordBuilderProcessor extends AbstractProcessor { private void process(TypeElement annotation, Element element) { var metaData = new RecordBuilderMetaDataLoader(processingEnv, s -> processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, s)).getMetaData(); - if (annotation.getQualifiedName().toString().equals(RECORD_BUILDER)) { - processRecordBuilder((TypeElement) element, metaData); - } else if (annotation.getQualifiedName().toString().equals(RECORD_INTERFACE)) { - processRecordInterface((TypeElement) element, element.getAnnotation(RecordInterface.class), metaData); - } else { + String annotationClass = annotation.getQualifiedName().toString(); + if ( annotationClass.equals(RECORD_BUILDER) ) + { + processRecordBuilder((TypeElement)element, metaData, Optional.empty()); + } + else if ( annotationClass.equals(RECORD_INTERFACE) ) + { + processRecordInterface((TypeElement)element, element.getAnnotation(RecordInterface.class).addRecordBuilder(), metaData, Optional.empty()); + } + else if ( annotationClass.equals(RECORD_BUILDER_INCLUDE) || annotationClass.equals(RECORD_INTERFACE_INCLUDE) ) + { + processIncludes(element, metaData, annotationClass); + } + else + { throw new RuntimeException("Unknown annotation: " + annotation); } } - private void processRecordInterface(TypeElement element, RecordInterface recordInterface, RecordBuilderMetaData metaData) { - if (!element.getKind().isInterface()) { + private void processIncludes(Element element, RecordBuilderMetaData metaData, String annotationClass) { + var annotationMirrorOpt = ElementUtils.findAnnotationMirror(processingEnv, element, annotationClass); + if ( annotationMirrorOpt.isEmpty() ) + { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not get annotation mirror for: " + annotationClass, element); + } + else + { + var values = processingEnv.getElementUtils().getElementValuesWithDefaults(annotationMirrorOpt.get()); + var classes = ElementUtils.getAnnotationValue(values, "value"); + if ( classes.isEmpty() ) + { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not get annotation value for: " + annotationClass, element); + } + else + { + var packagePattern = ElementUtils.getStringAttribute(ElementUtils.getAnnotationValue(values, "packagePattern").orElse(null), "*"); + var classesMirrors = ElementUtils.getClassesAttribute(classes.get()); + for ( TypeMirror mirror : classesMirrors ) + { + TypeElement typeElement = (TypeElement)processingEnv.getTypeUtils().asElement(mirror); + if ( typeElement == null ) + { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "Could not get element for: " + mirror, element); + } + else + { + var packageName = buildPackageName(packagePattern, element, typeElement); + if ( annotationClass.equals(RECORD_INTERFACE_INCLUDE) ) + { + var addRecordBuilderOpt = ElementUtils.getAnnotationValue(values, "addRecordBuilder"); + var addRecordBuilder = addRecordBuilderOpt.map(ElementUtils::getBooleanAttribute).orElse(true); + processRecordInterface(typeElement, addRecordBuilder, metaData, Optional.of(packageName)); + } + else + { + processRecordBuilder(typeElement, metaData, Optional.of(packageName)); + } + } + } + } + } + } + + private String buildPackageName(String packagePattern, Element builderElement, TypeElement includedClass) { + String replaced = packagePattern.replace("*", ((PackageElement)includedClass.getEnclosingElement()).getQualifiedName().toString()); + if (builderElement instanceof PackageElement) { + return replaced.replace("@", ((PackageElement)builderElement).getQualifiedName().toString()); + } + return replaced.replace("@", ((PackageElement)builderElement.getEnclosingElement()).getQualifiedName().toString()); + } + + private void processRecordInterface(TypeElement element, boolean addRecordBuilder, RecordBuilderMetaData metaData, Optional packageName) { + if ( !element.getKind().isInterface() ) + { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "RecordInterface only valid for interfaces.", element); return; } - var internalProcessor = new InternalRecordInterfaceProcessor(processingEnv, element, recordInterface, metaData); - if (!internalProcessor.isValid()) { + var internalProcessor = new InternalRecordInterfaceProcessor(processingEnv, element, addRecordBuilder, metaData, packageName); + if ( !internalProcessor.isValid() ) + { return; } writeRecordInterfaceJavaFile(element, internalProcessor.packageName(), internalProcessor.recordClassType(), internalProcessor.recordType(), metaData, internalProcessor::toRecord); } - private void processRecordBuilder(TypeElement record, RecordBuilderMetaData metaData) { + private void processRecordBuilder(TypeElement record, RecordBuilderMetaData metaData, Optional packageName) { // we use string based name comparison for the element kind, // as the ElementKind.RECORD enum doesn't exist on JRE releases // older than Java 14, and we don't want to throw unexpected // NoSuchFieldErrors - if (!"RECORD".equals(record.getKind().name())) { + if ( !"RECORD".equals(record.getKind().name()) ) + { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "RecordBuilder only valid for records.", record); return; } - var internalProcessor = new InternalRecordBuilderProcessor(record, metaData); + var internalProcessor = new InternalRecordBuilderProcessor(record, metaData, packageName); writeRecordBuilderJavaFile(record, internalProcessor.packageName(), internalProcessor.builderClassType(), internalProcessor.builderType(), metaData); } @@ -105,7 +177,7 @@ public class RecordBuilderProcessor extends AbstractProcessor { { String fullyQualifiedName = packageName.isEmpty() ? builderClassType.name() : (packageName + "." + builderClassType.name()); JavaFileObject sourceFile = filer.createSourceFile(fullyQualifiedName); - try ( Writer writer = sourceFile.openWriter() ) + try (Writer writer = sourceFile.openWriter()) { javaFile.writeTo(writer); } @@ -128,7 +200,7 @@ public class RecordBuilderProcessor extends AbstractProcessor { { String fullyQualifiedName = packageName.isEmpty() ? classType.name() : (packageName + "." + classType.name()); JavaFileObject sourceFile = filer.createSourceFile(fullyQualifiedName); - try ( Writer writer = sourceFile.openWriter() ) + try (Writer writer = sourceFile.openWriter()) { writer.write(recordSourceCode); } @@ -140,11 +212,10 @@ public class RecordBuilderProcessor extends AbstractProcessor { } private JavaFile javaFileBuilder(String packageName, TypeSpec type, RecordBuilderMetaData metaData) { - var javaFileBuilder = JavaFile.builder(packageName, type) - .skipJavaLangImports(true) - .indent(metaData.fileIndent()); + var javaFileBuilder = JavaFile.builder(packageName, type).skipJavaLangImports(true).indent(metaData.fileIndent()); var comment = metaData.fileComment(); - if ((comment != null) && !comment.isEmpty()) { + if ( (comment != null) && !comment.isEmpty() ) + { javaFileBuilder.addFileComment(comment); } return javaFileBuilder.build(); @@ -152,7 +223,8 @@ public class RecordBuilderProcessor extends AbstractProcessor { private void handleWriteError(TypeElement element, IOException e) { String message = "Could not create source file"; - if (e.getMessage() != null) { + if ( e.getMessage() != null ) + { message = message + ": " + e.getMessage(); } processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, message, element); diff --git a/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Builder.java b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Builder.java new file mode 100644 index 0000000..5ff66af --- /dev/null +++ b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Builder.java @@ -0,0 +1,24 @@ +/** + * Copyright 2019 Jordan Zimmerman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.soabase.recordbuilder.test; + +import io.soabase.recordbuilder.core.RecordInterface; + +@RecordInterface.Include({ + Thingy.class +}) +public class Builder { +} diff --git a/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Customer.java b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Customer.java new file mode 100644 index 0000000..be5e2b1 --- /dev/null +++ b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Customer.java @@ -0,0 +1,26 @@ +/** + * Copyright 2019 Jordan Zimmerman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.soabase.recordbuilder.test; + +import java.time.Instant; + +public interface Customer { + String name(); + + String address(); + + Instant activeDate(); +} diff --git a/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Pair.java b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Pair.java new file mode 100644 index 0000000..ad81601 --- /dev/null +++ b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Pair.java @@ -0,0 +1,18 @@ +/** + * Copyright 2019 Jordan Zimmerman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.soabase.recordbuilder.test; + +public record Pair(T t, U u) {} diff --git a/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Point.java b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Point.java new file mode 100644 index 0000000..ace872c --- /dev/null +++ b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Point.java @@ -0,0 +1,18 @@ +/** + * Copyright 2019 Jordan Zimmerman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.soabase.recordbuilder.test; + +public record Point(int x, int y) {} diff --git a/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Thingy.java b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Thingy.java new file mode 100644 index 0000000..2059e8e --- /dev/null +++ b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/Thingy.java @@ -0,0 +1,20 @@ +/** + * Copyright 2019 Jordan Zimmerman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.soabase.recordbuilder.test; + +public interface Thingy { + T getIt(); +} diff --git a/record-builder-test/src/main/java/io/soabase/recordbuilder/test/package-info.java b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/package-info.java new file mode 100644 index 0000000..9a53826 --- /dev/null +++ b/record-builder-test/src/main/java/io/soabase/recordbuilder/test/package-info.java @@ -0,0 +1,22 @@ +/** + * Copyright 2019 Jordan Zimmerman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@RecordBuilder.Include(value = {Point.class, Pair.class}, packagePattern = "*.foo") +@RecordInterface.Include(value = Customer.class, addRecordBuilder = false, packagePattern = "*.bar") +package io.soabase.recordbuilder.test; + +import io.soabase.recordbuilder.core.RecordBuilder; +import io.soabase.recordbuilder.core.RecordInterface;