/* * 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.IOException; import java.util.Arrays; /** * Represents an Array. * * @author Daniel Dreibrodt */ public class NSArray extends NSObject { private NSObject[] array; /** * Creates an empty array of the given length. * * @param length The number of elements this array will be able to hold. */ public NSArray(int length) { array = new NSObject[length]; } /** * Creates a array from an existing one * * @param a The array which should be wrapped by the NSArray */ public NSArray(NSObject... a) { array = a; } /** * Returns the object stored at the given index. * Equivalent to <code>getArray()[i]</code>. * * @param i The index of the object. * @return The object at the given index. */ public NSObject objectAtIndex(int i) { return array[i]; } /** * Remove the i-th element from the array. * The array will be resized. * * @param i The index of the object */ public void remove(int i) { if ((i >= array.length) || (i < 0)) throw new ArrayIndexOutOfBoundsException("invalid index:" + i + ";the array length is " + array.length); NSObject[] newArray = new NSObject[array.length - 1]; System.arraycopy(array, 0, newArray, 0, i); System.arraycopy(array, i + 1, newArray, i, array.length - i - 1); array = newArray; } /** * Stores an object at the specified index. * If there was another object stored at that index it will be replaced. * Equivalent to <code>getArray()[key] = value</code>. * * @param key The index where to store the object. * @param value The object. */ public void setValue(int key, Object value) { array[key] = NSObject.wrap(value); } /** * Returns the array of NSObjects represented by this NSArray. * Any changes to the values of this array will also affect the NSArray. * * @return The actual array represented by this NSArray. */ public NSObject[] getArray() { return array; } /** * Returns the size of the array. * * @return The number of elements that this array can store. */ public int count() { return array.length; } /** * Checks whether an object is present in the array or whether it is equal * to any of the objects in the array. * * @param obj The object to look for. * @return <code>true</code>, when the object could be found. <code>false</code> otherwise. * @see Object#equals(java.lang.Object) */ public boolean containsObject(Object obj) { NSObject nso = NSObject.wrap(obj); for (NSObject elem : array) { if(elem == null) { if(obj == null) return true; continue; } if (elem.equals(nso)) { return true; } } return false; } /** * Searches for an object in the array. If it is found its index will be * returned. This method also returns an index if the object is not the same * as the one stored in the array but has equal contents. * * @param obj The object to look for. * @return The index of the object, if it was found. -1 otherwise. * @see Object#equals(java.lang.Object) * @see #indexOfIdenticalObject(Object) */ public int indexOfObject(Object obj) { NSObject nso = NSObject.wrap(obj); for (int i = 0; i < array.length; i++) { if (array[i].equals(nso)) { return i; } } return -1; } /** * Searches for an object in the array. If it is found its index will be * returned. This method only returns the index of an object that is * <b>identical</b> to the given one. Thus objects that might contain the * same value as the given one will not be considered. * * @param obj The object to look for. * @return The index of the object, if it was found. -1 otherwise. * @see #indexOfObject(Object) */ public int indexOfIdenticalObject(Object obj) { NSObject nso = NSObject.wrap(obj); for (int i = 0; i < array.length; i++) { if (array[i] == nso) { return i; } } return -1; } /** * Returns the last object contained in this array. * Equivalent to <code>getArray()[getArray().length-1]</code>. * * @return The value of the highest index in the array. */ public NSObject lastObject() { return array[array.length - 1]; } /** * Returns a new array containing only the values stored at the given * indices. The values are sorted by their index. * * @param indexes The indices of the objects. * @return The new array containing the objects stored at the given indices. */ public NSObject[] objectsAtIndexes(int... indexes) { NSObject[] result = new NSObject[indexes.length]; Arrays.sort(indexes); for (int i = 0; i < indexes.length; i++) result[i] = array[indexes[i]]; return result; } @Override public boolean equals(Object obj) { if(obj == null) return false; if(obj.getClass().equals(NSArray.class)) { return Arrays.equals(((NSArray) obj).getArray(), this.array); } else { NSObject nso = NSObject.wrap(obj); if(nso.getClass().equals(NSArray.class)) { return Arrays.equals(((NSArray) nso).getArray(), this.array); } } return false; } @Override public int hashCode() { int hash = 7; hash = 89 * hash + Arrays.deepHashCode(this.array); return hash; } @Override void toXML(StringBuilder xml, int level) { indent(xml, level); xml.append("<array>"); xml.append(NSObject.NEWLINE); for (NSObject o : array) { o.toXML(xml, level + 1); xml.append(NSObject.NEWLINE); } indent(xml, level); xml.append("</array>"); } @Override void assignIDs(BinaryPropertyListWriter out) { super.assignIDs(out); for (NSObject obj : array) { obj.assignIDs(out); } } @Override void toBinary(BinaryPropertyListWriter out) throws IOException { out.writeIntHeader(0xA, array.length); for (NSObject obj : array) { out.writeID(out.getID(obj)); } } /** * Generates a valid ASCII property list which has this NSArray as its * root object. The generated property list complies with the format as * described in <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/PropertyLists/OldStylePlists/OldStylePLists.html"> * Property List Programming Guide - Old-Style ASCII Property Lists</a>. * * @return ASCII representation of this object. */ public String toASCIIPropertyList() { StringBuilder ascii = new StringBuilder(); toASCII(ascii, 0); ascii.append(NEWLINE); return ascii.toString(); } /** * Generates a valid ASCII property list in GnuStep format which has this * NSArray as its root object. The generated property list complies with * the format as described in <a href="http://www.gnustep.org/resources/documentation/Developer/Base/Reference/NSPropertyList.html"> * GnuStep - NSPropertyListSerialization class documentation * </a> * * @return GnuStep ASCII representation of this object. */ public String toGnuStepASCIIPropertyList() { StringBuilder ascii = new StringBuilder(); toASCIIGnuStep(ascii, 0); ascii.append(NEWLINE); return ascii.toString(); } @Override protected void toASCII(StringBuilder ascii, int level) { indent(ascii, level); ascii.append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN); int indexOfLastNewLine = ascii.lastIndexOf(NEWLINE); for (int i = 0; i < array.length; i++) { Class<?> objClass = array[i].getClass(); if ((objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class)) && indexOfLastNewLine != ascii.length()) { ascii.append(NEWLINE); indexOfLastNewLine = ascii.length(); array[i].toASCII(ascii, level + 1); } else { if (i != 0) ascii.append(" "); array[i].toASCII(ascii, 0); } if (i != array.length - 1) ascii.append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN); if (ascii.length() - indexOfLastNewLine > ASCII_LINE_LENGTH) { ascii.append(NEWLINE); indexOfLastNewLine = ascii.length(); } } ascii.append(ASCIIPropertyListParser.ARRAY_END_TOKEN); } @Override protected void toASCIIGnuStep(StringBuilder ascii, int level) { indent(ascii, level); ascii.append(ASCIIPropertyListParser.ARRAY_BEGIN_TOKEN); int indexOfLastNewLine = ascii.lastIndexOf(NEWLINE); for (int i = 0; i < array.length; i++) { Class<?> objClass = array[i].getClass(); if ((objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class)) && indexOfLastNewLine != ascii.length()) { ascii.append(NEWLINE); indexOfLastNewLine = ascii.length(); array[i].toASCIIGnuStep(ascii, level + 1); } else { if (i != 0) ascii.append(" "); array[i].toASCIIGnuStep(ascii, 0); } if (i != array.length - 1) ascii.append(ASCIIPropertyListParser.ARRAY_ITEM_DELIMITER_TOKEN); if (ascii.length() - indexOfLastNewLine > ASCII_LINE_LENGTH) { ascii.append(NEWLINE); indexOfLastNewLine = ascii.length(); } } ascii.append(ASCIIPropertyListParser.ARRAY_END_TOKEN); } }