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
60 changes: 49 additions & 11 deletions src/main/java/blue/contract/packager/DependencyProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import blue.contract.packager.utils.PackageMappingsUpdater;
import blue.contract.packager.utils.TopologicalSorter;
import blue.language.model.Node;
import blue.language.utils.BlueIdCalculator;

import java.util.*;

Expand All @@ -30,19 +31,53 @@ public DependencyProcessor(DependencyGraph graph) {

public List<BluePackage> process() {
List<String> processingOrder = graph.getProcessingOrder();

// First pass: Initialize packages and build initial mappings
for (String dirName : processingOrder) {
initializePackageAndMappings(dirName);
}

// Second pass: Process nodes with mappings available
for (String dirName : processingOrder) {
processDirectory(dirName);
}

return new ArrayList<>(processedPackages.values());
}

private void processDirectory(String dirName) {
private void initializePackageAndMappings(String dirName) {
DirectoryNode dir = graph.getDirectories().get(dirName);
BluePackage bluePackage = bluePackageInitializer.initialize(dirName, dir.getDependency(), processedPackages);
processedPackages.put(dirName, bluePackage);

System.out.println("Initializing mappings for package: " + dirName);

// Build initial mappings for all nodes in the package
for (Map.Entry<String, Node> entry : dir.getNodes().entrySet()) {
String nodeName = entry.getKey();
Node node = entry.getValue();

// Calculate blueId for the original node
String blueId = BlueIdCalculator.calculateBlueId(node);

System.out.println("Adding mapping for " + dirName + "::" + nodeName + " -> " + blueId);

// Update mappings
packageMappingsUpdater.update(dirName, nodeName, node, blueId, processedPackages);

// Verify mapping was added
String storedBlueId = bluePackage.getMappings().get(nodeName);
if (storedBlueId == null) {
System.err.println("WARNING: Mapping not added for " + nodeName + " in package " + dirName);
}
}
}

private void processDirectory(String dirName) {
DirectoryNode dir = graph.getDirectories().get(dirName);
Map<String, Node> nodes = new HashMap<>(dir.getNodes());
Set<String> processed = new HashSet<>();

List<String> processingOrder;
try {
processingOrder = topologicalSorter.sort(nodes);
Expand All @@ -60,15 +95,18 @@ private void processNode(String nodeName, Map<String, Node> nodes, Set<String> p
return;
}

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

nodes.put(nodeName, preprocessedNode);

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

packageMappingsUpdater.update(dirName, nodeName, preprocessedNode, processedPackages);
processed.add(nodeName);
Node node = nodes.get(nodeName);
try {
Node preprocessedNode = nodePreprocessor.preprocess(node, dirName, processedPackages);
nodes.put(nodeName, preprocessedNode);

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

processed.add(nodeName);
} catch (IllegalStateException e) {
throw new IllegalStateException("Error processing node '" + nodeName + "' in directory '" + dirName + "': " +
e.getMessage());
}
}
}
47 changes: 47 additions & 0 deletions src/main/java/blue/contract/packager/core/CoreTypeRegistry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package blue.contract.packager.core;

import blue.language.model.Node;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import static blue.language.utils.UncheckedObjectMapper.YAML_MAPPER;

public class CoreTypeRegistry {
private static Map<String, String> coreTypes;
private static final String DEFAULT_BLUE_RESOURCE_PATH = "transformation/DefaultBlue.blue";

public static void initialize() {
if (coreTypes != null) return;

try (InputStream inputStream = CoreTypeRegistry.class.getClassLoader()
.getResourceAsStream(DEFAULT_BLUE_RESOURCE_PATH)) {

if (inputStream == null) {
throw new RuntimeException("Unable to find DefaultBlue.blue in classpath");
}

Node defaultBlue = YAML_MAPPER.readValue(inputStream, Node.class);
coreTypes = new HashMap<>();

// Get the first item which contains the mappings
Node firstItem = defaultBlue.getItems().get(0);
Node mappingsNode = firstItem.getProperties().get("mappings");

if (mappingsNode != null && mappingsNode.getProperties() != null) {
mappingsNode.getProperties().forEach((typeName, blueIdNode) -> {
coreTypes.put(typeName, blueIdNode.getValue().toString());
});
}
} catch (Exception e) {
throw new RuntimeException("Error loading core type mappings", e);
}
}

public static String getBlueId(String typeName) {
if (coreTypes == null) {
initialize();
}
return coreTypes.get(typeName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static blue.contract.packager.BluePackageExporter.BLUE_FILE_EXTENSION;
import static blue.contract.packager.BluePackageExporter.EXTENDS_FILE_NAME;
import static blue.language.utils.UncheckedObjectMapper.YAML_MAPPER;
import static blue.contract.packager.BluePackageExporter.ROOT_DEPENDENCY;

public class ClasspathDependencyGraphBuilder implements DependencyGraphBuilder {
private final ClassLoader classLoader;
Expand Down Expand Up @@ -46,7 +47,7 @@ public DependencyGraph buildDependencyGraph(String rootDir) throws IOException {
private String readDependency(String path) throws IOException {
try (InputStream is = classLoader.getResourceAsStream(path)) {
if (is == null) {
throw new IOException("Dependency file not found: " + path);
return ROOT_DEPENDENCY;
}
Scanner scanner = new Scanner(is).useDelimiter("\\A");
String content = scanner.hasNext() ? scanner.next().trim() : "";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import static blue.contract.packager.BluePackageExporter.EXTENDS_FILE_NAME;
import static blue.language.utils.UncheckedObjectMapper.YAML_MAPPER;
import static blue.contract.packager.BluePackageExporter.ROOT_DEPENDENCY;

public class FileSystemDependencyGraphBuilder implements DependencyGraphBuilder {
private final Path rootPath;
Expand Down Expand Up @@ -43,7 +44,7 @@ public DependencyGraph buildDependencyGraph(String rootDir) throws IOException {

private String readDependency(Path path) throws IOException {
if (!Files.exists(path)) {
throw new IOException("Dependency file not found: " + path);
return ROOT_DEPENDENCY;
}
String content = Files.readString(path).trim();
if (content.isEmpty() || content.lines().count() != 1) {
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/blue/contract/packager/model/BluePackage.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
public class BluePackage {
private final String directoryName;
private final Node packageContent;
private Map<String, String> mappings;
private Map<String, Node> preprocessedNodes;
private final Map<String, String> mappings; // Changed to final
private final Map<String, Node> preprocessedNodes; // Changed to final

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

}

public String getDirectoryName() {
Expand All @@ -28,11 +27,15 @@ public Node getPackageContent() {
}

public Map<String, String> getMappings() {
return new HashMap<>(mappings);
return mappings; // Return direct reference instead of copy
}

public Map<String, Node> getPreprocessedNodes() {
return new HashMap<>(preprocessedNodes);
return preprocessedNodes; // Return direct reference instead of copy
}

public void addMapping(String nodeName, String blueId) {
this.mappings.put(nodeName, blueId);
}

public void addPreprocessedNode(String nodeName, Node node) {
Expand Down
111 changes: 84 additions & 27 deletions src/main/java/blue/contract/packager/model/DependencyGraph.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package blue.contract.packager.model;

import blue.contract.packager.utils.TypeReference;
import blue.language.model.Node;

import java.util.*;
Expand All @@ -8,57 +9,113 @@

public class DependencyGraph {
private Map<String, DirectoryNode> directories;
private Map<String, Set<String>> additionalDependencies;
private static final String CORE_PACKAGE = "Core";

public DependencyGraph() {
this.directories = new HashMap<>();
this.additionalDependencies = new HashMap<>();
}

public void addDirectory(String name, String dependency) {
DirectoryNode node = new DirectoryNode(name);
node.setDependency(dependency);
directories.put(name, node);
additionalDependencies.put(name, new HashSet<>());
}

public void addNode(String dirName, Node node) {
directories.get(dirName).addNode(node);
findPackageReferences(dirName, node);
}

public List<String> getProcessingOrder() {
List<String> order = new ArrayList<>();
Set<String> visited = new HashSet<>();
Set<String> recursionStack = new HashSet<>();
private void findPackageReferences(String dirName, Node node) {
if (node == null) return;

for (String dirName : directories.keySet()) {
if (hasCyclicDependency(dirName, visited, recursionStack, order)) {
throw new IllegalStateException("Cyclic dependency detected in directory structure: " +
String.join(" -> ", recursionStack) + " -> " + dirName);
// Check type field
if (node.getType() != null && node.getType().isInlineValue()) {
String typeValue = node.getType().getValue().toString();
TypeReference typeRef = new TypeReference(typeValue);
if (typeRef.isQualified() && !CORE_PACKAGE.equals(typeRef.getPackageName())) {
additionalDependencies.get(dirName).add(typeRef.getPackageName());
}
}

return order;
}

private boolean hasCyclicDependency(String dirName, Set<String> visited, Set<String> recursionStack, List<String> order) {
if (recursionStack.contains(dirName)) {
return true;
// Recursively check items
if (node.getItems() != null) {
for (Node item : node.getItems()) {
findPackageReferences(dirName, item);
}
}

if (visited.contains(dirName)) {
return false;
// Recursively check properties
if (node.getProperties() != null) {
for (Node propertyNode : node.getProperties().values()) {
findPackageReferences(dirName, propertyNode);
}
}
}

visited.add(dirName);
recursionStack.add(dirName);

DirectoryNode node = directories.get(dirName);
String dep = node.getDependency();
if (!dep.equals(ROOT_DEPENDENCY) && hasCyclicDependency(dep, visited, recursionStack, order)) {
return true;
public List<String> getProcessingOrder() {
Map<String, Integer> inDegree = new HashMap<>();
Map<String, List<String>> adjacencyList = new HashMap<>();

// Initialize data structures
for (String dir : directories.keySet()) {
inDegree.put(dir, 0);
adjacencyList.put(dir, new ArrayList<>());
}

recursionStack.remove(dirName);
order.add(dirName);
return false;

// Build dependency graph
for (String dir : directories.keySet()) {
// Extension-based dependency
String dep = directories.get(dir).getDependency();
if (!dep.equals(ROOT_DEPENDENCY)) {
adjacencyList.get(dep).add(dir);
inDegree.put(dir, inDegree.get(dir) + 1);
}

// Additional dependencies (:: syntax)
for (String additionalDep : additionalDependencies.get(dir)) {
if (!CORE_PACKAGE.equals(additionalDep) && !directories.containsKey(additionalDep)) {
throw new IllegalStateException("Referenced package not found: " + additionalDep +
" (referenced from " + dir + ")");
}
if (!CORE_PACKAGE.equals(additionalDep)) {
adjacencyList.get(additionalDep).add(dir);
inDegree.put(dir, inDegree.get(dir) + 1);
}
}
}

// Topological sort using Kahn's algorithm
Queue<String> queue = new LinkedList<>();
List<String> result = new ArrayList<>();

// Add all nodes with no dependencies to queue
for (Map.Entry<String, Integer> entry : inDegree.entrySet()) {
if (entry.getValue() == 0) {
queue.add(entry.getKey());
}
}

while (!queue.isEmpty()) {
String current = queue.poll();
result.add(current);

for (String dependent : adjacencyList.get(current)) {
inDegree.put(dependent, inDegree.get(dependent) - 1);
if (inDegree.get(dependent) == 0) {
queue.add(dependent);
}
}
}

if (result.size() != directories.size()) {
throw new IllegalStateException("Cyclic dependency detected in package structure");
}

return result;
}

public Map<String, DirectoryNode> getDirectories() {
Expand Down
Loading
Loading