org.sonarsource.scanner.maven
sonar-maven-plugin
diff --git a/stylesniffer-annotation-processor/pom.xml b/stylesniffer-annotation-processor/pom.xml
index a71eb6a..ae11e4d 100644
--- a/stylesniffer-annotation-processor/pom.xml
+++ b/stylesniffer-annotation-processor/pom.xml
@@ -29,6 +29,7 @@
4.0.0
stylesniffer-annotation-processor
+ 1.0.0-SNAPSHOT
jar
@@ -38,15 +39,24 @@
- ${java.sdk.version}
- ${java.sdk.version}
- ${source.encoding}
${basedir}/..
-
${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId}
+
+
+ org.thymeleaf
+ thymeleaf
+
+
+ dev.cookiecode
+ stylesniffer-api
+ 1.0.0-SNAPSHOT
+ compile
+
+
+
org.projectlombok
lombok
@@ -55,32 +65,45 @@
com.google.auto.service
auto-service
+
+
- org.thymeleaf
- thymeleaf
-
-
- flogger
com.google.flogger
+ flogger
- flogger-slf4j-backend
com.google.flogger
+ flogger-slf4j-backend
- slf4j-api
org.slf4j
+ slf4j-api
ch.qos.logback
logback-classic
+
- dev.cookiecode
- stylesniffer-api
- 1.0.0-SNAPSHOT
- compile
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
diff --git a/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/CaseStyleElementsCollector.java b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/CaseStyleElementsCollector.java
new file mode 100644
index 0000000..cb8b487
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/CaseStyleElementsCollector.java
@@ -0,0 +1,53 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.TypeElement;
+import lombok.NonNull;
+
+/**
+ * Collects elements annotated with {@link
+ * dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle}.
+ *
+ * This class is responsible for scanning the {@link RoundEnvironment} and extracting the fully
+ * qualified class names of elements annotated with {@code @RegisterCaseStyle}.
+ *
+ *
The collected class names are used in the template rendering process.
+ *
+ * @author Sebastien Vermeille
+ * @see dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle
+ */
+public class CaseStyleElementsCollector {
+
+ static final Class extends Annotation> ANNOTATION_CLASS = RegisterCaseStyle.class;
+
+ public List collectElements(@NonNull RoundEnvironment roundEnv) {
+ return roundEnv.getElementsAnnotatedWith(ANNOTATION_CLASS).stream()
+ .map(element -> ((TypeElement) element).getQualifiedName().toString())
+ .toList();
+ }
+}
diff --git a/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/FileWriter.java b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/FileWriter.java
new file mode 100644
index 0000000..5d2bd59
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/FileWriter.java
@@ -0,0 +1,64 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import static dev.cookiecode.stylesniffer.annotation.processor.RegisterCaseStyleAnnotationProcessor.GENERATED_CLASS_NAME;
+import static dev.cookiecode.stylesniffer.annotation.processor.RegisterCaseStyleAnnotationProcessor.GENERATED_CLASS_PACKAGE_NAME;
+
+import java.io.IOException;
+import javax.annotation.processing.ProcessingEnvironment;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * Handles the writing of the generated source code to a file.
+ *
+ * This class is responsible for creating a new Java source file and writing the generated code
+ * into it.
+ *
+ *
The file is created in the package {@code dev.cookiecode.stylesniffer.generated}.
+ *
+ * @author Sebastien Vermeille
+ */
+@RequiredArgsConstructor
+public class FileWriter {
+
+ static final String DOT = ".";
+ private final ProcessingEnvironment processingEnv;
+
+ /**
+ * Writes the generated code to a new Java source file.
+ *
+ * @param generatedCode The code to write into the file.
+ * @throws IOException If an I/O error occurs while writing the file.
+ */
+ public void writeToFile(@NonNull String generatedCode) throws IOException {
+ try (var writer =
+ processingEnv
+ .getFiler()
+ .createSourceFile(GENERATED_CLASS_PACKAGE_NAME + DOT + GENERATED_CLASS_NAME)
+ .openWriter()) {
+ writer.write(generatedCode);
+ }
+ }
+}
diff --git a/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/ProcessorTemplateEngine.java b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/ProcessorTemplateEngine.java
new file mode 100644
index 0000000..264cd6a
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/ProcessorTemplateEngine.java
@@ -0,0 +1,54 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import lombok.experimental.Delegate;
+import org.thymeleaf.TemplateEngine;
+import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
+
+/**
+ * Wrapper for Thymeleaf template engine pre-configured for the project
+ *
+ * @author Sebastien Vermeille
+ * @see TemplateEngine
+ */
+public class ProcessorTemplateEngine {
+
+ private static final String TEMPLATES_DIR = "templates/";
+ private static final String TEMPLATE_EXTENSION = ".tpl";
+ private static final String TEMPLATE_MODE = "TEXT";
+ private static final String TEMPLATE_ENCODING = "UTF-8";
+
+ @Delegate(types = TemplateEngine.class)
+ private final TemplateEngine templateEngine = new TemplateEngine();
+
+ public ProcessorTemplateEngine() {
+ final var templateResolver = new ClassLoaderTemplateResolver();
+ templateResolver.setPrefix(TEMPLATES_DIR);
+ templateResolver.setSuffix(TEMPLATE_EXTENSION);
+ templateResolver.setTemplateMode(TEMPLATE_MODE);
+ templateResolver.setCharacterEncoding(TEMPLATE_ENCODING);
+
+ templateEngine.setTemplateResolver(templateResolver);
+ }
+}
diff --git a/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/RegisterCaseStyleAnnotationProcessor.java b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/RegisterCaseStyleAnnotationProcessor.java
index 8372ce7..cca3453 100644
--- a/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/RegisterCaseStyleAnnotationProcessor.java
+++ b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/RegisterCaseStyleAnnotationProcessor.java
@@ -22,135 +22,72 @@
*/
package dev.cookiecode.stylesniffer.annotation.processor;
-import static java.time.LocalDateTime.now;
-import static java.time.ZoneOffset.UTC;
-import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+import static lombok.AccessLevel.*;
-import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
-import dev.cookiecode.stylesniffer.api.CaseStyle;
+import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
-import java.lang.annotation.Annotation;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Set;
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.Generated;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.annotation.processing.SupportedSourceVersion;
+import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
-import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
+import lombok.*;
import lombok.extern.flogger.Flogger;
-import org.thymeleaf.TemplateEngine;
-import org.thymeleaf.context.Context;
-import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
/**
- * Processes the dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle annotation, generating a
- * CaseStyleInjector class that registers all dev.cookiecode.stylesniffer.api.CaseStyle
- * implementations.
+ * Annotation processor for {@link dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle}.
*
- *
Uses Thymeleaf for code generation and Flogger for logging. Supports Java
- * SourceVersion.RELEASE_21.
+ *
This processor scans for classes annotated with {@code @RegisterCaseStyle}, collects them, and
+ * generates a {@code CaseStyleInjector} class using a template.
+ *
+ *
The actual processing tasks are delegated to {@link CaseStyleElementsCollector}, {@link
+ * TemplateRenderer}, and {@link FileWriter}, promoting the Single Responsibility Principle.
*
* @author Sebastien Vermeille
* @see dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle
* @see dev.cookiecode.stylesniffer.api.CaseStyle
*/
+@NoArgsConstructor(access = PUBLIC)
+@AllArgsConstructor(
+ access = PACKAGE,
+ onConstructor_ = {@VisibleForTesting})
+@Getter(
+ value = PACKAGE,
+ onMethod_ = {@VisibleForTesting})
@SupportedAnnotationTypes("dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle")
@SupportedSourceVersion(SourceVersion.RELEASE_21)
@Flogger
public class RegisterCaseStyleAnnotationProcessor extends AbstractProcessor {
- private static final String GENERATED_CLASS_PACKAGE_NAME =
- "dev.cookiecode.stylesniffer.generated";
- private static final String GENERATED_CLASS_NAME = "CaseStyleInjector";
- private static final String TEMPLATE_EXTENSION = ".tpl";
- private static final String TEMPLATE_MODE = "TEXT";
- private static final String UTF_8 = "UTF-8";
- private static final String TEMPLATES_DIR = "templates/";
- private static final String TEMPLATE_VARIABLE_PACKAGE_NAME = "packageName";
- private static final String TEMPLATE_VARIABLE_IMPORTS = "imports";
- private static final String TEMPLATE_VARIABLE_CLASS_NAME = "className";
- private static final String TEMPLATE_VARIABLE_ELEMENTS = "elements";
- private static final String TEMPLATE_VARIABLE_GENERATED_AT = "generatedAt";
- private static final String TEMPLATE_FILE_NAME = "case_style_injector";
- private static final String POINT = ".";
- private static final Class extends Annotation> ANNOTATION_CLASS = RegisterCaseStyle.class;
- private static final Class> IMPLEMENTED_INTERFACE_CLASS = CaseStyle.class;
-
- @Override
- public boolean process(
- final Set extends TypeElement> annotations, final RoundEnvironment roundEnv) {
- final var annotatedElements = roundEnv.getElementsAnnotatedWith(ANNOTATION_CLASS);
+ // Common constants shared across classes
+ public static final String GENERATED_CLASS_PACKAGE_NAME = "dev.cookiecode.stylesniffer.generated";
+ public static final String GENERATED_CLASS_NAME = "CaseStyleInjector";
- if (!annotatedElements.isEmpty()) {
- this.generateCaseStyleInjector(annotatedElements);
- }
+ private TemplateRenderer templateRenderer;
+ private FileWriter fileWriter;
+ private CaseStyleElementsCollector elementsCollector;
- return true; // Indicates that annotations are claimed
+ @Override
+ public synchronized void init(@NonNull ProcessingEnvironment processingEnv) {
+ super.init(processingEnv);
+ final var templateEngine = new ProcessorTemplateEngine();
+ this.templateRenderer = new TemplateRenderer(templateEngine);
+ this.fileWriter = new FileWriter(processingEnv);
+ this.elementsCollector = new CaseStyleElementsCollector();
}
- private void generateCaseStyleInjector(final Set extends Element> elements) {
+ @Override
+ public boolean process(
+ @NonNull Set extends TypeElement> annotations, @NonNull RoundEnvironment roundEnv) {
try {
- final var templateEngine = this.configureTemplateEngine();
- final var context = this.prepareTemplateContext(elements);
-
- final var generatedCode = templateEngine.process(TEMPLATE_FILE_NAME, context);
- this.writeGeneratedClassToFile(generatedCode);
-
- } catch (final IOException e) {
+ final var elements = elementsCollector.collectElements(roundEnv);
+ if (!elements.isEmpty()) {
+ final var generatedCode = templateRenderer.renderTemplate(elements);
+ fileWriter.writeToFile(generatedCode);
+ }
+ } catch (IOException e) {
log.atSevere().withCause(e).log("Failed to generate the %s class", GENERATED_CLASS_NAME);
}
- }
-
- private TemplateEngine configureTemplateEngine() {
- final var templateResolver = new ClassLoaderTemplateResolver();
- templateResolver.setPrefix(TEMPLATES_DIR);
- templateResolver.setSuffix(TEMPLATE_EXTENSION);
- templateResolver.setTemplateMode(TEMPLATE_MODE);
- templateResolver.setCharacterEncoding(UTF_8);
- final var templateEngine = new TemplateEngine();
- templateEngine.setTemplateResolver(templateResolver);
- return templateEngine;
- }
-
- private Context prepareTemplateContext(final Set extends Element> elements) {
- final var context = new Context();
- context.setVariable(TEMPLATE_VARIABLE_PACKAGE_NAME, GENERATED_CLASS_PACKAGE_NAME);
-
- final var imports =
- Set.of(
- Generated.class.getCanonicalName(),
- List.class.getCanonicalName(),
- ArrayList.class.getCanonicalName(),
- IMPLEMENTED_INTERFACE_CLASS.getCanonicalName());
-
- context.setVariable(TEMPLATE_VARIABLE_IMPORTS, imports.stream().sorted().toList());
- context.setVariable(TEMPLATE_VARIABLE_CLASS_NAME, GENERATED_CLASS_NAME);
-
- final var classesList =
- elements.stream()
- .map(element -> ((TypeElement) element).getQualifiedName().toString())
- .toList();
-
- context.setVariable(TEMPLATE_VARIABLE_ELEMENTS, classesList);
-
- final var moment = now(UTC).format(ISO_LOCAL_DATE_TIME);
- context.setVariable(TEMPLATE_VARIABLE_GENERATED_AT, moment);
-
- return context;
- }
-
- private void writeGeneratedClassToFile(final String generatedCode) throws IOException {
- final var file =
- this.processingEnv
- .getFiler()
- .createSourceFile(GENERATED_CLASS_PACKAGE_NAME + POINT + GENERATED_CLASS_NAME);
- try (final var writer = file.openWriter()) {
- writer.write(generatedCode);
- }
+ return true;
}
}
diff --git a/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/TemplateRenderer.java b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/TemplateRenderer.java
new file mode 100644
index 0000000..40bae69
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/main/java/dev/cookiecode/stylesniffer/annotation/processor/TemplateRenderer.java
@@ -0,0 +1,106 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import static dev.cookiecode.stylesniffer.annotation.processor.RegisterCaseStyleAnnotationProcessor.GENERATED_CLASS_NAME;
+import static dev.cookiecode.stylesniffer.annotation.processor.RegisterCaseStyleAnnotationProcessor.GENERATED_CLASS_PACKAGE_NAME;
+import static java.time.LocalDateTime.now;
+import static java.time.ZoneOffset.UTC;
+import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME;
+
+import com.google.common.annotations.VisibleForTesting;
+import dev.cookiecode.stylesniffer.api.CaseStyle;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import javax.annotation.processing.Generated;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import org.thymeleaf.context.Context;
+
+/**
+ * Handles the rendering of templates using Thymeleaf.
+ *
+ *
This class is responsible for setting up the template engine, preparing the context with
+ * necessary variables, and rendering the final template into a string.
+ *
+ *
It uses the {@code case_style_injector.tpl} template to generate the source code.
+ *
+ * @author Sebastien Vermeille
+ * @see org.thymeleaf.context.Context
+ */
+@RequiredArgsConstructor(onConstructor_ = {@VisibleForTesting})
+public class TemplateRenderer {
+
+ static final String TEMPLATE_FILE_NAME = "case_style_injector";
+
+ // Template variable constants
+ static final String PACKAGE_NAME = "packageName";
+ static final String CLASS_NAME = "className";
+ static final String ELEMENTS = "elements";
+ static final String IMPORTS = "imports";
+ static final String GENERATED_AT = "generatedAt";
+
+ private final ProcessorTemplateEngine templateEngine;
+
+ /**
+ * Renders the template with the given list of elements.
+ *
+ * @param elements List of fully qualified class names to include in the generated class.
+ * @return Rendered template as a string.
+ */
+ public String renderTemplate(@NonNull List elements) {
+ if (elements.isEmpty()) {
+ throw new IllegalStateException(
+ "Cannot render empty elements, upper layer should have prevented this to occurs.");
+ }
+
+ final var context = prepareTemplateContext(elements);
+ return templateEngine.process(TEMPLATE_FILE_NAME, context);
+ }
+
+ @VisibleForTesting
+ Context prepareTemplateContext(@NonNull List elements) {
+ if (elements.isEmpty()) {
+ throw new IllegalStateException(
+ "Cannot build context for empty elements, upper layer should have prevented this to occurs.");
+ }
+
+ final var context = new Context();
+ context.setVariable(PACKAGE_NAME, GENERATED_CLASS_PACKAGE_NAME);
+ context.setVariable(CLASS_NAME, GENERATED_CLASS_NAME);
+ context.setVariable(ELEMENTS, elements);
+ context.setVariable(
+ IMPORTS,
+ Set.of(
+ Generated.class.getCanonicalName(),
+ List.class.getCanonicalName(),
+ ArrayList.class.getCanonicalName(),
+ CaseStyle.class.getCanonicalName())
+ .stream()
+ .sorted()
+ .toList());
+ context.setVariable(GENERATED_AT, now(UTC).format(ISO_LOCAL_DATE_TIME));
+ return context;
+ }
+}
diff --git a/stylesniffer-annotation-processor/src/test/java/.gitkeep b/stylesniffer-annotation-processor/src/test/java/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/AnnotatedClassTest.java b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/AnnotatedClassTest.java
new file mode 100644
index 0000000..8a735ff
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/AnnotatedClassTest.java
@@ -0,0 +1,35 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
+
+/**
+ * Test class This class ensure that the annotation can be applied on the class (breaks compilation
+ * in case of changes in @RegisterCaseStyle) and is used in different mocks
+ *
+ * @author Sebastien Vermeille
+ */
+@RegisterCaseStyle
+@SuppressWarnings("java:S2187") // This is a compile time test
+public class AnnotatedClassTest {}
diff --git a/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/CaseStyleElementsCollectorTest.java b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/CaseStyleElementsCollectorTest.java
new file mode 100644
index 0000000..9070ffc
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/CaseStyleElementsCollectorTest.java
@@ -0,0 +1,87 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import static dev.cookiecode.stylesniffer.annotation.processor.CaseStyleElementsCollector.ANNOTATION_CLASS;
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+import java.util.Set;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.TypeElement;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+@ExtendWith(MockitoExtension.class)
+class CaseStyleElementsCollectorTest {
+
+ @Mock private RoundEnvironment roundEnvironment;
+
+ private CaseStyleElementsCollector caseStyleElementsCollector;
+
+ @BeforeEach
+ void setUp() {
+ caseStyleElementsCollector = new CaseStyleElementsCollector();
+ }
+
+ @Test
+ void collectElementsShouldReturnCaseStyleElementsGivenSomeWhereMocked() {
+
+ // GIVEN
+ final var name = mock(Name.class);
+ doReturn("some").when(name).toString();
+ final var mockedElement = mock(TypeElement.class);
+ doReturn(name).when(mockedElement).getQualifiedName();
+ final var mockedClasses = Set.of(mockedElement);
+ doReturn(mockedClasses).when(roundEnvironment).getElementsAnnotatedWith(ANNOTATION_CLASS);
+
+ // WHEN
+ var result = caseStyleElementsCollector.collectElements(roundEnvironment);
+
+ // THEN
+ assertThat(result).isNotEmpty();
+ }
+
+ @Test
+ void collectElementsShouldReturnAnEmptyListGivenNoAnnotatedElementsArePresent() {
+
+ // GIVEN
+ doReturn(emptySet()).when(roundEnvironment).getElementsAnnotatedWith(ANNOTATION_CLASS);
+
+ // WHEN
+ var result = caseStyleElementsCollector.collectElements(roundEnvironment);
+
+ // THEN
+ assertThat(result).isNotNull().isEmpty();
+ }
+}
diff --git a/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/FileWriterTest.java b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/FileWriterTest.java
new file mode 100644
index 0000000..5a8cb9f
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/FileWriterTest.java
@@ -0,0 +1,93 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import static dev.cookiecode.stylesniffer.annotation.processor.FileWriter.DOT;
+import static dev.cookiecode.stylesniffer.annotation.processor.RegisterCaseStyleAnnotationProcessor.GENERATED_CLASS_NAME;
+import static dev.cookiecode.stylesniffer.annotation.processor.RegisterCaseStyleAnnotationProcessor.GENERATED_CLASS_PACKAGE_NAME;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.io.Writer;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.tools.JavaFileObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+@ExtendWith(MockitoExtension.class)
+class FileWriterTest {
+
+ @Mock private ProcessingEnvironment processingEnvironment;
+
+ @InjectMocks private FileWriter fileWriter;
+
+ @Test
+ void writeToFileShouldThrowAnIOExceptionGivenThereIsAnIssue() throws Exception {
+ // GIVEN
+ final var filerMock = mock(Filer.class);
+ doThrow(new IOException("IO error")).when(filerMock).createSourceFile(anyString());
+ when(processingEnvironment.getFiler()).thenReturn(filerMock);
+
+ final var someValidCode = "some code that is not generating any error.";
+
+ assertThrows(
+ IOException.class,
+ () -> {
+ // WHEN
+ fileWriter.writeToFile(someValidCode);
+ });
+ }
+
+ @Test
+ void writeToFileShouldPassGeneratedCodeToWriteMethodGivenThereIsNoIssue() throws Exception {
+ // GIVEN
+ final var filerMock = mock(Filer.class);
+ when(processingEnvironment.getFiler()).thenReturn(filerMock);
+
+ final var javaFileObjectMock = mock(JavaFileObject.class);
+ doReturn(javaFileObjectMock)
+ .when(filerMock)
+ .createSourceFile(GENERATED_CLASS_PACKAGE_NAME + DOT + GENERATED_CLASS_NAME);
+
+ final var writerMock = mock(Writer.class);
+ when(javaFileObjectMock.openWriter()).thenReturn(writerMock);
+
+ final var someValidCode = "some code that is not generating any error.";
+
+ // WHEN
+ fileWriter.writeToFile(someValidCode);
+
+ // THEN
+ verify(writerMock, times(1)).write(someValidCode);
+ }
+}
diff --git a/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/RegisterCaseStyleAnnotationProcessorTest.java b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/RegisterCaseStyleAnnotationProcessorTest.java
new file mode 100644
index 0000000..ec2bc76
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/RegisterCaseStyleAnnotationProcessorTest.java
@@ -0,0 +1,166 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import static java.util.Collections.emptyList;
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.*;
+
+import java.io.IOException;
+import java.util.List;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import lombok.NonNull;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+@ExtendWith(MockitoExtension.class)
+class RegisterCaseStyleAnnotationProcessorTest {
+
+ @Mock private TemplateRenderer templateRenderer;
+
+ @Mock private FileWriter fileWriter;
+
+ @Mock private CaseStyleElementsCollector caseStyleElementsCollector;
+
+ @InjectMocks private RegisterCaseStyleAnnotationProcessor processor;
+
+ @Test
+ void processShouldReturnTrueEvenIfNoElementsWereRegistered() {
+ // GIVEN
+ final var roundEnv = noCaseStyleAnnotatedElements();
+
+ // WHEN
+ final var actualResult = processor.process(emptySet(), roundEnv);
+
+ // THEN
+ assertThat(actualResult).isTrue();
+ }
+
+ @Test
+ void processShouldReturnTrueEvenIfAnIOExceptionOccurs() throws Exception {
+ // GIVEN
+ final var elements = List.of("DummyStyle", "SuperDummyPlusStyle");
+ final var roundEnv = caseStyleAnnotatedElements(elements);
+
+ final var generatedCode = "some code;";
+ doReturn(generatedCode).when(templateRenderer).renderTemplate(elements);
+
+ doThrow(new IOException("File writer error")).when(fileWriter).writeToFile(generatedCode);
+
+ // WHEN
+ final var actualResult = processor.process(emptySet(), roundEnv);
+
+ // THEN
+ assertThat(actualResult).isTrue();
+ }
+
+ @Test
+ void processShouldNotInteractWithTemplateRendererGivenNoElementsAreCollected() {
+ // GIVEN
+ final var mockedRoundEnv = noCaseStyleAnnotatedElements();
+
+ // WHEN
+ processor.process(emptySet(), mockedRoundEnv);
+
+ // THEN
+ verifyNoInteractions(templateRenderer);
+ }
+
+ @Test
+ void processShouldNotInteractWithFileWriterGivenNoElementsAreCollected() {
+ // GIVEN
+ final var mockedRoundEnv = noCaseStyleAnnotatedElements();
+
+ // WHEN
+ processor.process(emptySet(), mockedRoundEnv);
+
+ // THEN
+ verifyNoInteractions(fileWriter);
+ }
+
+ @Test
+ void processShouldProvideRetrievedElementsToTemplateRendererGivenThereAreElements() {
+ // GIVEN
+ final var elements = List.of("DummyStyle", "SuperDummyPlusStyle");
+ final var roundEnv = caseStyleAnnotatedElements(elements);
+
+ // WHEN
+ processor.process(emptySet(), roundEnv);
+
+ // THEN
+ verify(templateRenderer, times(1)).renderTemplate(elements);
+ }
+
+ @Test
+ void processShouldProvideGeneratedCodeFileWriterGivenSomeCodeWereGenerated() throws Exception {
+ // GIVEN
+ final var elements = List.of("DummyStyle", "SuperDummyPlusStyle");
+ final var roundEnv = caseStyleAnnotatedElements(elements);
+ final var generatedCode = "Generated code;";
+ doReturn(generatedCode).when(templateRenderer).renderTemplate(elements);
+
+ // WHEN
+ processor.process(emptySet(), roundEnv);
+
+ // THEN
+ verify(fileWriter, times(1)).writeToFile(generatedCode);
+ }
+
+ @Test
+ void initShouldInitAllRequiredFields() {
+ // GIVEN
+ final var processingEnv = mock(ProcessingEnvironment.class);
+ final var processorWithoutInjectedMocks = new RegisterCaseStyleAnnotationProcessor();
+
+ // WHEN
+ processorWithoutInjectedMocks.init(processingEnv);
+
+ // THEN
+ assertThat(processorWithoutInjectedMocks.getTemplateRenderer()).isNotNull();
+ assertThat(processorWithoutInjectedMocks.getFileWriter()).isNotNull();
+ assertThat(processorWithoutInjectedMocks.getElementsCollector()).isNotNull();
+ }
+
+ private RoundEnvironment noCaseStyleAnnotatedElements() {
+ final var roundEnv = mock(RoundEnvironment.class);
+ final var noElements = emptyList();
+ doReturn(noElements).when(caseStyleElementsCollector).collectElements(roundEnv);
+ return roundEnv;
+ }
+
+ private RoundEnvironment caseStyleAnnotatedElements(@NonNull List caseStyleNames) {
+ final var roundEnv = mock(RoundEnvironment.class);
+ doReturn(caseStyleNames).when(caseStyleElementsCollector).collectElements(roundEnv);
+ return roundEnv;
+ }
+}
diff --git a/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/TemplateRendererTest.java b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/TemplateRendererTest.java
new file mode 100644
index 0000000..badfb5b
--- /dev/null
+++ b/stylesniffer-annotation-processor/src/test/java/dev/cookiecode/stylesniffer/annotation/processor/TemplateRendererTest.java
@@ -0,0 +1,128 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.annotation.processor;
+
+import static dev.cookiecode.stylesniffer.annotation.processor.TemplateRenderer.*;
+import static java.util.Collections.emptyList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.*;
+
+import java.util.List;
+import java.util.Set;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.thymeleaf.context.Context;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+@ExtendWith(MockitoExtension.class)
+class TemplateRendererTest {
+
+ @Mock private ProcessorTemplateEngine templateEngine;
+
+ private TemplateRenderer templateRenderer;
+
+ @BeforeEach
+ void setUp() {
+ templateRenderer = spy(new TemplateRenderer(templateEngine));
+ }
+
+ @Test
+ void renderTemplateShouldThrowAnIllegalStateExceptionGivenItReceivesAnEmptyListOfElements() {
+ // GIVEN
+ final List emptyElements = emptyList();
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ // WHEN
+ templateRenderer.renderTemplate(emptyElements);
+ });
+ }
+
+ @Test
+ void renderTemplateShouldInvokePrepareTemplateContextGivenItContainsElements() {
+ // GIVEN
+ final var elements = List.of("firstElement", "secondElement");
+
+ // WHEN
+ templateRenderer.renderTemplate(elements);
+
+ // THEN
+ verify(templateRenderer, times(1)).prepareTemplateContext(elements);
+ }
+
+ @Test
+ void
+ renderTemplateShouldInvokeTemplateEngineProcessWithPreviouslyCreatedContextAndTemplateFileNameGivenItContainsElements() {
+ // GIVEN
+ final var elements = List.of("firstElement", "secondElement");
+ final var templateContext = mock(Context.class);
+ doReturn(templateContext).when(templateRenderer).prepareTemplateContext(elements);
+
+ // WHEN
+ templateRenderer.renderTemplate(elements);
+
+ // THEN
+ verify(templateEngine, times(1)).process(TEMPLATE_FILE_NAME, templateContext);
+ }
+
+ @Test
+ void
+ prepareTemplateContextShouldThrowAnIllegalStateExceptionGivenItReceivesAnEmptyListOfElements() {
+ // GIVEN
+ final List emptyElements = emptyList();
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> {
+ // WHEN
+ templateRenderer.prepareTemplateContext(emptyElements);
+ });
+ }
+
+ @Test
+ void prepareTemplateContextShouldPopulateAllContextVariablesProperly() {
+ // GIVEN
+ final var elements = List.of("firstElement", "secondElement");
+ final var contextVariableNames =
+ Set.of(PACKAGE_NAME, CLASS_NAME, ELEMENTS, IMPORTS, GENERATED_AT);
+ // WHEN
+ var actualContext = templateRenderer.prepareTemplateContext(elements);
+
+ // THEN
+ assertThat(actualContext).isNotNull();
+ assertThat(actualContext.getVariableNames()).containsAll(contextVariableNames);
+ for (final var contextVariableName : contextVariableNames) {
+ assertThat(actualContext.getVariable(contextVariableName)).isNotNull().isNotEqualTo("");
+ }
+ assertThat(actualContext.getVariable(ELEMENTS)).isEqualTo(elements);
+ }
+}
diff --git a/stylesniffer-api/pom.xml b/stylesniffer-api/pom.xml
index 974ecce..ac9568b 100644
--- a/stylesniffer-api/pom.xml
+++ b/stylesniffer-api/pom.xml
@@ -29,6 +29,7 @@
4.0.0
stylesniffer-api
+ 1.0.0-SNAPSHOT
jar
@@ -38,19 +39,44 @@
- ${java.sdk.version}
- ${java.sdk.version}
- ${source.encoding}
${basedir}/..
-
${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId}
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+
+
+
lombok
org.projectlombok
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
+
diff --git a/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/StyleSniffer.java b/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/StyleSniffer.java
new file mode 100644
index 0000000..b071414
--- /dev/null
+++ b/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/StyleSniffer.java
@@ -0,0 +1,82 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import dev.cookiecode.stylesniffer.api.CaseStyle;
+import jakarta.annotation.Nullable;
+import java.util.*;
+
+/**
+ * Interface for a service that provides functionality for sniffing and managing case styles.
+ *
+ * This interface allows for querying case styles by name, retrieving supported case style names,
+ * and accessing case style names including their variants.
+ *
+ *
The implementation of this interface should handle the registration and retrieval of case
+ * styles as well as manage the variations of case styles.
+ *
+ * @author Sebastien Vermeille
+ */
+public interface StyleSniffer {
+
+ /**
+ * Retrieves a {@link CaseStyle} that matches the given name.
+ *
+ *
This method searches through the registered case styles and returns the first style that
+ * matches the provided name.
+ *
+ * @param name the name to match against case styles
+ * @return an {@link Optional} containing the matching {@code CaseStyle} if found, or an empty
+ * {@code Optional} if no match is found
+ */
+ Optional getCaseStyle(@Nullable final String name);
+
+ /**
+ * Retrieves a {@link CaseStyle} based on either its variant name or its primary name.
+ *
+ * @param variantOrName the variant or primary name to match against case styles
+ * @return an {@link Optional} containing the matching {@code CaseStyle} if found, or an empty
+ * {@code Optional} if no match is found
+ */
+ Optional getCaseStyleWithVariantOrName(@Nullable final String variantOrName);
+
+ /**
+ * Returns a set of names for all supported case styles.
+ *
+ * This method provides a set of unique names for the case styles currently recognized by this
+ * {@code StyleSniffer}.
+ *
+ * @return a {@link Set} of names for supported case styles
+ */
+ Set getSupportedCaseStyles();
+
+ /**
+ * Returns a set of names for all supported case styles, including all variants.
+ *
+ * This method provides a comprehensive list of all case style names recognized by this {@code
+ * StyleSniffer}, including both primary names and their variants.
+ *
+ * @return a {@link Set} of names for all supported case styles, including variants
+ */
+ Set getSupportedCaseStylesIncludingVariants();
+}
diff --git a/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/api/BaseCaseStyle.java b/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/api/BaseCaseStyle.java
new file mode 100644
index 0000000..fe472e9
--- /dev/null
+++ b/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/api/BaseCaseStyle.java
@@ -0,0 +1,53 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.api;
+
+import jakarta.annotation.Nullable;
+
+/**
+ * Base implementation of the {@link CaseStyle} interface that provides default implementations for
+ * {@code equals} and {@code hashCode} based on the case style's name.
+ *
+ * @author Sebastien Vermeille
+ */
+public abstract class BaseCaseStyle implements CaseStyle {
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+
+ CaseStyle caseStyle = (CaseStyle) obj;
+ return getName().equals(caseStyle.getName());
+ }
+
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+}
diff --git a/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/api/CaseStyle.java b/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/api/CaseStyle.java
index 02dc322..3aae360 100644
--- a/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/api/CaseStyle.java
+++ b/stylesniffer-api/src/main/java/dev/cookiecode/stylesniffer/api/CaseStyle.java
@@ -22,18 +22,28 @@
*/
package dev.cookiecode.stylesniffer.api;
+import static java.util.Collections.unmodifiableSet;
import static java.util.Set.of;
+import jakarta.annotation.Nullable;
import java.util.Set;
import lombok.NonNull;
/**
* Represents a naming convention (case style) that can be matched against a given string.
*
+ * @implNote Implementations should define specific case styles such as camelCase, snake_case, etc.
+ * Each case style must have a unique name and may have variant names.
* @author Sebastien Vermeille
*/
public interface CaseStyle {
+ /**
+ * Determines if the given string matches this case style.
+ *
+ * @param name the string to be checked, must not be null
+ * @return true if the string matches this case style, false otherwise
+ */
boolean matches(@NonNull String name);
/**
@@ -44,12 +54,20 @@ public interface CaseStyle {
String getName();
/**
- * Returns a set of variant names for the case style (e.g., "PascalCase" and "UpperCamelCase").
- * Implementations should override this method to provide variant names if applicable.
+ * Returns a set of variant names for the case style (e.g., "PascalCase" and "UpperCamelCase"). By
+ * default, only the primary name is returned.
*
- * @return a set of variant names
+ * @return an immutable set of variant names
*/
default Set getVariantNames() {
- return of(this.getName()); // By default, return only the primary name
+ return unmodifiableSet(of(getName()));
}
+
+ /**
+ * Determines equality based on the {@link #getName()} method.
+ *
+ * @param object the object to compare with
+ * @return true if the given object is a {@code CaseStyle} with the same name, false otherwise
+ */
+ boolean equals(@Nullable Object object);
}
diff --git a/stylesniffer-api/src/test/java/.gitkeep b/stylesniffer-api/src/test/java/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/BaseCaseStyleTest.java b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/BaseCaseStyleTest.java
new file mode 100644
index 0000000..cb95b55
--- /dev/null
+++ b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/BaseCaseStyleTest.java
@@ -0,0 +1,101 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+class BaseCaseStyleTest {
+
+ @Test
+ void equalsShouldReturnFalseGivenTwoDifferentCaseStylesAreProvided() {
+
+ // GIVEN
+ final var firstCaseStyle = new CaseStyleHavingNoVariantNamesImpl();
+ final var secondCaseStyle = new CaseStyleHavingTwoVariantNamesImpl();
+
+ // WHEN
+ final var actualResult = firstCaseStyle.equals(secondCaseStyle);
+
+ // THEN
+ assertThat(actualResult).isFalse();
+ }
+
+ @Test
+ void equalsShouldReturnFalseGivenNullIsProvided() {
+
+ // GIVEN
+ final var firstCaseStyle = new CaseStyleHavingNoVariantNamesImpl();
+
+ // WHEN
+ final var actualResult = firstCaseStyle.equals(null);
+
+ // THEN
+ assertThat(actualResult).isFalse();
+ }
+
+ @Test
+ void equalsShouldReturnTrueGivenTheSameInstanceIsProvidedTwice() {
+
+ // GIVEN
+ final var firstCaseStyle = new CaseStyleHavingNoVariantNamesImpl();
+
+ // WHEN
+ final var actualResult = firstCaseStyle.equals(firstCaseStyle);
+
+ // THEN
+ assertThat(actualResult).isTrue();
+ }
+
+ @Test
+ void equalsShouldReturnTrueGivenTwoInstancesOfTheSameCaseStyleAreCompared() {
+
+ // GIVEN
+ final var firstCaseStyle = new CaseStyleHavingNoVariantNamesImpl();
+ final var secondCaseStyle = new CaseStyleHavingNoVariantNamesImpl();
+
+ // WHEN
+ final var actualResult = firstCaseStyle.equals(secondCaseStyle);
+
+ // THEN
+ assertThat(actualResult).isTrue();
+ }
+
+ @Test
+ void hashCodeShouldReturnAUniqueValueBasedOnTheNameGivenACaseStyleIsProvided() {
+ // GIVEN
+ final var caseStyleInstance = new CaseStyleHavingNoVariantNamesImpl();
+
+ // WHEN
+ final var actualResult = caseStyleInstance.hashCode();
+
+ // THEN
+ assertThat(actualResult).isEqualTo(caseStyleInstance.getName().hashCode());
+ }
+}
diff --git a/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleHavingNoVariantNamesImpl.java b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleHavingNoVariantNamesImpl.java
new file mode 100644
index 0000000..1e663b0
--- /dev/null
+++ b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleHavingNoVariantNamesImpl.java
@@ -0,0 +1,44 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.api;
+
+import lombok.NonNull;
+
+/**
+ * Dummy implementation of CaseStyle having no variant names
+ *
+ * @author Sebastien Vermeille
+ */
+public class CaseStyleHavingNoVariantNamesImpl extends BaseCaseStyle {
+ @Override
+ public boolean matches(@NonNull String name) {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "CaseStyleHavingNoVariantName";
+ }
+
+ // intentionally do not override getVariantNames(); (used in a test)
+}
diff --git a/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleHavingTwoVariantNamesImpl.java b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleHavingTwoVariantNamesImpl.java
new file mode 100644
index 0000000..f00d630
--- /dev/null
+++ b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleHavingTwoVariantNamesImpl.java
@@ -0,0 +1,48 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.api;
+
+import java.util.Set;
+import lombok.NonNull;
+
+/**
+ * Dummy implementation of CaseStyle having some variant names
+ *
+ * @author Sebastien Vermeille
+ */
+public class CaseStyleHavingTwoVariantNamesImpl extends BaseCaseStyle {
+ @Override
+ public boolean matches(@NonNull String name) {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "ManyVariantsNamesCaseStyle";
+ }
+
+ @Override
+ public Set getVariantNames() {
+ return Set.of(getName(), "AnotherVariant", "andAlsoThatOne");
+ }
+}
diff --git a/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleTest.java b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleTest.java
new file mode 100644
index 0000000..78d8ddc
--- /dev/null
+++ b/stylesniffer-api/src/test/java/dev/cookiecode/stylesniffer/api/CaseStyleTest.java
@@ -0,0 +1,60 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.api;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Set;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+class CaseStyleTest {
+
+ @Test
+ void getVariantNamesShouldReturnByDefaultOnlyTheCaseStyleName() {
+ // GIVEN
+ final CaseStyle caseStyle = new CaseStyleHavingNoVariantNamesImpl();
+
+ // WHEN
+ final var actualVariantNames = caseStyle.getVariantNames();
+
+ // THEN
+ assertThat(actualVariantNames).hasSize(1).containsOnly(caseStyle.getName());
+ }
+
+ @Test
+ void getVariantNamesShouldReturnTheCompleteListOfVariantNamesWhenOverride() {
+ // GIVEN
+ final CaseStyle caseStyle = new CaseStyleHavingTwoVariantNamesImpl();
+
+ // WHEN
+ final var actualVariantNames = caseStyle.getVariantNames();
+
+ // THEN
+ assertThat(actualVariantNames).hasSizeGreaterThan(1).isNotSameAs(Set.of(caseStyle.getName()));
+ }
+}
diff --git a/stylesniffer-impl/pom.xml b/stylesniffer-impl/pom.xml
index cd3e98d..bdd6e83 100644
--- a/stylesniffer-impl/pom.xml
+++ b/stylesniffer-impl/pom.xml
@@ -38,48 +38,74 @@
- ${java.sdk.version}
- ${java.sdk.version}
- ${source.encoding}
${basedir}/..
-
${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId}
+
org.projectlombok
lombok
+
+ jakarta.annotation
+ jakarta.annotation-api
+
dev.cookiecode
stylesniffer-annotation-processor
+ 1.0.0-SNAPSHOT
provided
- ${project.version}
stylesniffer-api
dev.cookiecode
- compile
1.0.0-SNAPSHOT
+ compile
-
+
dev.cookiecode
stylesniffer-testkit
- test
1.0.0-SNAPSHOT
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ test
-
+ org.apache.maven.plugins
maven-compiler-plugin
+ default-compile
+ compile
+
+ compile
+
@@ -89,31 +115,8 @@
${java.sdk.version}
${java.sdk.version}
-
- compile
-
- default-compile
- compile
-
-
- org.apache.maven.plugins
- 3.13.0
-
-
- jacoco-maven-plugin
-
-
-
- true
-
-
- report-aggregate
-
- report-aggregate
- verify
- org.jacoco
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSniffer.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSniffer.java
deleted file mode 100644
index 54ea600..0000000
--- a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSniffer.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2024 Sebastien Vermeille
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-package dev.cookiecode.stylesniffer;
-
-import static java.lang.reflect.Modifier.isAbstract;
-import static java.util.stream.Collectors.toSet;
-
-import dev.cookiecode.stylesniffer.api.CaseStyle;
-import dev.cookiecode.stylesniffer.api.exception.StyleSnifferException;
-import dev.cookiecode.stylesniffer.generated.CaseStyleInjector;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import javax.annotation.Nullable;
-
-/**
- * The {@code StyleSniffer} class is responsible for managing and detecting various {@link
- * CaseStyle} implementations.
- *
- * It registers all case style classes that are annotated with {@link
- * dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle}, instantiates them, and stores them in
- * a list. It also provides methods to retrieve case styles based on names and to list all supported
- * case styles.
- *
- * @author Sebastien Vermeille
- */
-public class StyleSniffer {
-
- private final List caseStyles = new ArrayList<>();
-
- /**
- * Constructs a {@code StyleSniffer} instance and initializes case style recognizers.
- *
- * This constructor attempts to register all case style classes that are annotated with
- * {@code @RegisterCaseStyle}.
- *
- * @throws RuntimeException if there is an error during initialization
- */
- StyleSniffer() {
- try {
- this.registerAllAnnotatedCaseStyles();
- } catch (final Exception e) {
- throw new StyleSnifferException("Failed to initialize CaseStyleRecognizer", e);
- }
- }
-
- /**
- * Registers all case style classes that are annotated with {@code @RegisterCaseStyle}.
- *
- *
This method retrieves annotated case style classes using the {@link CaseStyleInjector} and
- * instantiates them, adding them to the internal list of case styles. Any instantiation failures
- * are wrapped in a {@code RuntimeException}.
- *
- * @throws RuntimeException if there is an error instantiating case style classes
- */
- private void registerAllAnnotatedCaseStyles() {
-
- final var annotatedClasses = new CaseStyleInjector().getAnnotatedCaseStyles();
-
- for (final var clazz : annotatedClasses) {
- if (CaseStyle.class.isAssignableFrom(clazz) && !isAbstract(clazz.getModifiers())) {
- try {
- this.caseStyles.add(clazz.getDeclaredConstructor().newInstance());
- } catch (final Exception e) {
- throw new StyleSnifferException(
- String.format("Failed to instantiate case style: %s", clazz.getName()), e);
- }
- }
- }
- }
-
- /**
- * Retrieves a {@link CaseStyle} that matches the given name.
- *
- *
This method searches through the registered case styles and returns the first style that
- * matches the provided name.
- *
- * @param name the name to match against case styles
- * @return an {@link Optional} containing the matching {@code CaseStyle} if found, or an empty
- * {@code Optional} if no match is found
- */
- public Optional getCaseStyle(@Nullable final String name) {
- if (name == null || name.isEmpty()) {
- return Optional.empty();
- }
-
- return this.caseStyles.stream().filter(style -> style.matches(name)).findFirst();
- }
-
- /**
- * Returns a set of names for all supported case styles.
- *
- * This method provides a set of unique names for the case styles currently recognized by this
- * {@code StyleSniffer}.
- *
- * @return a {@link Set} of names for supported case styles
- */
- public Set getSupportedCaseStyles() {
- return this.caseStyles.stream().map(CaseStyle::getName).collect(toSet());
- }
-
- /**
- * Returns a set of names for all supported case styles, including all variants.
- *
- * This method provides a comprehensive list of all case style names recognized by this {@code
- * StyleSniffer}, including both primary names and their variants.
- *
- * @return a {@link Set} of names for all supported case styles, including variants
- */
- public Set getSupportedCaseStylesIncludingVariants() {
- return this.caseStyles.stream()
- .flatMap(style -> style.getVariantNames().stream())
- .collect(toSet());
- }
-}
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSnifferFactory.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSnifferFactory.java
index 8d44266..99cd344 100644
--- a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSnifferFactory.java
+++ b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSnifferFactory.java
@@ -22,15 +22,15 @@
*/
package dev.cookiecode.stylesniffer;
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
+import dev.cookiecode.stylesniffer.generated.CaseStyleInjector;
+import lombok.experimental.UtilityClass;
/**
* Utility class for creating instances of {@link StyleSniffer}.
*
* @author Sebastien Vermeille
*/
-@NoArgsConstructor(access = AccessLevel.PRIVATE)
+@UtilityClass
public final class StyleSnifferFactory {
/**
@@ -39,6 +39,7 @@ public final class StyleSnifferFactory {
* @return a new {@link StyleSniffer} instance
*/
public static StyleSniffer createStyleSniffer() {
- return new StyleSniffer();
+ final var caseStyleClasses = new CaseStyleInjector().getAnnotatedCaseStyles();
+ return new StyleSnifferImpl(caseStyleClasses);
}
}
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSnifferImpl.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSnifferImpl.java
new file mode 100644
index 0000000..6c61b06
--- /dev/null
+++ b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/StyleSnifferImpl.java
@@ -0,0 +1,160 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import static java.lang.reflect.Modifier.isAbstract;
+import static java.util.Collections.unmodifiableList;
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.toSet;
+
+import com.google.common.annotations.VisibleForTesting;
+import dev.cookiecode.stylesniffer.api.CaseStyle;
+import dev.cookiecode.stylesniffer.api.exception.StyleSnifferException;
+import jakarta.annotation.Nullable;
+import java.lang.reflect.InvocationTargetException;
+import java.util.*;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import lombok.NonNull;
+
+/**
+ * The {@code StyleSniffer} class is responsible for managing and detecting various {@link
+ * CaseStyle} implementations.
+ *
+ * It registers all case style classes that are annotated with {@link
+ * dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle}, instantiates them, and stores them in
+ * a list. It also provides methods to retrieve case styles based on names and to list all supported
+ * case styles.
+ *
+ * @author Sebastien Vermeille
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public class StyleSnifferImpl implements StyleSniffer {
+
+ private List caseStyles = new ArrayList<>();
+
+ StyleSnifferImpl(@NonNull List> caseStyleClasses) {
+ registerCaseStyleClasses(caseStyleClasses);
+ }
+
+ @VisibleForTesting
+ void registerCaseStyleClass(@NonNull Class extends CaseStyle> caseStyleClass) {
+ registerCaseStyleClasses(Collections.singletonList(caseStyleClass));
+ }
+
+ @VisibleForTesting
+ void registerCaseStyleClasses(@NonNull List> caseStyleClasses) {
+ List newCaseStyles = new ArrayList<>(caseStyles);
+ newCaseStyles.addAll(
+ caseStyleClasses.stream()
+ .filter(this::isValidCaseStyleClass)
+ .map(this::instantiateCaseStyle)
+ .toList());
+
+ caseStyles =
+ unmodifiableList(
+ newCaseStyles); // ensure only this method can modify caseStyles items (no add nor
+ // delete)
+ }
+
+ /**
+ * Checks if the given class is a valid {@link CaseStyle} implementation.
+ *
+ * @param clazz the class to check
+ * @return {@code true} if the class is a valid {@code CaseStyle} implementation; {@code false}
+ * otherwise
+ */
+ @VisibleForTesting
+ boolean isValidCaseStyleClass(Class> clazz) {
+ return CaseStyle.class.isAssignableFrom(clazz) && !isAbstract(clazz.getModifiers());
+ }
+
+ /**
+ * Instantiates a {@link CaseStyle} class.
+ *
+ * @param clazz the class to instantiate
+ * @return the instantiated {@code CaseStyle}
+ * @throws StyleSnifferException if instantiation fails
+ */
+ @VisibleForTesting
+ CaseStyle instantiateCaseStyle(@NonNull Class extends CaseStyle> clazz) {
+ try {
+ return clazz.getDeclaredConstructor().newInstance();
+ } catch (InstantiationException e) {
+ throw new StyleSnifferException(
+ String.format("Cannot instantiate case style class %s. Is it abstract?", clazz.getName()),
+ e);
+ } catch (IllegalAccessException | NoSuchMethodException e) {
+ throw new StyleSnifferException(
+ String.format(
+ "Access violation while instantiating case style class %s. Does it has a public no args constructor?",
+ clazz.getName()),
+ e);
+ } catch (InvocationTargetException e) {
+ throw new StyleSnifferException(
+ "Exception thrown by constructor for case style class: " + clazz.getName(), e);
+ }
+ }
+
+ @Override
+ public Optional getCaseStyle(@Nullable final String name) {
+ return sanitizeInput(name)
+ .flatMap(
+ sanitizedName ->
+ caseStyles.stream().filter(style -> style.matches(sanitizedName)).findFirst());
+ }
+
+ @Override
+ public Optional getCaseStyleWithVariantOrName(@Nullable final String variantOrName) {
+ return sanitizeInput(variantOrName)
+ .flatMap(
+ sanitizedName ->
+ caseStyles.stream()
+ .filter(
+ style ->
+ style.getName().equals(sanitizedName)
+ || style.getVariantNames().contains(sanitizedName))
+ .findFirst());
+ }
+
+ /**
+ * Performs basic sanity checks on the input, such as trimming whitespace.
+ *
+ * @param name the input name to sanitize
+ * @return an {@link Optional} containing the sanitized name, or an empty {@link Optional} if the
+ * input is invalid
+ */
+ private Optional sanitizeInput(@Nullable final String name) {
+ return ofNullable(name).map(String::trim).filter(sanitizedName -> !sanitizedName.isEmpty());
+ }
+
+ @Override
+ public Set getSupportedCaseStyles() {
+ return caseStyles.stream().map(CaseStyle::getName).collect(toSet());
+ }
+
+ @Override
+ public Set getSupportedCaseStylesIncludingVariants() {
+ return caseStyles.stream().flatMap(style -> style.getVariantNames().stream()).collect(toSet());
+ }
+}
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/KebabCaseStyle.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/KebabCaseStyle.java
similarity index 94%
rename from stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/KebabCaseStyle.java
rename to stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/KebabCaseStyle.java
index eac8547..c5a99cd 100644
--- a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/KebabCaseStyle.java
+++ b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/KebabCaseStyle.java
@@ -20,12 +20,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import static java.lang.Character.isLowerCase;
import static java.util.Set.of;
import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
import dev.cookiecode.stylesniffer.api.CaseStyle;
import java.util.Set;
import lombok.NonNull;
@@ -38,7 +39,7 @@
* @author Sebastien Vermeille
*/
@RegisterCaseStyle
-public class KebabCaseStyle implements CaseStyle {
+public class KebabCaseStyle extends BaseCaseStyle {
private static final char SEPARATOR = '-';
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/LowerCamelCaseStyle.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/LowerCamelCaseStyle.java
similarity index 95%
rename from stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/LowerCamelCaseStyle.java
rename to stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/LowerCamelCaseStyle.java
index ad8d722..de71f40 100644
--- a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/LowerCamelCaseStyle.java
+++ b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/LowerCamelCaseStyle.java
@@ -20,13 +20,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import static java.lang.Character.isLowerCase;
import static java.lang.Character.isUpperCase;
import static java.util.Set.of;
import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
import dev.cookiecode.stylesniffer.api.CaseStyle;
import java.util.Set;
import lombok.NonNull;
@@ -40,7 +41,7 @@
* @author Sebastien Vermeille
*/
@RegisterCaseStyle
-public class LowerCamelCaseStyle implements CaseStyle {
+public class LowerCamelCaseStyle extends BaseCaseStyle {
/**
* Checks if the given name matches the CamelCase style.
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/PascalCaseStyle.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/PascalCaseStyle.java
similarity index 72%
rename from stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/PascalCaseStyle.java
rename to stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/PascalCaseStyle.java
index 92b9800..566910d 100644
--- a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/PascalCaseStyle.java
+++ b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/PascalCaseStyle.java
@@ -20,12 +20,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
-import static java.lang.Character.isLowerCase;
import static java.lang.Character.isUpperCase;
import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
import dev.cookiecode.stylesniffer.api.CaseStyle;
import java.util.Set;
import lombok.NonNull;
@@ -39,7 +39,7 @@
* @author Sebastien Vermeille
*/
@RegisterCaseStyle
-public class PascalCaseStyle implements CaseStyle {
+public class PascalCaseStyle extends BaseCaseStyle {
/**
* Checks if the given name matches the PascalCase style.
@@ -62,6 +62,10 @@ public Set getVariantNames() {
return Set.of(this.getName(), "UpperCamelCase", "CamelCase");
}
+ private Set invalidCharacters() {
+ return Set.of('_', ' ', '-', '*', ',', '\"', '\'', '#', '$', '@');
+ }
+
/**
* Determines if the given name is in PascalCase style.
*
@@ -74,26 +78,34 @@ public Set getVariantNames() {
* @return {@code true} if the name is in PascalCase, {@code false} otherwise
*/
private boolean isPascalCase(@NonNull final String name) {
- if (isUpperCase(name.charAt(0)) && !name.contains("_") && !name.contains(" ")) {
- boolean seenLowercase = false;
+ return !containsInvalidCharacters(name)
+ && startsWithUpperCase(name)
+ && followsPascalCasePattern(name);
+ }
+
+ private boolean startsWithUpperCase(@NonNull final String name) {
+ return isUpperCase(name.charAt(0));
+ }
+
+ private boolean followsPascalCasePattern(@NonNull final String name) {
+ boolean previousIsUpperCase = false;
- for (int i = 1; i < name.length(); i++) {
- final char currentChar = name.charAt(i);
- final char previousChar = name.charAt(i - 1);
+ for (int i = 1; i < name.length(); i++) {
+ char currentChar = name.charAt(i);
- if (isLowerCase(currentChar)) {
- seenLowercase = true;
- } else if (isUpperCase(currentChar)) {
- if (seenLowercase && isUpperCase(previousChar)) {
- continue;
- }
- if (!seenLowercase || isLowerCase(previousChar)) {
- continue;
- }
+ if (isUpperCase(currentChar)) {
+ if (previousIsUpperCase) {
+ continue;
}
+ previousIsUpperCase = true;
+ } else {
+ previousIsUpperCase = false;
}
- return true;
}
- return false;
+ return true;
+ }
+
+ private boolean containsInvalidCharacters(@NonNull String name) {
+ return name.chars().anyMatch(c -> invalidCharacters().contains((char) c));
}
}
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/ScreamingSnakeCaseStyle.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/ScreamingSnakeCaseStyle.java
similarity index 95%
rename from stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/ScreamingSnakeCaseStyle.java
rename to stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/ScreamingSnakeCaseStyle.java
index 87ebfe1..44f84cd 100644
--- a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/ScreamingSnakeCaseStyle.java
+++ b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/ScreamingSnakeCaseStyle.java
@@ -20,9 +20,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
import dev.cookiecode.stylesniffer.api.CaseStyle;
import java.util.Set;
import lombok.NonNull;
@@ -36,7 +37,7 @@
* @author Sebastien Vermeille
*/
@RegisterCaseStyle
-public class ScreamingSnakeCaseStyle implements CaseStyle {
+public class ScreamingSnakeCaseStyle extends BaseCaseStyle {
private static final char UNDERSCORE = '_';
diff --git a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/SnakeCaseStyle.java b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/SnakeCaseStyle.java
similarity index 94%
rename from stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/SnakeCaseStyle.java
rename to stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/SnakeCaseStyle.java
index 1fae7cc..84ca20f 100644
--- a/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/SnakeCaseStyle.java
+++ b/stylesniffer-impl/src/main/java/dev/cookiecode/stylesniffer/impl/casestyle/SnakeCaseStyle.java
@@ -20,11 +20,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import static java.lang.Character.isLowerCase;
import dev.cookiecode.stylesniffer.annotation.RegisterCaseStyle;
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
import dev.cookiecode.stylesniffer.api.CaseStyle;
import java.util.Set;
import lombok.NonNull;
@@ -37,7 +38,7 @@
* @author Sebastien Vermeille
*/
@RegisterCaseStyle
-public class SnakeCaseStyle implements CaseStyle {
+public class SnakeCaseStyle extends BaseCaseStyle {
private static final char UNDERSCORE = '_';
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/AbstractCaseStyle.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/AbstractCaseStyle.java
new file mode 100644
index 0000000..e2a7e78
--- /dev/null
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/AbstractCaseStyle.java
@@ -0,0 +1,32 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import dev.cookiecode.stylesniffer.api.CaseStyle;
+
+/**
+ * Test class used by {@link StyleSnifferImplTest}
+ *
+ * @author Sebastien Vermeille
+ */
+public abstract class AbstractCaseStyle implements CaseStyle {}
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithRuntimeException.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithRuntimeException.java
new file mode 100644
index 0000000..0166316
--- /dev/null
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithRuntimeException.java
@@ -0,0 +1,48 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
+import lombok.NonNull;
+
+/**
+ * Test class used by {@link StyleSnifferImplTest}
+ *
+ * @author Sebastien Vermeille
+ */
+public class FaultyCaseStyleWithRuntimeException extends BaseCaseStyle {
+
+ public FaultyCaseStyleWithRuntimeException() {
+ throw new RuntimeException("Runtime exception");
+ }
+
+ @Override
+ public boolean matches(@NonNull String name) {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "DummyCaseStyle";
+ }
+}
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithoutPublicConstructor.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithoutPublicConstructor.java
new file mode 100644
index 0000000..9d0d88c
--- /dev/null
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithoutPublicConstructor.java
@@ -0,0 +1,46 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
+import lombok.NonNull;
+
+/**
+ * Test class used by {@link StyleSnifferImplTest}
+ *
+ * @author Sebastien Vermeille
+ */
+public class FaultyCaseStyleWithoutPublicConstructor extends BaseCaseStyle {
+
+ private FaultyCaseStyleWithoutPublicConstructor() {}
+
+ @Override
+ public boolean matches(@NonNull String name) {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "DummyCaseStyle";
+ }
+}
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithoutPublicNoArgsConstructor.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithoutPublicNoArgsConstructor.java
new file mode 100644
index 0000000..0606ac2
--- /dev/null
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/FaultyCaseStyleWithoutPublicNoArgsConstructor.java
@@ -0,0 +1,46 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
+import lombok.NonNull;
+
+/**
+ * Test class used by {@link StyleSnifferImplTest}
+ *
+ * @author Sebastien Vermeille
+ */
+public class FaultyCaseStyleWithoutPublicNoArgsConstructor extends BaseCaseStyle {
+
+ public FaultyCaseStyleWithoutPublicNoArgsConstructor(String someParam) {}
+
+ @Override
+ public boolean matches(@NonNull String name) {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "DummyCaseStyle";
+ }
+}
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/StyleSnifferFactoryTest.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/StyleSnifferFactoryTest.java
new file mode 100644
index 0000000..b9aaf14
--- /dev/null
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/StyleSnifferFactoryTest.java
@@ -0,0 +1,61 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.lang.reflect.Constructor;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+class StyleSnifferFactoryTest {
+
+ @Test
+ void createStyleSnifferShouldNotGenerateAnyExceptions() {
+ // THEN
+ assertDoesNotThrow(
+ () -> {
+ // WHEN
+ final var instance = StyleSnifferFactory.createStyleSniffer();
+ instance.getCaseStyle("PascalCaseInput");
+ });
+ }
+
+ @Test
+ void instantiateStyleSnifferShouldNotThrowExceptions() {
+ assertDoesNotThrow(
+ () -> {
+ // GIVEN
+ Constructor constructor =
+ StyleSnifferFactory.class.getDeclaredConstructor();
+ constructor.setAccessible(true);
+
+ // WHEN
+ constructor.newInstance();
+ });
+ }
+}
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/StyleSnifferImplTest.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/StyleSnifferImplTest.java
new file mode 100644
index 0000000..d5ac044
--- /dev/null
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/StyleSnifferImplTest.java
@@ -0,0 +1,322 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer;
+
+import static java.util.Collections.emptyList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import dev.cookiecode.stylesniffer.api.exception.StyleSnifferException;
+import dev.cookiecode.stylesniffer.generated.CaseStyleInjector;
+import dev.cookiecode.stylesniffer.impl.casestyle.PascalCaseStyle;
+import java.util.ArrayList;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+class StyleSnifferImplTest {
+
+ private StyleSnifferImpl styleSniffer;
+
+ @BeforeEach
+ void setUp() {
+ styleSniffer = new StyleSnifferImpl(emptyList());
+ }
+
+ @Test
+ void isValidCaseStyleClassShouldReturnTrueGivenTheClassImplementsCaseStyleAndIsNotAbstract() {
+ // GIVEN
+ final var clazz = PascalCaseStyle.class;
+
+ // WHEN
+ final var actualResult = styleSniffer.isValidCaseStyleClass(clazz);
+
+ // THEN
+ assertThat(actualResult).isTrue();
+ }
+
+ @Test
+ void isValidCaseStyleClassShouldReturnFalseGivenTheClassImplementsCaseStyleButIsAbstract() {
+ // GIVEN
+ final var clazz = AbstractCaseStyle.class;
+
+ // WHEN
+ final var actualResult = styleSniffer.isValidCaseStyleClass(clazz);
+
+ // THEN
+ assertThat(actualResult).isFalse();
+ }
+
+ @Test
+ void isValidCaseStyleClassShouldReturnFalseGivenTheClassDoNotImplementsCaseStyle() {
+ // GIVEN
+ final var clazz = ArrayList.class;
+
+ // WHEN
+ final var actualResult = styleSniffer.isValidCaseStyleClass(clazz);
+
+ // THEN
+ assertThat(actualResult).isFalse();
+ }
+
+ @Test
+ void instantiateCaseStyleShouldThrowStyleSnifferExceptionInCaseOfAccessViolation() {
+
+ var thrown =
+ assertThrows(
+ StyleSnifferException.class,
+ () -> {
+ // GIVEN
+ final var clazz = FaultyCaseStyleWithoutPublicConstructor.class;
+
+ // WHEN
+ styleSniffer.instantiateCaseStyle(clazz);
+ });
+ assertThat(thrown.getMessage()).contains("Does it has a public no args constructor?");
+ }
+
+ @Test
+ void instantiateCaseStyleShouldThrowStyleSnifferExceptionInCaseOfNoEmptyConstructor() {
+
+ var thrown =
+ assertThrows(
+ StyleSnifferException.class,
+ () -> {
+ // GIVEN
+ final var clazz = FaultyCaseStyleWithoutPublicNoArgsConstructor.class;
+
+ // WHEN
+ styleSniffer.instantiateCaseStyle(clazz);
+ });
+ assertThat(thrown.getMessage()).contains("Does it has a public no args constructor?");
+ }
+
+ @Test
+ void instantiateCaseStyleShouldThrowStyleSnifferExceptionInCaseOfAbstractClass() {
+
+ var thrown =
+ assertThrows(
+ StyleSnifferException.class,
+ () -> {
+ // GIVEN
+ final var clazz = AbstractCaseStyle.class;
+
+ // WHEN
+ styleSniffer.instantiateCaseStyle(clazz);
+ });
+ assertThat(thrown.getMessage()).contains("Is it abstract?");
+ }
+
+ @Test
+ void
+ instantiateCaseStyleShouldThrowStyleSnifferExceptionInCaseTheConstructorGeneratesAnException() {
+
+ assertThrows(
+ StyleSnifferException.class,
+ () -> {
+ // GIVEN
+ final var clazz = FaultyCaseStyleWithRuntimeException.class;
+
+ // WHEN
+ styleSniffer.instantiateCaseStyle(clazz);
+ });
+ }
+
+ @Test
+ void instantiateCaseStyleShouldReturnANewInstanceInCaseOfSuccess() {
+
+ // GIVEN
+ final var clazz = PascalCaseStyle.class;
+
+ // WHEN
+ final var actualResult = styleSniffer.instantiateCaseStyle(clazz);
+
+ // THEN
+ assertThat(actualResult).isInstanceOf(PascalCaseStyle.class);
+ }
+
+ @Test
+ void getCaseStyleShouldReturnAnEmptyResultGivenNullInput() {
+ // GIVEN
+ final String nullInput = null;
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyle(nullInput);
+
+ // THEN
+ assertThat(result).isNotNull().isEmpty();
+ }
+
+ @Test
+ void getCaseStyleShouldReturnAnEmptyResultGivenEmptyInput() {
+ // GIVEN
+ final var emptyInput = "";
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyle(emptyInput);
+
+ // THEN
+ assertThat(result).isNotNull().isEmpty();
+ }
+
+ @Test
+ void getCaseStyleShouldReturnAnEmptyResultGivenSpaceInput() {
+ // GIVEN
+ final var spaceInput = " ";
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyle(spaceInput);
+
+ // THEN
+ assertThat(result).isNotNull().isEmpty();
+ }
+
+ @Test
+ void getCaseStyleShouldReturnAPascalCaseCaseStyleGivenInputIsWrittenInPascalCase() {
+ // GIVEN
+ final var pascalCaseInput = "SomePascalCase";
+
+ final var pascalCaseImplClass = PascalCaseStyle.class;
+ styleSniffer.registerCaseStyleClass(pascalCaseImplClass);
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyle(pascalCaseInput);
+
+ // THEN
+ assertThat(result).isPresent().get().isInstanceOf(pascalCaseImplClass);
+ }
+
+ @Test
+ void ggetCaseStyleWithVariantOrNameShouldReturnAnEmptyResultGivenNullInput() {
+ // GIVEN
+ final String nullInput = null;
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyleWithVariantOrName(nullInput);
+
+ // THEN
+ assertThat(result).isNotNull().isEmpty();
+ }
+
+ @Test
+ void getCaseStyleWithVariantOrNameShouldReturnAnEmptyResultGivenEmptyInput() {
+ // GIVEN
+ final var emptyInput = "";
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyleWithVariantOrName(emptyInput);
+
+ // THEN
+ assertThat(result).isNotNull().isEmpty();
+ }
+
+ @Test
+ void getCaseStyleWithVariantOrNameShouldReturnAnEmptyResultGivenSpaceInput() {
+ // GIVEN
+ final var spaceInput = " ";
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyleWithVariantOrName(spaceInput);
+
+ // THEN
+ assertThat(result).isNotNull().isEmpty();
+ }
+
+ @Test
+ void
+ getCaseStyleWithVariantOrNameShouldReturnPascalCaseCaseStyleGivenInputIsUpperCamelCaseVariant() {
+ // GIVEN
+ final String variantName = "UpperCamelCase";
+ styleSniffer = new StyleSnifferImpl(new CaseStyleInjector().getAnnotatedCaseStyles());
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyleWithVariantOrName(variantName);
+
+ // THEN
+ assertThat(result).isNotNull().isPresent().get().isInstanceOf(PascalCaseStyle.class);
+ }
+
+ @Test
+ void getCaseStyleWithVariantOrNameShouldReturnPascalCaseCaseStyleGivenInputIsUpperCamelCase() {
+ // GIVEN
+ final String name = "PascalCase";
+ styleSniffer = new StyleSnifferImpl(new CaseStyleInjector().getAnnotatedCaseStyles());
+
+ // WHEN
+ final var result = styleSniffer.getCaseStyleWithVariantOrName(name);
+
+ // THEN
+ assertThat(result).isNotNull().isPresent().get().isInstanceOf(PascalCaseStyle.class);
+ }
+
+ @Test
+ void
+ getSupportedCaseStylesShouldReturnANonEmptyListOfSupportedCaseStylesGivenSomeImplementationExists() {
+ // GIVEN
+ // case embedded styles implementations
+ styleSniffer.registerCaseStyleClasses(new CaseStyleInjector().getAnnotatedCaseStyles());
+
+ // WHEN
+ final var actuallySupportedCaseStyle = styleSniffer.getSupportedCaseStyles();
+
+ // THEN
+ assertThat(actuallySupportedCaseStyle).isNotEmpty();
+ }
+
+ @Test
+ void getSupportedCaseStylesShouldContainsPascalCaseGivenPascalCaseImplementationExist() {
+ // GIVEN
+ // case embedded styles implementations
+ styleSniffer.registerCaseStyleClasses(new CaseStyleInjector().getAnnotatedCaseStyles());
+
+ final var pascalCaseStyleName = new PascalCaseStyle().getName();
+
+ // WHEN
+ final var actuallySupportedCaseStyle = styleSniffer.getSupportedCaseStyles();
+
+ // THEN
+ assertThat(actuallySupportedCaseStyle).contains(pascalCaseStyleName);
+ }
+
+ @Test
+ void
+ getSupportedCaseStylesIncludingVariantsShouldContainsPascalCaseAndAllItsVariantsGivenPascalCaseImplementationExist() {
+ // GIVEN
+ // case embedded styles implementations
+ styleSniffer.registerCaseStyleClasses(new CaseStyleInjector().getAnnotatedCaseStyles());
+
+ final var pascalCaseStyle = new PascalCaseStyle();
+
+ // WHEN
+ final var actuallySupportedCaseStyle = styleSniffer.getSupportedCaseStylesIncludingVariants();
+
+ // THEN
+ assertThat(actuallySupportedCaseStyle)
+ .containsAll(pascalCaseStyle.getVariantNames())
+ .contains(pascalCaseStyle.getName());
+ }
+}
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/KebabCaseStyleTest.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/KebabCaseStyleTest.java
similarity index 90%
rename from stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/KebabCaseStyleTest.java
rename to stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/KebabCaseStyleTest.java
index f5bac9e..b6631e5 100644
--- a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/KebabCaseStyleTest.java
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/KebabCaseStyleTest.java
@@ -20,11 +20,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import static java.util.List.of;
-import dev.cookiecode.stylesniffer.testkit.BaseCaseStyleTest;
+import dev.cookiecode.stylesniffer.testkit.CaseStyleTestKit;
import java.util.List;
/**
@@ -34,7 +34,7 @@
*/
@SuppressWarnings(
"java:S2187") // sonar is not able to detect that BaseCaseStyleTest interface generates test
-class KebabCaseStyleTest implements BaseCaseStyleTest {
+class KebabCaseStyleTest implements CaseStyleTestKit {
@Override
public KebabCaseStyle createCaseStyle() {
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/LowerCamelCaseStyleTest.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/LowerCamelCaseStyleTest.java
similarity index 90%
rename from stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/LowerCamelCaseStyleTest.java
rename to stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/LowerCamelCaseStyleTest.java
index 7c7c444..20a9305 100644
--- a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/LowerCamelCaseStyleTest.java
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/LowerCamelCaseStyleTest.java
@@ -20,11 +20,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import static java.util.List.of;
-import dev.cookiecode.stylesniffer.testkit.BaseCaseStyleTest;
+import dev.cookiecode.stylesniffer.testkit.CaseStyleTestKit;
import java.util.List;
/**
@@ -34,7 +34,7 @@
*/
@SuppressWarnings(
"java:S2187") // sonar is not able to detect that BaseCaseStyleTest interface generates test
-class LowerCamelCaseStyleTest implements BaseCaseStyleTest {
+class LowerCamelCaseStyleTest implements CaseStyleTestKit {
@Override
public LowerCamelCaseStyle createCaseStyle() {
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/PascalCaseStyleTest.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/PascalCaseStyleTest.java
similarity index 76%
rename from stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/PascalCaseStyleTest.java
rename to stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/PascalCaseStyleTest.java
index 45e17e7..202805e 100644
--- a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/PascalCaseStyleTest.java
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/PascalCaseStyleTest.java
@@ -20,11 +20,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import static java.util.List.of;
-import dev.cookiecode.stylesniffer.testkit.BaseCaseStyleTest;
+import dev.cookiecode.stylesniffer.testkit.CaseStyleTestKit;
import java.util.List;
/**
@@ -34,7 +34,7 @@
*/
@SuppressWarnings(
"java:S2187") // sonar is not able to detect that BaseCaseStyleTest interface generates test
-class PascalCaseStyleTest implements BaseCaseStyleTest {
+class PascalCaseStyleTest implements CaseStyleTestKit {
@Override
public PascalCaseStyle createCaseStyle() {
@@ -43,11 +43,19 @@ public PascalCaseStyle createCaseStyle() {
@Override
public List nonMatchingInputs() {
- return of("some_snake_case", "someCamelCase", "some-kebab-case");
+ return of(
+ "some_snake_case",
+ "someCamelCase",
+ "some-kebab-case",
+ "S ome space",
+ "InvalidPascalCase_",
+ "Invalid-PascalCase",
+ "123Pascal");
}
@Override
public List matchingInputs() {
- return of("SomePascalCase", "Pascal", "PascalIsAName");
+ return of(
+ "SomePascalCase", "Pascal", "PascalIsAName", "PascalCaseMQTT", "PascalCaseUsing123Digits");
}
}
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/ScreamingSnakeCaseStyleTest.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/ScreamingSnakeCaseStyleTest.java
similarity index 90%
rename from stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/ScreamingSnakeCaseStyleTest.java
rename to stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/ScreamingSnakeCaseStyleTest.java
index 1a2953a..49e4d68 100644
--- a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/ScreamingSnakeCaseStyleTest.java
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/ScreamingSnakeCaseStyleTest.java
@@ -20,11 +20,11 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
import static java.util.List.of;
-import dev.cookiecode.stylesniffer.testkit.BaseCaseStyleTest;
+import dev.cookiecode.stylesniffer.testkit.CaseStyleTestKit;
import java.util.List;
/**
@@ -34,7 +34,7 @@
*/
@SuppressWarnings(
"java:S2187") // sonar is not able to detect that BaseCaseStyleTest interface generates test
-class ScreamingSnakeCaseStyleTest implements BaseCaseStyleTest {
+class ScreamingSnakeCaseStyleTest implements CaseStyleTestKit {
@Override
public ScreamingSnakeCaseStyle createCaseStyle() {
diff --git a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/SnakeCaseStyleTest.java b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/SnakeCaseStyleTest.java
similarity index 90%
rename from stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/SnakeCaseStyleTest.java
rename to stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/SnakeCaseStyleTest.java
index cc34351..d1a9411 100644
--- a/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/SnakeCaseStyleTest.java
+++ b/stylesniffer-impl/src/test/java/dev/cookiecode/stylesniffer/impl/casestyle/SnakeCaseStyleTest.java
@@ -20,9 +20,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-package dev.cookiecode.stylesniffer.impl;
+package dev.cookiecode.stylesniffer.impl.casestyle;
-import dev.cookiecode.stylesniffer.testkit.BaseCaseStyleTest;
+import dev.cookiecode.stylesniffer.testkit.CaseStyleTestKit;
import java.util.List;
/**
@@ -32,7 +32,7 @@
*/
@SuppressWarnings(
"java:S2187") // sonar is not able to detect that BaseCaseStyleTest interface generates test
-class SnakeCaseStyleTest implements BaseCaseStyleTest {
+class SnakeCaseStyleTest implements CaseStyleTestKit {
@Override
public SnakeCaseStyle createCaseStyle() {
diff --git a/stylesniffer-report-aggregate/pom.xml b/stylesniffer-report-aggregate/pom.xml
new file mode 100644
index 0000000..2d33cf8
--- /dev/null
+++ b/stylesniffer-report-aggregate/pom.xml
@@ -0,0 +1,86 @@
+
+
+
+ 4.0.0
+
+ stylesniffer-report-aggregate
+ 1.0.0-SNAPSHOT
+ pom
+ Aggregate Jacoco Coverage Report
+
+
+ dev.cookiecode
+ stylesniffer-parent
+ 1.0.0-SNAPSHOT
+
+
+
+ ${basedir}/..
+
+
+
+
+
+ dev.cookiecode
+ stylesniffer-annotation-processor
+ 1.0.0-SNAPSHOT
+
+
+ dev.cookiecode
+ stylesniffer-api
+ 1.0.0-SNAPSHOT
+
+
+ dev.cookiecode
+ stylesniffer-impl
+ 1.0.0-SNAPSHOT
+
+
+ dev.cookiecode
+ stylesniffer-testkit
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ report-aggregate
+ verify
+
+ report-aggregate
+
+
+
+
+
+
+
diff --git a/stylesniffer-testkit/pom.xml b/stylesniffer-testkit/pom.xml
index 64a467a..cbd294a 100644
--- a/stylesniffer-testkit/pom.xml
+++ b/stylesniffer-testkit/pom.xml
@@ -30,6 +30,7 @@
4.0.0
stylesniffer-testkit
+ 1.0.0-SNAPSHOT
jar
@@ -39,11 +40,7 @@
- ${java.sdk.version}
- ${java.sdk.version}
- ${source.encoding}
${basedir}/..
-
${project.sonar.root.projectKey}-${project.groupId}-${project.artifactId}
@@ -53,6 +50,10 @@
stylesniffer-api
1.0.0-SNAPSHOT
+
+ org.projectlombok
+ lombok
+
org.assertj
assertj-core
diff --git a/stylesniffer-testkit/src/main/java/dev/cookiecode/stylesniffer/testkit/BaseCaseStyleTest.java b/stylesniffer-testkit/src/main/java/dev/cookiecode/stylesniffer/testkit/CaseStyleTestKit.java
similarity index 97%
rename from stylesniffer-testkit/src/main/java/dev/cookiecode/stylesniffer/testkit/BaseCaseStyleTest.java
rename to stylesniffer-testkit/src/main/java/dev/cookiecode/stylesniffer/testkit/CaseStyleTestKit.java
index c87e2d8..efe3f04 100644
--- a/stylesniffer-testkit/src/main/java/dev/cookiecode/stylesniffer/testkit/BaseCaseStyleTest.java
+++ b/stylesniffer-testkit/src/main/java/dev/cookiecode/stylesniffer/testkit/CaseStyleTestKit.java
@@ -71,7 +71,7 @@
* Usage Example
*
* {@code
- * public class CamelCaseStyleTest implements BaseCaseStyleTest {
+ * public class CamelCaseStyleTest implements CaseStyleTestKit {
*
* @Override
* public CamelCaseStyle createCaseStyle() {
@@ -99,7 +99,7 @@
*/
@TestInstance(PER_CLASS)
@SuppressWarnings("unused") // Used by implementers in other Maven modules
-public interface BaseCaseStyleTest {
+public interface CaseStyleTestKit {
T createCaseStyle();
diff --git a/stylesniffer-testkit/src/test/java/.gitkeep b/stylesniffer-testkit/src/test/java/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/stylesniffer-testkit/src/test/java/dev/cookiecode/stylesniffer/testkit/CaseStyleTestKitTest.java b/stylesniffer-testkit/src/test/java/dev/cookiecode/stylesniffer/testkit/CaseStyleTestKitTest.java
new file mode 100644
index 0000000..edb0a3c
--- /dev/null
+++ b/stylesniffer-testkit/src/test/java/dev/cookiecode/stylesniffer/testkit/CaseStyleTestKitTest.java
@@ -0,0 +1,48 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.testkit;
+
+import java.util.List;
+
+/**
+ * Test class
+ *
+ * @author Sebastien Vermeille
+ */
+public class CaseStyleTestKitTest implements CaseStyleTestKit {
+
+ @Override
+ public DummyCaseStyle createCaseStyle() {
+ return new DummyCaseStyle();
+ }
+
+ @Override
+ public List matchingInputs() {
+ return List.of("dummy42", "dummy", "dummySomething", "dummy-some-thing");
+ }
+
+ @Override
+ public List nonMatchingInputs() {
+ return List.of("something", "not-duMmy");
+ }
+}
diff --git a/stylesniffer-testkit/src/test/java/dev/cookiecode/stylesniffer/testkit/DummyCaseStyle.java b/stylesniffer-testkit/src/test/java/dev/cookiecode/stylesniffer/testkit/DummyCaseStyle.java
new file mode 100644
index 0000000..bf7859b
--- /dev/null
+++ b/stylesniffer-testkit/src/test/java/dev/cookiecode/stylesniffer/testkit/DummyCaseStyle.java
@@ -0,0 +1,43 @@
+/*
+ * The MIT License
+ * Copyright © 2024 Sebastien Vermeille
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package dev.cookiecode.stylesniffer.testkit;
+
+import dev.cookiecode.stylesniffer.api.BaseCaseStyle;
+import lombok.NonNull;
+
+/**
+ * Test class A dummy implementation of CaseStyle used for testing purpose
+ *
+ * @author Sebastien Vermeille
+ */
+public class DummyCaseStyle extends BaseCaseStyle {
+ @Override
+ public boolean matches(@NonNull String name) {
+ return name.contains("dummy");
+ }
+
+ @Override
+ public String getName() {
+ return "dummyStyle";
+ }
+}