| /* |
| * plist - An open source library to parse and generate property lists |
| * Copyright (C) 2014 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.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.ObjectOutputStream; |
| import java.util.*; |
| |
| /** |
| * Abstract interface for any object contained in a property list. |
| * The names and functions of the various objects orient themselves |
| * towards Apple's Cocoa API. |
| * |
| * @author Daniel Dreibrodt |
| */ |
| public abstract class NSObject { |
| |
| /** |
| * The newline character used for generating the XML output. |
| * This constant will be different depending on the operating system on |
| * which you use this library. |
| */ |
| final static String NEWLINE = System.getProperty("line.separator"); |
| |
| /** |
| * The identation character used for generating the XML output. This is the |
| * tabulator character. |
| */ |
| final static String INDENT = "\t"; |
| |
| /** |
| * The maximum length of the text lines to be used when generating |
| * ASCII property lists. But this number is only a guideline it is not |
| * guaranteed that it will not be overstepped. |
| */ |
| final static int ASCII_LINE_LENGTH = 80; |
| |
| /** |
| * Generates the XML representation of the object (without XML headers or enclosing plist-tags). |
| * |
| * @param xml The StringBuilder onto which the XML representation is appended. |
| * @param level The indentation level of the object. |
| */ |
| abstract void toXML(StringBuilder xml, int level); |
| |
| /** |
| * Assigns IDs to all the objects in this NSObject subtree. |
| * |
| * @param out The writer object that handles the binary serialization. |
| */ |
| void assignIDs(BinaryPropertyListWriter out) { |
| out.assignID(this); |
| } |
| |
| /** |
| * Generates the binary representation of the object. |
| * |
| * @param out The output stream to serialize the object to. |
| * @throws java.io.IOException When an IO error occurs while writing to the stream or the object structure contains |
| * data that cannot be saved. |
| */ |
| abstract void toBinary(BinaryPropertyListWriter out) throws IOException; |
| |
| /** |
| * Generates a valid XML property list including headers using this object as root. |
| * |
| * @return The XML representation of the property list including XML header and doctype information. |
| */ |
| public String toXMLPropertyList() { |
| StringBuilder xml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); |
| xml.append(NSObject.NEWLINE); |
| xml.append("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"); |
| xml.append(NSObject.NEWLINE); |
| xml.append("<plist version=\"1.0\">"); |
| xml.append(NSObject.NEWLINE); |
| toXML(xml, 0); |
| xml.append(NSObject.NEWLINE); |
| xml.append("</plist>"); |
| return xml.toString(); |
| } |
| |
| /** |
| * Generates the ASCII representation of this object. |
| * The generated ASCII representation does not end with a newline. |
| * Complies with https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html |
| * |
| * @param ascii The StringBuilder onto which the ASCII representation is appended. |
| * @param level The indentation level of the object. |
| */ |
| protected abstract void toASCII(StringBuilder ascii, int level); |
| |
| /** |
| * Generates the ASCII representation of this object in the GnuStep format. |
| * The generated ASCII representation does not end with a newline. |
| * |
| * @param ascii The StringBuilder onto which the ASCII representation is appended. |
| * @param level The indentation level of the object. |
| */ |
| protected abstract void toASCIIGnuStep(StringBuilder ascii, int level); |
| |
| /** |
| * Helper method that adds correct identation to the xml output. |
| * Calling this method will add <code>level</code> number of tab characters |
| * to the <code>xml</code> string. |
| * |
| * @param xml The string builder for the XML document. |
| * @param level The level of identation. |
| */ |
| void indent(StringBuilder xml, int level) { |
| for (int i = 0; i < level; i++) |
| xml.append(INDENT); |
| } |
| |
| /** |
| * Wraps the given value inside a NSObject. |
| * |
| * @param value The value to represent as a NSObject. |
| * @return A NSObject representing the given value. |
| */ |
| public static NSNumber wrap(long value) { |
| return new NSNumber(value); |
| } |
| |
| /** |
| * Wraps the given value inside a NSObject. |
| * |
| * @param value The value to represent as a NSObject. |
| * @return A NSObject representing the given value. |
| */ |
| public static NSNumber wrap(double value) { |
| return new NSNumber(value); |
| } |
| |
| /** |
| * Wraps the given value inside a NSObject. |
| * |
| * @param value The value to represent as a NSObject. |
| * @return A NSObject representing the given value. |
| */ |
| public static NSNumber wrap(boolean value) { |
| return new NSNumber(value); |
| } |
| |
| /** |
| * Wraps the given value inside a NSObject. |
| * |
| * @param value The value to represent as a NSObject. |
| * @return A NSObject representing the given value. |
| */ |
| public static NSData wrap(byte[] value) { |
| return new NSData(value); |
| } |
| |
| /** |
| * Creates a NSArray with the contents of the given array. |
| * |
| * @param value The value to represent as a NSObject. |
| * @return A NSObject representing the given value. |
| * @throws RuntimeException When one of the objects contained in the array cannot be represented by a NSObject. |
| */ |
| public static NSArray wrap(Object[] value) { |
| NSArray arr = new NSArray(value.length); |
| for (int i = 0; i < value.length; i++) { |
| arr.setValue(i, wrap(value[i])); |
| } |
| return arr; |
| } |
| |
| /** |
| * Creates a NSDictionary with the contents of the given map. |
| * |
| * @param value The value to represent as a NSObject. |
| * @return A NSObject representing the given value. |
| * @throws RuntimeException When one of the values contained in the map cannot be represented by a NSObject. |
| */ |
| public static NSDictionary wrap(Map<String, Object> value) { |
| NSDictionary dict = new NSDictionary(); |
| for (String key : value.keySet()) |
| dict.put(key, wrap(value.get(key))); |
| return dict; |
| } |
| |
| /** |
| * Creates a NSSet with the contents of this set. |
| * |
| * @param value The value to represent as a NSObject. |
| * @return A NSObject representing the given value. |
| * @throws RuntimeException When one of the values contained in the set cannot be represented by a NSObject. |
| */ |
| public static NSSet wrap(Set<Object> value) { |
| NSSet set = new NSSet(); |
| for (Object o : value.toArray()) |
| set.addObject(wrap(o)); |
| return set; |
| } |
| |
| /** |
| * Creates a NSObject representing the given Java Object. |
| * |
| * Numerics of type bool, int, long, short, byte, float or double are wrapped as NSNumber objects. |
| * |
| * Strings are wrapped as NSString objects abd byte arrays as NSData objects. |
| * |
| * Date objects are wrapped as NSDate objects. |
| * |
| * Serializable classes are serialized and their data is stored in NSData objects. |
| * |
| * Arrays and Collection objects are converted to NSArrays where each array member is wrapped into a NSObject. |
| * |
| * Map objects are converted to NSDictionaries. Each key is converted to a string and each value wrapped into a NSObject. |
| * |
| * @param o The object to represent. |
| * @return A NSObject equivalent to the given object. |
| */ |
| public static NSObject wrap(Object o) { |
| if(o == null) |
| return null; |
| |
| if(o instanceof NSObject) |
| return (NSObject)o; |
| |
| Class<?> c = o.getClass(); |
| if (Boolean.class.equals(c)) { |
| return wrap((boolean) (Boolean) o); |
| } |
| if (Byte.class.equals(c)) { |
| return wrap((int) (Byte) o); |
| } |
| if (Short.class.equals(c)) { |
| return wrap((int) (Short) o); |
| } |
| if (Integer.class.equals(c)) { |
| return wrap((int) (Integer) o); |
| } |
| if (Long.class.isAssignableFrom(c)) { |
| return wrap((long) (Long) o); |
| } |
| if (Float.class.equals(c)) { |
| return wrap((double) (Float) o); |
| } |
| if (Double.class.isAssignableFrom(c)) { |
| return wrap((double) (Double) o); |
| } |
| if (String.class.equals(c)) { |
| return new NSString((String)o); |
| } |
| if (Date.class.equals(c)) { |
| return new NSDate((Date)o); |
| } |
| if(c.isArray()) { |
| Class<?> cc = c.getComponentType(); |
| if (cc.equals(byte.class)) { |
| return wrap((byte[]) o); |
| } |
| else if(cc.equals(boolean.class)) { |
| boolean[] array = (boolean[])o; |
| NSArray nsa = new NSArray(array.length); |
| for(int i=0;i<array.length;i++) |
| nsa.setValue(i, wrap(array[i])); |
| return nsa; |
| } |
| else if(float.class.equals(cc)) { |
| float[] array = (float[])o; |
| NSArray nsa = new NSArray(array.length); |
| for(int i=0;i<array.length;i++) |
| nsa.setValue(i, wrap(array[i])); |
| return nsa; |
| } |
| else if(double.class.equals(cc)) { |
| double[] array = (double[])o; |
| NSArray nsa = new NSArray(array.length); |
| for(int i=0;i<array.length;i++) |
| nsa.setValue(i, wrap(array[i])); |
| return nsa; |
| } |
| else if(short.class.equals(cc)) { |
| short[] array = (short[])o; |
| NSArray nsa = new NSArray(array.length); |
| for(int i=0;i<array.length;i++) |
| nsa.setValue(i, wrap(array[i])); |
| return nsa; |
| } |
| else if(int.class.equals(cc)) { |
| int[] array = (int[])o; |
| NSArray nsa = new NSArray(array.length); |
| for(int i=0;i<array.length;i++) |
| nsa.setValue(i, wrap(array[i])); |
| return nsa; |
| } |
| else if(long.class.equals(cc)) { |
| long[] array = (long[])o; |
| NSArray nsa = new NSArray(array.length); |
| for(int i=0;i<array.length;i++) |
| nsa.setValue(i, wrap(array[i])); |
| return nsa; |
| } |
| else { |
| return wrap((Object[]) o); |
| } |
| } |
| if (Map.class.isAssignableFrom(c)) { |
| Map map = (Map)o; |
| Set keys = map.keySet(); |
| NSDictionary dict = new NSDictionary(); |
| for(Object key:keys) { |
| Object val = map.get(key); |
| dict.put(String.valueOf(key), wrap(val)); |
| } |
| return dict; |
| } |
| if (Collection.class.isAssignableFrom(c)) { |
| Collection coll = (Collection)o; |
| return wrap(coll.toArray()); |
| } |
| return wrapSerialized(o); |
| } |
| |
| /** |
| * Serializes the given object using Java's default object serialization |
| * and wraps the serialized object in a NSData object. |
| * |
| * @param o The object to serialize and wrap. |
| * @return A NSData object |
| * @throws RuntimeException When the object could not be serialized. |
| */ |
| public static NSData wrapSerialized(Object o) { |
| try { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ObjectOutputStream oos = new ObjectOutputStream(baos); |
| oos.writeObject(o); |
| return new NSData(baos.toByteArray()); |
| } catch (IOException ex) { |
| throw new RuntimeException("The given object of class " + o.getClass().toString() + " could not be serialized and stored in a NSData object."); |
| } |
| } |
| |
| /** |
| * Converts this NSObject into an equivalent object |
| * of the Java Runtime Environment. |
| * <ul> |
| * <li>NSArray objects are converted to arrays.</li> |
| * <li>NSDictionary objects are converted to objects extending the java.util.Map class.</li> |
| * <li>NSSet objects are converted to objects extending the java.util.Set class.</li> |
| * <li>NSNumber objects are converted to primitive number values (int, long, double or boolean).</li> |
| * <li>NSString objects are converted to String objects.</li> |
| * <li>NSData objects are converted to byte arrays.</li> |
| * <li>NSDate objects are converted to java.util.Date objects.</li> |
| * <li>UID objects are converted to byte arrays.</li> |
| * </ul> |
| * @return A native java object representing this NSObject's value. |
| */ |
| public Object toJavaObject() { |
| if(this instanceof NSArray) { |
| NSObject[] arrayA = ((NSArray)this).getArray(); |
| Object[] arrayB = new Object[arrayA.length]; |
| for(int i = 0; i < arrayA.length; i++) { |
| arrayB[i] = arrayA[i].toJavaObject(); |
| } |
| return arrayB; |
| } else if (this instanceof NSDictionary) { |
| HashMap<String, NSObject> hashMapA = ((NSDictionary)this).getHashMap(); |
| HashMap<String, Object> hashMapB = new HashMap<String, Object>(hashMapA.size()); |
| for(String key:hashMapA.keySet()) { |
| hashMapB.put(key, hashMapA.get(key).toJavaObject()); |
| } |
| return hashMapB; |
| } else if(this instanceof NSSet) { |
| Set<NSObject> setA = ((NSSet)this).getSet(); |
| Set<Object> setB; |
| if(setA instanceof LinkedHashSet) { |
| setB = new LinkedHashSet<Object>(setA.size()); |
| } else { |
| setB = new TreeSet<Object>(); |
| } |
| for(NSObject o:setA) { |
| setB.add(o.toJavaObject()); |
| } |
| return setB; |
| } else if(this instanceof NSNumber) { |
| NSNumber num = (NSNumber)this; |
| switch(num.type()) { |
| case NSNumber.INTEGER : { |
| long longVal = num.longValue(); |
| if(longVal > Integer.MAX_VALUE || longVal < Integer.MIN_VALUE) { |
| return longVal; |
| } else { |
| return num.intValue(); |
| } |
| } |
| case NSNumber.REAL : { |
| return num.doubleValue(); |
| } |
| case NSNumber.BOOLEAN : { |
| return num.boolValue(); |
| } |
| default : { |
| return num.doubleValue(); |
| } |
| } |
| } else if(this instanceof NSString) { |
| return ((NSString)this).getContent(); |
| } else if(this instanceof NSData) { |
| return ((NSData)this).bytes(); |
| } else if(this instanceof NSDate) { |
| return ((NSDate)this).getDate(); |
| } else if(this instanceof UID) { |
| return ((UID)this).getBytes(); |
| } else { |
| return this; |
| } |
| } |
| } |