Skip to content

Commit 5847c6b

Browse files
dmealingclaude
andcommitted
feat(java): add MetaDataLoader.fromDirectory/fromUris/fromString factories
Unified cross-language factories matching TS/C#/Python. Each factory constructs a fresh loader via createManual(false, name), runs init() → load(sources) → register() in the canonical pipeline order, and wraps any failure as MetaDataLoadingException with phase context. fromResources(name, List<String>) also added for classpath-resource loading parity (replaces createFromResources). The deprecated createFromURIs / createFromResources aliases remain temporarily; they are removed in the subsequent FileMetaDataLoader retirement commit. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 7c833a5 commit 5847c6b

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

server/java/metadata/src/main/java/com/metaobjects/loader/MetaDataLoader.java

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@
2626
import java.io.InputStream;
2727
import java.net.URI;
2828
import java.nio.charset.StandardCharsets;
29+
import java.nio.file.Path;
2930
import java.util.ArrayList;
3031
import java.util.Arrays;
3132
import java.util.Collections;
3233
import java.util.List;
3334
import java.util.Map;
35+
import java.util.stream.Collectors;
3436
import java.util.concurrent.CompletableFuture;
3537
import java.util.concurrent.ConcurrentHashMap;
3638
import java.util.concurrent.ExecutionException;
@@ -214,6 +216,112 @@ public static MetaDataLoader createFromResources(String name, List<String> resou
214216
return createFromURIs(name, uris);
215217
}
216218

219+
///////////////////////////////////////////////////////////////////////
220+
// Unified static factories (cross-language consistent — see TS / C# / Python)
221+
222+
/**
223+
* Build a {@link DirectorySource} for the given path and load all files
224+
* in deterministic order. Convenience for the 99% case.
225+
*
226+
* @param name the loader name
227+
* @param directory the directory containing metadata files
228+
* @return a fully-initialized loader with all directory files loaded
229+
*/
230+
public static MetaDataLoader fromDirectory(String name, Path directory) {
231+
return fromDirectory(name, directory, new DirectorySource.Options());
232+
}
233+
234+
/**
235+
* Build a {@link DirectorySource} for the given path with the supplied
236+
* options and load all matching files.
237+
*
238+
* @param name the loader name
239+
* @param directory the directory containing metadata files
240+
* @param opts expansion options (exclude list, recursion)
241+
* @return a fully-initialized loader with all directory files loaded
242+
*/
243+
public static MetaDataLoader fromDirectory(String name, Path directory, DirectorySource.Options opts) {
244+
MetaDataLoader loader = createManual(false, name);
245+
try {
246+
loader.init();
247+
List<MetaDataSource> sources = new DirectorySource(directory, opts).expandToList();
248+
loader.load(sources);
249+
loader.register();
250+
} catch (MetaDataLoadingException e) {
251+
throw e;
252+
} catch (Exception e) {
253+
throw new MetaDataLoadingException(
254+
"Failed to load from directory " + directory, name,
255+
loader.getLoadingState().getCurrentPhase(), 0, e);
256+
}
257+
return loader;
258+
}
259+
260+
/**
261+
* Build {@link UriSource}s and load them. Replaces the deprecated
262+
* {@link #createFromURIs(String, List)} alias.
263+
*
264+
* @param name the loader name
265+
* @param uris model URIs to load
266+
* @return a fully-initialized loader with all URIs loaded
267+
*/
268+
public static MetaDataLoader fromUris(String name, List<URI> uris) {
269+
MetaDataLoader loader = createManual(false, name);
270+
try {
271+
loader.init();
272+
List<MetaDataSource> sources = uris.stream()
273+
.map(uri -> (MetaDataSource) new UriSource(uri))
274+
.collect(Collectors.toList());
275+
loader.load(sources);
276+
loader.register();
277+
} catch (MetaDataLoadingException e) {
278+
throw e;
279+
} catch (Exception e) {
280+
throw new MetaDataLoadingException(
281+
"Failed to load from URIs", name,
282+
loader.getLoadingState().getCurrentPhase(), 0, e);
283+
}
284+
return loader;
285+
}
286+
287+
/**
288+
* Load a list of classpath resource paths. Each path is wrapped as a
289+
* {@code model:resource:<path>} URI and routed through {@link #fromUris(String, List)}.
290+
*
291+
* @param name the loader name
292+
* @param resources classpath resource paths (no {@code model:} prefix needed)
293+
* @return a fully-initialized loader with all resources loaded
294+
*/
295+
public static MetaDataLoader fromResources(String name, List<String> resources) {
296+
List<URI> uris = new ArrayList<>();
297+
for (String r : resources) uris.add(URIHelper.toURI("model:resource:" + r));
298+
return fromUris(name, uris);
299+
}
300+
301+
/**
302+
* Load a single in-memory string of the given format.
303+
*
304+
* @param name the loader name
305+
* @param content the raw document content
306+
* @param format the document format
307+
* @return a fully-initialized loader with the inline content loaded
308+
*/
309+
public static MetaDataLoader fromString(String name, String content, MetaDataSource.MetaDataFormat format) {
310+
MetaDataLoader loader = createManual(false, name);
311+
try {
312+
loader.init();
313+
loader.load(List.of(new InMemoryStringSource(content, "<inline>", format)));
314+
loader.register();
315+
} catch (MetaDataLoadingException e) {
316+
throw e;
317+
} catch (Exception e) {
318+
throw new MetaDataLoadingException(
319+
"Failed to load from string", name,
320+
loader.getLoadingState().getCurrentPhase(), 0, e);
321+
}
322+
return loader;
323+
}
324+
217325
///////////////////////////////////////////////////////////////////////
218326
// Identity
219327

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.metaobjects.loader;
2+
3+
import com.metaobjects.registry.SharedRegistryTestBase;
4+
import org.junit.Test;
5+
import java.io.IOException;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import static org.junit.Assert.*;
9+
10+
/**
11+
* Tests for the unified static factories on {@link MetaDataLoader}:
12+
* {@link MetaDataLoader#fromDirectory(String, Path)},
13+
* {@link MetaDataLoader#fromUris(String, java.util.List)},
14+
* {@link MetaDataLoader#fromString(String, String, MetaDataSource.MetaDataFormat)}.
15+
*
16+
* <p>These factories supersede the deprecated {@code createFromURIs} /
17+
* {@code createFromResources} pair and match the cross-language convention
18+
* (TS/C#/Python all expose the same shape).</p>
19+
*/
20+
public class MetaDataLoaderFactoryTest extends SharedRegistryTestBase {
21+
22+
@Test public void fromDirectoryLoadsCanonicalJson() throws IOException {
23+
Path dir = Files.createTempDirectory("mdl-");
24+
Path file = dir.resolve("meta.tiny.json");
25+
Files.writeString(file, "{\"metadata.root\":{\"package\":\"x\",\"children\":[]}}");
26+
try {
27+
MetaDataLoader loader = MetaDataLoader.fromDirectory("factoryTestDir", dir);
28+
assertNotNull(loader.getRoot());
29+
} finally {
30+
Files.deleteIfExists(file);
31+
Files.deleteIfExists(dir);
32+
}
33+
}
34+
35+
@Test public void fromStringLoadsInlineJson() {
36+
MetaDataLoader loader = MetaDataLoader.fromString(
37+
"factoryTestString",
38+
"{\"metadata.root\":{\"package\":\"x\",\"children\":[]}}",
39+
MetaDataSource.MetaDataFormat.JSON);
40+
assertNotNull(loader.getRoot());
41+
}
42+
}

0 commit comments

Comments
 (0)