@Override public final void merge(MergeOperation<ASTNodeArtifact> operation, MergeContext context) throws IOException, InterruptedException { Objects.requireNonNull(operation, "operation must not be null!"); Objects.requireNonNull(context, "context must not be null!"); MergeStrategy<ASTNodeArtifact> astNodeStrategy = new ASTNodeStrategy(); if (LOG.isDebugEnabled()) { LOG.debug("Using strategy: " + astNodeStrategy); } MergeTriple<ASTNodeArtifact> triple = operation.getMergeTriple(); ASTNodeArtifact left = triple.getLeft(); ASTNodeArtifact right = triple.getRight(); ASTNodeArtifact target = operation.getTarget(); boolean safeMerge = true; int numChildNoTransform; try { numChildNoTransform = target.astnode.getClass().newInstance().getNumChildNoTransform(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(); } if (!isRoot() && numChildNoTransform > 0) { // this language element has a fixed number of children, we need to be careful with this one boolean leftChanges = left.hasChanges(false); boolean rightChanges = right.hasChanges(false); if (leftChanges && rightChanges) { if (LOG.isTraceEnabled()) { LOG.trace("Target " + target.getId() + " expects a fixed amount of children."); LOG.trace("Both " + left.getId() + " and " + right.getId() + " contain changes."); LOG.trace("We will report a conflict instead of performing the merge."); } safeMerge = false; // to be safe, we will report a conflict instead of merging ASTNodeArtifact targetParent = target.getParent(); targetParent.removeChild(target); Operation<ASTNodeArtifact> conflictOp = new ConflictOperation<>(left, left, right, targetParent); conflictOp.apply(context); } } if (safeMerge) { astNodeStrategy.merge(operation, context); } if (!context.isQuiet() && context.hasOutput()) { System.out.print(context.getStdIn()); } }