This set of fixes mainly address the issues where the JE backend does not handle attributes with options and subtypes correctly when they are being indexed.
With this fix:
- All values of an indexed attribute type will be indexed correctly on modifies, adds, and deletes.
- Updates to subordinate types will now update the superior type if its indexed.
- Adding and deleting superior attribute types that are not allowed by any object classes (ie. name) will be correctly handled
- Deleting all values from an attribute with no options will no longer delete the values from the same attribute but with options.
| | |
| | | Set<ASN1OctetString> addKeys, |
| | | Set<ASN1OctetString> delKeys) |
| | | { |
| | | replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys); |
| | | List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true); |
| | | List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true); |
| | | HashSet<AttributeValue> newValues; |
| | | HashSet<AttributeValue> oldValues; |
| | | |
| | | if(newAttributes == null) |
| | | { |
| | | indexAttribute(oldAttributes, delKeys); |
| | | } |
| | | else |
| | | { |
| | | if(oldAttributes == null) |
| | | { |
| | | indexAttribute(newAttributes, addKeys); |
| | | } |
| | | else |
| | | { |
| | | HashSet<ASN1OctetString> newKeys = |
| | | new HashSet<ASN1OctetString>(); |
| | | HashSet<ASN1OctetString> oldKeys = |
| | | new HashSet<ASN1OctetString>(); |
| | | indexAttribute(newAttributes, newKeys); |
| | | indexAttribute(oldAttributes, oldKeys); |
| | | |
| | | addKeys.addAll(newKeys); |
| | | addKeys.removeAll(oldKeys); |
| | | |
| | | delKeys.addAll(oldKeys); |
| | | delKeys.removeAll(newKeys); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | { |
| | | // Check whether any modifications apply to this indexed attribute. |
| | | boolean attributeModified = false; |
| | | AttributeType indexAttributeType = index.getAttributeType(); |
| | | Iterable<AttributeType> subTypes = |
| | | DirectoryServer.getSchema().getSubTypes(indexAttributeType); |
| | | |
| | | for (Modification mod : mods) |
| | | { |
| | | Attribute modAttr = mod.getAttribute(); |
| | | AttributeType modAttrType = modAttr.getAttributeType(); |
| | | if (modAttrType.equals(index.getAttributeType())) |
| | | if (modAttrType.equals(indexAttributeType)) |
| | | { |
| | | attributeModified = true; |
| | | break; |
| | | } |
| | | for(AttributeType subType : subTypes) |
| | | { |
| | | if(modAttrType.equals(subType)) |
| | | { |
| | | attributeModified = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if (attributeModified) |
| | | { |
| | |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.ArrayList; |
| | | |
| | | /** |
| | | * An implementation of an Indexer for attribute equality. |
| | |
| | | Set<ASN1OctetString> delKeys) |
| | | throws DatabaseException |
| | | { |
| | | // Optimize for the case where there are no attribute options |
| | | // involved and only simple addition and deletion of individual values. |
| | | // An issue with attribute options is that the values can not be assumed |
| | | // to be unique. |
| | | List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true); |
| | | List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true); |
| | | HashSet<AttributeValue> newValues; |
| | | HashSet<AttributeValue> oldValues; |
| | | |
| | | List<Attribute> beforeList; |
| | | List<Attribute> afterList; |
| | | beforeList = oldEntry.getAttribute(attributeType); |
| | | afterList = newEntry.getAttribute(attributeType); |
| | | |
| | | boolean hasOptions = false; |
| | | |
| | | if (beforeList != null) |
| | | if(newAttributes == null) |
| | | { |
| | | for (Attribute a : beforeList) |
| | | { |
| | | if (a.hasOptions()) |
| | | { |
| | | hasOptions = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (afterList != null) |
| | | { |
| | | for (Attribute a : afterList) |
| | | { |
| | | if (a.hasOptions()) |
| | | { |
| | | hasOptions = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | boolean hasOnlySimpleMods = true; |
| | | List<Modification> simpleMods = new ArrayList<Modification>(); |
| | | for (Modification mod : mods) |
| | | { |
| | | Attribute modAttr = mod.getAttribute(); |
| | | AttributeType modAttrType = modAttr.getAttributeType(); |
| | | if (modAttrType.equals(attributeType)) |
| | | { |
| | | if (modAttr.hasOptions()) |
| | | { |
| | | hasOptions = true; |
| | | } |
| | | switch (mod.getModificationType()) |
| | | { |
| | | case ADD: |
| | | simpleMods.add(mod); |
| | | break; |
| | | |
| | | case DELETE: |
| | | if (!modAttr.hasValue()) |
| | | { |
| | | hasOnlySimpleMods = false; |
| | | } |
| | | else |
| | | { |
| | | simpleMods.add(mod); |
| | | } |
| | | break; |
| | | |
| | | default: |
| | | hasOnlySimpleMods = false; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (hasOnlySimpleMods && !hasOptions) |
| | | { |
| | | // This is the optimized case where there are no attribute options |
| | | // involved and only simple addition and deletion of individual values. |
| | | // It should be efficient for adding and/or deleting a few values of an |
| | | // attribute with lots of values. |
| | | for (Modification mod : simpleMods) |
| | | { |
| | | Set<AttributeValue> values = mod.getAttribute().getValues(); |
| | | Set<ASN1OctetString> keys = |
| | | new HashSet<ASN1OctetString>(values.size()); |
| | | indexValues(values, keys); |
| | | |
| | | switch (mod.getModificationType()) |
| | | { |
| | | case ADD: |
| | | for (ASN1OctetString key : keys) |
| | | { |
| | | if (delKeys.contains(key)) |
| | | { |
| | | delKeys.remove(key); |
| | | } |
| | | else |
| | | { |
| | | addKeys.add(key); |
| | | } |
| | | } |
| | | break; |
| | | |
| | | case DELETE: |
| | | for (ASN1OctetString key : keys) |
| | | { |
| | | if (addKeys.contains(key)) |
| | | { |
| | | addKeys.remove(key); |
| | | } |
| | | else |
| | | { |
| | | delKeys.add(key); |
| | | } |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | indexAttribute(oldAttributes, delKeys); |
| | | } |
| | | else |
| | | { |
| | | // This is the non-optimized case that should be correct in all cases. |
| | | if(oldAttributes == null) |
| | | { |
| | | indexAttribute(newAttributes, addKeys); |
| | | } |
| | | else |
| | | { |
| | | newValues = new HashSet<AttributeValue>(); |
| | | oldValues = new HashSet<AttributeValue>(); |
| | | for(Attribute a : newAttributes) |
| | | { |
| | | newValues.addAll(a.getValues()); |
| | | } |
| | | for(Attribute a : oldAttributes) |
| | | { |
| | | oldValues.addAll(a.getValues()); |
| | | } |
| | | |
| | | Set<ASN1OctetString> oldSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(beforeList, oldSet); |
| | | HashSet<AttributeValue> valuesToAdd = |
| | | new HashSet<AttributeValue>(newValues); |
| | | HashSet<AttributeValue> valuesToDel = |
| | | new HashSet<AttributeValue>(oldValues); |
| | | valuesToAdd.removeAll(oldValues); |
| | | valuesToDel.removeAll(newValues); |
| | | |
| | | Set<ASN1OctetString> newSet = new HashSet<ASN1OctetString>(); |
| | | indexAttribute(afterList, newSet); |
| | | |
| | | HashSet<ASN1OctetString> removeSet = new HashSet<ASN1OctetString>(oldSet); |
| | | removeSet.removeAll(newSet); |
| | | delKeys.addAll(removeSet); |
| | | |
| | | HashSet<ASN1OctetString> addSet = new HashSet<ASN1OctetString>(newSet); |
| | | addSet.removeAll(oldSet); |
| | | addKeys.addAll(addSet); |
| | | indexValues(valuesToDel, delKeys); |
| | | indexValues(valuesToAdd, addKeys); |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | Set<ASN1OctetString> delKeys) |
| | | throws DatabaseException |
| | | { |
| | | List<Attribute> beforeList; |
| | | beforeList = oldEntry.getAttribute(attributeType); |
| | | List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true); |
| | | List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true); |
| | | HashSet<AttributeValue> newValues; |
| | | HashSet<AttributeValue> oldValues; |
| | | |
| | | // Pick out the modifications that apply to this indexed attribute |
| | | |
| | | /** |
| | | * FIXME unusual modifications can insert spurious index values |
| | | * The following sequence of modifications will insert A into the |
| | | * index, yet A is not a resulting value for the attribute. |
| | | * |
| | | * add: cn |
| | | * cn: A |
| | | * - |
| | | * replace: cn |
| | | * cn: B |
| | | * - |
| | | * |
| | | */ |
| | | |
| | | for (Modification mod : mods) |
| | | if(newAttributes == null) |
| | | { |
| | | Attribute modAttr = mod.getAttribute(); |
| | | AttributeType modAttrType = modAttr.getAttributeType(); |
| | | if (modAttrType.equals(attributeType)) |
| | | indexAttribute(oldAttributes, delKeys); |
| | | } |
| | | else |
| | | { |
| | | if(oldAttributes == null) |
| | | { |
| | | switch (mod.getModificationType()) |
| | | indexAttribute(newAttributes, addKeys); |
| | | } |
| | | else |
| | | { |
| | | newValues = new HashSet<AttributeValue>(); |
| | | oldValues = new HashSet<AttributeValue>(); |
| | | for(Attribute a : newAttributes) |
| | | { |
| | | case REPLACE: |
| | | case INCREMENT: |
| | | if (beforeList != null) |
| | | { |
| | | for (Attribute attr : beforeList) |
| | | { |
| | | if (attr.hasOptions(modAttr.getOptions())) |
| | | { |
| | | indexValues(attr.getValues(), delKeys); |
| | | } |
| | | } |
| | | } |
| | | indexValues(modAttr.getValues(), addKeys); |
| | | break; |
| | | |
| | | case ADD: |
| | | indexValues(modAttr.getValues(), addKeys); |
| | | break; |
| | | |
| | | case DELETE: |
| | | if (!modAttr.hasValue()) |
| | | { |
| | | if (beforeList != null) |
| | | { |
| | | for (Attribute attr : beforeList) |
| | | { |
| | | if (attr.hasOptions(modAttr.getOptions())) |
| | | { |
| | | indexValues(attr.getValues(), delKeys); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | indexValues(modAttr.getValues(), delKeys); |
| | | } |
| | | break; |
| | | newValues.addAll(a.getValues()); |
| | | } |
| | | for(Attribute a : oldAttributes) |
| | | { |
| | | oldValues.addAll(a.getValues()); |
| | | } |
| | | |
| | | HashSet<AttributeValue> valuesToAdd = |
| | | new HashSet<AttributeValue>(newValues); |
| | | HashSet<AttributeValue> valuesToDel = |
| | | new HashSet<AttributeValue>(oldValues); |
| | | valuesToAdd.removeAll(oldValues); |
| | | valuesToDel.removeAll(newValues); |
| | | |
| | | indexValues(valuesToDel, delKeys); |
| | | indexValues(valuesToAdd, addKeys); |
| | | } |
| | | } |
| | | } |
| | |
| | | Set<ASN1OctetString> delKeys) |
| | | throws DatabaseException |
| | | { |
| | | replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys); |
| | | List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true); |
| | | List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true); |
| | | if(oldAttributes == null) |
| | | { |
| | | if(newAttributes != null) |
| | | { |
| | | addKeys.add( |
| | | new ASN1OctetString(AttributeIndex.presenceKey.getData())); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | if(newAttributes == null) |
| | | { |
| | | delKeys.add( |
| | | new ASN1OctetString(AttributeIndex.presenceKey.getData())); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | import java.util.Comparator; |
| | | import java.util.HashSet; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | |
| | | Set<ASN1OctetString> addKeys, |
| | | Set<ASN1OctetString> delKeys) |
| | | { |
| | | replaceEntry(txn, oldEntry, newEntry, addKeys, delKeys); |
| | | List<Attribute> newAttributes = newEntry.getAttribute(attributeType, true); |
| | | List<Attribute> oldAttributes = oldEntry.getAttribute(attributeType, true); |
| | | HashSet<AttributeValue> newValues; |
| | | HashSet<AttributeValue> oldValues; |
| | | |
| | | if(newAttributes == null) |
| | | { |
| | | indexAttribute(oldAttributes, delKeys); |
| | | } |
| | | else |
| | | { |
| | | if(oldAttributes == null) |
| | | { |
| | | indexAttribute(newAttributes, addKeys); |
| | | } |
| | | else |
| | | { |
| | | HashSet<ASN1OctetString> newKeys = |
| | | new HashSet<ASN1OctetString>(); |
| | | HashSet<ASN1OctetString> oldKeys = |
| | | new HashSet<ASN1OctetString>(); |
| | | indexAttribute(newAttributes, newKeys); |
| | | indexAttribute(oldAttributes, oldKeys); |
| | | |
| | | addKeys.addAll(newKeys); |
| | | addKeys.removeAll(oldKeys); |
| | | |
| | | delKeys.addAll(oldKeys); |
| | | delKeys.removeAll(newKeys); |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | /** |
| | | * Generate the set of substring index keys for an attribute. |
| | | * @param attrList The attribute for which substring keys are required. |
| | | * @param addKeys The set into which the generated keys will be inserted. |
| | | * @param keys The set into which the generated keys will be inserted. |
| | | */ |
| | | private void indexAttribute(List<Attribute> attrList, |
| | | Set<ASN1OctetString> addKeys) |
| | | Set<ASN1OctetString> keys) |
| | | { |
| | | if (attrList == null) return; |
| | | |
| | | for (Attribute attr : attrList) |
| | | { |
| | | LinkedHashSet<AttributeValue> values = attr.getValues(); |
| | | for (AttributeValue value : values) |
| | | { |
| | | try |
| | | { |
| | | byte[] normalizedBytes = value.getNormalizedValue().value(); |
| | | indexValues(attr.getValues(), keys); |
| | | } |
| | | } |
| | | |
| | | substringKeys(normalizedBytes, addKeys); |
| | | } |
| | | catch (DirectoryException e) |
| | | /** |
| | | * Generate the set of index keys for a set of attribute values. |
| | | * @param values The set of attribute values to be indexed. |
| | | * @param keys The set into which the keys will be inserted. |
| | | */ |
| | | private void indexValues(Set<AttributeValue> values, |
| | | Set<ASN1OctetString> keys) |
| | | { |
| | | if (values == null) return; |
| | | |
| | | for (AttributeValue value : values) |
| | | { |
| | | try |
| | | { |
| | | byte[] normalizedBytes = value.getNormalizedValue().value(); |
| | | |
| | | substringKeys(normalizedBytes, keys); |
| | | } |
| | | catch (DirectoryException e) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | if (debugEnabled()) |
| | | { |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | TRACER.debugCaught(DebugLogLevel.ERROR, e); |
| | | } |
| | | } |
| | | } |
| | |
| | | SortKey[] sortKeys = sortOrder.getSortKeys(); |
| | | for(SortKey sortKey : sortKeys) |
| | | { |
| | | AttributeType attributeType = sortKey.getAttributeType(); |
| | | Iterable<AttributeType> subTypes = |
| | | DirectoryServer.getSchema().getSubTypes(attributeType); |
| | | for(Modification mod : mods) |
| | | { |
| | | if(mod.getAttribute().getAttributeType(). |
| | | equals(sortKey.getAttributeType())) |
| | | AttributeType modAttrType = mod.getAttribute().getAttributeType(); |
| | | if(modAttrType.equals(attributeType)) |
| | | { |
| | | sortAttributeModified = true; |
| | | break; |
| | | } |
| | | for(AttributeType subType : subTypes) |
| | | { |
| | | if(modAttrType.equals(subType)) |
| | | { |
| | | sortAttributeModified = true; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | if(sortAttributeModified) |
| | | { |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether this entry contains the specified attribute. |
| | | * Any subordinate attribute of the specified attribute will also |
| | | * be used in the determination. |
| | | * |
| | | * @param attributeType The attribute type for which to make the |
| | | * determination. |
| | | * |
| | | * @param attributeType The attribute type for which to |
| | | * make the determination. |
| | | * |
| | | * @return <CODE>true</CODE> if this entry contains the specified |
| | | * attribute, or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean hasAttribute(AttributeType attributeType) |
| | | { |
| | | return hasAttribute(attributeType, true); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Indicates whether this entry contains the specified attribute. |
| | | * |
| | | * @param attributeType The attribute type for which to |
| | | * make the determination. |
| | | * @param includeSubordinates Whether to include any subordinate |
| | | * attributes of the attribute type |
| | | * being retrieved. |
| | | * |
| | | * @return <CODE>true</CODE> if this entry contains the specified |
| | | * attribute, or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean hasAttribute(AttributeType attributeType, |
| | | boolean includeSubordinates) |
| | | { |
| | | if (userAttributes.containsKey(attributeType) || |
| | | operationalAttributes.containsKey(attributeType)) |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | if (attributeType.mayHaveSubordinateTypes()) |
| | | if (includeSubordinates && |
| | | attributeType.mayHaveSubordinateTypes()) |
| | | { |
| | | for (AttributeType at : schema.getSubTypes(attributeType)) |
| | | { |
| | |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether this entry contains the specified attribute |
| | | * with all of the options in the provided set. |
| | | * with all of the options in the provided set. Any subordinate |
| | | * attribute of the specified attribute will also be used in |
| | | * the determination. |
| | | * |
| | | * @param attributeType The attribute type for which to make |
| | | * the determination. |
| | | * @param attributeOptions The set of options to use in the |
| | | * determination. |
| | | * @param attributeType The attribute type for which to |
| | | * make the determination. |
| | | * @param attributeOptions The set of options to use in the |
| | | * determination. |
| | | * |
| | | * @return <CODE>true</CODE> if this entry contains the specified |
| | | * attribute, or <CODE>false</CODE> if not. |
| | |
| | | public boolean hasAttribute(AttributeType attributeType, |
| | | Set<String> attributeOptions) |
| | | { |
| | | return hasAttribute(attributeType, true, attributeOptions); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Indicates whether this entry contains the specified attribute |
| | | * with all of the options in the provided set. |
| | | * |
| | | * @param attributeType The attribute type for which to |
| | | * make the determination. |
| | | * @param includeSubordinates Whether to include any subordinate |
| | | * attributes of the attribute type |
| | | * being retrieved. |
| | | * @param attributeOptions The set of options to use in the |
| | | * determination. |
| | | * |
| | | * @return <CODE>true</CODE> if this entry contains the specified |
| | | * attribute, or <CODE>false</CODE> if not. |
| | | */ |
| | | public boolean hasAttribute(AttributeType attributeType, |
| | | boolean includeSubordinates, |
| | | Set<String> attributeOptions) |
| | | { |
| | | List<Attribute> attributes; |
| | | if (attributeType.mayHaveSubordinateTypes()) |
| | | if (includeSubordinates && |
| | | attributeType.mayHaveSubordinateTypes()) |
| | | { |
| | | attributes = new LinkedList<Attribute>(); |
| | | List<Attribute> attrs = userAttributes.get(attributeType); |
| | |
| | | return false; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the requested attribute element(s) for the specified |
| | | * attribute type. The list returned may include multiple elements |
| | | * if the same attribute exists in the entry multiple times with |
| | | * different sets of options. |
| | | * different sets of options. It may also include any subordinate |
| | | * attributes of the attribute being retrieved. |
| | | * |
| | | * @param attributeType The attribute type to retrieve. |
| | | * |
| | |
| | | */ |
| | | public List<Attribute> getAttribute(AttributeType attributeType) |
| | | { |
| | | if (attributeType.mayHaveSubordinateTypes()) |
| | | return getAttribute(attributeType, true); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Retrieves the requested attribute element(s) for the specified |
| | | * attribute type. The list returned may include multiple elements |
| | | * if the same attribute exists in the entry multiple times with |
| | | * different sets of options. |
| | | * |
| | | * @param attributeType The attribute type to retrieve. |
| | | * @param includeSubordinates Whether to include any subordinate |
| | | * attributes of the attribute type |
| | | * being retrieved. |
| | | * |
| | | * @return The requested attribute element(s) for the specified |
| | | * attribute type, or <CODE>null</CODE> if the specified |
| | | * attribute type is not present in this entry. |
| | | */ |
| | | public List<Attribute> getAttribute(AttributeType attributeType, |
| | | boolean includeSubordinates) |
| | | { |
| | | if (includeSubordinates && |
| | | attributeType.mayHaveSubordinateTypes()) |
| | | { |
| | | List<Attribute> attributes = new LinkedList<Attribute>(); |
| | | |
| | |
| | | * Retrieves the requested attribute element(s) for the attribute |
| | | * with the specified name or OID. The list returned may include |
| | | * multiple elements if the same attribute exists in the entry |
| | | * multiple times with different sets of options. |
| | | * multiple times with different sets of options. It may also |
| | | * include any subordinate attributes of the attribute being |
| | | * retrieved. |
| | | * <BR><BR> |
| | | * Note that this method should only be used in cases in which the |
| | | * Directory Server schema has no reference of an attribute type |
| | |
| | | { |
| | | if (attr.hasNameOrOID(lowerName)) |
| | | { |
| | | return getAttribute(attr); |
| | | return getAttribute(attr, true); |
| | | } |
| | | } |
| | | |
| | |
| | | { |
| | | if (attr.hasNameOrOID(lowerName)) |
| | | { |
| | | return getAttribute(attr); |
| | | return getAttribute(attr, true); |
| | | } |
| | | } |
| | | |
| | |
| | | return null; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the requested attribute element(s) for the specified |
| | | * attribute type. The list returned may include multiple elements |
| | | * if the same attribute exists in the entry multiple times with |
| | | * different sets of options. |
| | | * different sets of options. It may also include any subordinate |
| | | * attributes of the attribute being retrieved. |
| | | * |
| | | * @param attributeType The attribute type to retrieve. |
| | | * @param options The set of attribute options to include in |
| | | * matching elements. |
| | | * @param attributeType The attribute type to retrieve. |
| | | * @param options The set of attribute options to |
| | | * include in matching elements. |
| | | * |
| | | * @return The requested attribute element(s) for the specified |
| | | * attribute type, or <CODE>null</CODE> if the specified |
| | |
| | | public List<Attribute> getAttribute(AttributeType attributeType, |
| | | Set<String> options) |
| | | { |
| | | return getAttribute(attributeType, true, options); |
| | | } |
| | | |
| | | /** |
| | | * Retrieves the requested attribute element(s) for the specified |
| | | * attribute type. The list returned may include multiple elements |
| | | * if the same attribute exists in the entry multiple times with |
| | | * different sets of options. |
| | | * |
| | | * @param attributeType The attribute type to retrieve. |
| | | * @param includeSubordinates Whether to include any subordinate |
| | | * attributes of the attribute type |
| | | * being retrieved. |
| | | * @param options The set of attribute options to |
| | | * include in matching elements. |
| | | * |
| | | * @return The requested attribute element(s) for the specified |
| | | * attribute type, or <CODE>null</CODE> if the specified |
| | | * attribute type is not present in this entry with the |
| | | * provided set of options. |
| | | */ |
| | | public List<Attribute> getAttribute(AttributeType attributeType, |
| | | boolean includeSubordinates, |
| | | Set<String> options) |
| | | { |
| | | List<Attribute> attributes = new LinkedList<Attribute>(); |
| | | if (attributeType.mayHaveSubordinateTypes()) |
| | | if (includeSubordinates && |
| | | attributeType.mayHaveSubordinateTypes()) |
| | | { |
| | | List<Attribute> attrs = userAttributes.get(attributeType); |
| | | if (attrs != null) |
| | |
| | | public final <T> T getAttributeValue(AttributeType attributeType, |
| | | AttributeValueDecoder<T> decoder) throws DirectoryException |
| | | { |
| | | List<Attribute> attributes = getAttribute(attributeType); |
| | | List<Attribute> attributes = getAttribute(attributeType, true); |
| | | AttributeValueIterable values = |
| | | new AttributeValueIterable(attributes); |
| | | Iterator<AttributeValue> iterator = values.iterator(); |
| | |
| | | Collection<T> collection) |
| | | throws DirectoryException |
| | | { |
| | | List<Attribute> attributes = getAttribute(attributeType); |
| | | List<Attribute> attributes = getAttribute(attributeType, true); |
| | | AttributeValueIterable values = |
| | | new AttributeValueIterable(attributes); |
| | | |
| | |
| | | attachment = null; |
| | | |
| | | List<Attribute> attrList = |
| | | getAttribute(attribute.getAttributeType()); |
| | | getAttribute(attribute.getAttributeType(), false); |
| | | if (attrList == null) |
| | | { |
| | | // There are no instances of the specified attribute in this |
| | |
| | | |
| | | /** |
| | | * Removes all instances of the specified attribute type from this |
| | | * entry. If the provided attribute type is the objectclass type, |
| | | * then all objectclass values will be removed (but must be replaced |
| | | * for the entry to be valid). If the specified attribute type is |
| | | * not present in this entry, then this method will have no effect. |
| | | * entry, including any instances with options. If the provided |
| | | * attribute type is the objectclass type, then all objectclass |
| | | * values will be removed (but must be replaced for the entry to |
| | | * be valid). If the specified attribute type is not present in |
| | | * this entry, then this method will have no effect. |
| | | * |
| | | * @param attributeType The attribute type for the attribute to |
| | | * remove from this entry. |
| | |
| | | |
| | | /** |
| | | * Removes the attribute with the provided type and set of options |
| | | * from this entry. If the provided set of options is |
| | | * <CODE>null</CODE> or empty, then all occurrences of the specified |
| | | * attribute will be removed. Otherwise, only the instance with the |
| | | * exact set of options provided will be removed. This has no |
| | | * effect if the specified attribute is not present in this entry |
| | | * with the given set of options. |
| | | * from this entry. Only the instance with the exact set of |
| | | * options provided will be removed. This has no effect if the |
| | | * specified attribute is not present in this entry with the given |
| | | * set of options. |
| | | * |
| | | * @param attributeType The attribute type for the attribute to |
| | | * remove from this entry. |
| | |
| | | { |
| | | attachment = null; |
| | | |
| | | if ((options == null) || options.isEmpty()) |
| | | List<Attribute> attrList = userAttributes.get(attributeType); |
| | | if (attrList == null) |
| | | { |
| | | return removeAttribute(attributeType); |
| | | } |
| | | else |
| | | { |
| | | List<Attribute> attrList = userAttributes.get(attributeType); |
| | | attrList = operationalAttributes.get(attributeType); |
| | | if (attrList == null) |
| | | { |
| | | attrList = operationalAttributes.get(attributeType); |
| | | if (attrList == null) |
| | | { |
| | | return false; |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | boolean removed = false; |
| | | |
| | | Iterator<Attribute> iterator = attrList.iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | Attribute a = iterator.next(); |
| | | if (a.optionsEqual(options)) |
| | | { |
| | | iterator.remove(); |
| | | removed = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (attrList.isEmpty()) |
| | | { |
| | | userAttributes.remove(attributeType); |
| | | operationalAttributes.remove(attributeType); |
| | | } |
| | | |
| | | return removed; |
| | | } |
| | | |
| | | boolean removed = false; |
| | | |
| | | Iterator<Attribute> iterator = attrList.iterator(); |
| | | while (iterator.hasNext()) |
| | | { |
| | | Attribute a = iterator.next(); |
| | | if (a.optionsEqual(options)) |
| | | { |
| | | iterator.remove(); |
| | | removed = true; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (attrList.isEmpty()) |
| | | { |
| | | userAttributes.remove(attributeType); |
| | | operationalAttributes.remove(attributeType); |
| | | } |
| | | |
| | | return removed; |
| | | } |
| | | |
| | | |
| | |
| | | } |
| | | |
| | | List<Attribute> attrList = |
| | | getAttribute(attribute.getAttributeType()); |
| | | getAttribute(attribute.getAttributeType(), false); |
| | | if (attrList == null) |
| | | { |
| | | return false; |
| | |
| | | LinkedHashSet<AttributeValue> valueSet = attribute.getValues(); |
| | | if ((valueSet == null) || valueSet.isEmpty()) |
| | | { |
| | | return removeAttribute(attribute.getAttributeType()); |
| | | return removeAttribute(attribute.getAttributeType(), null); |
| | | } |
| | | |
| | | List<Attribute> attrList = |
| | | getAttribute(attribute.getAttributeType()); |
| | | getAttribute(attribute.getAttributeType(), false); |
| | | if (attrList == null) |
| | | { |
| | | return false; |
| | |
| | | |
| | | if (existingValueSet.isEmpty()) |
| | | { |
| | | return removeAttribute(attribute.getAttributeType()); |
| | | return removeAttribute(attribute.getAttributeType(), |
| | | null); |
| | | } |
| | | |
| | | return true; |
| | |
| | | public boolean hasValue(AttributeType attributeType, |
| | | Set<String> options, AttributeValue value) |
| | | { |
| | | List<Attribute> attrList = getAttribute(attributeType); |
| | | List<Attribute> attrList = getAttribute(attributeType, true); |
| | | if ((attrList == null) || attrList.isEmpty()) |
| | | { |
| | | return false; |
| | |
| | | break; |
| | | |
| | | case INCREMENT: |
| | | List<Attribute> attrList = getAttribute(t); |
| | | List<Attribute> attrList = getAttribute(t, false); |
| | | if ((attrList == null) || attrList.isEmpty()) |
| | | { |
| | | Message message = |
| | |
| | | objectClass: ds-cfg-branch |
| | | cn: Index |
| | | |
| | | dn: ds-cfg-attribute=name,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | | objectClass: ds-cfg-local-db-index |
| | | ds-cfg-attribute: name |
| | | ds-cfg-index-type: presence |
| | | ds-cfg-index-type: equality |
| | | ds-cfg-index-type: substring |
| | | ds-cfg-index-type: ordering |
| | | ds-cfg-index-type: approximate |
| | | ds-cfg-index-entry-limit: 4000 |
| | | |
| | | dn: ds-cfg-attribute=cn,cn=Index,ds-cfg-backend-id=indexRoot,cn=Backends,cn=config |
| | | changetype: add |
| | | objectClass: top |
| | |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "givenName: Aaren", |
| | | "givenName;lang-fr: test2", |
| | | "givenName;lang-cn: test2", |
| | | "givenName;lang-es: test3", |
| | | "sn: Atp", |
| | | "cn: Aaren Atp", |
| | | "initials: APA", |
| | |
| | | Entry newEntry; |
| | | EntryID entryID; |
| | | AttributeType attribute; |
| | | AttributeIndex index; |
| | | AttributeIndex titleIndex; |
| | | AttributeIndex nameIndex; |
| | | HashSet<ASN1OctetString> addKeys; |
| | | DatabaseEntry key; |
| | | PresenceIndexer presenceIndexer; |
| | |
| | | ArrayList<Modification> modifications = new ArrayList<Modification>(); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("title", "debugger"))); |
| | | |
| | | attribute = DirectoryServer.getAttributeType("title"); |
| | | LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>(); |
| | | values.add(new AttributeValue(attribute, "debugger2")); |
| | | LinkedHashSet<String> options = new LinkedHashSet<String>(1); |
| | | options.add("lang-en"); |
| | | Attribute attr = new Attribute(attribute, "title", options, values); |
| | | modifications.add(new Modification(ModificationType.ADD, attr)); |
| | | |
| | | modifications.add(new Modification(ModificationType.DELETE, new |
| | | Attribute("cn", "Aaren Atp"))); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("cn", "Aaren Rigor"))); |
| | | modifications.add(new Modification(ModificationType.ADD, new |
| | | Attribute("cn", "Aarenister Rigor"))); |
| | | |
| | | attribute = DirectoryServer.getAttributeType("givenname"); |
| | | values = new LinkedHashSet<AttributeValue>(); |
| | | values.add(new AttributeValue(attribute, "test")); |
| | | options = new LinkedHashSet<String>(1); |
| | | options.add("lang-de"); |
| | | attr = new Attribute(attribute, "givenName", options, values); |
| | | modifications.add(new Modification(ModificationType.ADD, attr)); |
| | | |
| | | attribute = DirectoryServer.getAttributeType("givenname"); |
| | | values = new LinkedHashSet<AttributeValue>(); |
| | | values.add(new AttributeValue(attribute, "test2")); |
| | | options = new LinkedHashSet<String>(1); |
| | | options.add("lang-cn"); |
| | | attr = new Attribute(attribute, "givenName", options, values); |
| | | modifications.add(new Modification(ModificationType.DELETE, attr)); |
| | | |
| | | attribute = DirectoryServer.getAttributeType("givenname"); |
| | | values = new LinkedHashSet<AttributeValue>(); |
| | | values.add(new AttributeValue(attribute, "newtest3")); |
| | | options = new LinkedHashSet<String>(1); |
| | | options.add("lang-es"); |
| | | attr = new Attribute(attribute, "givenName", options, values); |
| | | modifications.add(new Modification(ModificationType.REPLACE, attr)); |
| | | |
| | | modifications.add(new Modification(ModificationType.REPLACE, new |
| | | Attribute("employeenumber", "222"))); |
| | | |
| | |
| | | assertNotNull(entryID); |
| | | |
| | | |
| | | attribute = newEntry.getAttribute("title").get(0).getAttributeType(); |
| | | index = ec.getAttributeIndex(attribute); |
| | | attribute = DirectoryServer.getAttributeType("title"); |
| | | titleIndex = ec.getAttributeIndex(attribute); |
| | | attribute = DirectoryServer.getAttributeType("name"); |
| | | nameIndex = ec.getAttributeIndex(attribute); |
| | | |
| | | //This current entry in the DB shouldn't be in the presence index. |
| | | //This current entry in the DB shouldn't be in the presence titleIndex. |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData())); |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | assertEquals(titleIndex.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.FALSE); |
| | | |
| | | //This current entry should be in the presence nameIndex. |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | addKeys.add(new ASN1OctetString(AttributeIndex.presenceKey.getData())); |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(nameIndex.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | |
| | | ArrayList<Control> noControls = new ArrayList<Control>(0); |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | |
| | | entry.getAttribute("cn").get(0).getAttributeType(), |
| | | "Aaren Atp"))); |
| | | |
| | | options = new LinkedHashSet<String>(); |
| | | options.add("lang-de"); |
| | | assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("givenname", options).get(0).getAttributeType(), |
| | | "test"))); |
| | | options = new LinkedHashSet<String>(); |
| | | options.add("lang-cn"); |
| | | assertNull |
| | | (entry.getAttribute("givenname", options)); |
| | | options = new LinkedHashSet<String>(); |
| | | options.add("lang-es"); |
| | | assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("givenname", options).get(0).getAttributeType(), |
| | | "newtest3"))); |
| | | options = new LinkedHashSet<String>(); |
| | | options.add("lang-fr"); |
| | | assertTrue(entry.getAttribute("givenname", options).get(0).getValues().contains( |
| | | new AttributeValue( |
| | | entry.getAttribute("givenname", options).get(0).getAttributeType(), |
| | | "test2"))); |
| | | |
| | | assertTrue(entry.getAttribute("employeenumber").contains(new |
| | | Attribute("employeenumber", "222"))); |
| | | assertFalse(entry.getAttribute("employeenumber").contains(new |
| | | Attribute("employeenumber", "1"))); |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | presenceIndexer = new PresenceIndexer(index.getAttributeType()); |
| | | presenceIndexer = new PresenceIndexer(titleIndex.getAttributeType()); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.presenceIndex.containsID(null, key, entryID), |
| | | assertEquals(titleIndex.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(index.getAttributeType()); |
| | | presenceIndexer = new PresenceIndexer(nameIndex.getAttributeType()); |
| | | presenceIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | assertEquals(nameIndex.presenceIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | orderingIndexer = new OrderingIndexer(titleIndex.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.orderingIndex.containsID(null, key, entryID), |
| | | assertEquals(titleIndex.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(index.getAttributeType()); |
| | | orderingIndexer = new OrderingIndexer(nameIndex.getAttributeType()); |
| | | orderingIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | assertEquals(nameIndex.orderingIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | equalityIndexer = new EqualityIndexer(titleIndex.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.equalityIndex.containsID(null, key, entryID), |
| | | assertEquals(titleIndex.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(index.getAttributeType(), |
| | | index.getConfiguration().getSubstringLength()); |
| | | equalityIndexer = new EqualityIndexer(nameIndex.getAttributeType()); |
| | | equalityIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | assertEquals(nameIndex.equalityIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(titleIndex.getAttributeType(), |
| | | titleIndex.getConfiguration().getSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | } |
| | | assertEquals(index.substringIndex.containsID(null, key, entryID), |
| | | assertEquals(titleIndex.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | |
| | | addKeys = new HashSet<ASN1OctetString>(); |
| | | substringIndexer = new SubstringIndexer(nameIndex.getAttributeType(), |
| | | nameIndex.getConfiguration().getSubstringLength()); |
| | | substringIndexer.indexEntry(null, entry, addKeys); |
| | | |
| | | key = new DatabaseEntry(); |
| | | for (ASN1OctetString keyBytes : addKeys) { |
| | | key.setData(keyBytes.value()); |
| | | assertEquals(nameIndex.substringIndex.containsID(null, key, entryID), |
| | | ConditionResult.TRUE); |
| | | } |
| | | } |
| | | finally |
| | | { |
| | |
| | | invalidReason = new MessageBuilder(); |
| | | failOnlyForStrictEvaluation(e); |
| | | } |
| | | |
| | | /** |
| | | * Tests schema checking for an entry that includes an attribute not |
| | | * defined in any objectClasses but the subtypes of the attribute are. |
| | | * |
| | | * @throws Exception If an unexpected problem occurs. |
| | | */ |
| | | @Test() |
| | | public void testInvalidSuperiorAttribute() |
| | | throws Exception |
| | | { |
| | | // The LDIF reader won't let us do this directly, so we have to hack around |
| | | // it. |
| | | Entry e = TestCaseUtils.makeEntry( |
| | | "dn: uid=test.user,o=test", |
| | | "objectClass: top", |
| | | "objectClass: person", |
| | | "objectClass: organizationalPerson", |
| | | "objectClass: inetOrgPerson", |
| | | "objectClass: account", |
| | | "uid: test.user", |
| | | "givenName: Test", |
| | | "sn: User", |
| | | "cn: Test User"); |
| | | |
| | | e.addAttribute(new Attribute("name", "foo"), |
| | | new LinkedList<AttributeValue>()); |
| | | |
| | | assertFalse(e.conformsToSchema(null, false, true, true, new MessageBuilder())); |
| | | } |
| | | } |
| | | |
| | |
| | | assertTrue(e.hasAttribute(ocType)); |
| | | assertTrue(e.hasAttribute(cnType)); |
| | | assertTrue(e.hasAttribute(nameType)); |
| | | assertFalse(e.hasAttribute(nameType, false)); |
| | | assertFalse(e.hasAttribute(uidType)); |
| | | assertTrue(e.hasAttribute(mnType)); |
| | | |
| | |
| | | assertTrue(e.hasAttribute(ocType, options)); |
| | | assertTrue(e.hasAttribute(cnType, options)); |
| | | assertTrue(e.hasAttribute(nameType, options)); |
| | | assertFalse(e.hasAttribute(nameType, false, options)); |
| | | assertFalse(e.hasAttribute(uidType, options)); |
| | | assertTrue(e.hasAttribute(mnType, options)); |
| | | |
| | |
| | | assertTrue(e.hasAttribute(ocType, options)); |
| | | assertTrue(e.hasAttribute(cnType, options)); |
| | | assertTrue(e.hasAttribute(nameType, options)); |
| | | assertFalse(e.hasAttribute(nameType, false, options)); |
| | | assertFalse(e.hasAttribute(uidType, options)); |
| | | assertTrue(e.hasAttribute(mnType, options)); |
| | | |
| | |
| | | assertFalse(e.hasAttribute(ocType, options)); |
| | | assertTrue(e.hasAttribute(cnType, options)); |
| | | assertTrue(e.hasAttribute(nameType, options)); |
| | | assertFalse(e.hasAttribute(nameType, false, options)); |
| | | assertFalse(e.hasAttribute(uidType, options)); |
| | | assertFalse(e.hasAttribute(mnType, options)); |
| | | |
| | |
| | | assertFalse(e.hasAttribute(ocType, options)); |
| | | assertFalse(e.hasAttribute(cnType, options)); |
| | | assertFalse(e.hasAttribute(nameType, options)); |
| | | assertFalse(e.hasAttribute(nameType, false, options)); |
| | | assertFalse(e.hasAttribute(uidType, options)); |
| | | assertFalse(e.hasAttribute(mnType, options)); |
| | | |
| | |
| | | assertFalse(e.hasAttribute(ocType, options)); |
| | | assertFalse(e.hasAttribute(cnType, options)); |
| | | assertFalse(e.hasAttribute(nameType, options)); |
| | | assertFalse(e.hasAttribute(nameType, false, options)); |
| | | assertFalse(e.hasAttribute(uidType, options)); |
| | | assertFalse(e.hasAttribute(mnType, options)); |
| | | } |
| | |
| | | assertNotNull(attrs); |
| | | assertEquals(attrs.size(), 6); |
| | | |
| | | attrs = e.getAttribute(nameType, false); |
| | | assertNull(attrs); |
| | | |
| | | attrs = e.getAttribute(uidType); |
| | | assertNull(attrs); |
| | | |
| | |
| | | assertNotNull(attrs); |
| | | assertEquals(attrs.size(), 6); |
| | | |
| | | attrs = e.getAttribute(nameType, false, options); |
| | | assertNull(attrs); |
| | | |
| | | attrs = e.getAttribute(uidType, options); |
| | | assertNull(attrs); |
| | | |
| | |
| | | assertNotNull(attrs); |
| | | assertEquals(attrs.size(), 6); |
| | | |
| | | attrs = e.getAttribute(nameType, false, options); |
| | | assertNull(attrs); |
| | | |
| | | attrs = e.getAttribute(uidType, options); |
| | | assertNull(attrs); |
| | | |
| | |
| | | assertNotNull(attrs); |
| | | assertEquals(attrs.size(), 3); |
| | | |
| | | attrs = e.getAttribute(nameType, false, options); |
| | | assertNull(attrs); |
| | | |
| | | attrs = e.getAttribute(uidType, options); |
| | | assertNull(attrs); |
| | | |