1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.text.diff;
18
19 import java.util.ArrayList;
20 import java.util.List;
21
22 /**
23 * This class handles sequences of replacements resulting from a comparison.
24 * <p>
25 * The comparison of two objects sequences leads to the identification of common
26 * parts and parts which only belong to the first or to the second sequence. The
27 * common parts appear in the edit script in the form of <em>keep</em> commands,
28 * they can be considered as synchronization objects between the two sequences.
29 * These synchronization objects split the two sequences in synchronized
30 * sub-sequences. The first sequence can be transformed into the second one by
31 * replacing each synchronized sub-sequence of the first sequence by the
32 * corresponding sub-sequence of the second sequence. This is a synthetic way to
33 * see an {@link EditScript edit script}, replacing individual
34 * {@link DeleteCommand delete}, {@link KeepCommand keep} and
35 * {@link InsertCommand insert} commands by fewer replacements acting on
36 * complete sub-sequences.
37 * </p>
38 * <p>
39 * This class is devoted to perform this interpretation. It visits an
40 * {@link EditScript edit script} (because it implements the
41 * {@link CommandVisitor CommandVisitor} interface) and calls a user-supplied
42 * handler implementing the {@link ReplacementsHandler ReplacementsHandler}
43 * interface to process the sub-sequences.
44 * </p>
45 *
46 * @see ReplacementsHandler
47 * @see EditScript
48 * @see StringsComparator
49 *
50 * @param <T> object type
51 * @since 1.0
52 */
53 public class ReplacementsFinder<T> implements CommandVisitor<T> {
54
55 /**
56 * List of pending insertions.
57 */
58 private final List<T> pendingInsertions;
59 /**
60 * List of pending deletions.
61 */
62 private final List<T> pendingDeletions;
63 /**
64 * Count of elements skipped.
65 */
66 private int skipped;
67
68 /** Handler to call when synchronized sequences are found. */
69 private final ReplacementsHandler<T> handler;
70
71 /**
72 * Simple constructor. Creates a new instance of {@link ReplacementsFinder}.
73 *
74 * @param handler handler to call when synchronized sequences are found
75 */
76 public ReplacementsFinder(final ReplacementsHandler<T> handler) {
77 pendingInsertions = new ArrayList<>();
78 pendingDeletions = new ArrayList<>();
79 skipped = 0;
80 this.handler = handler;
81 }
82
83 /**
84 * Add an object to the pending insertions set.
85 *
86 * @param object object to insert
87 */
88 @Override
89 public void visitInsertCommand(final T object) {
90 pendingInsertions.add(object);
91 }
92
93 /**
94 * Handle a synchronization object.
95 * <p>
96 * When a synchronization object is identified, the pending insertions and
97 * pending deletions sets are provided to the user handler as subsequences.
98 * </p>
99 *
100 * @param object synchronization object detected
101 */
102 @Override
103 public void visitKeepCommand(final T object) {
104 if (pendingDeletions.isEmpty() && pendingInsertions.isEmpty()) {
105 ++skipped;
106 } else {
107 handler.handleReplacement(skipped, pendingDeletions, pendingInsertions);
108 pendingDeletions.clear();
109 pendingInsertions.clear();
110 skipped = 1;
111 }
112 }
113
114 /**
115 * Add an object to the pending deletions set.
116 *
117 * @param object object to delete
118 */
119 @Override
120 public void visitDeleteCommand(final T object) {
121 pendingDeletions.add(object);
122 }
123
124 }