| /* |
| * 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; |
| import java.util.Collection; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * A NSDictionary is a collection of keys and values, essentially a Hashtable. |
| * The keys are simple Strings whereas the values can be any kind of NSObject. |
| * |
| * You can access the keys through the function <code>allKeys()</code>. Access |
| * to the objects stored for each key is given through the function |
| * <code>objectoForKey(String key)</code>. |
| * |
| * @author Daniel Dreibrodt |
| * @see java.util.Hashtable |
| * @see com.dd.plist.NSObject |
| */ |
| public class NSDictionary extends NSObject implements Map<String, NSObject> { |
| |
| private HashMap<String, NSObject> dict; |
| |
| /** |
| * Creates a new empty NSDictionary. |
| */ |
| public NSDictionary() { |
| //With a linked HashMap the order of elements in the dictionary is kept. |
| dict = new LinkedHashMap<String, NSObject>(); |
| } |
| |
| /** |
| * Gets the hashmap which stores the keys and values of this dictionary. |
| * Changes to the hashmap's contents are directly reflected in this |
| * dictionary. |
| * |
| * @return The hashmap which is used by this dictionary to store its contents. |
| */ |
| public HashMap<String, NSObject> getHashMap() { |
| return dict; |
| } |
| |
| /** |
| * Gets the NSObject stored for the given key. |
| * |
| * @param key The key. |
| * @return The object. |
| */ |
| public NSObject objectForKey(String key) { |
| return dict.get(key); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#size() |
| */ |
| public int size() { |
| return dict.size(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#isEmpty() |
| */ |
| public boolean isEmpty() { |
| return dict.isEmpty(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#containsKey(java.lang.Object) |
| */ |
| public boolean containsKey(Object key) { |
| return dict.containsKey(key); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#containsValue(java.lang.Object) |
| */ |
| public boolean containsValue(Object value) { |
| if(value == null) |
| return false; |
| NSObject wrap = NSObject.wrap(value); |
| return dict.containsValue(wrap); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#get(java.lang.Object) |
| */ |
| public NSObject get(Object key) { |
| return dict.get(key); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#putAll(java.util.Map) |
| */ |
| public void putAll(Map<? extends String, ? extends NSObject> values) { |
| for (Object object : values.entrySet()) { |
| @SuppressWarnings("unchecked") |
| Map.Entry<String, NSObject> entry = (Map.Entry<String, NSObject>) object; |
| put(entry.getKey(), entry.getValue()); |
| } |
| } |
| |
| /** |
| * Puts a new key-value pair into this dictionary. |
| * If the value is null, no operation will be performed on the dictionary. |
| * |
| * @param key The key. |
| * @param obj The value. |
| * @return The value previously associated to the given key, |
| * or null, if no value was associated to it. |
| */ |
| public NSObject put(String key, NSObject obj) { |
| if(key == null) |
| return null; |
| if(obj == null) |
| return dict.get(key); |
| return dict.put(key, obj); |
| } |
| |
| /** |
| * Puts a new key-value pair into this dictionary. |
| * If key or value are null, no operation will be performed on the dictionary. |
| * |
| * @param key The key. |
| * @param obj The value. Supported object types are numbers, byte-arrays, dates, strings and arrays or sets of those. |
| * @return The value previously associated to the given key, |
| * or null, if no value was associated to it. |
| */ |
| public NSObject put(String key, Object obj) { |
| return put(key, NSObject.wrap(obj)); |
| } |
| |
| /** |
| * Removes a key-value pair from this dictionary. |
| * |
| * @param key The key |
| * @return the value previously associated to the given key. |
| */ |
| public NSObject remove(String key) { |
| return dict.remove(key); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#remove(java.lang.Object) |
| */ |
| public NSObject remove(Object key) { |
| return dict.remove(key); |
| } |
| |
| /** |
| * Removes all key-value pairs from this dictionary. |
| * @see java.util.Map#clear() |
| */ |
| public void clear() { |
| dict.clear(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#keySet() |
| */ |
| public Set<String> keySet() { |
| return dict.keySet(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#values() |
| */ |
| public Collection<NSObject> values() { |
| return dict.values(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see java.util.Map#entrySet() |
| */ |
| public Set<Entry<String, NSObject>> entrySet() { |
| return dict.entrySet(); |
| } |
| |
| /** |
| * Checks whether a given key is contained in this dictionary. |
| * |
| * @param key The key that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsKey(String key) { |
| return dict.containsKey(key); |
| } |
| |
| /** |
| * Checks whether a given value is contained in this dictionary. |
| * |
| * @param val The value that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsValue(NSObject val) { |
| return val != null && dict.containsValue(val); |
| } |
| |
| /** |
| * Checks whether a given value is contained in this dictionary. |
| * |
| * @param val The value that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsValue(String val) { |
| for (NSObject o : dict.values()) { |
| if (o.getClass().equals(NSString.class)) { |
| NSString str = (NSString) o; |
| if (str.getContent().equals(val)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether a given value is contained in this dictionary. |
| * |
| * @param val The value that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsValue(long val) { |
| for (NSObject o : dict.values()) { |
| if (o.getClass().equals(NSNumber.class)) { |
| NSNumber num = (NSNumber) o; |
| if (num.isInteger() && num.intValue() == val) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether a given value is contained in this dictionary. |
| * |
| * @param val The value that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsValue(double val) { |
| for (NSObject o : dict.values()) { |
| if (o.getClass().equals(NSNumber.class)) { |
| NSNumber num = (NSNumber) o; |
| if (num.isReal() && num.doubleValue() == val) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether a given value is contained in this dictionary. |
| * |
| * @param val The value that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsValue(boolean val) { |
| for (NSObject o : dict.values()) { |
| if (o.getClass().equals(NSNumber.class)) { |
| NSNumber num = (NSNumber) o; |
| if (num.isBoolean() && num.boolValue() == val) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether a given value is contained in this dictionary. |
| * |
| * @param val The value that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsValue(Date val) { |
| for (NSObject o : dict.values()) { |
| if (o.getClass().equals(NSDate.class)) { |
| NSDate dat = (NSDate) o; |
| if (dat.getDate().equals(val)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Checks whether a given value is contained in this dictionary. |
| * |
| * @param val The value that will be searched for. |
| * @return Whether the key is contained in this dictionary. |
| */ |
| public boolean containsValue(byte[] val) { |
| for (NSObject o : dict.values()) { |
| if (o.getClass().equals(NSData.class)) { |
| NSData dat = (NSData) o; |
| if (Arrays.equals(dat.bytes(), val)) |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Counts the number of contained key-value pairs. |
| * |
| * @return The size of this NSDictionary. |
| */ |
| public int count() { |
| return dict.size(); |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| return (obj.getClass().equals(this.getClass()) && ((NSDictionary) obj).dict.equals(dict)); |
| } |
| |
| /** |
| * Gets a list of all keys used in this NSDictionary. |
| * |
| * @return The list of all keys used in this NSDictionary. |
| */ |
| public String[] allKeys() { |
| return dict.keySet().toArray(new String[count()]); |
| } |
| |
| @Override |
| public int hashCode() { |
| int hash = 7; |
| hash = 83 * hash + (this.dict != null ? this.dict.hashCode() : 0); |
| return hash; |
| } |
| |
| @Override |
| void toXML(StringBuilder xml, int level) { |
| indent(xml, level); |
| xml.append("<dict>"); |
| xml.append(NSObject.NEWLINE); |
| for (String key : dict.keySet()) { |
| NSObject val = objectForKey(key); |
| indent(xml, level + 1); |
| xml.append("<key>"); |
| //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 (key.contains("&") || key.contains("<") || key.contains(">")) { |
| xml.append("<![CDATA["); |
| xml.append(key.replaceAll("]]>", "]]]]><![CDATA[>")); |
| xml.append("]]>"); |
| } else { |
| xml.append(key); |
| } |
| xml.append("</key>"); |
| xml.append(NSObject.NEWLINE); |
| val.toXML(xml, level + 1); |
| xml.append(NSObject.NEWLINE); |
| } |
| indent(xml, level); |
| xml.append("</dict>"); |
| } |
| |
| @Override |
| void assignIDs(BinaryPropertyListWriter out) { |
| super.assignIDs(out); |
| for (Map.Entry<String, NSObject> entry : dict.entrySet()) { |
| new NSString(entry.getKey()).assignIDs(out); |
| } |
| for (Map.Entry<String, NSObject> entry : dict.entrySet()) { |
| entry.getValue().assignIDs(out); |
| } |
| } |
| |
| @Override |
| void toBinary(BinaryPropertyListWriter out) throws IOException { |
| out.writeIntHeader(0xD, dict.size()); |
| Set<Map.Entry<String, NSObject>> entries = dict.entrySet(); |
| for (Map.Entry<String, NSObject> entry : entries) { |
| out.writeID(out.getID(new NSString(entry.getKey()))); |
| } |
| for (Map.Entry<String, NSObject> entry : entries) { |
| out.writeID(out.getID(entry.getValue())); |
| } |
| } |
| |
| /** |
| * Generates a valid ASCII property list which has this NSDictionary 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 |
| * NSDictionary 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.DICTIONARY_BEGIN_TOKEN); |
| ascii.append(NEWLINE); |
| String[] keys = allKeys(); |
| for (String key : keys) { |
| NSObject val = objectForKey(key); |
| indent(ascii, level + 1); |
| ascii.append("\""); |
| ascii.append(NSString.escapeStringForASCII(key)); |
| ascii.append("\" ="); |
| Class<?> objClass = val.getClass(); |
| if (objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class)) { |
| ascii.append(NEWLINE); |
| val.toASCII(ascii, level + 2); |
| } else { |
| ascii.append(" "); |
| val.toASCII(ascii, 0); |
| } |
| ascii.append(ASCIIPropertyListParser.DICTIONARY_ITEM_DELIMITER_TOKEN); |
| ascii.append(NEWLINE); |
| } |
| indent(ascii, level); |
| ascii.append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN); |
| } |
| |
| @Override |
| protected void toASCIIGnuStep(StringBuilder ascii, int level) { |
| indent(ascii, level); |
| ascii.append(ASCIIPropertyListParser.DICTIONARY_BEGIN_TOKEN); |
| ascii.append(NEWLINE); |
| String[] keys = dict.keySet().toArray(new String[dict.size()]); |
| for (String key : keys) { |
| NSObject val = objectForKey(key); |
| indent(ascii, level + 1); |
| ascii.append("\""); |
| ascii.append(NSString.escapeStringForASCII(key)); |
| ascii.append("\" ="); |
| Class<?> objClass = val.getClass(); |
| if (objClass.equals(NSDictionary.class) || objClass.equals(NSArray.class) || objClass.equals(NSData.class)) { |
| ascii.append(NEWLINE); |
| val.toASCIIGnuStep(ascii, level + 2); |
| } else { |
| ascii.append(" "); |
| val.toASCIIGnuStep(ascii, 0); |
| } |
| ascii.append(ASCIIPropertyListParser.DICTIONARY_ITEM_DELIMITER_TOKEN); |
| ascii.append(NEWLINE); |
| } |
| indent(ascii, level); |
| ascii.append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN); |
| } |
| } |