net.sf.saxon.om.LargeAttributeMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Saxon-HE Show documentation
Show all versions of Saxon-HE Show documentation
The XSLT and XQuery Processor
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2018-2023 Saxonica Limited
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.om;
import net.sf.saxon.ma.trie.ImmutableHashTrieMap;
import net.sf.saxon.transpile.CSharpReplaceBody;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* An implementation of AttributeMap suitable for larger collections of attributes (say, more than five).
* This provides direct access to an attribute by name, avoiding the cost of a sequential search. The
* map preserves the order of insertion of attributes: this is done by maintaining a doubly-linked list
* of attributes in insertion order (when an attribute is replaced by another with the same name, it
* currently occupies the position of the original; but this is not guaranteed).
*/
public class LargeAttributeMap implements AttributeMap {
private static class AttributeInfoLink {
AttributeInfo payload;
NodeName prior;
NodeName next;
}
private ImmutableHashTrieMap attributes;
private NodeName first = null;
private NodeName last = null;
private int _size;
private LargeAttributeMap() {}
public LargeAttributeMap(List atts) {
assert !atts.isEmpty();
this.attributes = emptyMap();
this._size = atts.size();
AttributeInfoLink current = null;
for (AttributeInfo att : atts) {
if (attributes.get(att.getNodeName()) != null) {
throw new IllegalArgumentException("Attribute map contains duplicates");
}
AttributeInfoLink link = new AttributeInfoLink();
link.payload = att;
if (current == null) {
first = att.getNodeName();
} else {
current.next = att.getNodeName();
link.prior = current.payload.getNodeName();
}
current = link;
attributes = attributes.put(att.getNodeName(), link);
}
last = current.payload.getNodeName();
}
private LargeAttributeMap(ImmutableHashTrieMap attributes, int size, NodeName first, NodeName last) {
this.attributes = attributes;
this._size = size;
this.first = first;
this.last = last;
}
@CSharpReplaceBody(code="return System.Collections.Immutable.ImmutableDictionary.Create();")
private ImmutableHashTrieMap emptyMap() {
return ImmutableHashTrieMap.empty();
}
/**
* Return the number of attributes in the map.
*
* @return The number of attributes in the map.
*/
@Override
public int size() {
return _size;
}
@Override
public AttributeInfo get(NodeName name) {
AttributeInfoLink link = attributes.get(name);
return link == null ? null : link.payload;
}
@Override
public AttributeInfo get(NamespaceUri uri, String local) {
NodeName name = new FingerprintedQName("", uri, local);
return get(name);
}
@Override
public AttributeInfo getByFingerprint(int fingerprint, NamePool namePool) {
NodeName name = new FingerprintedQName(namePool.getStructuredQName(fingerprint), fingerprint);
return get(name);
}
@Override
public AttributeMap put(AttributeInfo att) {
AttributeInfoLink existing = attributes.get(att.getNodeName());
AttributeInfoLink link = new AttributeInfoLink();
NodeName last2 = last;
link.payload = att;
if (existing == null) {
link.prior = last;
last2 = att.getNodeName();
AttributeInfoLink oldLast = attributes.get(last);
AttributeInfoLink penult = new AttributeInfoLink();
penult.payload = oldLast.payload;
penult.next = att.getNodeName();
penult.prior = oldLast.prior;
attributes = attributes.put(last, penult);
} else {
link.prior = existing.prior;
link.next = existing.next;
}
ImmutableHashTrieMap att2 = attributes.put(att.getNodeName(), link);
int size2 = existing == null ? _size + 1 : _size;
return new LargeAttributeMap(att2, size2, first, last2);
}
@Override
public AttributeMap remove(NodeName name) {
// Not actually used (or tested)
if (attributes.get(name) == null) {
return this;
} else {
NodeName first2 = first;
NodeName last2 = last;
ImmutableHashTrieMap att2 = attributes.remove(name);
AttributeInfoLink existing = attributes.get(name);
if (existing.prior != null) {
AttributeInfoLink priorLink = attributes.get(existing.prior);
AttributeInfoLink priorLink2 = new AttributeInfoLink();
priorLink2.payload = priorLink.payload;
priorLink2.prior = priorLink.prior;
priorLink2.next = existing.next;
att2 = att2.put(existing.prior, priorLink2);
} else {
first2 = existing.next;
}
if (existing.next != null) {
AttributeInfoLink nextLink = attributes.get(existing.next);
AttributeInfoLink nextLink2 = new AttributeInfoLink();
nextLink2.payload = nextLink.payload;
nextLink2.next = nextLink.next;
nextLink2.prior = existing.prior;
att2 = att2.put(existing.next, nextLink2);
} else {
last2 = existing.prior;
}
return new LargeAttributeMap(att2, _size - 1, first2, last2);
}
}
@Override
public Iterator iterator() {
return new Iterator() {
NodeName current = first;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public AttributeInfo next() {
AttributeInfoLink link = attributes.get(current);
current = link.next;
return link.payload;
}
};
}
@Override
public synchronized ArrayList asList() {
ArrayList result = new ArrayList<>(_size);
for (AttributeInfo att : this) {
result.add(att);
}
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(256);
for (AttributeInfo att : this) {
sb.append(att.getNodeName().getDisplayName()).append("=\"").append(att.getValue()).append("\" ");
}
return sb.toString().trim();
}
}