soughtValue = soughtValue.convert(BuiltInAtomicType.STRING, true, context).asAtomic();
} else {
// If the key value is numeric, promote it to a double
// TODO: this could result in two decimals comparing equal because they convert to the same double
BuiltInAtomicType itemType = soughtValue.getPrimitiveType();
if (itemType.equals(BuiltInAtomicType.INTEGER) ||
itemType.equals(BuiltInAtomicType.DECIMAL) ||
itemType.equals(BuiltInAtomicType.FLOAT)) {
soughtValue = new DoubleValue(((NumericValue)soughtValue).getDoubleValue());
}
}
// If the sought value is untypedAtomic and the equality matching mode is
// "convertUntypedToOther", then we construct and search one index for each
// primitive atomic type that could occur in the result of the "use" expression,
// and merge the results. We rely on the fact that in this case, there will only
// be one key definition.
// NOTE: This is much more elaborate than it needs to be. The option convertUntypedToOther
// is used for an index used to support a general comparison. This reports an error if two
// non-comparable values are compared. We could report an error immediately if foundItemTypes
// includes a type that is not comparable to the soughtValue. In practice we only need a maximum
// of two indexes: one for the sought item type, and one for untypedAtomic.
HashSet<BuiltInAtomicType> foundItemTypes = null;
AtomicValue value = soughtValue;
if (soughtValue instanceof UntypedAtomicValue && definition.isConvertUntypedToOther()) {
// We try string first, but at the same time as building an index for strings,
// we collect details of the other types actually encountered for the use expression
BuiltInAtomicType useType = definition.getIndexedItemType();
if (useType.equals(BuiltInAtomicType.ANY_ATOMIC)) {
foundItemTypes = new HashSet<BuiltInAtomicType>(10);
useType = BuiltInAtomicType.STRING;
}
value = soughtValue.convert(useType, true, context).asAtomic();
}
// No special action needed for anyURI to string promotion (it just seems to work: tests idky44, 45)
int keySetNumber = keySet.getKeySetNumber();
BuiltInAtomicType itemType = value.getPrimitiveType();
HashMap index;
synchronized (doc) {
// Need to synchronize to prevent two threads that use the same stylesheet indexing the same source
// document simultaneously. We could synchronize on either the key definition or the document
// (ideally we would use the combination of the two), but the document is less likely to cause
// unnecessary contention: it's more likely that an index definition applies to large numbers of
// documents than that a document has large numbers of indexes.
Object indexObject = getIndex(doc, keySetNumber, itemType);
if (indexObject instanceof String) {
// index is under construction
XPathException de = new XPathException("Key definition is circular");
de.setXPathContext(context);
de.setErrorCode("XTDE0640");
throw de;
}
index = (HashMap)indexObject;
// If the index does not yet exist, then create it.
if (index==null) {
// Mark the index as being under construction, in case the definition is circular
putIndex(doc, keySetNumber, itemType, "Under Construction", context);
index = buildIndex(keySet, itemType, foundItemTypes, doc, context);
putIndex(doc, keySetNumber, itemType, index, context);
if (foundItemTypes != null) {
// build indexes for each item type actually found
for (Iterator<BuiltInAtomicType> f = foundItemTypes.iterator(); f.hasNext();) {
BuiltInAtomicType t = f.next();
if (!t.equals(BuiltInAtomicType.STRING)) {
putIndex(doc, keySetNumber, t, "Under Construction", context);
index = buildIndex(keySet, t, null, doc, context);
putIndex(doc, keySetNumber, t, index, context);
}
}
}
}
}
if (foundItemTypes == null) {
ArrayList nodes = (ArrayList)index.get(getCollationKey(value, itemType, collation, context));
if (nodes==null) {
return EmptyIterator.getInstance();
} else {
return new ListIterator(nodes);
}
} else {
// we need to search the indexes for all possible types, and combine the results.
SequenceIterator result = null;
WeakReference<HashMap<Long, Object>> ref = docIndexes.get(doc);
if (ref != null) {
HashMap<Long, Object> indexList = ref.get();
if (indexList != null) {
for (Iterator<Long> i=indexList.keySet().iterator(); i.hasNext();) {
long key = (i.next()).longValue();
if (((key >> 32)) == keySetNumber) {
int typefp = (int)key;
BuiltInAtomicType type = (BuiltInAtomicType)BuiltInType.getSchemaType(typefp);
Object indexObject2 = getIndex(doc, keySetNumber, type);
if (indexObject2 instanceof String) {
// index is under construction
XPathException de = new XPathException("Key definition is circular");