/* * Part of the CCNx Java Library. * * Copyright (C) 2008, 2009, 2011, 2013 Palo Alto Research Center, Inc. * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. You should have received * a copy of the GNU Lesser General Public License along with this library; * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, * Fifth Floor, Boston, MA 02110-1301 USA. */ package org.ccnx.ccn.io.content; import java.io.IOException; import java.security.InvalidParameterException; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.TreeMap; import org.ccnx.ccn.CCNHandle; import org.ccnx.ccn.impl.CCNFlowControl.SaveType; import org.ccnx.ccn.impl.encoding.CCNProtocolDTags; import org.ccnx.ccn.impl.encoding.GenericXMLEncodable; import org.ccnx.ccn.impl.encoding.XMLDecoder; import org.ccnx.ccn.impl.encoding.XMLEncodable; import org.ccnx.ccn.impl.encoding.XMLEncoder; import org.ccnx.ccn.io.ErrorStateException; import org.ccnx.ccn.protocol.ContentName; import org.ccnx.ccn.protocol.ContentObject; import org.ccnx.ccn.protocol.KeyLocator; import org.ccnx.ccn.protocol.PublisherPublicKeyDigest; public class KeyValueSet extends GenericXMLEncodable implements XMLEncodable, Map<String, Object> { protected TreeMap<String, KeyValuePair> _set = new TreeMap<String, KeyValuePair>(); /** * A CCNNetworkObject wrapper around KeyValueSet, used for easily saving and retrieving * versioned KeyValueSets to CCN. A typical pattern for using network objects to save * objects that happen to be encodable or serializable is to incorporate such a static * member wrapper class subclassing CCNEncodableObject, CCNSerializableObject, or * CCNNetworkObject itself inside the main class definition. */ public static class KeyValueSetObject extends CCNEncodableObject<KeyValueSet> { public KeyValueSetObject(ContentName name, KeyValueSet data, SaveType saveType, CCNHandle handle) throws IOException { super(KeyValueSet.class, true, name, data, saveType, handle); } public KeyValueSetObject(ContentName name, KeyValueSet data, SaveType saveType, PublisherPublicKeyDigest publisher, KeyLocator locator, CCNHandle handle) throws IOException { super(KeyValueSet.class, true, name, data, saveType, publisher, locator, handle); } public KeyValueSetObject(ContentName name, CCNHandle handle) throws ContentDecodingException, IOException { super(KeyValueSet.class, true, name, (PublisherPublicKeyDigest)null, handle); } public KeyValueSetObject(ContentName name, PublisherPublicKeyDigest publisher, CCNHandle handle) throws ContentDecodingException, IOException { super(KeyValueSet.class, true, name, publisher, handle); } public KeyValueSetObject(ContentObject firstBlock, CCNHandle handle) throws ContentDecodingException, IOException { super(KeyValueSet.class, true, firstBlock, handle); } public KeyValueSet contents() throws ContentNotReadyException, ContentGoneException, ErrorStateException { return data(); } } public KeyValueSet() {} /** * Create a KeyValueSet and initialize its contents to match that of a Java Properties * collection. * @param propertySet */ public KeyValueSet(Properties propertySet) { if (null == propertySet) return; for (Object property : propertySet.keySet()) { if (!(property instanceof String)) { throw new IllegalArgumentException("Not a valid Properties -- key not a String!"); } put((String) property, propertySet.getProperty((String)property)); } } /** * Create a KeyValueSet and initialize its contents to that of a * collection. * @param propertySet */ public KeyValueSet(Map<String, Object> values) { if (null == values) return; putAll(values); } /** * Add a new key value pair to the set. Key must be unique. * @param key key for the value * @param value the value - may be Integer, Float, String, byte[], or ContentName */ public Object put(String key, Object value) { KeyValuePair kvp = _set.get(key); if (kvp != null) { throw new InvalidParameterException("Duplicate keys not allowed"); } kvp = new KeyValuePair(key, value); _set.put(key, kvp); return value; } /** * @param key key of the Object to get * @return the value */ public Object get(String key) { KeyValuePair kvp = _set.get(key); return kvp == null ? null : _set.get(key).getValue(); } public int size() { return _set.size(); } public void clear() { _set.clear(); } public boolean containsKey(Object key) { return _set.containsKey(key); } public boolean containsValue(Object value) { for (KeyValuePair kvp : _set.values()) { if (value.equals(kvp.getValue())) return true; } return false; } public Set<java.util.Map.Entry<String, Object>> entrySet() { HashSet<Map.Entry<String, Object>> hs = new HashSet<Map.Entry<String, Object>>(); for (KeyValuePair kvp : _set.values()) { hs.add(kvp); } return hs; } public Object get(Object key) { return _set.get(key).getValue(); } public boolean isEmpty() { return _set.isEmpty(); } public Set<String> keySet() { return _set.keySet(); } public void putAll(Map<? extends String, ? extends Object> t) { for (String k : t.keySet()) { put(k, t.get(k)); } } public Object remove(Object key) { KeyValuePair kvp = _set.remove(key); return kvp.getValue(); } public Collection<Object> values() { ArrayList<Object> al = new ArrayList<Object>(); for (String k : _set.keySet()) { al.add(_set.get(k).getValue()); } return al; } @Override public void decode(XMLDecoder decoder) throws ContentDecodingException { decoder.readStartElement(getElementLabel()); synchronized (_set) { while (decoder.peekStartElement(CCNProtocolDTags.Entry)) { KeyValuePair kvp = new KeyValuePair(); kvp.decode(decoder); _set.put(kvp.getKey(), kvp); } } decoder.readEndElement(); } @Override public void encode(XMLEncoder encoder) throws ContentEncodingException { if (!validate()) { throw new ContentEncodingException("Cannot encode " + this.getClass().getName() + ": field values missing."); } encoder.writeStartElement(getElementLabel()); for (KeyValuePair kvp : _set.values()) { kvp.encode(encoder); } encoder.writeEndElement(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final KeyValueSet other = (KeyValueSet) obj; if (!other.keySet().equals(keySet())) return false; for (String key : keySet()) { if (! _set.get(key).equals(other._set.get(key))) return false; } return true; } @Override public int hashCode() { return keySet().hashCode(); } @Override public long getElementLabel() {return CCNProtocolDTags.KeyValueSet;} @Override public boolean validate() { return true; } }