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          return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end));
67      }
68  
69      @Override
70      public AppendableCharSequence append(char c) {
71          if (pos == chars.length) {
72              char[] old = chars;
73              chars = new char[old.length << 1];
74              System.arraycopy(old, 0, chars, 0, old.length);
75          }
76          chars[pos++] = c;
77          return this;
78      }
79  
80      @Override
81      public AppendableCharSequence append(CharSequence csq) {
82          return append(csq, 0, csq.length());
83      }
84  
85      @Override
86      public AppendableCharSequence append(CharSequence csq, int start, int end) {
87          if (csq.length() < end) {
88              throw new IndexOutOfBoundsException();
89          }
90          int length = end - start;
91          if (length > chars.length - pos) {
92              chars = expand(chars, pos + length, pos);
93          }
94          if (csq instanceof AppendableCharSequence) {
95              // Optimize append operations via array copy
96              AppendableCharSequence seq = (AppendableCharSequence) csq;
97              char[] src = seq.chars;
98              System.arraycopy(src, start, chars, pos, length);
99              pos += length;
100             return this;
101         }
102         for (int i = start; i < end; i++) {
103             chars[pos++] = csq.charAt(i);
104         }
105 
106         return this;
107     }
108 
109     /**
110      * Reset the {@link AppendableCharSequence}. Be aware this will only reset the current internal position and not
111      * shrink the internal char array.
112      */
113     public void reset() {
114         pos = 0;
115     }
116 
117     @Override
118     public String toString() {
119         return new String(chars, 0, pos);
120     }
121 
122     /**
123      * Create a new {@link String} from the given start to end.
124      */
125     public String substring(int start, int end) {
126         int length = end - start;
127         if (start > pos || length > pos) {
128             throw new IndexOutOfBoundsException();
129         }
130         return new String(chars, start, length);
131     }
132 
133     /**
134      * Create a new {@link String} from the given start to end.
135      * This method is considered unsafe as index values are assumed to be legitimate.
136      * Only underlying array bounds checking is done.
137      */
138     public String subStringUnsafe(int start, int end) {
139         return new String(chars, start, end - start);
140     }
141 
142     private static char[] expand(char[] array, int neededSpace, int size) {
143         int newCapacity = array.length;
144         do {
145             // double capacity until it is big enough
146             newCapacity <<= 1;
147 
148             if (newCapacity < 0) {
149                 throw new IllegalStateException();
150             }
151 
152         } while (neededSpace > newCapacity);
153 
154         char[] newArray = new char[newCapacity];
155         System.arraycopy(array, 0, newArray, 0, size);
156 
157         return newArray;
158     }
159 }