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    *   http://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  
19  import java.util.Arrays;
20  
21  public final class AppendableCharSequence implements CharSequence, Appendable {
22      private char[] chars;
23      private int pos;
24  
25      public AppendableCharSequence(int length) {
26          if (length < 1) {
27              throw new IllegalArgumentException("length: " + length + " (length: >= 1)");
28          }
29          chars = new char[length];
30      }
31  
32      private AppendableCharSequence(char[] chars) {
33          if (chars.length < 1) {
34              throw new IllegalArgumentException("length: " + chars.length + " (length: >= 1)");
35          }
36          this.chars = chars;
37          pos = chars.length;
38      }
39  
40      @Override
41      public int length() {
42          return pos;
43      }
44  
45      @Override
46      public char charAt(int index) {
47          if (index > pos) {
48              throw new IndexOutOfBoundsException();
49          }
50          return chars[index];
51      }
52  
53      /**
54       * Access a value in this {@link CharSequence}.
55       * This method is considered unsafe as index values are assumed to be legitimate.
56       * Only underlying array bounds checking is done.
57       * @param index The index to access the underlying array at.
58       * @return The value at {@code index}.
59       */
60      public char charAtUnsafe(int index) {
61          return chars[index];
62      }
63  
64      @Override
65      public AppendableCharSequence subSequence(int start, int end) {
66          if (start == end) {
67              // If start and end index is the same we need to return an empty sequence to conform to the interface.
68              // As our expanding logic depends on the fact that we have a char[] with length > 0 we need to construct
69              // an instance for which this is true.
70              return new AppendableCharSequence(Math.min(16, chars.length));
71          }
72          return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end));
73      }
74  
75      @Override
76      public AppendableCharSequence append(char c) {
77          if (pos == chars.length) {
78              char[] old = chars;
79              chars = new char[old.length << 1];
80              System.arraycopy(old, 0, chars, 0, old.length);
81          }
82          chars[pos++] = c;
83          return this;
84      }
85  
86      @Override
87      public AppendableCharSequence append(CharSequence csq) {
88          return append(csq, 0, csq.length());
89      }
90  
91      @Override
92      public AppendableCharSequence append(CharSequence csq, int start, int end) {
93          if (csq.length() < end) {
94              throw new IndexOutOfBoundsException();
95          }
96          int length = end - start;
97          if (length > chars.length - pos) {
98              chars = expand(chars, pos + length, pos);
99          }
100         if (csq instanceof AppendableCharSequence) {
101             // Optimize append operations via array copy
102             AppendableCharSequence seq = (AppendableCharSequence) csq;
103             char[] src = seq.chars;
104             System.arraycopy(src, start, chars, pos, length);
105             pos += length;
106             return this;
107         }
108         for (int i = start; i < end; i++) {
109             chars[pos++] = csq.charAt(i);
110         }
111 
112         return this;
113     }
114 
115     /**
116      * Reset the {@link AppendableCharSequence}. Be aware this will only reset the current internal position and not
117      * shrink the internal char array.
118      */
119     public void reset() {
120         pos = 0;
121     }
122 
123     @Override
124     public String toString() {
125         return new String(chars, 0, pos);
126     }
127 
128     /**
129      * Create a new {@link String} from the given start to end.
130      */
131     public String substring(int start, int end) {
132         int length = end - start;
133         if (start > pos || length > pos) {
134             throw new IndexOutOfBoundsException();
135         }
136         return new String(chars, start, length);
137     }
138 
139     /**
140      * Create a new {@link String} from the given start to end.
141      * This method is considered unsafe as index values are assumed to be legitimate.
142      * Only underlying array bounds checking is done.
143      */
144     public String subStringUnsafe(int start, int end) {
145         return new String(chars, start, end - start);
146     }
147 
148     private static char[] expand(char[] array, int neededSpace, int size) {
149         int newCapacity = array.length;
150         do {
151             // double capacity until it is big enough
152             newCapacity <<= 1;
153 
154             if (newCapacity < 0) {
155                 throw new IllegalStateException();
156             }
157 
158         } while (neededSpace > newCapacity);
159 
160         char[] newArray = new char[newCapacity];
161         System.arraycopy(array, 0, newArray, 0, size);
162 
163         return newArray;
164     }
165 }