View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.util.internal;
17  
18  import static io.netty.util.internal.ObjectUtil.checkPositive;
19  import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
20  
21  import java.util.Arrays;
22  
23  public final class AppendableCharSequence implements CharSequence, Appendable {
24      private char[] chars;
25      private int pos;
26  
27      public AppendableCharSequence(int length) {
28          chars = new char[checkPositive(length, "length")];
29      }
30  
31      private AppendableCharSequence(char[] chars) {
32          this.chars = checkNonEmpty(chars, "chars");
33          pos = chars.length;
34      }
35  
36      public void setLength(int length) {
37          if (length < 0 || length > pos) {
38              throw new IllegalArgumentException("length: " + length + " (length: >= 0, <= " + pos + ')');
39          }
40          this.pos = length;
41      }
42  
43      @Override
44      public int length() {
45          return pos;
46      }
47  
48      @Override
49      public char charAt(int index) {
50          if (index > pos) {
51              throw new IndexOutOfBoundsException();
52          }
53          return chars[index];
54      }
55  
56      /**
57       * Access a value in this {@link CharSequence}.
58       * This method is considered unsafe as index values are assumed to be legitimate.
59       * Only underlying array bounds checking is done.
60       * @param index The index to access the underlying array at.
61       * @return The value at {@code index}.
62       */
63      public char charAtUnsafe(int index) {
64          return chars[index];
65      }
66  
67      @Override
68      public AppendableCharSequence subSequence(int start, int end) {
69          if (start == end) {
70              // If start and end index is the same we need to return an empty sequence to conform to the interface.
71              // As our expanding logic depends on the fact that we have a char[] with length > 0 we need to construct
72              // an instance for which this is true.
73              return new AppendableCharSequence(Math.min(16, chars.length));
74          }
75          return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end));
76      }
77  
78      @Override
79      public AppendableCharSequence append(char c) {
80          if (pos == chars.length) {
81              char[] old = chars;
82              chars = new char[old.length << 1];
83              System.arraycopy(old, 0, chars, 0, old.length);
84          }
85          chars[pos++] = c;
86          return this;
87      }
88  
89      @Override
90      public AppendableCharSequence append(CharSequence csq) {
91          return append(csq, 0, csq.length());
92      }
93  
94      @Override
95      public AppendableCharSequence append(CharSequence csq, int start, int end) {
96          if (csq.length() < end) {
97              throw new IndexOutOfBoundsException("expected: csq.length() >= ("
98                      + end + "),but actual is (" + csq.length() + ")");
99          }
100         int length = end - start;
101         if (length > chars.length - pos) {
102             chars = expand(chars, pos + length, pos);
103         }
104         if (csq instanceof AppendableCharSequence) {
105             // Optimize append operations via array copy
106             AppendableCharSequence seq = (AppendableCharSequence) csq;
107             char[] src = seq.chars;
108             System.arraycopy(src, start, chars, pos, length);
109             pos += length;
110             return this;
111         }
112         for (int i = start; i < end; i++) {
113             chars[pos++] = csq.charAt(i);
114         }
115 
116         return this;
117     }
118 
119     /**
120      * Reset the {@link AppendableCharSequence}. Be aware this will only reset the current internal position and not
121      * shrink the internal char array.
122      */
123     public void reset() {
124         pos = 0;
125     }
126 
127     @Override
128     public String toString() {
129         return new String(chars, 0, pos);
130     }
131 
132     /**
133      * Create a new {@link String} from the given start to end.
134      */
135     public String substring(int start, int end) {
136         int length = end - start;
137         if (start > pos || length > pos) {
138             throw new IndexOutOfBoundsException("expected: start and length <= ("
139                     + pos + ")");
140         }
141         return new String(chars, start, length);
142     }
143 
144     /**
145      * Create a new {@link String} from the given start to end.
146      * This method is considered unsafe as index values are assumed to be legitimate.
147      * Only underlying array bounds checking is done.
148      */
149     public String subStringUnsafe(int start, int end) {
150         return new String(chars, start, end - start);
151     }
152 
153     private static char[] expand(char[] array, int neededSpace, int size) {
154         int newCapacity = array.length;
155         do {
156             // double capacity until it is big enough
157             newCapacity <<= 1;
158 
159             if (newCapacity < 0) {
160                 throw new IllegalStateException();
161             }
162 
163         } while (neededSpace > newCapacity);
164 
165         char[] newArray = new char[newCapacity];
166         System.arraycopy(array, 0, newArray, 0, size);
167 
168         return newArray;
169     }
170 }