1 package org.robolectric.res.android;
2
3 import java.io.File;
4 import java.nio.file.Path;
5 import org.robolectric.res.Fs;
6
7 // transliterated from
8 // https://android.googlesource.com/platform/system/core/+/android-9.0.0_r12/libutils/String8.cpp
9 // and
10 // https://android.googlesource.com/platform/system/core/+/android-9.0.0_r12/include/utils/String8.h
11 @SuppressWarnings("NewApi")
12 public class String8 {
13
14 private StringBuilder mString;
15
String8()16 public String8() {
17 this("");
18 }
19
String8(Path value)20 public String8(Path value) {
21 this(Fs.externalize(value));
22 }
23
String8(String value)24 public String8(String value) {
25 mString = new StringBuilder(value);
26 }
27
String8(String8 path)28 public String8(String8 path) {
29 this(path.string());
30 }
31
String8(String value, int len)32 public String8(String value, int len) {
33 this(value.substring(0, len));
34 }
35
length()36 int length() {
37 return mString.length();
38 }
39 //String8 String8::format(const char* fmt, ...)
40 //{
41 // va_list args;
42 // va_start(args, fmt);
43 // String8 result(formatV(fmt, args));
44 // va_end(args);
45 // return result;
46 //}
47 //String8 String8::formatV(const char* fmt, va_list args)
48 //{
49 // String8 result;
50 // result.appendFormatV(fmt, args);
51 // return result;
52 //}
53 //void String8::clear() {
54 // SharedBuffer::bufferFromData(mString)->release();
55 // mString = getEmptyString();
56 //}
57 //void String8::setTo(const String8& other)
58 //{
59 // SharedBuffer::bufferFromData(other.mString)->acquire();
60 // SharedBuffer::bufferFromData(mString)->release();
61 // mString = other.mString;
62 //}
63 //status_t String8::setTo(const char* other)
64 //{
65 // const char *newString = allocFromUTF8(other, strlen(other));
66 // SharedBuffer::bufferFromData(mString)->release();
67 // mString = newString;
68 // if (mString) return NO_ERROR;
69 // mString = getEmptyString();
70 // return NO_MEMORY;
71 //}
72 //status_t String8::setTo(const char* other, size_t len)
73 //{
74 // const char *newString = allocFromUTF8(other, len);
75 // SharedBuffer::bufferFromData(mString)->release();
76 // mString = newString;
77 // if (mString) return NO_ERROR;
78 // mString = getEmptyString();
79 // return NO_MEMORY;
80 //}
81 //status_t String8::setTo(const char16_t* other, size_t len)
82 //{
83 // const char *newString = allocFromUTF16(other, len);
84 // SharedBuffer::bufferFromData(mString)->release();
85 // mString = newString;
86 // if (mString) return NO_ERROR;
87 // mString = getEmptyString();
88 // return NO_MEMORY;
89 //}
90 //status_t String8::setTo(const char32_t* other, size_t len)
91 //{
92 // const char *newString = allocFromUTF32(other, len);
93 // SharedBuffer::bufferFromData(mString)->release();
94 // mString = newString;
95 // if (mString) return NO_ERROR;
96 // mString = getEmptyString();
97 // return NO_MEMORY;
98 //}
99 //status_t String8::append(const String8& other)
100 //{
101 // const size_t otherLen = other.bytes();
102 // if (bytes() == 0) {
103 // setTo(other);
104 // return NO_ERROR;
105 // } else if (otherLen == 0) {
106 // return NO_ERROR;
107 // }
108 // return real_append(other.string(), otherLen);
109 //}
append(final String other)110 public String8 append(final String other) {
111 mString.append(other);
112 return this;
113 }
114 //status_t String8::append(const char* other, size_t otherLen)
115 //{
116 // if (bytes() == 0) {
117 // return setTo(other, otherLen);
118 // } else if (otherLen == 0) {
119 // return NO_ERROR;
120 // }
121 // return real_append(other, otherLen);
122 //}
123 //status_t String8::appendFormat(const char* fmt, ...)
124 //{
125 // va_list args;
126 // va_start(args, fmt);
127 // status_t result = appendFormatV(fmt, args);
128 // va_end(args);
129 // return result;
130 //}
131 //status_t String8::appendFormatV(const char* fmt, va_list args)
132 //{
133 // int n, result = NO_ERROR;
134 // va_list tmp_args;
135 // /* args is undefined after vsnprintf.
136 // * So we need a copy here to avoid the
137 // * second vsnprintf access undefined args.
138 // */
139 // va_copy(tmp_args, args);
140 // n = vsnprintf(NULL, 0, fmt, tmp_args);
141 // va_end(tmp_args);
142 // if (n != 0) {
143 // size_t oldLength = length();
144 // char* buf = lockBuffer(oldLength + n);
145 // if (buf) {
146 // vsnprintf(buf + oldLength, n + 1, fmt, args);
147 // } else {
148 // result = NO_MEMORY;
149 // }
150 // }
151 // return result;
152 //}
153 //status_t String8::real_append(const char* other, size_t otherLen)
154 //{
155 // const size_t myLen = bytes();
156 //
157 // SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
158 // ->editResize(myLen+otherLen+1);
159 // if (buf) {
160 // char* str = (char*)buf->data();
161 // mString = str;
162 // str += myLen;
163 // memcpy(str, other, otherLen);
164 // str[otherLen] = '\0';
165 // return NO_ERROR;
166 // }
167 // return NO_MEMORY;
168 //}
169 //char* String8::lockBuffer(size_t size)
170 //{
171 // SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
172 // ->editResize(size+1);
173 // if (buf) {
174 // char* str = (char*)buf->data();
175 // mString = str;
176 // return str;
177 // }
178 // return NULL;
179 //}
180 //void String8::unlockBuffer()
181 //{
182 // unlockBuffer(strlen(mString));
183 //}
184 //status_t String8::unlockBuffer(size_t size)
185 //{
186 // if (size != this->size()) {
187 // SharedBuffer* buf = SharedBuffer::bufferFromData(mString)
188 // ->editResize(size+1);
189 // if (! buf) {
190 // return NO_MEMORY;
191 // }
192 // char* str = (char*)buf->data();
193 // str[size] = 0;
194 // mString = str;
195 // }
196 // return NO_ERROR;
197 //}
198 //ssize_t String8::find(const char* other, size_t start) const
199 //{
200 // size_t len = size();
201 // if (start >= len) {
202 // return -1;
203 // }
204 // const char* s = mString+start;
205 // const char* p = strstr(s, other);
206 // return p ? p-mString : -1;
207 //}
208 //bool String8::removeAll(const char* other) {
209 // ssize_t index = find(other);
210 // if (index < 0) return false;
211 // char* buf = lockBuffer(size());
212 // if (!buf) return false; // out of memory
213 // size_t skip = strlen(other);
214 // size_t len = size();
215 // size_t tail = index;
216 // while (size_t(index) < len) {
217 // ssize_t next = find(other, index + skip);
218 // if (next < 0) {
219 // next = len;
220 // }
221 // memmove(buf + tail, buf + index + skip, next - index - skip);
222 // tail += next - index - skip;
223 // index = next;
224 // }
225 // unlockBuffer(tail);
226 // return true;
227 //}
228 //void String8::toLower()
229 //{
230 // toLower(0, size());
231 //}
232 //void String8::toLower(size_t start, size_t length)
233 //{
234 // const size_t len = size();
235 // if (start >= len) {
236 // return;
237 // }
238 // if (start+length > len) {
239 // length = len-start;
240 // }
241 // char* buf = lockBuffer(len);
242 // buf += start;
243 // while (length > 0) {
244 // *buf = tolower(*buf);
245 // buf++;
246 // length--;
247 // }
248 // unlockBuffer(len);
249 //}
250 //void String8::toUpper()
251 //{
252 // toUpper(0, size());
253 //}
254 //void String8::toUpper(size_t start, size_t length)
255 //{
256 // const size_t len = size();
257 // if (start >= len) {
258 // return;
259 // }
260 // if (start+length > len) {
261 // length = len-start;
262 // }
263 // char* buf = lockBuffer(len);
264 // buf += start;
265 // while (length > 0) {
266 // *buf = toupper(*buf);
267 // buf++;
268 // length--;
269 // }
270 // unlockBuffer(len);
271 //}
272 //size_t String8::getUtf32Length() const
273 //{
274 // return utf8_to_utf32_length(mString, length());
275 //}
276 //int32_t String8::getUtf32At(size_t index, size_t *next_index) const
277 //{
278 // return utf32_from_utf8_at(mString, length(), index, next_index);
279 //}
280 //void String8::getUtf32(char32_t* dst) const
281 //{
282 // utf8_to_utf32(mString, length(), dst);
283 //}
284 //// ---------------------------------------------------------------------------
285 //// Path functions
286 //void String8::setPathName(const char* name)
287 //{
288 // setPathName(name, strlen(name));
289 //}
290 //void String8::setPathName(const char* name, size_t len)
291 //{
292 // char* buf = lockBuffer(len);
293 // memcpy(buf, name, len);
294 // // remove trailing path separator, if present
295 // if (len > 0 && buf[len-1] == OS_PATH_SEPARATOR)
296 // len--;
297 // buf[len] = '\0';
298 // unlockBuffer(len);
299 //}
getPathLeaf()300 String8 getPathLeaf() {
301 final int cp;
302 final String buf = mString.toString();
303 cp = buf.lastIndexOf('/');
304 if (cp == -1) {
305 return new String8(this);
306 } else {
307 return new String8(buf.substring(cp + 1));
308 }
309 }
310 //String8 String8::getPathDir(void) const
311 //{
312 // const char* cp;
313 // const char*const str = mString;
314 // cp = strrchr(str, OS_PATH_SEPARATOR);
315 // if (cp == NULL)
316 // return String8("");
317 // else
318 // return String8(str, cp - str);
319 //}
320 //String8 String8::walkPath(String8* outRemains) const
321 //{
322 // const char* cp;
323 // const char*const str = mString;
324 // const char* buf = str;
325 // cp = strchr(buf, OS_PATH_SEPARATOR);
326 // if (cp == buf) {
327 // // don't include a leading '/'.
328 // buf = buf+1;
329 // cp = strchr(buf, OS_PATH_SEPARATOR);
330 // }
331 // if (cp == NULL) {
332 // String8 res = buf != str ? String8(buf) : *this;
333 // if (outRemains) *outRemains = String8("");
334 // return res;
335 // }
336 // String8 res(buf, cp-buf);
337 // if (outRemains) *outRemains = String8(cp+1);
338 // return res;
339 //}
340
341 /*
342 * Helper function for finding the start of an extension in a pathname.
343 *
344 * Returns a index inside mString, or -1 if no extension was found.
345 */
find_extension()346 private int find_extension()
347 {
348 int lastSlashIndex;
349
350 final StringBuilder str = mString;
351 // only look at the filename
352 lastSlashIndex = str.lastIndexOf(File.pathSeparator);
353 if (lastSlashIndex == -1) {
354 lastSlashIndex = 0;
355 } else {
356 lastSlashIndex++;
357 }
358 // find the last dot
359 return str.lastIndexOf(".", lastSlashIndex);
360 }
361
getPathExtension()362 public String getPathExtension()
363 {
364 int extIndex;
365 extIndex = find_extension();
366 if (extIndex != -1) {
367 return mString.substring(extIndex);
368 }
369 else {
370 return "";
371 }
372 }
373
getBasePath()374 String8 getBasePath() {
375 int extIndex;
376 extIndex = find_extension();
377 if (extIndex == -1) {
378 return new String8(this);
379 } else {
380 return new String8(mString.substring(extIndex));
381 }
382 }
383
appendPath(String name)384 public String8 appendPath(String name) {
385 if (name.length() == 0) {
386 // nothing to do
387 return this;
388 }
389 if (name.charAt(0) != '/') {
390 mString.append('/');
391 }
392 mString.append(name);
393 return this;
394 }
395
396 //String8& String8::convertToResPath()
397 //{
398 //#if OS_PATH_SEPARATOR != RES_PATH_SEPARATOR
399 // size_t len = length();
400 // if (len > 0) {
401 // char * buf = lockBuffer(len);
402 // for (char * end = buf + len; buf < end; ++buf) {
403 // if (*buf == OS_PATH_SEPARATOR)
404 // *buf = RES_PATH_SEPARATOR;
405 // }
406 // unlockBuffer(len);
407 // }
408 //#endif
409 // return *this;
410 //}
411 //}; // namespace android
412
string()413 public final String string() {
414 return mString.toString();
415 }
416
417 @Override
toString()418 public String toString() {
419 return mString.toString();
420 }
421
422 @Override
equals(Object o)423 public boolean equals(Object o) {
424 if (this == o) {
425 return true;
426 }
427 if (!(o instanceof String8)) {
428 return false;
429 }
430
431 String8 string8 = (String8) o;
432
433 return mString != null ? mString.toString().equals(string8.mString.toString()) : string8.mString == null;
434 }
435
436 @Override
hashCode()437 public int hashCode() {
438 return mString != null ? mString.hashCode() : 0;
439 }
440
441 }
442
443
444