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     public void setLength(int length) {
41         if (length < 0 || length > pos) {
42             throw new IllegalArgumentException("length: " + length + " (length: >= 0, <= " + pos + ')');
43         }
44         this.pos = length;
45     }
46
47     @Override
48     public int length() {
49         return pos;
50     }
51
52     @Override
53     public char charAt(int index) {
54         if (index > pos) {
55             throw new IndexOutOfBoundsException();
56         }
57         return chars[index];
58     }
59
60     /**
61      * Access a value in this {@link CharSequence}.
62      * This method is considered unsafe as index values are assumed to be legitimate.
63      * Only underlying array bounds checking is done.
64      * @param index The index to access the underlying array at.
65      * @return The value at {@code index}.
66      */

67     public char charAtUnsafe(int index) {
68         return chars[index];
69     }
70
71     @Override
72     public AppendableCharSequence subSequence(int start, int end) {
73         if (start == end) {
74             // If start and end index is the same we need to return an empty sequence to conform to the interface.
75             // As our expanding logic depends on the fact that we have a char[] with length > 0 we need to construct
76             // an instance for which this is true.
77             return new AppendableCharSequence(Math.min(16, chars.length));
78         }
79         return new AppendableCharSequence(Arrays.copyOfRange(chars, start, end));
80     }
81
82     @Override
83     public AppendableCharSequence append(char c) {
84         if (pos == chars.length) {
85             char[] old = chars;
86             chars = new char[old.length << 1];
87             System.arraycopy(old, 0, chars, 0, old.length);
88         }
89         chars[pos++] = c;
90         return this;
91     }
92
93     @Override
94     public AppendableCharSequence append(CharSequence csq) {
95         return append(csq, 0, csq.length());
96     }
97
98     @Override
99     public AppendableCharSequence append(CharSequence csq, int start, int end) {
100         if (csq.length() < end) {
101             throw new IndexOutOfBoundsException("expected: csq.length() >= ("
102                     + end + "),but actual is (" + csq.length() + ")");
103         }
104         int length = end - start;
105         if (length > chars.length - pos) {
106             chars = expand(chars, pos + length, pos);
107         }
108         if (csq instanceof AppendableCharSequence) {
109             // Optimize append operations via array copy
110             AppendableCharSequence seq = (AppendableCharSequence) csq;
111             char[] src = seq.chars;
112             System.arraycopy(src, start, chars, pos, length);
113             pos += length;
114             return this;
115         }
116         for (int i = start; i < end; i++) {
117             chars[pos++] = csq.charAt(i);
118         }
119
120         return this;
121     }
122
123     /**
124      * Reset the {@link AppendableCharSequence}. Be aware this will only reset the current internal position and not
125      * shrink the internal char array.
126      */

127     public void reset() {
128         pos = 0;
129     }
130
131     @Override
132     public String toString() {
133         return new String(chars, 0, pos);
134     }
135
136     /**
137      * Create a new {@link String} from the given start to end.
138      */

139     public String substring(int start, int end) {
140         int length = end - start;
141         if (start > pos || length > pos) {
142             throw new IndexOutOfBoundsException("expected: start and length <= ("
143                     + pos + ")");
144         }
145         return new String(chars, start, length);
146     }
147
148     /**
149      * Create a new {@link String} from the given start to end.
150      * This method is considered unsafe as index values are assumed to be legitimate.
151      * Only underlying array bounds checking is done.
152      */

153     public String subStringUnsafe(int start, int end) {
154         return new String(chars, start, end - start);
155     }
156
157     private static char[] expand(char[] array, int neededSpace, int size) {
158         int newCapacity = array.length;
159         do {
160             // double capacity until it is big enough
161             newCapacity <<= 1;
162
163             if (newCapacity < 0) {
164                 throw new IllegalStateException();
165             }
166
167         } while (neededSpace > newCapacity);
168
169         char[] newArray = new char[newCapacity];
170         System.arraycopy(array, 0, newArray, 0, size);
171
172         return newArray;
173     }
174 }
175