Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions src/main/java/blue/contract/packager/DependencyProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class DependencyProcessor {
private final DependencyGraph graph;
private final Map<String, BluePackage> processedPackages;
private final Map<String, Map<String, BluePackage>> processedPackages; // Map<TypeName, Map<Version, BluePackage>>
private final TopologicalSorter topologicalSorter;
private final NodePreprocessor nodePreprocessor;
private final BluePackageInitializer bluePackageInitializer;
Expand All @@ -30,45 +30,63 @@ public DependencyProcessor(DependencyGraph graph) {

public List<BluePackage> process() {
List<String> processingOrder = graph.getProcessingOrder();
for (String dirName : processingOrder) {
processDirectory(dirName);
for (String typeAndVersion : processingOrder) {
String[] parts = typeAndVersion.split("=");
processDirectory(parts[0], parts[1]);
}
return new ArrayList<>(processedPackages.values());
return processedPackages.values().stream()
.flatMap(versionMap -> versionMap.values().stream())
.toList();
}

private void processDirectory(String dirName) {
DirectoryNode dir = graph.getDirectories().get(dirName);
BluePackage bluePackage = bluePackageInitializer.initialize(dirName, dir.getDependency(), processedPackages);
processedPackages.put(dirName, bluePackage);
private void processDirectory(String typeName, String version) {
DirectoryNode dir = graph.getDirectories().get(typeName).get(version);
BluePackage bluePackage = bluePackageInitializer.initialize(
typeName,
version,
dir.getDependency(),
processedPackages
);
processedPackages
.computeIfAbsent(typeName, k -> new HashMap<>())
.put(version, bluePackage);

Map<String, Node> nodes = new HashMap<>(dir.getNodes());
Set<String> processed = new HashSet<>();
List<String> processingOrder;
try {
processingOrder = topologicalSorter.sort(nodes);
} catch (IllegalStateException e) {
throw new IllegalStateException("Cyclic dependency detected in directory '" + dirName + "': " + e.getMessage());
throw new IllegalStateException(
String.format("Cyclic dependency detected in directory '%s' version '%s': %s",
typeName, version, e.getMessage())
);
}

for (String nodeName : processingOrder) {
processNode(nodeName, nodes, processed, dirName);
processNode(nodeName, nodes, processed, typeName, version);
}
}

private void processNode(String nodeName, Map<String, Node> nodes, Set<String> processed, String dirName) {
private void processNode(String nodeName, Map<String, Node> nodes, Set<String> processed,
String typeName, String version) {
if (processed.contains(nodeName)) {
return;
}

Node node = nodes.values().stream().filter(n -> n.getName().equals(nodeName)).findFirst().orElseThrow();
Node preprocessedNode = nodePreprocessor.preprocess(node, dirName, processedPackages);
Node node = nodes.values().stream()
.filter(n -> n.getName().equals(nodeName))
.findFirst()
.orElseThrow();

Node preprocessedNode = nodePreprocessor.preprocess(node, typeName, version, processedPackages);

nodes.put(nodeName, preprocessedNode);

BluePackage bluePackage = processedPackages.get(dirName);
BluePackage bluePackage = processedPackages.get(typeName).get(version);
bluePackage.addPreprocessedNode(nodeName, preprocessedNode);

packageMappingsUpdater.update(dirName, nodeName, preprocessedNode, processedPackages);
packageMappingsUpdater.update(typeName, version, nodeName, preprocessedNode, processedPackages);
processed.add(nodeName);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package blue.contract.packager.graphbuilder;

import blue.contract.packager.model.DependencyGraph;
import blue.contract.packager.model.TypeDependency;
import blue.language.model.Node;

import java.io.IOException;
Expand Down Expand Up @@ -34,16 +35,34 @@ public DependencyGraph buildDependencyGraph(String rootDir) throws IOException {
String[] directories = content.split("\n");

for (String dir : directories) {
String dependency = readDependency(rootDir + "/" + dir + "/_extends.txt");
graph.addDirectory(dir, dependency);
processFiles(rootDir, dir, graph);
String typePath = rootDir + "/" + dir;
String[] versions = getVersions(typePath);

for (String version : versions) {
TypeDependency dependency = readDependency(typePath + "/" + version + "/" + EXTENDS_FILE_NAME);
graph.addDirectory(dir, version, dependency);
processFiles(rootDir, dir, version, graph);
}
}
}

return graph;
}

private String readDependency(String path) throws IOException {
private String[] getVersions(String typePath) throws IOException {
URL typeUrl = classLoader.getResource(typePath);
if (typeUrl == null) {
throw new IOException("Type directory not found: " + typePath);
}

try (InputStream is = typeUrl.openStream()) {
Scanner scanner = new Scanner(is).useDelimiter("\\A");
String content = scanner.hasNext() ? scanner.next() : "";
return content.split("\n");
}
}

private TypeDependency readDependency(String path) throws IOException {
try (InputStream is = classLoader.getResourceAsStream(path)) {
if (is == null) {
throw new IOException("Dependency file not found: " + path);
Expand All @@ -53,12 +72,12 @@ private String readDependency(String path) throws IOException {
if (content.isEmpty() || content.lines().count() != 1) {
throw new IOException("Invalid dependency file format. Expected one line in: " + path);
}
return content;
return TypeDependency.parse(content);
}
}

private void processFiles(String rootDir, String dirName, DependencyGraph graph) throws IOException {
String dirPath = rootDir + "/" + dirName + "/";
private void processFiles(String rootDir, String dirName, String version, DependencyGraph graph) throws IOException {
String dirPath = rootDir + "/" + dirName + "/" + version + "/";
URL dirUrl = classLoader.getResource(dirPath);
if (dirUrl == null) {
return;
Expand All @@ -75,7 +94,7 @@ private void processFiles(String rootDir, String dirName, DependencyGraph graph)
try (InputStream fileIs = classLoader.getResourceAsStream(filePath)) {
if (fileIs != null) {
Node contract = YAML_MAPPER.readValue(fileIs, Node.class);
graph.addNode(dirName, contract);
graph.addNode(dirName, version, contract);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package blue.contract.packager.graphbuilder;

import blue.contract.packager.model.DependencyGraph;
import blue.contract.packager.model.TypeDependency;
import blue.language.model.Node;

import java.io.IOException;
Expand All @@ -27,38 +28,52 @@ public DependencyGraph buildDependencyGraph(String rootDir) throws IOException {
throw new IOException("Root directory not found: " + fullPath);
}

try (DirectoryStream<Path> stream = Files.newDirectoryStream(fullPath)) {
for (Path path : stream) {
if (Files.isDirectory(path)) {
String dirName = path.getFileName().toString();
String dependency = readDependency(path.resolve(EXTENDS_FILE_NAME));
graph.addDirectory(dirName, dependency);
processFiles(path, dirName, graph);
try (DirectoryStream<Path> typeStream = Files.newDirectoryStream(fullPath)) {
for (Path typePath : typeStream) {
if (Files.isDirectory(typePath)) {
String typeName = typePath.getFileName().toString();
processTypeDirectory(typePath, typeName, graph);
}
}
}

return graph;
}

private String readDependency(Path path) throws IOException {
private void processTypeDirectory(Path typePath, String typeName, DependencyGraph graph) throws IOException {
try (DirectoryStream<Path> versionStream = Files.newDirectoryStream(typePath)) {
for (Path versionPath : versionStream) {
if (Files.isDirectory(versionPath)) {
String version = versionPath.getFileName().toString();
TypeDependency dependency = readDependency(versionPath.resolve(EXTENDS_FILE_NAME));
graph.addDirectory(typeName, version, dependency);
processFiles(versionPath, typeName, version, graph);
}
}
}
}

private TypeDependency readDependency(Path path) throws IOException {
if (!Files.exists(path)) {
throw new IOException("Dependency file not found: " + path);
}
String content = Files.readString(path).trim();
if (content.isEmpty() || content.lines().count() != 1) {
throw new IOException("Invalid dependency file format. Expected one line in: " + path);
}
return content;
try {
return TypeDependency.parse(content);
} catch (IllegalArgumentException e) {
throw new IOException("Invalid dependency format in file: " + path + ". " + e.getMessage());
}
}

private void processFiles(Path dirPath, String dirName, DependencyGraph graph) throws IOException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dirPath, "*.blue")) {
private void processFiles(Path versionPath, String typeName, String version, DependencyGraph graph) throws IOException {
try (DirectoryStream<Path> stream = Files.newDirectoryStream(versionPath, "*.blue")) {
for (Path file : stream) {
Node contract = YAML_MAPPER.readValue(Files.readString(file), Node.class);
graph.addNode(dirName, contract);
graph.addNode(typeName, version, contract);
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.io.IOException;
import java.util.List;
import java.util.Map;

public class SequentialDependencyGraphBuilder implements DependencyGraphBuilder {
private final List<DependencyGraphBuilder> builders;
Expand All @@ -27,17 +28,29 @@ public DependencyGraph buildDependencyGraph(String rootDir) throws IOException {
}

private void mergeGraphs(DependencyGraph target, DependencyGraph source) {
for (String dirName : source.getDirectories().keySet()) {
DirectoryNode sourceDir = source.getDirectories().get(dirName);
DirectoryNode targetDir = target.getDirectories().get(dirName);

if (targetDir != null) {
throw new IllegalStateException("Directory name collision detected: " + dirName);
}

target.addDirectory(dirName, sourceDir.getDependency());
for (Node node : sourceDir.getNodes().values()) {
target.addNode(dirName, node);
for (Map.Entry<String, Map<String, DirectoryNode>> typeEntry : source.getDirectories().entrySet()) {
String typeName = typeEntry.getKey();
Map<String, DirectoryNode> sourceVersions = typeEntry.getValue();

for (Map.Entry<String, DirectoryNode> versionEntry : sourceVersions.entrySet()) {
String version = versionEntry.getKey();
DirectoryNode sourceDir = versionEntry.getValue();

// Check if this type+version combination already exists in the target
Map<String, DirectoryNode> targetVersions = target.getDirectories().get(typeName);
if (targetVersions != null && targetVersions.containsKey(version)) {
throw new IllegalStateException(
String.format("Directory collision detected: %s version %s", typeName, version)
);
}

// Add the directory with its dependency
target.addDirectory(typeName, version, sourceDir.getDependency());

// Add all nodes from the source directory
for (Node node : sourceDir.getNodes().values()) {
target.addNode(typeName, version, node);
}
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions src/main/java/blue/contract/packager/model/BluePackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,27 @@

public class BluePackage {
private final String directoryName;
private final String version;
private final Node packageContent;
private Map<String, String> mappings;
private Map<String, Node> preprocessedNodes;

public BluePackage(String directoryName, Node packageContent) {
public BluePackage(String directoryName, String version, Node packageContent) {
this.directoryName = directoryName;
this.version = version;
this.packageContent = packageContent;
this.mappings = new HashMap<>();
this.preprocessedNodes = new HashMap<>();

}

public String getDirectoryName() {
return directoryName;
}

public String getVersion() {
return version;
}

public Node getPackageContent() {
return packageContent;
}
Expand Down
Loading
Loading