de.intarsys.pdf.cos.COSDictionary Maven / Gradle / Ivy
/*
* Copyright (c) 2007, intarsys consulting GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of intarsys nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package de.intarsys.pdf.cos;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Represents a collection of associations (Map).
*
*
* The keys of the association are COSName objects, the value may be any
* COSDocumentElement
*
*
*/
public class COSDictionary extends COSCompositeObject {
public static class Entry implements Map.Entry {
private COSName key;
private COSDocumentElement value;
public Entry(COSName key, COSDocumentElement value) {
this.key = key;
this.value = value;
}
public Object getKey() {
return key;
}
public Object getValue() {
return value.dereference();
}
public Object setValue(Object newValue) {
return value = (COSDocumentElement) newValue;
}
}
/**
* Create an empty {@link COSDictionary}.
*
* @return Create an empty {@link COSDictionary}.
*/
public static COSDictionary create() {
return new COSDictionary();
}
/**
* Create an empty {@link COSDictionary} with an initial capacity.
*
* @return Create an empty {@link COSDictionary} with an initial capacity.
*/
public static COSDictionary create(int size) {
return new COSDictionary(size);
}
/** the map from a COSName to a COSDocumentElement */
private final Map objects;
protected COSDictionary() {
super();
this.objects = new HashMap();
}
protected COSDictionary(int size) {
super();
this.objects = new HashMap(size);
}
protected COSDictionary(Map objects) {
super();
this.objects = objects;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#accept(de.intarsys.pdf.cos.ICOSObjectVisitor)
*/
@Override
public java.lang.Object accept(ICOSObjectVisitor visitor)
throws COSVisitorException {
return visitor.visitFromDictionary(this);
}
/**
* Add all objects from dict
. Associations already available
* in this are replaced with new content .
*
* @param dict
* The collection of associations to add to this.
*/
public void addAll(COSDictionary dict) {
willChange(this);
for (Iterator i = dict.basicEntryIterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
COSName key = (COSName) entry.getKey();
COSDocumentElement element = (COSDocumentElement) entry.getValue();
basicPutPropagate(key, element.copyShallowNested());
}
if (objectListeners != null) {
triggerChanged(null, null, null);
}
}
/**
* Add all values from dict
that are not yet defined in the
* receiver.
*
* @param dict
* The dictionary with the associations to add.
*/
public void addIfAbsent(COSDictionary dict) {
boolean changed = false;
for (Iterator i = dict.basicEntryIterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
COSName key = (COSName) entry.getKey();
COSDocumentElement element = (COSDocumentElement) entry.getValue();
if (!containsKey(key)) {
if (!changed) {
changed = true;
willChange(this);
}
COSObject newObject = element.copyShallowNested().dereference();
basicPutPropagate(key, newObject);
}
}
if (changed && (objectListeners != null)) {
triggerChanged(null, null, null);
}
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#getCOSDictionary()
*/
@Override
public COSDictionary asDictionary() {
return this;
}
/**
* Remove all associations from this.
*
*/
protected void basicClearPropagate() {
for (Iterator i = objects.values().iterator(); i.hasNext();) {
COSDocumentElement element = (COSDocumentElement) i.next();
COSObject dereferenced = element.dereference();
willChange(dereferenced);
ICOSContainer newContainer = element.removeContainer(this);
dereferenced.triggerChanged(COSObject.SLOT_CONTAINER, null,
newContainer);
}
objects.clear();
}
/**
* An iterator over all entries. The value of the entries returned are the
* not - dereferenced elements.
*
* @return An interator over all entries.
*/
public Iterator basicEntryIterator() {
return objects.entrySet().iterator();
}
/**
* The {@link COSDocumentElement} associated with key
.
*
* @param key
* The name to lookup
*
* @return The {@link COSDocumentElement} associated with key
.
*/
public COSDocumentElement basicGet(COSName key) {
return (COSDocumentElement) objects.get(key);
}
/**
* An iterator over all values. The objects returned are not dereferenced.
*
* @return An iterator over all values.
*/
@Override
public Iterator basicIterator() {
return objects.values().iterator();
}
/**
* Add a {@link COSDocumentElement} to the collection.
*
* @param key
* The key where to store the new element.
* @param element
* The {@link COSDocumentElement} to store.
*
* @return The {@link COSDocumentElement} associated with key
* so far.
*/
protected COSDocumentElement basicPutPropagate(COSName key,
COSDocumentElement element) {
COSObject dereferenced = element.dereference();
COSDocumentElement containable = element.containable();
//
willChange(dereferenced);
ICOSContainer newContainer = containable.addContainer(this);
COSDocumentElement oldContainable = (COSDocumentElement) objects.put(
key, containable);
dereferenced.triggerChanged(COSObject.SLOT_CONTAINER, null,
newContainer);
//
if (oldContainable != null && oldContainable != containable) {
COSObject oldDereferenced = oldContainable.dereference();
willChange(oldDereferenced);
newContainer = oldContainable.removeContainer(this);
oldDereferenced.triggerChanged(COSObject.SLOT_CONTAINER, null,
newContainer);
}
return oldContainable;
}
/**
* Add a document element to the collection.
*
* The element is inserted without change propagation.
*
* This should not be used by the application level programmer. It is public
* for package visibility reasons.
*
* @param key
* The key where to store the new element.
* @param element
* The {@link COSDocumentElement} to store.
*
* @return The {@link COSDocumentElement} associated with key
* so far.
*/
public COSDocumentElement basicPutSilent(COSName key,
COSDocumentElement element) {
COSDocumentElement containable = element.containable();
containable.addContainer(this);
COSDocumentElement oldContainable = (COSDocumentElement) objects.put(
key, containable);
if (oldContainable != null && oldContainable != containable) {
oldContainable.removeContainer(this);
}
return oldContainable;
}
/**
* Remove the element associated with key
from the collection .
*
* @param key
* The key of the element to be removed
*
* @return The {@link COSDocumentElement} removed or null.
*/
protected COSDocumentElement basicRemovePropagate(COSName key) {
COSDocumentElement element = (COSDocumentElement) objects.remove(key);
if (element == null) {
return null;
}
COSObject dereferenced = element.dereference();
willChange(dereferenced);
ICOSContainer newContainer = element.removeContainer(this);
dereferenced.triggerChanged(COSObject.SLOT_CONTAINER, null,
newContainer);
return element;
}
/**
* Remove the element associated with key
from the collection .
*
* The element is removed without change propagation.
*
* This should not be used by the application level programmer. It is public
* for package visibility reasons.
*
* @param key
* The key of the element to be removed
*
* @return The {@link COSDocumentElement} removed or null.
*/
public COSDocumentElement basicRemoveSilent(COSName key) {
COSDocumentElement element = (COSDocumentElement) objects.remove(key);
if (element == null) {
return null;
}
element.removeContainer(this);
return element;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#basicToString()
*/
@Override
protected String basicToString() {
return objects.toString();
}
/**
* Remove all associations from the receiver.
*/
public void clear() {
willChange(this);
basicClearPropagate();
if (objectListeners != null) {
triggerChanged(null, null, null);
}
}
/**
* Answer true if key
is a valid key in the collection .
*
* @param key
* The key whose existence is to be checked.
*
* @return Answer true if key
is a valid key in the
* collection .
*/
public boolean containsKey(COSName key) {
return objects.containsKey(key);
}
/**
* Answer true
if obj
is contained in the
* collection
*
* @param obj
* The object to look up in the collection
*
* @return Answer true
if obj
is contained in
* the collection
*/
public boolean containsValue(COSObject obj) {
for (Iterator i = entryIterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
COSObject current = (COSObject) entry.getValue();
if (current.equals(obj)) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#copyBasic()
*/
@Override
protected COSObject copyBasic() {
return create(size());
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSCompositeObject#copyDeep(java.util.Map)
*/
@Override
public COSObject copyDeep(Map copied) {
COSDictionary result = (COSDictionary) super.copyDeep(copied);
for (Iterator i = objects.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
COSName key = (COSName) entry.getKey();
COSDocumentElement element = (COSDocumentElement) entry.getValue();
COSObject copy = element.copyDeep(copied);
result.basicPutSilent(key, copy);
}
return result;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#copyShallow()
*/
@Override
public COSObject copyShallow() {
COSDictionary result = (COSDictionary) super.copyShallow();
for (Iterator i = basicEntryIterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
COSName key = (COSName) entry.getKey();
COSDocumentElement element = (COSDocumentElement) entry.getValue();
result.basicPutSilent(key, element.copyShallowNested());
}
return result;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#copySubGraph(java.util.Map)
*/
@Override
protected COSObject copySubGraph(Map copied) {
COSDictionary result = (COSDictionary) super.copySubGraph(copied);
// for (Iterator i = objects.entrySet().iterator(); i.hasNext();) {
// Map.Entry entry = (Map.Entry) i.next();
// COSName key = (COSName) entry.getKey();
// COSDocumentElement element = (COSDocumentElement) entry.getValue();
// COSObject copy = null;
// if (element.isReference()) {
// copy = (COSObject) copied.get(element);
// if (copy == null) {
// if (value.hasNavigationPathTo(this)) {
// copy = value.copySubGraph(copied);
// } else {
// copy = value;
// copied.put(value.getIndirectObject(), copy);
// }
// }
// } else {
// copy = value.copySubGraph(copied);
// }
// result.basicPut(key, copy);
// }
return result;
}
/**
* An iterator over all entries, returning a collection of {@link Entry}
* instances. The {@link Entry} values are dereferenced.
*
* @return An iterator over all entries, returning a collection of
* {@link Entry} instances.
*
*/
public Iterator entryIterator() {
return new Iterator() {
private Iterator it = getObjects().entrySet().iterator();
public boolean hasNext() {
return it.hasNext();
}
public Object next() {
Map.Entry entry = (Map.Entry) it.next();
return new Entry((COSName) entry.getKey(),
(COSDocumentElement) entry.getValue());
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object o) {
return this.equals(o, new HashSet());
}
@Override
protected boolean equals(Object o, Set visited) {
if (isIndirect()) {
if (visited.contains(getIndirectObject())) {
return true;
}
visited.add(getIndirectObject());
}
if (this == o) {
return true;
}
if (!(o instanceof COSDictionary)) {
return false;
}
COSDictionary other = (COSDictionary) o;
if (size() != other.size()) {
return false;
}
for (Iterator i = keySet().iterator(); i.hasNext();) {
COSName key = (COSName) i.next();
COSDocumentElement eThis = get(key);
COSObject oThis = eThis.dereference();
COSDocumentElement eOther = other.get(key);
COSObject oOther = eOther.dereference();
if ((oThis == null) && (oOther != null)) {
return false;
}
if (!oThis.equals(oOther, visited)) {
return false;
}
}
return true;
}
/**
* The {@link COSObject} associated with key
.
*
* @param key
* The key to lookup
*
* @return The {@link COSObject} associated with key
.
*/
public COSObject get(COSName key) {
COSDocumentElement element = basicGet(key);
return (element == null) ? COSNull.NULL : element.dereference();
}
protected Map getObjects() {
return objects;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#iterator()
*/
@Override
public java.util.Iterator iterator() {
return new Iterator() {
private Iterator i = getObjects().values().iterator();
public boolean hasNext() {
return i.hasNext();
}
public Object next() {
return ((COSDocumentElement) i.next()).dereference();
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* The key of obj when it is contained in this or {@link COSNull}.
*
* @param obj
* The object to look up in the collection
*
* @return The key of obj when it is contained in this or {@link COSNull}.
*/
public COSObject keyOf(COSObject obj) {
for (Iterator i = entryIterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
COSObject current = (COSObject) entry.getValue();
if (current.equals(obj)) {
return (COSName) entry.getKey();
}
}
return COSNull.NULL;
}
/**
* The set of keys. Keys are {@link COSName} instances.
*
* @return The set of keys .
*/
public Set keySet() {
return objects.keySet();
}
/**
* Add an association to the collection.
*
* @param key
* The key where to store the object
* @param object
* The object to store in the collection
*
* @return The {@link COSObject} associated with key
so far.
*/
public COSObject put(COSName key, COSObject object) {
if (object == null) {
return remove(key);
}
willChange(this);
COSDocumentElement oldElement = basicPutPropagate(key, object);
COSObject oldObject = null;
if (oldElement == null) {
oldObject = COSNull.NULL;
} else {
oldObject = oldElement.dereference();
}
if (objectListeners != null) {
triggerChanged(key, oldObject, object);
}
return oldObject;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#referenceIndirect(de.intarsys.pdf.cos.COSIndirectObject)
*/
@Override
public COSIndirectObject referenceIndirect(COSObject object) {
COSIndirectObject ref = super.referenceIndirect(object);
for (Iterator it = objects.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
if (entry.getValue() == object) {
entry.setValue(ref);
// there can be only one...
break;
}
}
return ref;
}
/**
* Remove the element from the collection associated with key
.
*
* @param key
* The key of the object to remove
*
* @return The {@link COSObject} removed or null.
*/
public COSObject remove(COSName key) {
willChange(this);
COSDocumentElement element = basicRemovePropagate(key);
COSObject oldObject;
if (element == null) {
oldObject = COSNull.NULL;
} else {
oldObject = element.dereference();
if (objectListeners != null) {
triggerChanged(key, oldObject, COSNull.NULL);
}
}
return oldObject;
}
/*
* (non-Javadoc)
*
* @see de.intarsys.pdf.cos.COSObject#restoreState(java.lang.Object)
*/
@Override
public void restoreState(Object object) {
super.restoreState(object);
objects.clear();
objects.putAll(((COSDictionary) object).objects);
triggerChanged(null, null, null);
}
/*
* (non-Javadoc)
*
* @see de.intarsys.tools.objectsession.ISaveStateSupport#saveState()
*/
public Object saveState() {
COSObject result = new COSDictionary(new HashMap(this.objects));
result.container = this.container.saveStateContainer();
return result;
}
/**
* The number of elements in this.
*
* @return The number of elements in this.
*/
public int size() {
return objects.size();
}
/**
* A list of {@link COSObject} instances within this.
*
* @return A list of {@link COSObject} instances within this.
*/
public List values() {
List result = new ArrayList();
for (Iterator it = iterator(); it.hasNext();) {
result.add(it.next());
}
return result;
}
}