Skip to content

Commit 6c6d41d

Browse files
committed
[SdkCodelistRepository]: Use codelists.json to discover codelist files. Load contents of the codelist files on demand.
1 parent 1f7e3a2 commit 6c6d41d

8 files changed

Lines changed: 670 additions & 101 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package eu.europa.ted.eforms.sdk.domain.codelist;
2+
3+
import java.io.Serializable;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
6+
public class CodelistForIndex implements Serializable {
7+
private static final long serialVersionUID = 4221672498810948002L;
8+
9+
private String id;
10+
private String parentId;
11+
private String filename;
12+
private String description;
13+
14+
@JsonProperty("_label")
15+
private String labelId;
16+
17+
public String getId() {
18+
return id;
19+
}
20+
21+
public String getParentId() {
22+
return parentId;
23+
}
24+
25+
public String getFilename() {
26+
return filename;
27+
}
28+
29+
public String getDescription() {
30+
return description;
31+
}
32+
33+
@JsonProperty("_label")
34+
public String getLabel() {
35+
return labelId;
36+
}
37+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package eu.europa.ted.eforms.sdk.domain.codelist;
2+
3+
import java.io.Serializable;
4+
import java.util.List;
5+
6+
public class CodelistsIndex implements Serializable {
7+
private static final long serialVersionUID = -6549217565224309697L;
8+
9+
private List<CodelistForIndex> codelists;
10+
11+
public List<CodelistForIndex> getCodelists() {
12+
return codelists;
13+
}
14+
}

src/main/java/eu/europa/ted/eforms/sdk/repository/SdkCodelistRepository.java

Lines changed: 50 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
import java.util.Objects;
1313
import java.util.Optional;
1414
import java.util.stream.Collectors;
15-
import java.util.stream.Stream;
16-
import org.apache.commons.io.FilenameUtils;
1715
import org.apache.commons.lang3.StringUtils;
1816
import org.apache.commons.lang3.Validate;
1917
import org.apache.commons.lang3.builder.EqualsBuilder;
@@ -22,12 +20,16 @@
2220
import org.jooq.lambda.Unchecked;
2321
import org.slf4j.Logger;
2422
import org.slf4j.LoggerFactory;
23+
import com.fasterxml.jackson.databind.DeserializationFeature;
24+
import com.fasterxml.jackson.databind.ObjectMapper;
2525
import com.helger.genericode.v10.CodeListDocument;
2626
import com.helger.genericode.v10.Identification;
2727
import com.helger.genericode.v10.LongName;
2828
import com.helger.genericode.v10.Row;
2929
import com.helger.genericode.v10.SimpleCodeList;
3030
import com.helger.genericode.v10.Value;
31+
import eu.europa.ted.eforms.sdk.domain.codelist.CodelistForIndex;
32+
import eu.europa.ted.eforms.sdk.domain.codelist.CodelistsIndex;
3133
import eu.europa.ted.eforms.sdk.entity.SdkCodelist;
3234
import eu.europa.ted.eforms.sdk.entity.SdkEntityFactory;
3335
import eu.europa.ted.util.GenericodeTools;
@@ -40,7 +42,7 @@ public class SdkCodelistRepository extends HashMap<String, SdkCodelist> {
4042
private transient Path codelistsDir;
4143
private String sdkVersion;
4244

43-
private final Map<String, CodeListDocument> codelistContentsByCodelistId;
45+
private final Map<String, Pair<Path, CodeListDocument>> codelistInfoByCodelistIds;
4446

4547
@SuppressWarnings("unused")
4648
private SdkCodelistRepository() {
@@ -55,7 +57,7 @@ public SdkCodelistRepository(final String sdkVersion, final Path codelistsDir) {
5557
"Codelists directory [%s] is not found or not a directory", codelistsDir);
5658

5759
try {
58-
this.codelistContentsByCodelistId = getCodelistContentsByCodelistIds(codelistsDir);
60+
this.codelistInfoByCodelistIds = getCodelistInfoByCodelistIds(codelistsDir);
5961
} catch (IOException e) {
6062
throw new RuntimeException(
6163
MessageFormat.format("Failed to load codelists from [{0}]", codelistsDir), e);
@@ -86,13 +88,15 @@ public SdkCodelist getOrDefault(final Object codelistId, final SdkCodelist defau
8688

8789
private Optional<SdkCodelist> loadSdkCodelist(final String codeListId)
8890
throws InstantiationException {
89-
logger.debug("Loading SDK codelist with ID [{}] for SDK version [{}] from path [{}]",
90-
codeListId, sdkVersion, codelistsDir);
91+
logger.debug("Loading SDK codelist with ID [{}] for SDK version [{}]", codeListId, sdkVersion);
9192

9293
// Find the SDK codelist .gc file that corresponds to the passed reference.
9394
// Stream the data from that file.
9495
final Optional<CodeListDocument> codelist =
95-
Optional.ofNullable(codelistContentsByCodelistId.get(codeListId));
96+
Optional.ofNullable(codelistInfoByCodelistIds.get(codeListId))
97+
.map(Unchecked.function((Pair<Path, CodeListDocument> codelistInfo) -> Optional
98+
.ofNullable(codelistInfo.getValue())
99+
.orElse(getCodelistContents(codelistInfo.getKey()))));
96100

97101
if (codelist.isEmpty()) {
98102
return Optional.empty();
@@ -127,8 +131,8 @@ private Optional<SdkCodelist> loadSdkCodelist(final String codeListId)
127131
final Optional<SdkCodelist> result = Optional.of(SdkEntityFactory.getSdkCodelist(sdkVersion,
128132
codeListId, codelistVersion.orElse(null), codes, parentId));
129133

130-
logger.debug("Finished loading SDK codelist with ID [{}] for SDK version [{}] from path [{}]",
131-
codeListId, sdkVersion, codelistsDir);
134+
logger.debug("Finished loading SDK codelist with ID [{}] for SDK version [{}]", codeListId,
135+
sdkVersion);
132136

133137
return result;
134138
}
@@ -158,69 +162,67 @@ public static Optional<String> extractLongNameWithIdentifier(
158162
}
159163

160164
/**
161-
* Loads all of the codelists by reading codelist files under a given a folder.
165+
* Loads the paths of all of the codelists by looking for and reading the codelists index.
166+
* <p>
167+
* The result is a map which associates information (file path and a placeholder for contents) for
168+
* each codelist with its ID.
162169
*
163-
* @param codelistsDir The folder containing all codelist files
164-
* @return A map of codelist IDs to codelist contents
170+
* @param codelistsDir The folder containing the codelists index and files
171+
* @return A map of codelist IDs to pairs of codelist file paths and contents
165172
* @throws IOException If there are failures when discovering and parsing the files
166173
*/
167-
private Map<String, CodeListDocument> getCodelistContentsByCodelistIds(final Path codelistsDir)
168-
throws IOException {
174+
private Map<String, Pair<Path, CodeListDocument>> getCodelistInfoByCodelistIds(
175+
final Path codelistsDir) throws IOException {
169176
Validate.notNull(codelistsDir, "Undefined codelists directory");
170177
Validate.isTrue(Files.isDirectory(codelistsDir),
171178
MessageFormat.format("Not a directory: {0}", codelistsDir));
172179

173-
logger.debug("Getting codelist file paths from directory [{}]", codelistsDir);
180+
final Path indexFile = Path.of(codelistsDir.toString(), "codelists.json");
174181

175-
final int depth = 1; // Flat folder, not recursive for now.
182+
logger.debug("Loading codelists index from [{}]", indexFile);
183+
CodelistsIndex codelistsIndex =
184+
createObjectMapper().readValue(indexFile.toFile(), CodelistsIndex.class);
185+
186+
final Map<String, Pair<Path, CodeListDocument>> result = new HashMap<>();
187+
codelistsIndex.getCodelists().stream().forEach((CodelistForIndex codelist) -> {
188+
final String codelistId = codelist.getId();
189+
final Path codelistPath = Path.of(codelistsDir.toString(), codelist.getFilename());
190+
191+
logger.trace("Adding path [{}] for codelist [{}]", codelistPath, codelistId);
192+
if (!Files.isRegularFile(codelistPath)) {
193+
logger.warn("Codelist file [{}] not found. Codelist [{}] will be skipped", codelistPath,
194+
codelistId);
195+
} else {
196+
// We're only interested in populating the codelist filepaths for now.
197+
// The contents of each document will be populated for each codelist later, on demand.
198+
result.put(codelistId, Pair.of(codelistPath, null));
199+
}
200+
});
201+
202+
return result;
176203

177-
try (Stream<Path> walk = Files.walk(codelistsDir, depth)) {
178-
return walk
179-
.filter(this::isGenericodeFile)
180-
.map(Unchecked.function(this::getCodelistIdAndContents))
181-
.filter(Objects::nonNull)
182-
.collect(Collectors.toMap(Pair::getKey, Pair::getValue));
183-
}
184204
}
185205

186206
/**
187-
* Gets the codelist ID from a codelist file.
207+
* Gets the codelist contents from a codelist file.
188208
*
189209
* @param codelistPath The codelist file's path
190-
* @return The codelist ID and the codelist file's contents as a key/value pair
210+
* @return The codelist file's contents
191211
* @throws FileNotFoundException If the codelist file's path is undefined or not an existing file
192212
*/
193-
private Pair<String, CodeListDocument> getCodelistIdAndContents(Path codelistPath)
194-
throws FileNotFoundException {
213+
private CodeListDocument getCodelistContents(Path codelistPath) throws FileNotFoundException {
195214
Validate.notNull(codelistPath, "Undefined codelist path");
196215

197216
if (!Files.isRegularFile(codelistPath)) {
198217
throw new FileNotFoundException(codelistPath.toString());
199218
}
200219

201-
final Optional<CodeListDocument> codelist =
202-
Optional.ofNullable(GenericodeTools.getMarshaller().read(codelistPath));
203-
204-
if (codelist.isPresent()) {
205-
// We use the longName as a ID, PK in the the DB.
206-
// But for the filenames we do not always follow this convention.
207-
// So we need to map.
208-
return codelist
209-
.map(CodeListDocument::getIdentification)
210-
.map((Identification identification) -> identification.getLongNameAtIndex(0))
211-
.map(LongName::getValue)
212-
.map((String longName) -> Pair.of(longName, codelist.get()))
213-
.orElse(null);
214-
}
215-
216-
return null;
220+
logger.debug("Reading from file [{}]", codelistPath);
221+
return Optional.ofNullable(GenericodeTools.getMarshaller().read(codelistPath)).orElse(null);
217222
}
218223

219-
private boolean isGenericodeFile(final Path path) {
220-
return path != null
221-
&& Files.isRegularFile(path)
222-
&& GenericodeTools.EXTENSION_DOT_GC
223-
.equals(MessageFormat.format(".{0}", FilenameUtils.getExtension(path.toString())));
224+
private ObjectMapper createObjectMapper() {
225+
return new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
224226
}
225227

226228
@Override

src/test/java/eu/europa/ted/eforms/sdk/repository/SdkCodelistRepositoryTest.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,10 @@
1111
import eu.europa.ted.eforms.sdk.entity.SdkCodelist;
1212

1313
class SdkCodelistRepositoryTest {
14-
private SdkCodelistRepository repository;
1514
private Identification identification;
1615

1716
@BeforeEach
1817
public void setUp() {
19-
repository =
20-
new SdkCodelistRepository("999.0", Path.of("src", "test", "resources", "codelists", "/"));
21-
2218
identification = new Identification();
2319

2420
final LongName longName1 = new LongName("test-codelist-parentId");
@@ -31,20 +27,32 @@ public void setUp() {
3127

3228
@Test
3329
void testGetObject() {
34-
Optional<SdkCodelist> codelist = Optional.ofNullable(repository.get("test-codelist"));
30+
final SdkCodelistRepository repository =
31+
new SdkCodelistRepository("999.0", Path.of("src", "test", "resources", "codelists", "/"));
32+
33+
Optional<SdkCodelist> codelist = Optional.ofNullable(repository.get("accessibility"));
3534

36-
assertEquals("test-codelist", codelist.map(SdkCodelist::getCodelistId).orElse(null));
37-
assertEquals(Arrays.asList("inc"), codelist.map(SdkCodelist::getCodes).orElse(null));
35+
assertEquals("accessibility", codelist.map(SdkCodelist::getCodelistId).orElse(null));
36+
assertEquals(Arrays.asList("inc", "n-inc", "n-inc-just"),
37+
codelist.map(SdkCodelist::getCodes).orElse(null));
38+
39+
codelist = Optional.ofNullable(repository.get("criterion"));
40+
assertEquals("criterion", codelist.map(SdkCodelist::getCodelistId).orElse(null));
41+
assertEquals(Arrays.asList("autorisation", "aver-year-to", "bankr-nat", "bankruptcy"),
42+
codelist.map(SdkCodelist::getCodes).orElse(null));
3843
}
3944

4045
@Test
4146
void testGetOrDefaultObjectSdkCodelist() {
42-
SdkCodelist defaultCodelist =
47+
final SdkCodelistRepository repository =
48+
new SdkCodelistRepository("999.0", Path.of("src", "test", "resources", "codelists", "/"));
49+
50+
final SdkCodelist defaultCodelist =
4351
new DummySdkCodelist("default-codelist", "1", Arrays.asList("code1", "code2"), null);
4452

4553
Optional<SdkCodelist> codelist =
46-
Optional.ofNullable(repository.getOrDefault("test-codelist", defaultCodelist));
47-
assertEquals("test-codelist", codelist.map(SdkCodelist::getCodelistId).orElse(null));
54+
Optional.ofNullable(repository.getOrDefault("accessibility", defaultCodelist));
55+
assertEquals("accessibility", codelist.map(SdkCodelist::getCodelistId).orElse(null));
4856

4957
codelist =
5058
Optional.ofNullable(repository.getOrDefault("nonexisting-codelist", defaultCodelist));

0 commit comments

Comments
 (0)