src.main.java.com.dd.plist.NSDictionary Maven / Gradle / Ivy
Show all versions of dd-plist Show documentation
/*
* 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.*;
import java.util.stream.Collectors;
/**
* The NSDictionary class is a collection of NSObject instances that are identified by strings.
* It represents a hash map where the keys are strings and the values NSObject instances.
* The implementation is based on a linked hash map, so that the order of the elements in the dictionary is preserved.
*
* You can access the keys through the function allKeys()
.
* Access to the objects stored for each key is given through the function objectForKey(String key)
.
*
* @author Daniel Dreibrodt
* @see Foundation NSDictionary documentation
* @see java.util.LinkedHashMap
*/
public class NSDictionary extends NSObject implements Map {
private final HashMap dict;
/**
* Creates a new NSDictionary instance.
*/
public NSDictionary() {
this.dict = new LinkedHashMap<>();
}
/**
* Gets the hash map which stores the keys and values of this dictionary.
* Changes to the hash map are directly reflected in this dictionary.
*
* @return The hash map which is used by this dictionary to store its contents.
*/
public HashMap getHashMap() {
return this.dict;
}
/**
* Gets the NSObject stored for the given key.
*
* @param key The key.
* @return The object.
*/
public NSObject objectForKey(String key) {
return this.dict.get(key);
}
public int size() {
return this.dict.size();
}
public boolean isEmpty() {
return this.dict.isEmpty();
}
public boolean containsKey(Object key) {
return this.dict.containsKey(key);
}
public boolean containsValue(Object value) {
if (value == null)
return false;
NSObject wrap = NSObject.fromJavaObject(value);
return this.dict.containsValue(wrap);
}
public NSObject get(Object key) {
return this.dict.get(key);
}
public void putAll(Map extends String, ? extends NSObject> values) {
for (Object object : values.entrySet()) {
@SuppressWarnings("unchecked")
Map.Entry entry = (Map.Entry) object;
this.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 this.dict.get(key);
return this.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 this.put(key, NSObject.fromJavaObject(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 this.dict.remove(key);
}
public NSObject remove(Object key) {
return this.dict.remove(key);
}
/**
* Removes all key-value pairs from this dictionary.
*
* @see java.util.Map#clear()
*/
public void clear() {
this.dict.clear();
}
public Set keySet() {
return this.dict.keySet();
}
public Collection values() {
return this.dict.values();
}
public Set> entrySet() {
return this.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 this.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 && this.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 : this.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 : this.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 : this.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 : this.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 : this.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 : this.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 this.dict.size();
}
@Override
public boolean equals(Object obj) {
if (obj == null) return false;
return obj.getClass().equals(this.getClass()) && ((NSDictionary) obj).dict.equals(this.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 this.dict.keySet().toArray(new String[this.count()]);
}
@Override
public int hashCode() {
int hash = 7;
hash = 83 * hash + this.dict.hashCode();
return hash;
}
@Override
public NSDictionary clone() {
NSDictionary clone = new NSDictionary();
for (Entry entry : this.dict.entrySet()) {
clone.dict.put(entry.getKey(), entry.getValue() != null ? entry.getValue().clone() : null);
}
return clone;
}
@Override
public Object toJavaObject() {
HashMap clonedMap = new HashMap<>(this.dict.size());
for (String key : this.dict.keySet()) {
clonedMap.put(key, this.dict.get(key).toJavaObject());
}
return clonedMap;
}
@Override
public int compareTo(NSObject o) {
Objects.requireNonNull(o);
if (o == this) {
return 0;
} else if (o instanceof NSDictionary) {
NSDictionary other = (NSDictionary) o;
long uniqueForMe = this.dict.keySet().stream().filter(e -> !other.dict.containsKey(e)).count();
long uniqueForOther = other.dict.keySet().stream().filter(e -> !this.dict.containsKey(e)).count();
int keyDifference = Long.compare(uniqueForMe, uniqueForOther);
if (keyDifference != 0) {
return keyDifference;
}
for (String key : this.dict.keySet().stream().sorted(String::compareTo).collect(Collectors.toCollection(LinkedList::new))) {
int itemDifference = this.objectForKey(key).compareTo(other.objectForKey(key));
if (itemDifference != 0) {
return itemDifference;
}
}
return 0;
} else {
return this.getClass().getName().compareTo(o.getClass().getName());
}
}
@Override
void toXML(StringBuilder xml, int level) {
this.indent(xml, level);
xml.append("");
xml.append(NSObject.NEWLINE);
for (String key : this.dict.keySet()) {
NSObject val = this.objectForKey(key);
this.indent(xml, level + 1);
xml.append("");
//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("", "]]]]>"));
xml.append("]]>");
} else {
xml.append(key);
}
xml.append(" ");
xml.append(NSObject.NEWLINE);
val.toXML(xml, level + 1);
xml.append(NSObject.NEWLINE);
}
this.indent(xml, level);
xml.append(" ");
}
@Override
void assignIDs(BinaryPropertyListWriter out) {
super.assignIDs(out);
for (Map.Entry entry : this.dict.entrySet()) {
new NSString(entry.getKey()).assignIDs(out);
}
for (Map.Entry entry : this.dict.entrySet()) {
entry.getValue().assignIDs(out);
}
}
@Override
void toBinary(BinaryPropertyListWriter out) throws IOException {
out.writeIntHeader(0xD, this.dict.size());
Set> entries = this.dict.entrySet();
for (Map.Entry entry : entries) {
out.writeID(out.getID(new NSString(entry.getKey())));
}
for (Map.Entry 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
* Property List Programming Guide - Old-Style ASCII Property Lists.
*
* @return ASCII representation of this object.
*/
public String toASCIIPropertyList() {
StringBuilder ascii = new StringBuilder();
this.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
* GnuStep - NSPropertyListSerialization class documentation
*
*
* @return GnuStep ASCII representation of this object.
*/
public String toGnuStepASCIIPropertyList() {
StringBuilder ascii = new StringBuilder();
this.toASCIIGnuStep(ascii, 0);
ascii.append(NEWLINE);
return ascii.toString();
}
@Override
protected void toASCII(StringBuilder ascii, int level) {
this.indent(ascii, level);
ascii.append(ASCIIPropertyListParser.DICTIONARY_BEGIN_TOKEN);
ascii.append(NEWLINE);
String[] keys = this.allKeys();
for (String key : keys) {
NSObject val = this.objectForKey(key);
this.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);
}
this.indent(ascii, level);
ascii.append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);
}
@Override
protected void toASCIIGnuStep(StringBuilder ascii, int level) {
this.indent(ascii, level);
ascii.append(ASCIIPropertyListParser.DICTIONARY_BEGIN_TOKEN);
ascii.append(NEWLINE);
String[] keys = this.dict.keySet().toArray(new String[0]);
for (String key : keys) {
NSObject val = this.objectForKey(key);
this.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);
}
this.indent(ascii, level);
ascii.append(ASCIIPropertyListParser.DICTIONARY_END_TOKEN);
}
}