Line data Source code
1 : // Copyright (C) 2017 The Android Open Source Project 2 : // 3 : // Licensed under the Apache License, Version 2.0 (the "License"); 4 : // you may not use this file except in compliance with the License. 5 : // You may obtain a copy of the License at 6 : // 7 : // http://www.apache.org/licenses/LICENSE-2.0 8 : // 9 : // Unless required by applicable law or agreed to in writing, software 10 : // distributed under the License is distributed on an "AS IS" BASIS, 11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 : // See the License for the specific language governing permissions and 13 : // limitations under the License. 14 : 15 : package com.google.gerrit.server.fixes; 16 : 17 : import static java.util.Objects.requireNonNull; 18 : 19 : /** 20 : * A modifier of a string. It allows to replace multiple parts of a string by indicating those parts 21 : * with indices based on the unmodified string. There is one limitation though: Replacements which 22 : * affect lower indices of the string must be specified before replacements for higher indices. 23 : */ 24 : class StringModifier { 25 : 26 : private final StringBuilder stringBuilder; 27 : 28 1 : private int characterShift = 0; 29 1 : private int previousEndOffset = Integer.MIN_VALUE; 30 : 31 1 : StringModifier(String string) { 32 1 : requireNonNull(string, "string must not be null"); 33 1 : stringBuilder = new StringBuilder(string); 34 1 : } 35 : 36 : /** 37 : * Replaces part of the string with another content. When called multiple times, the calls must be 38 : * ordered according to increasing start indices. Overlapping replacement regions aren't 39 : * supported. 40 : * 41 : * @param startIndex the beginning index in the unmodified string (inclusive) 42 : * @param endIndex the ending index in the unmodified string (exclusive) 43 : * @param replacement the string which should be used instead of the original content 44 : * @throws StringIndexOutOfBoundsException if the start index is smaller than the end index of a 45 : * previous call of this method 46 : */ 47 : public void replace(int startIndex, int endIndex, String replacement) { 48 1 : requireNonNull(replacement, "replacement string must not be null"); 49 1 : if (previousEndOffset > startIndex) { 50 1 : throw new StringIndexOutOfBoundsException( 51 1 : String.format( 52 : "Not supported to replace the content starting at index %s after previous " 53 : + "replacement which ended at index %s", 54 1 : startIndex, previousEndOffset)); 55 : } 56 1 : int shiftedStartIndex = startIndex + characterShift; 57 1 : int shiftedEndIndex = endIndex + characterShift; 58 1 : if (shiftedEndIndex > stringBuilder.length()) { 59 1 : throw new StringIndexOutOfBoundsException( 60 1 : String.format("end %s > length %s", shiftedEndIndex, stringBuilder.length())); 61 : } 62 1 : stringBuilder.replace(shiftedStartIndex, shiftedEndIndex, replacement); 63 : 64 1 : int replacedContentLength = endIndex - startIndex; 65 1 : characterShift += replacement.length() - replacedContentLength; 66 1 : previousEndOffset = endIndex; 67 1 : } 68 : 69 : /** 70 : * Returns the modified string including all specified replacements. 71 : * 72 : * @return the modified string 73 : */ 74 : public String getResult() { 75 1 : return stringBuilder.toString(); 76 : } 77 : }