All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.main.java.com.dd.plist.NSString Maven / Gradle / Ivy

Go to download

This library enables Java applications to work with property lists in various formats. Supported formats for reading and writing are OS X/iOS binary and XML property lists. ASCII property lists are also supported. The library also provides access to basic functions of NeXTSTEP/Cocoa classes like NSDictionary, NSArray, etc.

The newest version!
/*
 * plist - An open source library to parse and generate property lists
 * Copyright (C) 2011 Daniel Dreibrodt
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.dd.plist;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Objects;
import java.util.Scanner;

/**
 * The NSString class is a wrapper for a string.
 *
 * @author Daniel Dreibrodt
 * @see Foundation NSString documentation
 */
public class NSString extends NSObject {
    private static CharsetEncoder asciiEncoder, utf16beEncoder, utf8Encoder;

    private String content;

    /**
     * Creates a new NSString instance from its binary representation.
     *
     * @param bytes    The binary representation.
     * @param encoding The string encoding (name of the charset).
     * @throws UnsupportedEncodingException When the given encoding is not supported by the JRE.
     * @see java.lang.String#String(byte[], String)
     */
    public NSString(byte[] bytes, String encoding) throws UnsupportedEncodingException {
        this(bytes, 0, bytes.length, encoding);
    }

    /**
     * Creates a new NSString instance from its binary representation.
     *
     * @param bytes      An array containing the binary representation of the string.
     * @param startIndex The offset inside the array at which the string data starts.
     * @param endIndex   The offset inside the array at which the string data ends.
     * @param encoding   The string encoding (name of the charset).
     * @throws UnsupportedEncodingException When the given encoding is not supported by the JRE.
     * @see java.lang.String#String(byte[], int, int, String)
     */
    public NSString(byte[] bytes, final int startIndex, final int endIndex, String encoding) throws UnsupportedEncodingException {
        this.content = new String(bytes, startIndex, endIndex - startIndex, encoding);
    }

    /**
     * Creates a new NSString instance with the specified content.
     *
     * @param string The string that will be contained in the NSString.
     */
    public NSString(String string) {
        this.content = string;
    }

    /**
     * Gets the integer value of this string.
     *
     * @return The integer value of this string, assuming a decimal representation
     * and skipping whitespace at the beginning of the string. If the string
     * does not contain a valid decimal representation of a number, 0 is returned.
     * If the string contains an integer larger than Integer.MAX_VALUE, Integer.MAX_VALUE is returned.
     * If the string contains an integer less than Integer.MIN_VALUE, Integer.MIN_VALUE is returned.
     */
    public int intValue() {
        double d = this.doubleValue();

        if (d > Integer.MAX_VALUE) {
            return Integer.MAX_VALUE;
        }

        if (d < Integer.MIN_VALUE) {
            return Integer.MIN_VALUE;
        }

        return (int) d;
    }

    /**
     * Gets the floating-point value of this string.
     *
     * @return The floating-point value of this string, assuming a decimal representation
     * and skipping whitespace at the beginning of the string. If the string
     * does not contain a valid decimal representation of a number, 0 is returned.
     * If the string contains an integer larger than Float.MAX_VALUE, Float.MAX_VALUE is returned.
     * If the string contains an integer less than -Float.MAX_VALUE, -Float.MAX_VALUE is returned.
     */
    public float floatValue() {
        double d = this.doubleValue();

        if (d > Float.MAX_VALUE) {
            return Float.MAX_VALUE;
        }

        if (d < -Float.MAX_VALUE) {
            return -Float.MAX_VALUE;
        }

        return (float) d;
    }

    /**
     * Gets the floating-point value (double precision) of this string.
     *
     * @return The floating-point value of this string, assuming a decimal representation
     * and skipping whitespace at the beginning of the string. If the string does not contain
     * a valid decimal representation of a floating-point number, 0 is returned.
     */
    public double doubleValue() {
        Scanner s = new Scanner(this.content.trim()).useLocale(Locale.ROOT).useDelimiter("[^\\d.+-]+");
        if (s.hasNextDouble()) {
            return s.nextDouble();
        } else {
            return 0d;
        }
    }

    /**
     * Gets the boolean value of this string.
     *
     * @return The boolean value of this string. Leading whitespaces are ignored. Any + or - sign and leading zeroes are
     * ignored.
     * If the remaining string starts with 'Y', 'y', 'T', 't' or a positive digit (1-9), true is returned.
     * Otherwise, false is returned.
     * 

* Examples: * "YES" is true * "true" is true * " YES" is true * "+1" is true * "-9" is true * " +01" is true * "0" is false * "false" is false * "no" is false * "1FALSE" is true * "0TRUE" is true * "FALSE1" is false */ public boolean boolValue() { Scanner s = new Scanner(this.content.trim()).useLocale(Locale.ROOT); return s.hasNext("([+-]?0*)?[YyTt1-9].*"); } /** * Gets the string content of this instance. * * @return This string contained in this instance. */ public String getContent() { return this.content; } /** * Sets the string content of this instance. * * @param c The new content of this string object. */ public void setContent(String c) { this.content = c; } /** * Appends a string to this string. * * @param s The string to append. */ public void append(NSString s) { this.append(s.getContent()); } /** * Appends a string to this string. * * @param s The string to append. */ public void append(String s) { this.content += s; } /** * Prepends a string to this string. * * @param s The string to prepend. */ public void prepend(String s) { this.content = s + this.content; } /** * Prepends a string to this string. * * @param s The string to prepend. */ public void prepend(NSString s) { this.prepend(s.getContent()); } @Override public boolean equals(Object obj) { return obj != null && this.getClass() == obj.getClass() && this.content.equals(((NSString) obj).content); } @Override public int hashCode() { return this.content.hashCode(); } @Override public NSString clone() { return new NSString(this.content); } @Override public String toString() { return this.content; } @Override public Object toJavaObject() { return this.content; } @Override void toXML(StringBuilder xml, int level) { this.indent(xml, level); xml.append(""); //Make sure that the string is encoded in UTF-8 for the XML output synchronized (NSString.class) { if (utf8Encoder == null) utf8Encoder = StandardCharsets.UTF_8.newEncoder(); else utf8Encoder.reset(); try { ByteBuffer byteBuf = utf8Encoder.encode(CharBuffer.wrap(this.content)); byte[] bytes = new byte[byteBuf.remaining()]; byteBuf.get(bytes); this.content = new String(bytes, StandardCharsets.UTF_8); } catch (Exception ex) { throw new RuntimeException("Could not encode the NSString into UTF-8: " + ex.getMessage()); } } String cleanedContent = escapeStringForXml(this.content); //According to http://www.w3.org/TR/REC-xml/#syntax node values must not //contain the characters < or &. Also the > character should be escaped. if (cleanedContent.contains("&") || cleanedContent.contains("<") || cleanedContent.contains(">")) { xml.append("", "]]]]>")); xml.append("]]>"); } else { xml.append(cleanedContent); } xml.append(""); } @Override public void toBinary(BinaryPropertyListWriter out) throws IOException { CharBuffer charBuf = CharBuffer.wrap(this.content); int kind; ByteBuffer byteBuf; synchronized (NSString.class) { if (asciiEncoder == null) asciiEncoder = StandardCharsets.US_ASCII.newEncoder(); else asciiEncoder.reset(); if (asciiEncoder.canEncode(charBuf)) { kind = 0x5; // standard ASCII byteBuf = asciiEncoder.encode(charBuf); } else { if (utf16beEncoder == null) utf16beEncoder = StandardCharsets.UTF_16BE.newEncoder(); else utf16beEncoder.reset(); kind = 0x6; // UTF-16-BE byteBuf = utf16beEncoder.encode(charBuf); } } byte[] bytes = new byte[byteBuf.remaining()]; byteBuf.get(bytes); out.writeIntHeader(kind, this.content.length()); out.write(bytes); } @Override protected void toASCII(StringBuilder ascii, int level) { this.indent(ascii, level); ascii.append("\""); //According to https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html //non-ASCII characters are not escaped but simply written into the //file, thus actually violating the ASCII plain text format. //We will escape the string anyway because current Xcode project files (ASCII property lists) also escape their strings. ascii.append(escapeStringForASCII(this.content)); ascii.append("\""); } @Override protected void toASCIIGnuStep(StringBuilder ascii, int level) { this.indent(ascii, level); ascii.append("\""); ascii.append(escapeStringForASCII(this.content)); ascii.append("\""); } @Override public int compareTo(NSObject o) { Objects.requireNonNull(o); if (o == this) { return 0; } else if (o instanceof NSString) { return this.getContent().compareTo(((NSString) o).getContent()); } else { return this.getClass().getName().compareTo(o.getClass().getName()); } } /** * Escapes a string for use in ASCII property lists. * * @param s The unescaped string. * @return The escaped string. */ static String escapeStringForASCII(String s) { StringBuilder out = new StringBuilder(); for (char c : s.toCharArray()) { if (c > 127) { //non-ASCII Unicode out.append("\\U"); String hex = Integer.toHexString(c); while (hex.length() < 4) hex = "0" + hex; out.append(hex); } else if (c == '\\') { out.append("\\\\"); } else if (c == '\"') { out.append("\\\""); } else if (c == '\b') { out.append("\\b"); } else if (c == '\n') { out.append("\\n"); } else if (c == '\r') { out.append("\\r"); } else if (c == '\t') { out.append("\\t"); } else { out.append(c); } } return out.toString(); } static String escapeStringForXml(String s) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < s.length(); i++) { int codePoint = s.codePointAt(i); if (codePoint > 0xFFFF) { i++; } if ((codePoint == 0x9) || (codePoint == 0xA) || (codePoint == 0xD) || ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) || ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) || ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF))) { sb.appendCodePoint(codePoint); } } return sb.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy