opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/EntryHistorical.java
@@ -61,6 +61,8 @@ */ public class EntryHistorical { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** Name of the attribute used to store historical information. */ public static final String HISTORICAL_ATTRIBUTE_NAME = "ds-sync-hist"; /** @@ -72,8 +74,6 @@ /** Name of the entryuuid attribute. */ public static final String ENTRYUUID_ATTRIBUTE_NAME = "entryuuid"; private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** * The delay to purge the historical information. * <p> @@ -303,7 +303,7 @@ // Read from this entryHistorical, // Create one empty if none was existing in this entryHistorical. AttributeDescription attrDesc = new AttributeDescription(modAttr); AttributeDescription attrDesc = AttributeDescription.create(modAttr); AttrHistorical attrHist = attributesHistorical.get(attrDesc); if (attrHist == null) { @@ -350,8 +350,7 @@ for (Map.Entry<AttributeDescription, AttrHistorical> mapEntry : attributesHistorical.entrySet()) { AttributeDescription attrDesc = mapEntry.getKey(); AttributeType type = attrDesc.attributeType; String optionsString = attrDesc.toOptionsString(); String options = attrDesc.toString(); AttrHistorical attrHist = mapEntry.getValue(); CSN deleteTime = attrHist.getDeleteTime(); @@ -370,7 +369,7 @@ // this hist must be purged now, so skip its encoding continue; } String strValue = encode(DEL, type, optionsString, attrValHist.getValueDeleteTime(), value); String strValue = encode(DEL, options, attrValHist.getValueDeleteTime(), value); builder.add(strValue); } else if (attrValHist.getValueUpdateTime() != null) @@ -387,18 +386,18 @@ // unit tests do not like changing it if (attrDel && updateTime == deleteTime && value != null) { strValue = encode(REPL, type, optionsString, updateTime, value); strValue = encode(REPL, options, updateTime, value); attrDel = false; } else if (value != null) { strValue = encode(ADD, type, optionsString, updateTime, value); strValue = encode(ADD, options, updateTime, value); } else { // "add" without any value is suspicious. Tests never go there. // Is this used to encode "add" with an empty string? strValue = encode(ADD, type, optionsString, updateTime); strValue = encode(ADD, options, updateTime); } builder.add(strValue); @@ -412,8 +411,7 @@ // this hist must be purged now, so skip its encoding continue; } String strValue = encode(ATTRDEL, type, optionsString, deleteTime); builder.add(strValue); builder.add(encode(ATTRDEL, options, deleteTime)); } } @@ -445,16 +443,14 @@ return needsPurge; } private String encode(HistAttrModificationKey modKey, AttributeType type, String optionsString, CSN changeTime) private String encode(HistAttrModificationKey modKey, String options, CSN changeTime) { return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime + ":" + modKey; return options + ":" + changeTime + ":" + modKey; } private String encode(HistAttrModificationKey modKey, AttributeType type, String optionsString, CSN changeTime, ByteString value) private String encode(HistAttrModificationKey modKey, String options, CSN changeTime, ByteString value) { return type.getNormalizedPrimaryName() + optionsString + ":" + changeTime + ":" + modKey + ":" + value; return options + ":" + changeTime + ":" + modKey + ":" + value; } /** @@ -580,7 +576,7 @@ AttrHistorical attrInfo = newHistorical.attributesHistorical.get(attrDesc); if (attrInfo == null) { attrInfo = AttrHistorical.createAttributeHistorical(attrDesc.attributeType); attrInfo = AttrHistorical.createAttributeHistorical(attrDesc.getAttributeType()); newHistorical.attributesHistorical.put(attrDesc, attrInfo); } attrInfo.assign(histVal.getHistKey(), histVal.getAttributeValue(), csn); opendj-server-legacy/src/main/java/org/opends/server/replication/plugin/HistoricalAttributeValue.java
@@ -135,7 +135,7 @@ isModDN = true; } } this.attrDesc = attrType != null ? new AttributeDescription(attrType, options) : null; this.attrDesc = attrType != null ? AttributeDescription.create(attrType, options) : null; csn = new CSN(token[1]); histKey = HistAttrModificationKey.decodeKey(token[2]); @@ -159,6 +159,16 @@ } } private AttributeType getAttributeType() { return attrDesc != null ? attrDesc.getAttributeType() : null; } private Set<String> getOptions() { return attrDesc != null ? attrDesc.getOptions() : Collections.<String> emptySet(); } /** * Get the String form of the attribute type. * @@ -216,8 +226,8 @@ */ public Modification generateMod() { AttributeBuilder builder = new AttributeBuilder(attrDesc.attributeType, attrString); builder.setOptions(attrDesc.options); AttributeBuilder builder = new AttributeBuilder(getAttributeType(), attrString); builder.setOptions(getOptions()); if (histKey != ATTRDEL) { @@ -247,7 +257,7 @@ */ public boolean isADDOperation() { return attrDesc.attributeType == null && !isModDN; return getAttributeType() == null && !isModDN; } /** @@ -258,7 +268,7 @@ */ public boolean isMODDNOperation() { return attrDesc.attributeType == null && isModDN; return getAttributeType() == null && isModDN; } @Override @@ -266,7 +276,7 @@ { final StringBuilder sb = new StringBuilder(); sb.append(attrString); for (String option : attrDesc.options) for (String option : getOptions()) { sb.append(";").append(option); } opendj-server-legacy/src/main/java/org/opends/server/types/AbstractAttribute.java
@@ -33,8 +33,6 @@ import org.forgerock.opendj.ldap.DecodeException; import org.forgerock.opendj.ldap.schema.MatchingRule; import static org.opends.server.util.StaticUtils.*; /** An abstract base class for implementing new types of {@link Attribute}. */ @org.opends.server.types.PublicAPI( stability = org.opends.server.types.StabilityLevel.UNCOMMITTED, @@ -146,27 +144,16 @@ /** * {@inheritDoc} * <p> * This implementation returns <code>true</code> if the provided * collection of options is <code>null</code> or empty. If the * This implementation returns {@code true} if the provided * collection of options is {@code null} or empty. If the * collection is non-empty and this attribute does not have any * options then it returns <code>false</code>. Otherwise, each * option in the provided collection is checked using * {@link #hasOption(String)} and <code>true</code> is * options then it returns {@code false}. Otherwise, {@code true} is * returned if all the provided options are present. */ @Override public boolean hasAllOptions(Collection<String> options) { if (options == null || options.isEmpty()) { return true; } if (hasOptions()) { return hasAllOptions0(options); } return false; return AttributeDescription.containsAllOptions(getOptions(), options); } @Override @@ -194,24 +181,12 @@ * This implementation calls {@link #getOptions()} to * retrieve this attribute's set of options and then compares them * one at a time against the provided option. All comparisons are * case insensitive (this is why we iterate through the set of * options, rather than doing a simpler set membership test). * case insensitive. */ @Override public boolean hasOption(String option) { String noption = toLowerCase(option); // Cannot use Set.contains() because the options are not normalized. for (String o : getOptions()) { String no = toLowerCase(o); if (no.equals(noption)) { return true; } } return false; return AttributeDescription.containsOption(getOptions(), option); } /** @@ -259,29 +234,12 @@ @Override public boolean optionsEqual(Set<String> options) { if (options == null) if (options != null) { return !hasOptions(); return getOptions().size() == options.size() && hasAllOptions(options); } if (getOptions().size() == options.size()) { return hasAllOptions0(options); } return false; } /** Cannot use Set.containsAll() because the set of options are not normalized. */ private boolean hasAllOptions0(Collection<String> options) { for (String option : options) { if (!hasOption(option)) { return false; } } return true; return !hasOptions(); } @Override opendj-server-legacy/src/main/java/org/opends/server/types/Attribute.java
@@ -61,10 +61,10 @@ * * @param assertionValue * The assertion value for which to make the determination. * @return <CODE>UNDEFINED</CODE> if this attribute does not have * an approximate matching rule, <CODE>TRUE</CODE> if at * @return {@link ConditionResult#UNDEFINED} if this attribute does not have * an approximate matching rule, {@link ConditionResult#TRUE} if at * least one value is approximately equal to the provided * value, or <CODE>false</CODE> otherwise. * value, or {@link ConditionResult#FALSE} otherwise. */ ConditionResult approximatelyEqualTo(ByteString assertionValue); @@ -73,8 +73,8 @@ * * @param value * The value for which to make the determination. * @return <CODE>true</CODE> if this attribute has the specified * value, or <CODE>false</CODE> if not. * @return {@code true} if this attribute has the specified * value, or {@code false} if not. */ boolean contains(ByteString value); @@ -84,8 +84,8 @@ * * @param values * The set of values for which to make the determination. * @return <CODE>true</CODE> if this attribute contains all the * values in the provided collection, or <CODE>false</CODE> * @return {@code true} if this attribute contains all the * values in the provided collection, or {@code false} * if it does not contain at least one of them. */ boolean containsAll(Collection<ByteString> values); @@ -95,8 +95,8 @@ * * @param assertionValue * The assertion value for which to make the determination. * @return <CODE>true</CODE> if this attribute matches the specified assertion * value, or <CODE>false</CODE> if not. * @return {@code true} if this attribute matches the specified assertion * value, or {@code false} if not. */ ConditionResult matchesEqualityAssertion(ByteString assertionValue); @@ -107,9 +107,9 @@ * * @param o * The object for which to make the determination. * @return <CODE>true</CODE> if the provided object is an * @return {@code true} if the provided object is an * attribute that is equal to this attribute, or * <CODE>false</CODE> if not. * {@code false} if not. */ @Override boolean equals(Object o); @@ -152,30 +152,26 @@ * * @param assertionValue * The assertion value for which to make the determination. * @return <CODE>UNDEFINED</CODE> if this attribute does not have * an ordering matching rule, <CODE>TRUE</CODE> if at * @return {@link ConditionResult#UNDEFINED} if this attribute does not have * an ordering matching rule, {@link ConditionResult#TRUE} if at * least one value is greater than or equal to the provided * assertion value, or <CODE>false</CODE> otherwise. * assertion value, or {@link ConditionResult#FALSE} otherwise. */ ConditionResult greaterThanOrEqualTo(ByteString assertionValue); /** * Indicates whether this attribute has all of the options in the * provided collection. * Indicates whether this attribute has all of the options in the provided collection. * * @param options * The collection of options for which to make the * determination (may be <code>null</code>). * @return <CODE>true</CODE> if this attribute has all of the * specified options, or <CODE>false</CODE> if it does not * have at least one of them. * The collection of options for which to make the determination (may be {@code null}). * @return {@code true} if this attribute has all of the specified options, * or {@code false} if it does not have at least one of them. */ boolean hasAllOptions(Collection<String> options); /** * Retrieves the hash code for this attribute. It will be calculated * as the sum of the hash code for the attribute type and all * values. * as the sum of the hash code for the attribute type and all values. * * @return The hash code for this attribute. */ @@ -187,24 +183,24 @@ * * @param option * The option for which to make the determination. * @return <CODE>true</CODE> if this attribute has the specified * option, or <CODE>false</CODE> if not. * @return {@code true} if this attribute has the specified option, * or {@code false} if not. */ boolean hasOption(String option); /** * Indicates whether this attribute has any options at all. * * @return <CODE>true</CODE> if this attribute has at least one * option, or <CODE>false</CODE> if not. * @return {@code true} if this attribute has at least one * option, or {@code false} if not. */ boolean hasOptions(); /** * Returns <code>true</code> if this attribute contains no * Returns {@code true} if this attribute contains no * attribute values. * * @return <CODE>true</CODE> if this attribute contains no * @return {@code true} if this attribute contains no * attribute values. */ boolean isEmpty(); @@ -229,7 +225,7 @@ * Returns an iterator over the attribute values in this attribute. * The attribute values are returned in the order in which they were * added this attribute. The returned iterator does not support * attribute value removals via its <code>remove</code> method. * attribute value removals via {@link Iterator#remove()}. * * @return An iterator over the attribute values in this attribute. */ @@ -242,10 +238,10 @@ * * @param assertionValue * The assertion value for which to make the determination. * @return <CODE>UNDEFINED</CODE> if this attribute does not have * an ordering matching rule, <CODE>TRUE</CODE> if at * @return {@link ConditionResult#UNDEFINED} if this attribute does not have * an ordering matching rule, {@link ConditionResult#TRUE} if at * least one value is less than or equal to the provided * assertion value, or <CODE>false</CODE> otherwise. * assertion value, or {@link ConditionResult#FALSE} otherwise. */ ConditionResult lessThanOrEqualTo(ByteString assertionValue); @@ -259,10 +255,10 @@ * The subAny components to use in the determination. * @param subFinal * The subFinal component to use in the determination. * @return <CODE>UNDEFINED</CODE> if this attribute does not have * a substring matching rule, <CODE>TRUE</CODE> if at * @return {@link ConditionResult#UNDEFINED} if this attribute does not have * a substring matching rule, {@link ConditionResult#TRUE} if at * least one value matches the provided substring, or * <CODE>FALSE</CODE> otherwise. * {@link ConditionResult#FALSE} otherwise. */ ConditionResult matchesSubstring(ByteString subInitial, List<ByteString> subAny, ByteString subFinal); @@ -273,8 +269,8 @@ * * @param options * The set of options for which to make the determination * (may be <code>null</code>). * @return <CODE>true</CODE> if this attribute has exactly the * (may be {@code null}). * @return {@code true} if this attribute has exactly the * specified set of options. */ boolean optionsEqual(Set<String> options); opendj-server-legacy/src/main/java/org/opends/server/types/AttributeDescription.java
@@ -24,37 +24,251 @@ */ package org.opends.server.types; import static org.opends.server.util.StaticUtils.*; import java.util.Collection; import java.util.Iterator; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import org.forgerock.util.Reject; /** Temporary class until we move to {@link org.forgerock.opendj.ldap.AttributeDescription}. */ public final class AttributeDescription public final class AttributeDescription implements Comparable<AttributeDescription> { final AttributeType attributeType; final Set<String> options; private final AttributeType attributeType; private final Set<String> options; AttributeDescription(Attribute attr) private AttributeDescription(AttributeType attributeType, Set<String> options) { this(attr.getAttributeType(), attr.getOptions()); } AttributeDescription(AttributeType attributeType, Set<String> options) { Reject.ifNull(attributeType); Reject.ifNull(options); this.attributeType = attributeType; this.options = options; } String toOptionsString() /** * Creates an attribute description with the attribute type and options of the provided * {@link Attribute}. * * @param attr * The attribute. * @return The attribute description. * @throws NullPointerException * If {@code attributeType} or {@code options} was {@code null}. */ public static AttributeDescription create(Attribute attr) { if (options != null) return create(attr.getAttributeType(), attr.getOptions()); } /** * Creates an attribute description having the provided attribute type and options. * * @param attributeType * The attribute type. * @param options * The attribute options. * @return The attribute description. * @throws NullPointerException * If {@code attributeType} or {@code options} was {@code null}. */ public static AttributeDescription create(AttributeType attributeType, Set<String> options) { return new AttributeDescription(attributeType, options); } /** * Returns the attribute type associated with this attribute description. * * @return The attribute type associated with this attribute description. */ public AttributeType getAttributeType() { return attributeType; } /** * Returns the set of not normalized options contained in this attribute description. * * @return A set containing the not normalized options. */ public Set<String> getOptions() { return options; } /** * Indicates whether the provided set of not normalized options contains the provided option. * * @param options * The set of not normalized options where to do the search. * @param optionToFind * The option for which to make the determination. * @return {@code true} if the provided set of options has the provided option, or {@code false} * if not. */ public static boolean containsOption(Set<String> options, String optionToFind) { String normToFind = toLowerCase(optionToFind); // Cannot use Set.contains() because the options are not normalized. for (String o : options) { StringBuilder optionsBuilder = new StringBuilder(); for (String s : options) String norm = toLowerCase(o); if (norm.equals(normToFind)) { optionsBuilder.append(';').append(s); return true; } return optionsBuilder.toString(); } return ""; return false; } /** * Indicates whether the provided first set of not normalized options contains all values from the * second set of not normalized options. * * @param options1 * The first set of not normalized options where to do the search. * @param options2 * The second set of not normalized options that must all be found. * @return {@code true} if the first provided set of options has all the options from the second * provided set of options. */ public static boolean containsAllOptions(Collection<String> options1, Collection<String> options2) { if (options1 == options2) { return true; } else if (isEmpty(options2)) { return true; } else if (isEmpty(options1)) { return false; } // normalize all options before calling containsAll() Set<String> set1 = toLowercaseSet(options1); Set<String> set2 = toLowercaseSet(options2); return set1.size() >= set2.size() && set1.containsAll(set2); } /** * Indicates whether the provided first set of not normalized options equals the second set of not * normalized options. * * @param options1 * The first set of not normalized options. * @param options2 * The second set of not normalized options. * @return {@code true} if the first provided set of options equals the second provided set of * options. */ public static boolean optionsEqual(Set<String> options1, Set<String> options2) { if (options1 == options2) { return true; } else if (isEmpty(options2)) { return isEmpty(options1); } else if (isEmpty(options1)) { return false; } // normalize all options before calling containsAll() Set<String> set1 = toLowercaseSet(options1); Set<String> set2 = toLowercaseSet(options2); return set1.equals(set2); } private static boolean isEmpty(Collection<String> col) { return col == null || col.isEmpty(); } private static SortedSet<String> toLowercaseSet(Collection<String> strings) { final SortedSet<String> results = new TreeSet<>(); for (String s : strings) { results.add(toLowerCase(s)); } return results; } @Override public int compareTo(AttributeDescription other) { if (this == other) { return 0; } return compare(attributeType, options, other.attributeType, other.options); } /** * Compares the first attribute type and options to the second attribute type and options, as if * they were both instances of {@link AttributeDescription}. * <p> * The attribute types are compared first and then, if equal, the options are normalized, sorted, * and compared. * * @param attrType1 * The first attribute type to be compared. * @param options1 * The first options to be compared. * @param attrType2 * The second attribute type to be compared. * @param options2 * The second options to be compared. * @return A negative integer, zero, or a positive integer as this attribute description is less * than, equal to, or greater than the specified attribute description. * @throws NullPointerException * If {@code name} was {@code null}. * @see AttributeDescription#compareTo(AttributeDescription) */ public static int compare(AttributeType attrType1, Set<String> options1, AttributeType attrType2, Set<String> options2) { int cmp = attrType1.compareTo(attrType2); if (cmp != 0) { return cmp; } if (options1 == options2) { return 0; } return compare(toLowercaseSet(options1), toLowercaseSet(options2)); } private static int compare(SortedSet<String> options1, SortedSet<String> options2) { Iterator<String> it1 = options1.iterator(); Iterator<String> it2 = options2.iterator(); while (it1.hasNext() && it2.hasNext()) { int cmp = it1.next().compareTo(it2.next()); if (cmp != 0) { return cmp; } } if (it1.hasNext()) { return 1; } else if (it2.hasNext()) { return -1; } return 0; } @Override @@ -69,7 +283,7 @@ return false; } final AttributeDescription other = (AttributeDescription) obj; return attributeType.equals(other.attributeType) && options.equals(other.options); return attributeType.equals(other.attributeType) && optionsEqual(options, other.options); } @Override @@ -85,6 +299,13 @@ @Override public String toString() { return getClass().getSimpleName() + "(" + "attributeType=" + attributeType + ", options=" + options + ")"; final StringBuilder buffer = new StringBuilder(); buffer.append(attributeType.getNormalizedPrimaryName()); for (String option : options) { buffer.append(';'); buffer.append(option); } return buffer.toString(); } } } opendj-server-legacy/src/test/java/org/opends/server/types/AttributeBuilderTest.java
@@ -1396,7 +1396,7 @@ /** * Tests {@link Attribute#hasAllOptions(java.util.Collection)}. * Tests {@link Attribute#hasAllOptions(Collection)}. * * @param testCase * Test case index (useful for debugging). @@ -1416,20 +1416,21 @@ AttributeType type, String name, String[] options, String[] values) throws Exception { // Check hasAllOptions(). Assert.assertTrue(a.hasAllOptions(null)); Assert.assertTrue(a.hasAllOptions(Collections.<String> emptySet())); Assert.assertTrue(a.hasAllOptions(Arrays.asList(options))); if (options.length > 1) { Assert.assertTrue(a.hasAllOptions(Arrays.asList(options).subList(1, options.length))); Assert.assertTrue(a.hasAllOptions(Arrays.asList(options).subList(1, options.length))); } List<String> tmp = newArrayList(options); tmp.add("xxxx"); Assert.assertFalse(a.hasAllOptions(tmp)); Assert.assertFalse(a.hasAllOptions(newHashSet("xxxx"))); tmp.clear(); for (String option : options) {