Skip to content

Commit f739b92

Browse files
committed
Update to 0.17.0, new way to define elements
* @Datapath is no longer necessary and has been removed * Ethylene compile dependency is now `latest` (0.24.0) * @child annotation is now a path pointing to the child pointer (path), list, or node * When constructing lists of children, it is possible to mix paths and nodes, and chain references indefinitely * Passing a list in config when the element accepts a non-list child is possible iff the list is non-empty (the first element is used) * The @dataobject does not specify paths (or exist at all) if children are used * @default annotations can be placed on the element or data class to specify default elements * Additional methods added to ElementPath, in line with bringing the functionality closer to that of Java's Path * Improvements to error messages: element class information is printed when applicable, element path likewise
1 parent e37ccea commit f739b92

31 files changed

+1320
-2148
lines changed

autodoc/src/main/kotlin/com/github/steanky/element/gradle/plugin/autodoc/AutodocTask.kt

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.github.steanky.element.gradle.plugin.autodoc
22

33
import com.github.steanky.element.core.ElementFactory
44
import com.github.steanky.element.core.annotation.Child
5-
import com.github.steanky.element.core.annotation.ChildPath
65
import com.github.steanky.element.core.annotation.DataObject
76
import com.github.steanky.element.core.annotation.FactoryMethod
87
import com.github.steanky.element.core.annotation.document.Description
@@ -13,20 +12,19 @@ import com.github.steanky.element.core.key.Constants
1312
import kotlinx.serialization.json.Json
1413
import kotlinx.serialization.json.encodeToJsonElement
1514
import org.gradle.api.logging.Logger
16-
import org.gradle.api.tasks.*
15+
import org.gradle.api.tasks.Input
16+
import org.gradle.api.tasks.OutputFile
17+
import org.gradle.api.tasks.SourceTask
18+
import org.gradle.api.tasks.TaskAction
1719
import java.io.File
18-
19-
import java.lang.RuntimeException
2020
import java.util.regex.Pattern
2121
import javax.annotation.processing.*
2222
import javax.lang.model.SourceVersion
23-
import javax.lang.model.element.*
24-
import javax.lang.model.type.ArrayType
25-
import javax.lang.model.type.DeclaredType
26-
import javax.lang.model.type.TypeKind
27-
import javax.lang.model.type.TypeMirror
28-
import javax.lang.model.type.TypeVariable
29-
import javax.lang.model.type.WildcardType
23+
import javax.lang.model.element.ElementKind
24+
import javax.lang.model.element.ExecutableElement
25+
import javax.lang.model.element.TypeElement
26+
import javax.lang.model.element.VariableElement
27+
import javax.lang.model.type.*
3028
import javax.lang.model.util.Types
3129
import javax.tools.JavaFileObject
3230
import javax.tools.ToolProvider
@@ -339,7 +337,7 @@ abstract class AutodocTask : SourceTask() {
339337
}
340338

341339
private fun processParameters(typeElement: TypeElement): List<Parameter> {
342-
typeElement.dataType()?.let { (dataType, mappings) ->
340+
typeElement.dataType()?.let { (dataType, _) ->
343341
val parameters = dataType
344342
.getAnnotationsByType(com.github.steanky.element.core.annotation.document.Parameter::class.java)
345343
if (!parameters.isNullOrEmpty()) {
@@ -352,7 +350,7 @@ abstract class AutodocTask : SourceTask() {
352350
return dataType.recordComponents.map { component ->
353351
val accessor = component.accessor
354352

355-
val type = accessor.type() ?: tryLink(accessor, mappings) ?: simpleTypeName(component.asType())
353+
val type = accessor.type() ?: simpleTypeName(component.asType())
356354
val name = accessor.name()
357355
val behavior = accessor.description()
358356

@@ -366,23 +364,6 @@ abstract class AutodocTask : SourceTask() {
366364
return listOf()
367365
}
368366

369-
private fun tryLink(component: ExecutableElement, map: Map<String, VariableElement>): String? {
370-
component.getAnnotation(ChildPath::class.java)?.let { childPath ->
371-
map[childPath.value]?.let { variableElement ->
372-
processingEnv.typeUtils.asElement(variableElement.asType()).asTypeElement()?.model()?.let { (model, _) ->
373-
return model.value
374-
}
375-
376-
return null
377-
}
378-
379-
logger.error("No child dependency named ${childPath.value}")
380-
return null
381-
}
382-
383-
return null
384-
}
385-
386367
private fun simpleTypeName(componentType: TypeMirror): String {
387368
val typeUtils = processingEnv.typeUtils
388369

@@ -477,8 +458,8 @@ abstract class AutodocTask : SourceTask() {
477458
typeUtils.directSupertypes(component).forEach {superMirror ->
478459
superMirror as DeclaredType
479460

480-
val mirorErasure = typeUtils.erasure(superMirror)
481-
if (typeUtils.isSameType(mapType, mirorErasure)) {
461+
val mirrorErasure = typeUtils.erasure(superMirror)
462+
if (typeUtils.isSameType(mapType, mirrorErasure)) {
482463
val typeArguments = superMirror.typeArguments
483464
return "map of ${simpleTypeName(typeArguments[0])} -> ${simpleTypeName(typeArguments[1])}"
484465
}

buildSrc/src/main/kotlin/element.java-conventions.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugins {
33
}
44

55
group = "com.github.steanky"
6-
version = "0.16.0"
6+
version = "0.17.0"
77

88
java {
99
toolchain.languageVersion.set(JavaLanguageVersion.of(17))

core/src/main/java/com/github/steanky/element/core/BasicElementInspector.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ public BasicElementInspector(final @NotNull FactoryResolver factoryResolver,
3737
public @NotNull Information inspect(final @NotNull Class<?> elementClass) {
3838
final int modifiers = elementClass.getModifiers();
3939
if (!Modifier.isStatic(elementClass.getModifiers()) && elementClass.getDeclaringClass() != null) {
40-
throw elementException(elementClass, "non-static with declaring class");
40+
throw elementException(elementClass, "Non-static nested class");
4141
}
4242

4343
if (!Modifier.isPublic(modifiers)) {
44-
throw elementException(elementClass, "not public");
44+
throw elementException(elementClass, "Not public");
4545
}
4646

4747
final Mutable<ConfigProcessor<?>> mutable = new MutableObject<>(

core/src/main/java/com/github/steanky/element/core/BasicElementTypeIdentifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,6 @@ public BasicElementTypeIdentifier(final @NotNull KeyParser keyParser) {
3434
return keyParser.parseKey(value);
3535
}
3636

37-
throw elementException(elementType, "no ElementModel annotation");
37+
throw elementException(elementType, "No @Model annotation");
3838
}
3939
}

core/src/main/java/com/github/steanky/element/core/ElementException.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,22 @@
11
package com.github.steanky.element.core;
22

3+
import com.github.steanky.element.core.path.ElementPath;
4+
35
/**
4-
* Represents a generic exception thrown by various parts of the Element API.
6+
* Represents a generic exception thrown by various parts of the Element API. Can have an associated {@link ElementPath}
7+
* and {@link Class} which, respectively, define a path and/or class related to this error.
58
*/
69
public class ElementException extends RuntimeException {
10+
/**
11+
* The element class associated with this error.
12+
*/
13+
private Class<?> elementClass;
14+
15+
/**
16+
* The element path associated with this error.
17+
*/
18+
private ElementPath elementPath;
19+
720
/**
821
* Creates a new ElementException with no detail message or cause.
922
*/
@@ -38,4 +51,64 @@ public ElementException(String message, Throwable cause) {
3851
public ElementException(Throwable cause) {
3952
super(cause);
4053
}
54+
55+
/**
56+
* Sets the element class associated with this error. This method does nothing if an element class is already set.
57+
*
58+
* @param elementClass the element class
59+
*/
60+
public void setElementClass(Class<?> elementClass) {
61+
if (this.elementClass == null) {
62+
this.elementClass = elementClass;
63+
}
64+
}
65+
66+
/**
67+
* Sets the {@link ElementPath} associated with this error. This method does nothing if an error path is already set.
68+
*
69+
* @param elementPath the path
70+
*/
71+
public void setElementPath(ElementPath elementPath) {
72+
if (this.elementPath == null) {
73+
this.elementPath = elementPath;
74+
}
75+
}
76+
77+
/**
78+
* The element class which had an error.
79+
*
80+
* @return the element class
81+
*/
82+
public Class<?> elementClass() {
83+
return elementClass;
84+
}
85+
86+
/**
87+
* The path of the element object which had an error.
88+
*
89+
* @return the error path
90+
*/
91+
public ElementPath errorPath() {
92+
return elementPath;
93+
}
94+
95+
@Override
96+
public String getMessage() {
97+
final String baseMessage = super.getMessage();
98+
final StringBuilder builder = new StringBuilder(baseMessage.length() + 100);
99+
final Class<?> elementClass = this.elementClass;
100+
final ElementPath elementPath = this.elementPath;
101+
102+
builder.append("\"").append(baseMessage).append("\"");
103+
if (elementClass != null) {
104+
builder.append(System.lineSeparator()).append("Relevant class: ").append(elementClass);
105+
}
106+
107+
if (elementPath != null) {
108+
builder.append(System.lineSeparator()).append("Data path: '").append(elementPath).append("'");
109+
}
110+
111+
112+
return builder.toString();
113+
}
41114
}

core/src/main/java/com/github/steanky/element/core/HashRegistry.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33
import net.kyori.adventure.key.Key;
44
import org.jetbrains.annotations.NotNull;
55

6-
import java.util.*;
6+
import java.util.Collection;
7+
import java.util.Map;
8+
import java.util.Objects;
9+
10+
import static com.github.steanky.element.core.util.Validate.elementException;
711

812
/**
913
* A {@link Map}-based Registry implementation. Uses a backing immutable map combined with copy-on-write semantics to
@@ -75,7 +79,7 @@ public void registerBulk(final @NotNull Collection<? extends Map.Entry<? extends
7579
Objects.requireNonNull(key);
7680
final TRegistrant registrant = map.get(key);
7781
if (registrant == null) {
78-
throw new NoSuchElementException("no registrant under key " + key);
82+
throw elementException("No registrant under key " + key);
7983
}
8084

8185
return registrant;

core/src/main/java/com/github/steanky/element/core/annotation/Child.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@
22

33
import com.github.steanky.element.core.context.ElementContext;
44
import com.github.steanky.element.core.dependency.DependencyProvider;
5-
import com.github.steanky.element.core.key.Constants;
6-
import com.github.steanky.element.core.key.KeyString;
5+
import com.github.steanky.element.core.path.ElementPath;
76
import org.jetbrains.annotations.NotNull;
87

98
import java.lang.annotation.*;
@@ -17,10 +16,10 @@
1716
@Target(ElementType.PARAMETER)
1817
public @interface Child {
1918
/**
20-
* The identifier of this child dependency.
19+
* The path of the child dependency, relative to the configuration object on which it is defined. Syntax is
20+
* interpreted as if by calling {@link ElementPath#of(String)}.
2121
*
22-
* @return the identifier of this child dependency, or {@link Constants#DEFAULT} to indicate that the value of its
23-
* Model annotation should be used instead
22+
* @return the value of this annotation
2423
*/
25-
@NotNull @KeyString String value() default Constants.DEFAULT;
24+
@NotNull String value();
2625
}

core/src/main/java/com/github/steanky/element/core/annotation/ChildPath.java

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)