001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU General
007: * Public License Version 2 only ("GPL") or the Common Development and Distribution
008: * License("CDDL") (collectively, the "License"). You may not use this file except in
009: * compliance with the License. You can obtain a copy of the License at
010: * http://www.netbeans.org/cddl-gplv2.html or nbbuild/licenses/CDDL-GPL-2-CP. See the
011: * License for the specific language governing permissions and limitations under the
012: * License. When distributing the software, include this License Header Notice in
013: * each file and include the License file at nbbuild/licenses/CDDL-GPL-2-CP. Sun
014: * designates this particular file as subject to the "Classpath" exception as
015: * provided by Sun in the GPL Version 2 section of the License file that
016: * accompanied this code. If applicable, add the following below the License Header,
017: * with the fields enclosed by brackets [] replaced by your own identifying
018: * information: "Portions Copyrighted [year] [name of copyright owner]"
019: *
020: * Contributor(s):
021: *
022: * The Original Software is NetBeans. The Initial Developer of the Original Software
023: * is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun Microsystems, Inc. All
024: * Rights Reserved.
025: *
026: * If you wish your version of this file to be governed by only the CDDL or only the
027: * GPL Version 2, indicate your decision by adding "[Contributor] elects to include
028: * this software in this distribution under the [CDDL or GPL Version 2] license." If
029: * you do not indicate a single choice of license, a recipient has the option to
030: * distribute your version of this file under either the CDDL, the GPL Version 2 or
031: * to extend the choice of license to its licensees as provided above. However, if
032: * you add GPL Version 2 code and therefore, elected the GPL Version 2 license, then
033: * the option applies only if the new code is made subject to such option by the
034: * copyright holder.
035: */
036:
037: package org.netbeans.installer.utils;
038:
039: import java.io.ByteArrayInputStream;
040: import java.io.ByteArrayOutputStream;
041: import java.io.IOException;
042: import java.io.InputStream;
043: import java.io.PrintWriter;
044: import java.io.StringWriter;
045: import java.io.UnsupportedEncodingException;
046: import java.net.MalformedURLException;
047: import java.net.URL;
048: import java.text.MessageFormat;
049: import java.text.SimpleDateFormat;
050: import java.util.ArrayList;
051: import java.util.Arrays;
052: import java.util.Date;
053: import java.util.List;
054: import java.util.Locale;
055: import java.util.Map;
056: import java.util.Properties;
057: import java.util.regex.Matcher;
058: import java.util.regex.Pattern;
059: import org.netbeans.installer.utils.helper.Status;
060: import org.netbeans.installer.utils.exceptions.ParseException;
061: import org.netbeans.installer.utils.helper.Platform;
062:
063: /**
064: *
065: * @author Kirill Sorokin
066: */
067: public abstract class StringUtils {
068: ////////////////////////////////////////////////////////////////////////////
069: // Static
070: public static String format(final String message,
071: final Object... arguments) {
072: return MessageFormat.format(message, arguments);
073: }
074:
075: public static String leftTrim(final String string) {
076: return string.replaceFirst(LEFT_WHITESPACE, EMPTY_STRING);
077: }
078:
079: public static String rightTrim(final String string) {
080: return string.replaceFirst(RIGHT_WHITESPACE, EMPTY_STRING);
081: }
082:
083: public static char fetchMnemonic(final String string) {
084: // source is org.openide.awt.Mnemonics
085: int i = findMnemonicAmpersand(string);
086: return (i >= 0) ? string.charAt(i + 1) : NO_MNEMONIC;
087: }
088:
089: public static String stripMnemonic(final String string) {
090: int i = findMnemonicAmpersand(string);
091: String s = string;
092: if (i >= 0) {
093: if (string.startsWith("<html>")) { // NOI18N
094: // Workaround for JDK bug #6510775
095: s = string.substring(0, i) + "<u>"
096: + string.charAt(i + 1) + "</u>" + // NOI18N
097: string.substring(i + 2);
098: } else {
099: s = string.substring(0, i) + string.substring(i + 1);
100: }
101: }
102: return s;
103: }
104:
105: // source - org.openide.awt.Mnenonics;
106: public static int findMnemonicAmpersand(String text) {
107: int i = -1;
108: boolean isHTML = text.startsWith("<html>");
109:
110: do {
111: // searching for the next ampersand
112: i = text.indexOf(MNEMONIC_CHAR, i + 1);
113:
114: if ((i >= 0) && ((i + 1) < text.length())) {
115: if (isHTML) {
116: boolean startsEntity = false;
117: for (int j = i + 1; j < text.length(); j++) {
118: char c = text.charAt(j);
119: if (c == ';') {
120: startsEntity = true;
121: break;
122: }
123: if (!Character.isLetterOrDigit(c)) {
124: break;
125: }
126: }
127: if (!startsEntity) {
128: return i;
129: }
130: } else {
131: // before ' '
132: if (text.charAt(i + 1) == ' ') {
133: continue;
134:
135: // before ', and after '
136: } else if ((text.charAt(i + 1) == '\'') && (i > 0)
137: && (text.charAt(i - 1) == '\'')) {
138: continue;
139: }
140:
141: // ampersand is marking mnemonics
142: return i;
143: }
144: }
145: } while (i >= 0);
146:
147: return -1;
148: }
149:
150: public static String capitalizeFirst(final String string) {
151: return EMPTY_STRING + Character.toUpperCase(string.charAt(0))
152: + string.substring(1);
153: }
154:
155: public static String getGetterName(final String propertyName) {
156: return "get" + capitalizeFirst(propertyName);
157: }
158:
159: public static String getBooleanGetterName(final String propertyName) {
160: return "is" + capitalizeFirst(propertyName);
161: }
162:
163: public static String getSetterName(final String propertyName) {
164: return "set" + capitalizeFirst(propertyName);
165: }
166:
167: public static String getFilenameFromUrl(final String string) {
168: String url = string.trim();
169:
170: int index = Math.max(url.lastIndexOf(FORWARD_SLASH), url
171: .lastIndexOf(BACK_SLASH));
172: int length = url.length();
173: return (index > 0 && (index < length - 1)) ? url.substring(
174: index + 1, length) : null;
175: }
176:
177: public static String formatSize(final long longBytes) {
178: StringBuffer result = new StringBuffer();
179:
180: double bytes = (double) longBytes;
181:
182: // try as GB
183: double gigabytes = bytes / 1024. / 1024. / 1024.;
184: if (gigabytes > 1.) {
185: return String.format("%.1f GB", gigabytes);
186: }
187:
188: // try as MB
189: double megabytes = bytes / 1024. / 1024.;
190: if (megabytes > 1.) {
191: return String.format("%.1f MB", megabytes);
192: }
193:
194: // try as KB
195: double kilobytes = bytes / 1024.;
196: if (kilobytes > .5) {
197: return String.format("%.1f KB", kilobytes);
198: }
199:
200: // return as bytes
201: return EMPTY_STRING + longBytes + " B";
202: }
203:
204: public static String asHexString(final byte[] bytes) {
205: StringBuilder builder = new StringBuilder();
206:
207: for (int i = 0; i < bytes.length; i++) {
208: byte b = bytes[i];
209:
210: String byteHex = Integer.toHexString(b);
211: if (byteHex.length() == 1) {
212: byteHex = "0" + byteHex;
213: }
214: if (byteHex.length() > 2) {
215: byteHex = byteHex.substring(byteHex.length() - 2);
216: }
217:
218: builder.append(byteHex);
219: }
220:
221: return builder.toString();
222: }
223:
224: public static String pad(final String string, final int number) {
225: StringBuilder builder = new StringBuilder();
226:
227: for (int i = 0; i < number; i++) {
228: builder.append(string);
229: }
230:
231: return builder.toString();
232: }
233:
234: public static String escapeRegExp(final String string) {
235: return string.replace(BACK_SLASH, BACK_SLASH + BACK_SLASH);
236: }
237:
238: public static String[] splitByLines(CharSequence cs) {
239: return splitByLines(cs.toString());
240: }
241:
242: public static String[] splitByLines(String s) {
243: return s.split(NEW_LINE_PATTERN, -1);
244: }
245:
246: public static String readStream(final InputStream stream)
247: throws IOException {
248: StringBuilder builder = new StringBuilder();
249:
250: byte[] buffer = new byte[1024];
251: while (stream.available() > 0) {
252: int read = stream.read(buffer);
253:
254: String readString = new String(buffer, 0, read);
255: String[] strings = splitByLines(readString);
256: for (int i = 0; i < strings.length; i++) {
257: builder.append(strings[i]);
258: if (i != strings.length - 1) {
259: builder.append(SystemUtils.getLineSeparator());
260: }
261: }
262: }
263:
264: return builder.toString();
265: }
266:
267: public static String httpFormat(final Date date) {
268: return new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z",
269: Locale.US).format(date);
270: }
271:
272: public static String asPath(final Class clazz) {
273: return clazz.getPackage().getName().replace('.', '/');
274: }
275:
276: public static String replace(final String string,
277: final String replacement, final int begin, final int end) {
278: return string.substring(0, begin) + replacement
279: + string.substring(end);
280: }
281:
282: /**
283: * Escapes the path using the platform-specific escape rules.
284: *
285: * @param path Path to escape.
286: * @return Escaped path.
287: */
288: public static String escapePath(final String path) {
289: String localPath = path;
290:
291: if (localPath.indexOf(' ') > -1) {
292: if (SystemUtils.isWindows()) {
293: localPath = QUOTE + localPath + QUOTE;
294: } else {
295: localPath = localPath
296: .replace(SPACE, BACK_SLASH + SPACE); //NOI18N
297: }
298: }
299:
300: return localPath;
301: }
302:
303: /**
304: * Joins a command string and its arguments into a single string using the
305: * platform-specific rules.
306: *
307: * @param commandArray The command and its arguments.
308: * @return The joined string.
309: */
310: public static String joinCommand(final String... commandArray) {
311: StringBuffer command = new StringBuffer();
312:
313: for (int i = 0; i < commandArray.length; i++) {
314: command.append(escapePath(commandArray[i]));
315: if (i != commandArray.length - 1) {
316: command.append(SPACE); //NOI18N
317: }
318: }
319:
320: return command.toString();
321: }
322:
323: // object -> string .////////////////////////////////////////////////////////////
324: public static String asString(final Throwable throwable) {
325: final StringWriter writer = new StringWriter();
326:
327: throwable.printStackTrace(new PrintWriter(writer));
328: return writer.toString();
329: }
330:
331: public static String asString(final List<? extends Object> objects) {
332: return asString(objects.toArray(), 0, objects.size(), ", ");
333: }
334:
335: public static String asString(final List<? extends Object> objects,
336: final String separator) {
337: return asString(objects.toArray(), 0, objects.size(), separator);
338: }
339:
340: public static String asString(final List<? extends Object> objects,
341: final int offset, final int length, final String separator) {
342: return asString(objects.toArray(), offset, length, separator);
343: }
344:
345: public static String asString(final Object[] objects) {
346: return asString(objects, 0, objects.length, ", ");
347: }
348:
349: public static String asString(final Object[] objects,
350: final String separator) {
351: return asString(objects, 0, objects.length, separator);
352: }
353:
354: public static String asString(final Object[] objects,
355: final int offset, final int length, final String separator) {
356: final StringBuilder result = new StringBuilder();
357:
358: for (int i = offset; i < offset + length; i++) {
359: result.append(EMPTY_STRING + objects[i]);
360:
361: if (i != offset + length - 1) {
362: result.append(separator);
363: }
364: }
365:
366: return result.toString();
367: }
368:
369: // base64 ///////////////////////////////////////////////////////////////////////
370: public static String base64Encode(final String string)
371: throws UnsupportedEncodingException {
372: return base64Encode(string, ENCODING_UTF8);
373: }
374:
375: public static String base64Encode(final String string,
376: final String charset) throws UnsupportedEncodingException {
377: final StringBuilder builder = new StringBuilder();
378: final byte[] bytes = string.getBytes(charset);
379:
380: int i;
381: for (i = 0; i < bytes.length - 2; i += 3) {
382: int byte1 = bytes[i] & BIN_11111111;
383: int byte2 = bytes[i + 1] & BIN_11111111;
384: int byte3 = bytes[i + 2] & BIN_11111111;
385:
386: builder.append(BASE64_TABLE[byte1 >> 2]);
387: builder.append(BASE64_TABLE[((byte1 << 4) & BIN_00110000)
388: | (byte2 >> 4)]);
389: builder.append(BASE64_TABLE[((byte2 << 2) & BIN_00111100)
390: | (byte3 >> 6)]);
391: builder.append(BASE64_TABLE[byte3 & BIN_00111111]);
392: }
393:
394: if (i == bytes.length - 2) {
395: int byte1 = bytes[i] & BIN_11111111;
396: int byte2 = bytes[i + 1] & BIN_11111111;
397:
398: builder.append(BASE64_TABLE[byte1 >> 2]);
399: builder.append(BASE64_TABLE[((byte1 << 4) & BIN_00110000)
400: | (byte2 >> 4)]);
401: builder.append(BASE64_TABLE[(byte2 << 2) & BIN_00111100]);
402: builder.append(BASE64_PAD);
403: }
404:
405: if (i == bytes.length - 1) {
406: int byte1 = bytes[i] & BIN_11111111;
407:
408: builder.append(BASE64_TABLE[byte1 >> 2]);
409: builder.append(BASE64_TABLE[(byte1 << 4) & BIN_00110000]);
410: builder.append(BASE64_PAD);
411: builder.append(BASE64_PAD);
412: }
413:
414: return builder.toString();
415: }
416:
417: public static String base64Decode(final String string)
418: throws UnsupportedEncodingException {
419: return base64Decode(string, ENCODING_UTF8);
420: }
421:
422: public static String base64Decode(final String string,
423: final String charset) throws UnsupportedEncodingException {
424: int completeBlocksNumber = string.length() / 4;
425: int missingBytesNumber = 0;
426:
427: if (string.endsWith("=")) {
428: completeBlocksNumber--;
429: missingBytesNumber++;
430: }
431: if (string.endsWith("==")) {
432: missingBytesNumber++;
433: }
434:
435: int decodedLength = (completeBlocksNumber * 3)
436: + (3 - missingBytesNumber);
437: byte[] decodedBytes = new byte[decodedLength];
438:
439: int encodedCounter = 0;
440: int decodedCounter = 0;
441: for (int i = 0; i < completeBlocksNumber; i++) {
442: int byte1 = BASE64_REVERSE_TABLE[string
443: .charAt(encodedCounter++)];
444: int byte2 = BASE64_REVERSE_TABLE[string
445: .charAt(encodedCounter++)];
446: int byte3 = BASE64_REVERSE_TABLE[string
447: .charAt(encodedCounter++)];
448: int byte4 = BASE64_REVERSE_TABLE[string
449: .charAt(encodedCounter++)];
450:
451: decodedBytes[decodedCounter++] = (byte) ((byte1 << 2) | (byte2 >> 4));
452: decodedBytes[decodedCounter++] = (byte) ((byte2 << 4) | (byte3 >> 2));
453: decodedBytes[decodedCounter++] = (byte) ((byte3 << 6) | byte4);
454: }
455:
456: if (missingBytesNumber == 1) {
457: int byte1 = BASE64_REVERSE_TABLE[string
458: .charAt(encodedCounter++)];
459: int byte2 = BASE64_REVERSE_TABLE[string
460: .charAt(encodedCounter++)];
461: int byte3 = BASE64_REVERSE_TABLE[string
462: .charAt(encodedCounter++)];
463:
464: decodedBytes[decodedCounter++] = (byte) ((byte1 << 2) | (byte2 >> 4));
465: decodedBytes[decodedCounter++] = (byte) ((byte2 << 4) | (byte3 >> 2));
466: }
467:
468: if (missingBytesNumber == 2) {
469: int byte1 = BASE64_REVERSE_TABLE[string
470: .charAt(encodedCounter++)];
471: int byte2 = BASE64_REVERSE_TABLE[string
472: .charAt(encodedCounter++)];
473:
474: decodedBytes[decodedCounter++] = (byte) ((byte1 << 2) | (byte2 >> 4));
475: }
476:
477: return new String(decodedBytes, charset);
478: }
479:
480: // normal <-> ascii only ////////////////////////////////////////////////////////
481: public static String parseAscii(final String string) {
482: final Properties properties = new Properties();
483:
484: // we don't really care about enconding here, as the input string is
485: // expected to be ASCII-only, which means it's the same for any encoding
486: try {
487: properties.load(new ByteArrayInputStream(("key=" + string)
488: .getBytes()));
489: } catch (IOException e) {
490: ErrorManager.notifyWarning("Cannot parse string", e);
491: return string;
492: }
493:
494: return (String) properties.get("key");
495: }
496:
497: public static String convertToAscii(final String string) {
498: final Properties properties = new Properties();
499:
500: properties.put("uberkey", string);
501:
502: final ByteArrayOutputStream baos = new ByteArrayOutputStream();
503: try {
504: properties.store(baos, EMPTY_STRING);
505: } catch (IOException e) {
506: ErrorManager.notifyWarning("Cannot convert string", e);
507: return string;
508: }
509:
510: final Matcher matcher = Pattern.compile("uberkey=(.*)$",
511: Pattern.MULTILINE).matcher(baos.toString());
512:
513: if (matcher.find()) {
514: return matcher.group(1);
515: } else {
516: return string;
517: }
518: }
519:
520: // string -> object /////////////////////////////////////////////////////////////
521: public static List<String> asList(final String string) {
522: return asList(string, ", ");
523: }
524:
525: public static List<String> asList(final String string,
526: final String separator) {
527: return Arrays.asList(string.split(separator));
528: }
529:
530: public static Locale parseLocale(final String string) {
531: final String[] parts = string.split("_");
532:
533: switch (parts.length) {
534: case 1:
535: return new Locale(parts[0]);
536: case 2:
537: return new Locale(parts[0], parts[1]);
538: default:
539: return new Locale(parts[0], parts[1], parts[2]);
540: }
541: }
542:
543: public static String getLocalizedString(
544: final Map<Locale, String> stringsMap, final Locale inLocale) {
545: final String message = stringsMap.get(inLocale);
546: if (message == null
547: && !inLocale.equals(new Locale(EMPTY_STRING))) {
548: final Locale upLocale;
549: if (!inLocale.getVariant().equals(EMPTY_STRING)) {
550: upLocale = new Locale(inLocale.getLanguage(), inLocale
551: .getCountry());
552: } else if (!inLocale.getCountry().equals(EMPTY_STRING)) {
553: upLocale = new Locale(inLocale.getLanguage());
554: } else {
555: upLocale = new Locale(EMPTY_STRING);
556: }
557: return getLocalizedString(stringsMap, upLocale);
558: } else {
559: return message;
560: }
561: }
562:
563: public static URL parseUrl(final String string)
564: throws ParseException {
565: try {
566: return new URL(string);
567: } catch (MalformedURLException e) {
568: throw new ParseException("Cannot parse URL", e);
569: }
570: }
571:
572: public static Platform parsePlatform(final String string)
573: throws ParseException {
574: for (Platform platform : Platform.values()) {
575: if (platform.getCodeName().equals(string)) {
576: return platform;
577: }
578: }
579:
580: throw new ParseException(ResourceUtils.getString(
581: StringUtils.ERROR_UNKNOWN_PLATFORM, string));
582: }
583:
584: public static List<Platform> parsePlatforms(final String string)
585: throws ParseException {
586: final List<Platform> platforms = new ArrayList<Platform>();
587:
588: for (String name : asList(string, " ")) {
589: final Platform platform = parsePlatform(name);
590:
591: if (!platforms.contains(platform)) {
592: platforms.add(platform);
593: }
594: }
595:
596: return platforms;
597: }
598:
599: public static Status parseStatus(final String string)
600: throws ParseException {
601: for (Status status : Status.values()) {
602: if (status.getName().equals(string)) {
603: return status;
604: }
605: }
606:
607: throw new ParseException(ResourceUtils.getString(
608: StringUtils.ERROR_CANNOT_PARSE_STATUS, string));
609: }
610:
611: /////////////////////////////////////////////////////////////////////////////////
612: // Constants
613: public static final String BACK_SLASH = "\\"; // NOI18N
614: public static final String FORWARD_SLASH = "/"; // NOI18N
615: public static final String DOUBLE_BACK_SLASH = "\\\\"; // NOI18N
616:
617: public static final String ENCODING_UTF8 = "UTF-8"; // NOI18N
618:
619: public static final String CR = "\r"; // NOI18N
620: public static final String LF = "\n"; // NOI18N
621: public static final String DOT = "."; // NOI18N
622: public static final String EMPTY_STRING = ""; // NOI18N
623: public static final String CRLF = CR + LF;
624: public static final String CRLFCRLF = CRLF + CRLF;
625: public static final String SPACE = " "; // NOI18N
626: public static final String QUOTE = "\""; // NOI18N
627: public static final String EQUAL = "="; // NOI18N
628: public static final String UNDERSCORE = "_"; // NOI18N
629:
630: public static final String NEW_LINE_PATTERN = "(?:\r\n|\n|\r)"; // NOI18N
631:
632: private static final String LEFT_WHITESPACE = "^\\s+"; // NOI18N
633: private static final String RIGHT_WHITESPACE = "\\s+$"; // NOI18N
634:
635: private static final char MNEMONIC_CHAR = '&';
636: private static final String MNEMONIC = "&"; // NOI18N
637: private static final char NO_MNEMONIC = '\u0000';
638:
639: private static final char[] BASE64_TABLE = new char[] { 'A', 'B',
640: 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
641: 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
642: 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
643: 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
644: 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
645: '+', '/' };
646:
647: private static final byte[] BASE64_REVERSE_TABLE = new byte[] { -1,
648: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
649: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
650: -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1,
651: -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1,
652: -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
653: 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1,
654: -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
655: 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
656:
657: private static final char BASE64_PAD = '=';
658:
659: private static final int BIN_11111111 = 0xff;
660: private static final int BIN_00110000 = 0x30;
661: private static final int BIN_00111100 = 0x3c;
662: private static final int BIN_00111111 = 0x3f;
663:
664: public static final String ERROR_CANNOT_PARSE_STATUS = "StrU.error.cannot.parse.status";//NOI18N
665: public static final String ERROR_UNKNOWN_PLATFORM = "StrU.error.unknown.platform";//NOI18N
666: }
|