Skip to content

Commit 2e06074

Browse files
authored
Feat/optimize (#18)
* fix: generic value with lists * feat: improve tests * feat: add LocalDate * feat: finish test optimization * feat: version
1 parent 825678a commit 2e06074

24 files changed

Lines changed: 3087 additions & 2284 deletions

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=1.4.0
1+
version=1.5.0

src/main/java/fr/traqueur/structura/StructuraProcessor.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
public class StructuraProcessor {
2424

2525
private final boolean validateOnParse;
26-
private final Yaml yaml;
26+
private Yaml yaml; // Not final for lazy initialization
2727

2828
private final RecordInstanceFactory recordFactory;
2929
private final FieldMapper fieldMapper;
@@ -35,8 +35,8 @@ public class StructuraProcessor {
3535
* @param validateOnParse if true, validates instances after parsing
3636
*/
3737
public StructuraProcessor(boolean validateOnParse) {
38-
this.yaml = new Yaml();
3938
this.validateOnParse = validateOnParse;
39+
// yaml will be initialized on first use (lazy initialization)
4040

4141
this.fieldMapper = new FieldMapper();
4242
this.recordFactory = new RecordInstanceFactory(fieldMapper);
@@ -45,6 +45,19 @@ public StructuraProcessor(boolean validateOnParse) {
4545
recordFactory.setValueConverter(valueConverter);
4646
}
4747

48+
/**
49+
* Gets the Yaml instance, initializing it lazily on first access.
50+
* This improves performance by only creating the Yaml parser when needed.
51+
*
52+
* @return the Yaml instance
53+
*/
54+
private Yaml getYaml() {
55+
if (yaml == null) {
56+
yaml = new Yaml();
57+
}
58+
return yaml;
59+
}
60+
4861
/**
4962
* Parses a YAML string to a record instance.
5063
*
@@ -58,7 +71,7 @@ public <T extends Loadable> T parse(String yamlString, Class<T> settingsClass) {
5871
validateInput(yamlString, settingsClass);
5972

6073
try {
61-
Map<String, Object> settings = yaml.load(yamlString);
74+
Map<String, Object> settings = getYaml().load(yamlString);
6275
if (settings == null) {
6376
settings = Map.of();
6477
}
@@ -90,7 +103,7 @@ public <E extends Enum<E> & Loadable> void parseEnum(String yamlString, Class<E>
90103
validateInput(yamlString, enumClass);
91104

92105
try {
93-
Map<String, Object> settings = yaml.load(yamlString);
106+
Map<String, Object> settings = getYaml().load(yamlString);
94107
if (settings == null) {
95108
throw new StructuraException("YAML content is empty or null for enum " + enumClass.getName());
96109
}

src/main/java/fr/traqueur/structura/api/Structura.java

Lines changed: 78 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,15 @@ public static StructuraBuilder builder() {
5757

5858
/**
5959
* Builder class for creating a {@link StructuraProcessor} with custom configurations.
60-
* This allows you to enable or disable validation during parsing.
60+
*
61+
* <p>Default settings:</p>
62+
* <ul>
63+
* <li>Validation is enabled during parsing</li>
64+
* </ul>
6165
*/
6266
public static class StructuraBuilder {
6367
private boolean validateOnParse = true;
6468

65-
/**
66-
* Constructs a new {@link StructuraBuilder} with default settings.
67-
* By default, validation is enabled during parsing.
68-
*/
69-
public StructuraBuilder() {
70-
// Default constructor
71-
}
72-
7369
/**
7470
* Sets whether to validate the configuration during parsing.
7571
*
@@ -101,6 +97,44 @@ public static void with(StructuraProcessor processor) {
10197
PROCESSOR = processor;
10298
}
10399

100+
/**
101+
* Reads content from various sources (Path, File, or resource).
102+
*
103+
* @param source the source to read from (Path, File, or String for resources)
104+
* @return the content as a String
105+
* @throws IOException if reading fails
106+
*/
107+
private static String readContent(Object source) throws IOException {
108+
return switch (source) {
109+
case Path path -> Files.readString(path);
110+
case File file -> Files.readString(file.toPath());
111+
case String resourcePath -> {
112+
try (var stream = Structura.class.getResourceAsStream(resourcePath)) {
113+
if (stream == null) {
114+
throw new StructuraException("Resource not found: " + resourcePath);
115+
}
116+
yield new String(stream.readAllBytes());
117+
}
118+
}
119+
default -> throw new IllegalArgumentException("Unsupported source type: " + source.getClass().getName());
120+
};
121+
}
122+
123+
/**
124+
* Gets a user-friendly name for a source object.
125+
*
126+
* @param source the source object
127+
* @return a descriptive name for error messages
128+
*/
129+
private static String getSourceName(Object source) {
130+
return switch (source) {
131+
case Path p -> p.toAbsolutePath().toString();
132+
case File f -> f.getAbsolutePath();
133+
case String s -> s;
134+
default -> "unknown";
135+
};
136+
}
137+
104138
/**
105139
* Parses a YAML content string into an enum of type E.
106140
*
@@ -122,13 +156,7 @@ public static <E extends Enum<E> & Loadable> void parseEnum(String yamlContent,
122156
* @throws StructuraException if there is an error during file reading or parsing
123157
*/
124158
public static <E extends Enum<E> & Loadable> void loadEnum(Path filePath, Class<E> enumClass) {
125-
126-
try {
127-
String content = Files.readString(filePath);
128-
parseEnum(content, enumClass);
129-
} catch (IOException e) {
130-
throw new StructuraException("Unable to read file: " + filePath.toAbsolutePath(), e);
131-
}
159+
loadEnumInternal(filePath, enumClass);
132160
}
133161

134162
/**
@@ -140,12 +168,7 @@ public static <E extends Enum<E> & Loadable> void loadEnum(Path filePath, Class<
140168
* @throws StructuraException if there is an error during file reading or parsing
141169
*/
142170
public static <E extends Enum<E> & Loadable> void loadEnum(File file, Class<E> enumClass) {
143-
try {
144-
String content = Files.readString(file.toPath());
145-
parseEnum(content, enumClass);
146-
} catch (IOException e) {
147-
throw new StructuraException("Unable to read file " + file.getAbsolutePath(), e);
148-
}
171+
loadEnumInternal(file, enumClass);
149172
}
150173

151174
/**
@@ -157,14 +180,23 @@ public static <E extends Enum<E> & Loadable> void loadEnum(File file, Class<E> e
157180
* @throws StructuraException if there is an error during resource reading or parsing
158181
*/
159182
public static <E extends Enum<E> & Loadable> void loadEnumFromResource(String resourcePath, Class<E> enumClass) {
160-
try (var stream = Structura.class.getResourceAsStream(resourcePath)) {
161-
if (stream == null) {
162-
throw new StructuraException("Resource not found: " + resourcePath);
163-
}
164-
String content = new String(stream.readAllBytes());
183+
loadEnumInternal(resourcePath, enumClass);
184+
}
185+
186+
/**
187+
* Internal implementation for loading enums from various sources.
188+
*
189+
* @param source the source to load from (Path, File, or String for resources)
190+
* @param enumClass the class of the enum to load
191+
* @param <E> the type of the enum, which must implement {@link Loadable}
192+
* @throws StructuraException if there is an error during reading or parsing
193+
*/
194+
private static <E extends Enum<E> & Loadable> void loadEnumInternal(Object source, Class<E> enumClass) {
195+
try {
196+
String content = readContent(source);
165197
parseEnum(content, enumClass);
166198
} catch (IOException e) {
167-
throw new StructuraException("Unable to read ressource " + resourcePath, e);
199+
throw new StructuraException("Unable to read file: " + getSourceName(source), e);
168200
}
169201
}
170202

@@ -191,12 +223,7 @@ public static <T extends Loadable> T parse(String yamlContent, Class<T> configCl
191223
* @throws StructuraException if there is an error during file reading or parsing
192224
*/
193225
public static <T extends Loadable> T load(Path filePath, Class<T> configClass) {
194-
try {
195-
String content = Files.readString(filePath);
196-
return parse(content, configClass);
197-
} catch (IOException e) {
198-
throw new StructuraException("Unable to read file: " + filePath.toAbsolutePath(), e);
199-
}
226+
return loadInternal(filePath, configClass);
200227
}
201228

202229
/**
@@ -209,12 +236,7 @@ public static <T extends Loadable> T load(Path filePath, Class<T> configClass) {
209236
* @throws StructuraException if there is an error during file reading or parsing
210237
*/
211238
public static <T extends Loadable> T load(File file, Class<T> configClass) {
212-
try {
213-
String content = Files.readString(file.toPath());
214-
return parse(content, configClass);
215-
} catch (IOException e) {
216-
throw new StructuraException("Unable to read file: " + file.getAbsolutePath(), e);
217-
}
239+
return loadInternal(file, configClass);
218240
}
219241

220242
/**
@@ -227,14 +249,24 @@ public static <T extends Loadable> T load(File file, Class<T> configClass) {
227249
* @throws StructuraException if there is an error during resource reading or parsing
228250
*/
229251
public static <T extends Loadable> T loadFromResource(String resourcePath, Class<T> configClass) {
230-
try (var stream = Structura.class.getResourceAsStream(resourcePath)) {
231-
if (stream == null) {
232-
throw new StructuraException("Resource not found: " + resourcePath);
233-
}
234-
String content = new String(stream.readAllBytes());
252+
return loadInternal(resourcePath, configClass);
253+
}
254+
255+
/**
256+
* Internal implementation for loading configurations from various sources.
257+
*
258+
* @param source the source to load from (Path, File, or String for resources)
259+
* @param configClass the class of the configuration to load
260+
* @param <T> the type of the configuration, which must implement {@link Loadable}
261+
* @return an instance of T populated with the parsed data
262+
* @throws StructuraException if there is an error during reading or parsing
263+
*/
264+
private static <T extends Loadable> T loadInternal(Object source, Class<T> configClass) {
265+
try {
266+
String content = readContent(source);
235267
return parse(content, configClass);
236268
} catch (IOException e) {
237-
throw new StructuraException("Unable to read ressource " + resourcePath, e);
269+
throw new StructuraException("Unable to read file: " + getSourceName(source), e);
238270
}
239271
}
240272
}

0 commit comments

Comments
 (0)