Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .cz.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
name = "cz_conventional_commits"
tag_format = "v$version"
version_scheme = "semver"
version = "2.0.0"
version = "2.0.1"
update_changelog_on_bump = true
59 changes: 59 additions & 0 deletions src/main/java/blue/language/Blue.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import blue.language.snapshot.ResolvedSnapshot;
import blue.language.utils.*;
import blue.language.utils.limits.CompositeLimits;
import blue.language.utils.limits.ExcludedPathLimits;
import blue.language.utils.limits.Limits;

import java.util.ArrayList;
Expand All @@ -40,6 +41,7 @@
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Predicate;

import static blue.language.utils.UncheckedObjectMapper.JSON_MAPPER;
import static blue.language.utils.UncheckedObjectMapper.YAML_MAPPER;
Expand Down Expand Up @@ -109,6 +111,52 @@ public Node resolve(Node node, Limits limits) {
return merger.resolve(node, effectiveLimits);
}

public Node resolvePreservingPaths(Node node, Collection<String> preservedPaths) {
return resolvePreservingPaths(node, NO_LIMITS, preservedPaths);
}

public Node resolvePreservingPaths(Node node, Limits limits, Collection<String> preservedPaths) {
if (node == null) {
throw new IllegalArgumentException("node must not be null");
}
Set<String> canonicalPreservedPaths = canonicalPreservedPaths(preservedPaths);
if (canonicalPreservedPaths.isEmpty()) {
return resolve(node.clone(), limits);
}
if (canonicalPreservedPaths.contains("/")) {
return node.clone();
}

Limits preservingLimits = limits == NO_LIMITS
? ExcludedPathLimits.excluding(canonicalPreservedPaths)
: new CompositeLimits(limits, ExcludedPathLimits.excluding(canonicalPreservedPaths));
Node resolved = resolve(node.clone(), preservingLimits);
for (String path : canonicalPreservedPaths) {
Node preserved = NodePathEditor.getOrNull(node, path);
if (preserved != null) {
NodePathEditor.put(resolved, path, preserved.clone());
}
}
return resolved;
}

public List<String> selectPaths(Node node, Collection<String> pathPatterns, Predicate<Node> predicate) {
return NodePathSelector.select(node, pathPatterns, predicate);
}

public Node resolvePreservingMatchingPaths(Node node,
Collection<String> pathPatterns,
Predicate<Node> predicate) {
return resolvePreservingMatchingPaths(node, NO_LIMITS, pathPatterns, predicate);
}

public Node resolvePreservingMatchingPaths(Node node,
Limits limits,
Collection<String> pathPatterns,
Predicate<Node> predicate) {
return resolvePreservingPaths(node, limits, selectPaths(node, pathPatterns, predicate));
}

public Node reverse(Node node) {
return new MergeReverser().reverse(node);
}
Expand Down Expand Up @@ -612,6 +660,17 @@ private boolean canMinimizePatchedOverride(JsonPatch patch) {
return true;
}

private Set<String> canonicalPreservedPaths(Collection<String> preservedPaths) {
if (preservedPaths == null || preservedPaths.isEmpty()) {
return Collections.emptySet();
}
Set<String> canonicalPaths = new HashSet<>();
for (String preservedPath : preservedPaths) {
canonicalPaths.add(JsonPointer.canonicalize(preservedPath));
}
return canonicalPaths;
}

private NodeProvider processorSnapshotNodeProvider() {
Set<String> processorTypeBlueIds = new HashSet<>(PROCESSOR_MANAGED_TYPE_BLUE_IDS);
if (documentProcessor != null) {
Expand Down
80 changes: 50 additions & 30 deletions src/main/java/blue/language/merge/Merger.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,11 @@ private void mergeObject(Node target, Node source, Limits limits) {
properties.forEach((key, value) -> {
if (limits.shouldMergePathSegment(key, value)) {
limits.enterPathSegment(key, value);
mergeProperty(target, key, value, limits);
limits.exitPathSegment();
try {
mergeProperty(target, key, value, limits);
} finally {
limits.exitPathSegment();
}
}
});
}
Expand All @@ -137,17 +140,17 @@ private void mergeChildren(Node target, List<Node> sourceChildren, Limits limits

if (targetChildren == null) {
if (startsWithPrevious(sourceChildren)) {
targetChildren = resolvePreviousAnchor(sourceChildren.get(0), limits);
targetChildren = resolvePreviousAnchor(sourceChildren.get(0), limits, target.getItemType());
target.items(targetChildren);
validatePreviousAnchor(targetChildren, sourceChildren.get(0));
if (LIST_MERGE_POLICY_APPEND_ONLY.equals(mergePolicy)) {
mergeAppendOnlyChildren(targetChildren, sourceChildren, limits);
mergeAppendOnlyChildren(targetChildren, sourceChildren, limits, target.getItemType());
} else {
mergePositionalChildren(targetChildren, sourceChildren, limits);
mergePositionalChildren(targetChildren, sourceChildren, limits, target.getItemType());
}
return;
}
targetChildren = resolveInitialChildren(sourceChildren, limits);
targetChildren = resolveInitialChildren(sourceChildren, limits, target.getItemType());
target.items(targetChildren);
return;
}
Expand All @@ -157,13 +160,13 @@ private void mergeChildren(Node target, List<Node> sourceChildren, Limits limits
}

if (LIST_MERGE_POLICY_APPEND_ONLY.equals(mergePolicy)) {
mergeAppendOnlyChildren(targetChildren, sourceChildren, limits);
mergeAppendOnlyChildren(targetChildren, sourceChildren, limits, target.getItemType());
} else {
mergePositionalChildren(targetChildren, sourceChildren, limits);
mergePositionalChildren(targetChildren, sourceChildren, limits, target.getItemType());
}
}

private List<Node> resolveInitialChildren(List<Node> sourceChildren, Limits limits) {
private List<Node> resolveInitialChildren(List<Node> sourceChildren, Limits limits, Node itemType) {
List<Node> result = new ArrayList<>();
int start = startsWithPrevious(sourceChildren) ? 1 : 0;
for (int i = start; i < sourceChildren.size(); i++) {
Expand All @@ -175,17 +178,17 @@ private List<Node> resolveInitialChildren(List<Node> sourceChildren, Limits limi
}
child = withoutPosition(child);
}
Node resolvedChild = resolveListChild(child, limits, String.valueOf(result.size()));
Node resolvedChild = resolveListChild(child, limits, String.valueOf(result.size()), itemType);
if (resolvedChild != null) {
result.add(resolvedChild);
}
}
return result;
}

private void mergeAppendOnlyChildren(List<Node> targetChildren, List<Node> sourceChildren, Limits limits) {
private void mergeAppendOnlyChildren(List<Node> targetChildren, List<Node> sourceChildren, Limits limits, Node itemType) {
if (startsWithPrevious(sourceChildren)) {
appendChildren(targetChildren, sourceChildren, 1, limits);
appendChildren(targetChildren, sourceChildren, 1, limits, itemType);
return;
}

Expand All @@ -197,13 +200,13 @@ private void mergeAppendOnlyChildren(List<Node> targetChildren, List<Node> sourc

for (int i = 0; i < sourceChildren.size(); i++) {
if (i >= targetChildren.size()) {
Node resolvedChild = resolveListChild(sourceChildren.get(i), limits, String.valueOf(i));
Node resolvedChild = resolveListChild(sourceChildren.get(i), limits, String.valueOf(i), itemType);
if (resolvedChild != null) {
targetChildren.add(resolvedChild);
}
continue;
}
Node sourceChild = resolveListChild(sourceChildren.get(i), limits, String.valueOf(i));
Node sourceChild = resolveListChild(sourceChildren.get(i), limits, String.valueOf(i), itemType);
if (sourceChild == null) {
continue;
}
Expand All @@ -217,16 +220,16 @@ private void mergeAppendOnlyChildren(List<Node> targetChildren, List<Node> sourc
}
}

private void mergePositionalChildren(List<Node> targetChildren, List<Node> sourceChildren, Limits limits) {
private void mergePositionalChildren(List<Node> targetChildren, List<Node> sourceChildren, Limits limits, Node itemType) {
boolean hasPositionControls = sourceChildren.stream().anyMatch(child -> child.getPosition() != null);
int start = startsWithPrevious(sourceChildren) ? 1 : 0;

if (!hasPositionControls) {
if (startsWithPrevious(sourceChildren)) {
appendChildren(targetChildren, sourceChildren, start, limits);
appendChildren(targetChildren, sourceChildren, start, limits, itemType);
return;
}
mergeLegacyPositionalChildren(targetChildren, sourceChildren, start, limits);
mergeLegacyPositionalChildren(targetChildren, sourceChildren, start, limits, itemType);
return;
}

Expand All @@ -241,17 +244,17 @@ private void mergePositionalChildren(List<Node> targetChildren, List<Node> sourc
if (!positions.add(position)) {
throw new IllegalArgumentException("Duplicate \"$pos\" value in list: " + position);
}
mergeOrReplacePosition(targetChildren, position, withoutPosition(sourceChild), limits);
mergeOrReplacePosition(targetChildren, position, withoutPosition(sourceChild), limits, itemType);
} else {
Node resolvedChild = resolveListChild(sourceChild, limits, String.valueOf(targetChildren.size()));
Node resolvedChild = resolveListChild(sourceChild, limits, String.valueOf(targetChildren.size()), itemType);
if (resolvedChild != null) {
targetChildren.add(resolvedChild);
}
}
}
}

private void mergeLegacyPositionalChildren(List<Node> targetChildren, List<Node> sourceChildren, int start, Limits limits) {
private void mergeLegacyPositionalChildren(List<Node> targetChildren, List<Node> sourceChildren, int start, Limits limits, Node itemType) {
int sourceLength = sourceChildren.size() - start;
if (sourceLength < targetChildren.size()) {
throw new IllegalArgumentException(String.format(
Expand All @@ -263,7 +266,7 @@ private void mergeLegacyPositionalChildren(List<Node> targetChildren, List<Node>
for (int i = 0; i < sourceLength; i++) {
Node sourceChild = sourceChildren.get(start + i);
if (i >= targetChildren.size()) {
Node resolvedChild = resolveListChild(sourceChild, limits, String.valueOf(i));
Node resolvedChild = resolveListChild(sourceChild, limits, String.valueOf(i), itemType);
if (resolvedChild != null) {
targetChildren.add(resolvedChild);
}
Expand All @@ -273,16 +276,19 @@ private void mergeLegacyPositionalChildren(List<Node> targetChildren, List<Node>
}
}

private void mergeOrReplacePosition(List<Node> targetChildren, int position, Node overlay, Limits limits) {
private void mergeOrReplacePosition(List<Node> targetChildren, int position, Node overlay, Limits limits, Node itemType) {
Node effectiveItemType = targetChildren.get(position).getType() != null
? targetChildren.get(position).getType()
: itemType;
if (isEmptyPlaceholder(targetChildren.get(position)) || overlay.getValue() != null || overlay.getItems() != null) {
Node resolvedChild = resolveListChild(overlay, limits, String.valueOf(position));
Node resolvedChild = resolveListChild(overlay, limits, String.valueOf(position), effectiveItemType);
if (resolvedChild != null) {
targetChildren.set(position, resolvedChild);
}
return;
}
if (overlay.getType() != null) {
Node resolvedOverlay = resolveListChild(overlay, limits, String.valueOf(position));
Node resolvedOverlay = resolveListChild(overlay, limits, String.valueOf(position), effectiveItemType);
if (resolvedOverlay != null) {
mergeObject(targetChildren.get(position), resolvedOverlay, limits);
}
Expand All @@ -291,16 +297,16 @@ private void mergeOrReplacePosition(List<Node> targetChildren, int position, Nod
merge(targetChildren.get(position), overlay, limits);
}

private void appendChildren(List<Node> targetChildren, List<Node> sourceChildren, int start, Limits limits) {
private void appendChildren(List<Node> targetChildren, List<Node> sourceChildren, int start, Limits limits, Node itemType) {
for (int i = start; i < sourceChildren.size(); i++) {
Node resolvedChild = resolveListChild(sourceChildren.get(i), limits, String.valueOf(targetChildren.size()));
Node resolvedChild = resolveListChild(sourceChildren.get(i), limits, String.valueOf(targetChildren.size()), itemType);
if (resolvedChild != null) {
targetChildren.add(resolvedChild);
}
}
}

private List<Node> resolvePreviousAnchor(Node previousAnchor, Limits limits) {
private List<Node> resolvePreviousAnchor(Node previousAnchor, Limits limits, Node itemType) {
List<Node> fetched = nodeProvider.fetchByBlueId(previousAnchor.getPreviousBlueId());
if (fetched == null || fetched.isEmpty()) {
throw new IllegalArgumentException("No content found for $previous blueId: " + previousAnchor.getPreviousBlueId());
Expand All @@ -311,7 +317,7 @@ private List<Node> resolvePreviousAnchor(Node previousAnchor, Limits limits) {
: fetched;
List<Node> resolved = new ArrayList<>();
for (int i = 0; i < previousChildren.size(); i++) {
Node resolvedChild = resolveListChild(previousChildren.get(i), limits, String.valueOf(i));
Node resolvedChild = resolveListChild(previousChildren.get(i), limits, String.valueOf(i), itemType);
if (resolvedChild != null) {
resolved.add(resolvedChild);
}
Expand Down Expand Up @@ -342,7 +348,7 @@ private boolean isEmptyPlaceholder(Node node) {
&& node.getValueType() == null;
}

private Node resolveListChild(Node child, Limits limits, String segment) {
private Node resolveListChild(Node child, Limits limits, String segment, Node itemType) {
if (child.getPreviousBlueId() != null || child.getPosition() != null) {
throw new IllegalArgumentException("List control items must be consumed before resolving list children.");
}
Expand All @@ -351,12 +357,26 @@ private Node resolveListChild(Node child, Limits limits, String segment) {
}
limits.enterPathSegment(segment, child);
try {
return resolve(child, limits);
return resolve(applyItemType(child, itemType), limits);
} finally {
limits.exitPathSegment();
}
}

private Node applyItemType(Node child, Node itemType) {
if (child.getType() != null || child.getBlueId() != null || itemType == null) {
return child;
}
return child.clone().type(itemTypeReference(itemType));
}

private Node itemTypeReference(Node itemType) {
if (itemType.getBlueId() != null) {
return new Node().blueId(itemType.getBlueId());
}
return itemType.clone();
}

private Node withoutPosition(Node node) {
Node clone = node.clone();
clone.position(null);
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/blue/language/model/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public Object getValue() {
return value;
}

public Object getRawValue() {
return value;
}

public List<Node> getItems() {
return items;
}
Expand Down Expand Up @@ -324,6 +328,10 @@ public Node getAsNode(String path) {
return (Node) get(path);
}

public Node getNode(String path) {
return NodePathAccessor.getNode(this, path);
}

public String getAsText(String path) {
return (String) get(path);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public String contractKey() {
return contractKey;
}

public String scopePath() {
return scopePath;
}

public Node contractNode() {
return contractNode != null ? contractNode.toNode() : null;
}
Expand Down Expand Up @@ -99,6 +103,20 @@ public Node documentAt(String absolutePointer) {
return runtime().nodeAt(absolutePointer);
}

public FrozenNode canonicalFrozenAt(String absolutePointer) {
if (absolutePointer == null || absolutePointer.isEmpty()) {
return null;
}
return runtime().canonicalFrozenAt(absolutePointer);
}

public FrozenNode resolvedFrozenAt(String absolutePointer) {
if (absolutePointer == null || absolutePointer.isEmpty()) {
return null;
}
return runtime().resolvedFrozenAt(absolutePointer);
}

public boolean documentContains(String absolutePointer) {
if (absolutePointer == null || absolutePointer.isEmpty()) {
return false;
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/blue/language/snapshot/FrozenNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,11 @@ public FrozenNode item(int index) {

public FrozenNode at(String pointer) {
List<String> segments = JsonPointer.split(pointer);
return at(segments);
}

public FrozenNode at(List<String> pointerSegments) {
List<String> segments = pointerSegments != null ? pointerSegments : Collections.emptyList();
if (segments.isEmpty()) {
return this;
}
Expand Down
Loading
Loading