Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@

import static java.util.Objects.requireNonNull;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
Expand Down Expand Up @@ -355,9 +357,59 @@ private void walkChildren(
CompileContext ctx,
Set<NodeId> pathNodeIds) {

Deque<ChildWalkFrame> frames = new ArrayDeque<>();
frames.push(childWalkFrame(sourceNodeId, parentPath, declaringTypeId, null, ctx));

while (!frames.isEmpty()) {
ChildWalkFrame frame = frames.peek();
if (frame.hasNext()) {
Child child = frame.next();
ChildExpansion expansion =
processChild(
child.reference,
child.targetId,
child.node,
frame.parentPath,
frame.declaringTypeId,
table,
ctx,
pathNodeIds);

if (expansion != null) {
frames.push(
childWalkFrame(
expansion.targetId, expansion.path, expansion.declaringTypeId, expansion, ctx));
}
} else {
frames.pop();

ChildExpansion expansion = frame.expansion;
if (expansion != null) {
pathNodeIds.remove(expansion.targetId);

if (expansion.memberTypeId != null) {
expandMemberType(
expansion.declaration,
expansion.memberTypeId,
expansion.path,
expansion.targetId,
table,
ctx);
}
}
}
}
}

private ChildWalkFrame childWalkFrame(
NodeId sourceNodeId,
BrowsePath parentPath,
NodeId declaringTypeId,
@Nullable ChildExpansion expansion,
CompileContext ctx) {

List<Reference> refs = getReferences(sourceNodeId, ctx);

record Child(Reference reference, NodeId targetId, UaNode node) {}
List<Child> children = new ArrayList<>();

for (Reference r : refs) {
Expand Down Expand Up @@ -418,20 +470,10 @@ record Child(Reference reference, NodeId targetId, UaNode node) {}
.thenComparing(c -> c.reference.getReferenceTypeId().toParseableString())
.thenComparing(c -> c.targetId.toParseableString()));

for (Child child : children) {
processChild(
child.reference,
child.targetId,
child.node,
parentPath,
declaringTypeId,
table,
ctx,
pathNodeIds);
}
return new ChildWalkFrame(parentPath, declaringTypeId, children, expansion);
}

private void processChild(
private @Nullable ChildExpansion processChild(
Reference reference,
NodeId targetId,
UaNode node,
Expand Down Expand Up @@ -471,7 +513,7 @@ private void processChild(
path,
targetId));
}
return;
return null;
}
table.pathOccupants.put(path, targetId);

Expand Down Expand Up @@ -502,7 +544,7 @@ private void processChild(
path,
targetId));
}
return;
return null;
}

if (ruleIds.isEmpty()) {
Expand All @@ -520,7 +562,7 @@ private void processChild(
+ ")",
path,
targetId));
return;
return null;
}

if (ruleRefs.size() > 1) {
Expand Down Expand Up @@ -569,7 +611,7 @@ private void processChild(
nodeClass + " declaration at " + path + " has no HasTypeDefinition reference",
path,
targetId));
return;
return null;
}

if (memberTypeXnis.size() > 1) {
Expand All @@ -587,7 +629,7 @@ private void processChild(
+ "; exactly one is required",
path,
targetId));
return;
return null;
}

ExpandedNodeId memberTypeXni = memberTypeXnis.get(0);
Expand All @@ -603,7 +645,7 @@ private void processChild(
+ " is not resolvable in this server",
path,
targetId));
return;
return null;
}
}

Expand Down Expand Up @@ -660,7 +702,7 @@ private void processChild(
}
}
}
return;
return null;
}

if (path.depth() >= MAX_DEPTH) {
Expand All @@ -670,7 +712,7 @@ private void processChild(
"declaration hierarchy exceeds depth " + MAX_DEPTH + " at " + path,
path,
targetId));
return;
return null;
}

if (!pathNodeIds.add(targetId)) {
Expand All @@ -680,20 +722,13 @@ private void processChild(
"declaration " + targetId + " reached again along its own path at " + path,
path,
targetId));
return;
}

try {
// Explicit on-declaration children first: they win over same-path member-type defaults
// (Part 3 §6.3.3.3).
walkChildren(targetId, path, declaringTypeId, table, ctx, pathNodeIds);
} finally {
pathNodeIds.remove(targetId);
return null;
}

if (memberTypeId != null) {
expandMemberType(declaration, memberTypeId, path, targetId, table, ctx);
}
// Explicit on-declaration children are traversed before member-type expansion, so they win over
// same-path member-type defaults (Part 3 §6.3.3.3). The continuation also removes targetId from
// the active path after its children have been processed.
return new ChildExpansion(targetId, path, declaringTypeId, declaration, memberTypeId);
}

/**
Expand Down Expand Up @@ -1696,6 +1731,43 @@ private static List<ModelDiagnostic> dedupe(List<ModelDiagnostic> diagnostics) {
return List.copyOf(new LinkedHashSet<>(diagnostics));
}

private record Child(Reference reference, NodeId targetId, UaNode node) {}

private record ChildExpansion(
NodeId targetId,
BrowsePath path,
NodeId declaringTypeId,
InstanceDeclaration declaration,
@Nullable NodeId memberTypeId) {}

private static final class ChildWalkFrame {
final BrowsePath parentPath;
final NodeId declaringTypeId;
final List<Child> children;
final @Nullable ChildExpansion expansion;

int nextChild;

ChildWalkFrame(
BrowsePath parentPath,
NodeId declaringTypeId,
List<Child> children,
@Nullable ChildExpansion expansion) {
this.parentPath = parentPath;
this.declaringTypeId = declaringTypeId;
this.children = children;
this.expansion = expansion;
}

boolean hasNext() {
return nextChild < children.size();
}

Child next() {
return children.get(nextChild++);
}
}

private static final class CompileContext {
final Map<NodeId, CompiledType> memo = new HashMap<>();
final Set<NodeId> inProgress = new HashSet<>();
Expand Down
Loading