From 4cb8262e95fde77e6a0d2c84f1aa118e3b1ee850 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Thu, 06 Nov 2014 14:50:48 +0000
Subject: [PATCH] OPENDJ-1591 CR-5092 Switch server to SDK matching rules
---
opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java | 1765 ----------------------------------------------------------
1 files changed, 23 insertions(+), 1,742 deletions(-)
diff --git a/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java b/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
index e4382c7..2cdb892 100644
--- a/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
+++ b/opendj3-server-dev/src/server/org/opends/server/schema/CollationMatchingRuleFactory.java
@@ -26,55 +26,34 @@
*/
package org.opends.server.schema;
-import java.nio.CharBuffer;
-import java.text.CollationKey;
-import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.config.server.ConfigException;
-import org.forgerock.opendj.ldap.ByteSequence;
-import org.forgerock.opendj.ldap.ByteString;
-import org.forgerock.opendj.ldap.ByteStringBuilder;
-import org.forgerock.opendj.ldap.ConditionResult;
-import org.forgerock.opendj.ldap.DecodeException;
import org.forgerock.opendj.ldap.ResultCode;
+import org.forgerock.opendj.ldap.schema.CoreSchema;
+import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.forgerock.opendj.ldap.schema.Schema;
-import org.forgerock.opendj.ldap.spi.IndexQueryFactory;
-import org.forgerock.opendj.ldap.spi.IndexingOptions;
import org.opends.server.admin.server.ConfigurationChangeListener;
-import org.opends.server.admin.std.meta.CollationMatchingRuleCfgDefn.MatchingRuleType;
import org.opends.server.admin.std.server.CollationMatchingRuleCfg;
-import org.opends.server.api.AbstractMatchingRule;
-import org.opends.server.api.ExtensibleIndexer;
-import org.opends.server.api.ExtensibleMatchingRule;
-import org.opends.server.api.MatchingRule;
import org.opends.server.api.MatchingRuleFactory;
-import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
-import org.opends.server.util.StaticUtils;
import static org.opends.messages.ConfigMessages.*;
-import static org.opends.messages.CoreMessages.*;
import static org.opends.messages.SchemaMessages.*;
-import static org.opends.server.schema.SchemaConstants.*;
-import static org.opends.server.util.ServerConstants.*;
/**
* This class is a factory class for Collation matching rules. It
@@ -87,25 +66,6 @@
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
-
- // Whether equality matching rules are enabled.
- private boolean equalityMatchingRuleType;
-
- // Whether less-than matching rules are enabled.
- private boolean lessThanMatchingRuleType;
-
- // Whether less-than-equal-to matching rules are enabled.
- private boolean lessThanEqualToMatchingRuleType;
-
- // Whether less-than-equal-to matching rules are enabled.
- private boolean greaterThanMatchingRuleType;
-
- // Whether greater-than matching rules are enabled.
- private boolean greaterThanEqualToMatchingRuleType;
-
- // Whether greater-than-equal-to matching rules are enabled.
- private boolean substringMatchingRuleType;
-
// Stores the list of available locales on this JVM.
private static final Set<Locale> supportedLocales = new HashSet<Locale>(
Arrays.asList(Locale.getAvailableLocales()));
@@ -118,7 +78,6 @@
new HashMap<String, MatchingRule>();
-
/**
* Creates a new instance of CollationMatchingRuleFactory.
*/
@@ -127,19 +86,15 @@
super();
}
-
-
/**
* {@inheritDoc}
*/
@Override
- public final Collection<MatchingRule> getMatchingRules()
+ public final Collection<org.forgerock.opendj.ldap.schema.MatchingRule> getMatchingRules()
{
return Collections.unmodifiableCollection(matchingRules.values());
}
-
-
/**
* Adds a new mapping of OID and MatchingRule.
*
@@ -153,22 +108,6 @@
matchingRules.put(oid, matchingRule);
}
-
-
- /**
- * Returns the Matching rule for the specified OID.
- *
- * @param oid
- * OID of the matching rule to be searched.
- * @return MatchingRule corresponding to an OID.
- */
- private MatchingRule getMatchingRule(String oid)
- {
- return matchingRules.get(oid);
- }
-
-
-
/**
* Clears the Map containing matching Rules.
*/
@@ -177,63 +116,6 @@
matchingRules.clear();
}
-
-
- /**
- * Reads the configuration and initializes matching rule types.
- *
- * @param ruleTypes
- * The Set containing allowed matching rule types.
- */
- private void initializeMatchingRuleTypes(SortedSet<MatchingRuleType> ruleTypes)
- {
- for (MatchingRuleType type : ruleTypes)
- {
- switch (type)
- {
- case EQUALITY:
- equalityMatchingRuleType = true;
- break;
- case LESS_THAN:
- lessThanMatchingRuleType = true;
- break;
- case LESS_THAN_OR_EQUAL_TO:
- lessThanEqualToMatchingRuleType = true;
- break;
- case GREATER_THAN:
- greaterThanMatchingRuleType = true;
- break;
- case GREATER_THAN_OR_EQUAL_TO:
- greaterThanEqualToMatchingRuleType = true;
- break;
- case SUBSTRING:
- substringMatchingRuleType = true;
- break;
- default:
- // No default values allowed.
- }
- }
- }
-
-
-
- /**
- * Creates a new Collator instance.
- *
- * @param locale
- * Locale for the collator
- * @return Returns a new Collator instance
- */
- private Collator createCollator(Locale locale)
- {
- Collator collator = Collator.getInstance(locale);
- collator.setStrength(Collator.PRIMARY);
- collator.setDecomposition(Collator.FULL_DECOMPOSITION);
- return collator;
- }
-
-
-
/**
* {@inheritDoc}
*/
@@ -241,7 +123,7 @@
public void initializeMatchingRule(CollationMatchingRuleCfg configuration)
throws ConfigException, InitializationException
{
- initializeMatchingRuleTypes(configuration.getMatchingRuleType());
+ final Schema coreSchema = CoreSchema.getInstance();
for (String collation : configuration.getCollation())
{
CollationMapper mapper = new CollationMapper(collation);
@@ -257,12 +139,22 @@
Locale locale = getLocale(languageTag);
if (locale != null)
{
- createLessThanMatchingRule(mapper, locale);
- createLessThanOrEqualToMatchingRule(mapper, locale);
- createEqualityMatchingRule(mapper, locale);
- createGreaterThanOrEqualToMatchingRule(mapper, locale);
- createGreaterThanMatchingRule(mapper, locale);
- createSubstringMatchingRule(mapper, locale);
+ try
+ {
+ final int[] numericSuffixes = { 1, 2, 3, 4, 5, 6 };
+ for (int suffix : numericSuffixes)
+ {
+ final String oid = nOID + "." + suffix;
+ addMatchingRule(oid, coreSchema.getMatchingRule(oid));
+ }
+ // the default (equality) matching rule
+ addMatchingRule(nOID, coreSchema.getMatchingRule(nOID));
+ }
+ catch (Exception e)
+ {
+ logger.error(LocalizableMessage.raw("Error when adding a collation matching rule with oid %s, tag %s: %s",
+ nOID, languageTag, e.getMessage()));
+ }
}
else
{
@@ -327,19 +219,13 @@
// Clear the associated matching rules.
resetRules();
- initializeMatchingRuleTypes(configuration.getMatchingRuleType());
+ final Schema coreSchema = CoreSchema.getInstance();
for (String collation : configuration.getCollation())
{
// validation has already been performed in isConfigurationChangeAcceptable()
CollationMapper mapper = new CollationMapper(collation);
- String languageTag = mapper.getLanguageTag();
- Locale locale = getLocale(languageTag);
- createLessThanMatchingRule(mapper, locale);
- createLessThanOrEqualToMatchingRule(mapper, locale);
- createEqualityMatchingRule(mapper, locale);
- createGreaterThanOrEqualToMatchingRule(mapper, locale);
- createGreaterThanMatchingRule(mapper, locale);
- createSubstringMatchingRule(mapper, locale);
+ String nOID = mapper.getNumericOID();
+ addMatchingRule(nOID, coreSchema.getMatchingRule(nOID));
}
try
@@ -360,8 +246,6 @@
return new ConfigChangeResult(resultCode, adminActionRequired, messages);
}
-
-
/**
* {@inheritDoc}
*/
@@ -415,191 +299,6 @@
}
-
- private Collection<String> copyNames(MatchingRule matchingRule)
- {
- Collection<String> defaultNames = new HashSet<String>();
- if (matchingRule != null)
- {
- defaultNames.addAll(matchingRule.getNames());
- }
- return defaultNames;
- }
-
-
-
- /**
- * Creates Less-than Matching Rule.
- *
- * @param mapper
- * CollationMapper containing OID and the language Tag.
- * @param locale
- * Locale value
- */
- private void createLessThanMatchingRule(CollationMapper mapper, Locale locale)
- {
- if (!lessThanMatchingRuleType) return;
-
- String oid = mapper.getNumericOID() + ".1";
- String lTag = mapper.getLanguageTag();
-
- Collection<String> names = copyNames(getMatchingRule(oid));
- names.add(lTag + ".lt");
- names.add(lTag + ".1");
-
- MatchingRule matchingRule =
- new CollationLessThanMatchingRule(oid, names, locale);
- addMatchingRule(oid, matchingRule);
- }
-
-
-
- /**
- * Creates Less-Than-Equal-To Matching Rule.
- *
- * @param mapper
- * CollationMapper containing OID and the language Tag.
- * @param locale
- * Locale value
- */
- private void createLessThanOrEqualToMatchingRule(
- CollationMapper mapper, Locale locale)
- {
- if (!lessThanEqualToMatchingRuleType) return;
-
- String oid = mapper.getNumericOID() + ".2";
- String lTag = mapper.getLanguageTag();
-
- Collection<String> names = copyNames(getMatchingRule(oid));
- names.add(lTag + ".lte");
- names.add(lTag + ".2");
-
- MatchingRule matchingRule =
- new CollationLessThanOrEqualToMatchingRule(oid, names, locale);
- addMatchingRule(oid, matchingRule);
- }
-
-
-
- /**
- * Creates Equality Matching Rule.
- *
- * @param mapper
- * CollationMapper containing OID and the language Tag.
- * @param locale
- * Locale value
- */
- private void createEqualityMatchingRule(CollationMapper mapper, Locale locale)
- {
- if (!equalityMatchingRuleType)
- {
- return;
- }
-
- // Register the default OID as equality matching rule.
- String lTag = mapper.getLanguageTag();
- String nOID = mapper.getNumericOID();
-
- Collection<String> defaultNames = copyNames(getMatchingRule(nOID));
- defaultNames.add(lTag);
- MatchingRule matchingRule =
- new CollationEqualityMatchingRule(nOID, defaultNames, locale);
- addMatchingRule(nOID, matchingRule);
-
- // Register OID.3 as the equality matching rule.
- String OID = mapper.getNumericOID() + ".3";
- Collection<String> names = copyNames(getMatchingRule(OID));
- names.add(lTag + ".eq");
- names.add(lTag + ".3");
-
- MatchingRule equalityMatchingRule =
- new CollationEqualityMatchingRule(OID, names, locale);
- addMatchingRule(OID, equalityMatchingRule);
- }
-
-
-
- /**
- * Creates Greater-than-equal-to Matching Rule.
- *
- * @param mapper
- * CollationMapper containing OID and the language Tag.
- * @param locale
- * Locale value
- */
- private void createGreaterThanOrEqualToMatchingRule(
- CollationMapper mapper, Locale locale)
- {
- if (!greaterThanEqualToMatchingRuleType) return;
-
- String oid = mapper.getNumericOID() + ".4";
- String lTag = mapper.getLanguageTag();
-
- Collection<String> names = copyNames(getMatchingRule(oid));
- names.add(lTag + ".gte");
- names.add(lTag + ".4");
-
- MatchingRule matchingRule =
- new CollationGreaterThanOrEqualToMatchingRule(oid, names, locale);
- addMatchingRule(oid, matchingRule);
- }
-
-
-
- /**
- * Creates Greater-than Matching Rule.
- *
- * @param mapper
- * CollationMapper containing OID and the language Tag.
- * @param locale
- * Locale value
- */
- private void createGreaterThanMatchingRule(CollationMapper mapper,
- Locale locale)
- {
- if (!greaterThanMatchingRuleType) return;
-
- String oid = mapper.getNumericOID() + ".5";
- String lTag = mapper.getLanguageTag();
-
- Collection<String> names = copyNames(getMatchingRule(oid));
- names.add(lTag + ".gt");
- names.add(lTag + ".5");
-
- MatchingRule matchingRule =
- new CollationGreaterThanMatchingRule(oid, names, locale);
- addMatchingRule(oid, matchingRule);
- }
-
-
-
- /**
- * Creates substring Matching Rule.
- *
- * @param mapper
- * CollationMapper containing OID and the language Tag.
- * @param locale
- * Locale value
- */
- private void createSubstringMatchingRule(CollationMapper mapper,
- Locale locale)
- {
- if (!substringMatchingRuleType) return;
-
- String oid = mapper.getNumericOID() + ".6";
- String lTag = mapper.getLanguageTag();
-
- Collection<String> names = copyNames(getMatchingRule(oid));
- names.add(lTag + ".sub");
- names.add(lTag + ".6");
-
- MatchingRule matchingRule =
- new CollationSubstringMatchingRule(oid, names, locale);
- addMatchingRule(oid, matchingRule);
- }
-
-
-
/**
* Verifies if the locale is supported by the JVM.
*
@@ -648,1424 +347,6 @@
/**
- * Evaluates and converts 2 consecutive characters of the provided string
- * starting at startPos and converts them into a single escaped char.
- *
- * @param hexString
- * The hexadecimal string containing the escape sequence.
- * @param startPos
- * The starting position of the hexadecimal escape sequence.
- * @return The escaped character
- * @throws DecodeException
- * If the provided string contains invalid hexadecimal digits .
- */
- private static char hexToEscapedChar(String hexString, int startPos)
- throws DecodeException
- {
- // The two positions must be the hex characters that
- // comprise the escaped value.
- if ((startPos + 1) >= hexString.length())
- {
- LocalizableMessage message =
- ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString,
- startPos + 1);
- throw DecodeException.error(message);
- }
- byte byteValue = 0;
- switch (hexString.charAt(startPos))
- {
- case 0x30: // '0'
- break;
- case 0x31: // '1'
- byteValue = (byte) 0x10;
- break;
- case 0x32: // '2'
- byteValue = (byte) 0x20;
- break;
- case 0x33: // '3'
- byteValue = (byte) 0x30;
- break;
- case 0x34: // '4'
- byteValue = (byte) 0x40;
- break;
- case 0x35: // '5'
- byteValue = (byte) 0x50;
- break;
- case 0x36: // '6'
- byteValue = (byte) 0x60;
- break;
- case 0x37: // '7'
- byteValue = (byte) 0x70;
- break;
- case 0x38: // '8'
- byteValue = (byte) 0x80;
- break;
- case 0x39: // '9'
- byteValue = (byte) 0x90;
- break;
- case 0x41: // 'A'
- case 0x61: // 'a'
- byteValue = (byte) 0xA0;
- break;
- case 0x42: // 'B'
- case 0x62: // 'b'
- byteValue = (byte) 0xB0;
- break;
- case 0x43: // 'C'
- case 0x63: // 'c'
- byteValue = (byte) 0xC0;
- break;
- case 0x44: // 'D'
- case 0x64: // 'd'
- byteValue = (byte) 0xD0;
- break;
- case 0x45: // 'E'
- case 0x65: // 'e'
- byteValue = (byte) 0xE0;
- break;
- case 0x46: // 'F'
- case 0x66: // 'f'
- byteValue = (byte) 0xF0;
- break;
- default:
- LocalizableMessage message =
- ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString, startPos);
- throw DecodeException.error(message);
- }
-
- switch (hexString.charAt(++startPos))
- {
- case 0x30: // '0'
- break;
- case 0x31: // '1'
- byteValue |= (byte) 0x01;
- break;
- case 0x32: // '2'
- byteValue |= (byte) 0x02;
- break;
- case 0x33: // '3'
- byteValue |= (byte) 0x03;
- break;
- case 0x34: // '4'
- byteValue |= (byte) 0x04;
- break;
- case 0x35: // '5'
- byteValue |= (byte) 0x05;
- break;
- case 0x36: // '6'
- byteValue |= (byte) 0x06;
- break;
- case 0x37: // '7'
- byteValue |= (byte) 0x07;
- break;
- case 0x38: // '8'
- byteValue |= (byte) 0x08;
- break;
- case 0x39: // '9'
- byteValue |= (byte) 0x09;
- break;
- case 0x41: // 'A'
- case 0x61: // 'a'
- byteValue |= (byte) 0x0A;
- break;
- case 0x42: // 'B'
- case 0x62: // 'b'
- byteValue |= (byte) 0x0B;
- break;
- case 0x43: // 'C'
- case 0x63: // 'c'
- byteValue |= (byte) 0x0C;
- break;
- case 0x44: // 'D'
- case 0x64: // 'd'
- byteValue |= (byte) 0x0D;
- break;
- case 0x45: // 'E'
- case 0x65: // 'e'
- byteValue |= (byte) 0x0E;
- break;
- case 0x46: // 'F'
- case 0x66: // 'f'
- byteValue |= (byte) 0x0F;
- break;
- default:
- LocalizableMessage message =
- ERR_SEARCH_FILTER_INVALID_ESCAPED_BYTE.get(hexString, startPos);
- throw DecodeException.error(message);
- }
- return (char) byteValue;
- }
-
- /**
- * Collation Extensible matching rule.
- */
- private abstract class CollationMatchingRule
- extends AbstractMatchingRule
- implements ExtensibleMatchingRule
- {
- // Names for this class.
- private final Collection<String> names;
-
- // Collator for performing equality match.
- protected final Collator collator;
-
- // Numeric OID of the rule.
- private final String nOID;
-
- // Locale associated with this rule.
- private final Locale locale;
-
- // Indexer of this rule.
- protected ExtensibleIndexer indexer;
-
-
-
- /**
- * Constructs a new CollationMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- this.names = names;
- this.collator = createCollator(locale);
- this.locale = locale;
- this.nOID = nOID;
- }
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Collection<String> getNames()
- {
- return Collections.unmodifiableCollection(names);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getOID()
- {
- return nOID;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getDescription()
- {
- // There is no standard description for this matching rule.
- return null;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getSyntaxOID()
- {
- return SYNTAX_DIRECTORY_STRING_OID;
- }
-
-
-
- /**
- * Returns the name of the index database for this matching rule. An
- * index name for this rule will be based upon the Locale. This will
- * ensure that multiple collation matching rules corresponding to
- * the same Locale can share the same index database.
- *
- * @return The name of the index for this matching rule.
- */
- public String getIndexName()
- {
- String language = locale.getLanguage();
- String country = locale.getCountry();
- String variant = locale.getVariant();
- StringBuilder builder = new StringBuilder(language);
- if (country != null && country.length() > 0)
- {
- builder.append("_");
- builder.append(locale.getCountry());
- }
- if (variant != null && variant.length() > 0)
- {
- builder.append("_");
- builder.append(locale.getVariant());
- }
- return builder.toString();
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Collection<ExtensibleIndexer> getIndexers()
- {
- if (indexer == null)
- {
- // The default implementation contains shared indexer and
- // doesn't use the config.
- indexer = new CollationSharedExtensibleIndexer(this);
- }
- return Collections.singletonList(indexer);
- }
- }
-
- /**
- * Collation rule for Equality matching rule.
- */
- private final class CollationEqualityMatchingRule
- extends CollationMatchingRule
- implements OrderingMatchingRule
- {
-
- /**
- * The serial version identifier required to satisfy the compiler because
- * this class implements the <CODE>java.io.Serializable</CODE> interface.
- * This value was generated using the <CODE>serialver</CODE> command-line
- * utility included with the Java SDK.
- */
- private static final long serialVersionUID = 3990778178484159862L;
-
-
-
- /**
- * Constructs a new CollationEqualityMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationEqualityMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- super(nOID, names, locale);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ByteString normalizeAttributeValue(ByteSequence value)
- throws DecodeException
- {
- CollationKey key = collator.getCollationKey(value.toString());
- return ByteString.wrap(key.toByteArray());
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConditionResult valuesMatch(ByteSequence attributeValue,
- ByteSequence assertionValue)
- {
- return ConditionResult.valueOf(assertionValue.equals(attributeValue));
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public <T> T createIndexQuery(ByteSequence assertionValue,
- IndexQueryFactory<T> factory) throws DecodeException
- {
- // Normalize the assertion value.
- return factory.createExactMatchQuery(indexer
- .getExtensibleIndexID(), normalizeAttributeValue(assertionValue));
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int compare(byte[] arg0, byte[] arg1)
- {
- return StaticUtils.compare(arg0, arg1);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int compareValues(ByteSequence value1, ByteSequence value2)
- {
- return value1.compareTo(value2);
- }
- }
-
- /**
- * Collation rule for Substring matching rule.
- */
- private final class CollationSubstringMatchingRule extends
- CollationMatchingRule
- {
- // Substring Indexer associated with this instance.
- private CollationSubstringExtensibleIndexer subIndexer;
-
-
-
- /**
- * Constructs a new CollationSubstringMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationSubstringMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- super(nOID, names, locale);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ByteString normalizeAttributeValue(ByteSequence value)
- throws DecodeException
- {
- CollationKey key = collator.getCollationKey(value.toString());
- return ByteString.wrap(key.toByteArray());
- }
-
-
-
- /**
- * Utility class which abstracts a substring assertion value.
- */
- private final class Assertion
- {
- // Initial part of the substring filter.
- private String subInitial;
-
- // any parts of the substring filter.
- private List<String> subAny;
-
- // Final part of the substring filter.
- private String subFinal;
-
-
-
- /**
- * Creates a new instance of Assertion.
- *
- * @param subInitial
- * Initial part of the filter.
- * @param subAny
- * Any part of the filter.
- * @param subFinal
- * Final part of the filter.
- */
- private Assertion(String subInitial, List<String> subAny,
- String subFinal)
- {
- this.subInitial = subInitial;
- this.subAny = subAny;
- this.subFinal = subFinal;
- }
-
-
-
- /**
- * Returns the Initial part of the assertion.
- *
- * @return Initial part of assertion.
- */
- private String getInitial()
- {
- return subInitial;
- }
-
-
-
- /**
- * Returns the any part of the assertion.
- *
- * @return Any part of the assertion.
- */
- private List<String> getAny()
- {
- return subAny;
- }
-
-
-
- /**
- * Returns the final part of the assertion.
- *
- * @return Final part of the assertion.
- */
- private String getFinal()
- {
- return subFinal;
- }
- }
-
- private Assertion parseAssertion(ByteSequence value) throws DecodeException
- {
- // Get a string representation of the value.
- String filterString = value.toString();
- int endPos = filterString.length();
-
- // Find the locations of all the asterisks in the value. Also,
- // check to see if there are any escaped values, since they will
- // need special treatment.
- boolean hasEscape = false;
- LinkedList<Integer> asteriskPositions = new LinkedList<Integer>();
- for (int i = 0; i < endPos; i++)
- {
- if (filterString.charAt(i) == 0x2A) // The asterisk.
- {
- asteriskPositions.add(i);
- }
- else if (filterString.charAt(i) == 0x5C) // The backslash.
- {
- hasEscape = true;
- }
- }
-
- // If there were no asterisks, then this isn't a substring filter.
- if (asteriskPositions.isEmpty())
- {
- throw DecodeException.error(
- ERR_SEARCH_FILTER_SUBSTRING_NO_ASTERISKS.get(filterString, 0, endPos));
- }
-
- // If the value starts with an asterisk, then there is no
- // subInitial component. Otherwise, parse out the subInitial.
- String subInitial;
- int firstPos = asteriskPositions.removeFirst();
- if (firstPos == 0)
- {
- subInitial = null;
- }
- else
- {
- if (hasEscape)
- {
- CharBuffer buffer = CharBuffer.allocate(firstPos);
- for (int i = 0; i < firstPos; i++)
- {
- if (filterString.charAt(i) == 0x5C)
- {
- char escapeValue = hexToEscapedChar(filterString, i + 1);
- i += 2; // Move to the next sequence.
- buffer.put(escapeValue);
- }
- else
- {
- buffer.put(filterString.charAt(i));
- }
- }
-
- char[] subInitialChars = new char[buffer.position()];
- buffer.flip();
- buffer.get(subInitialChars);
- subInitial = new String(subInitialChars);
- }
- else
- {
- subInitial = filterString.substring(0, firstPos);
- }
- }
-
- // Next, process through the rest of the asterisks to get the
- // subAny values.
- List<String> subAny = new ArrayList<String>();
- for (int asteriskPos : asteriskPositions)
- {
- int length = asteriskPos - firstPos - 1;
-
- if (hasEscape)
- {
- CharBuffer buffer = CharBuffer.allocate(length);
- for (int i = firstPos + 1; i < asteriskPos; i++)
- {
- if (filterString.charAt(i) == 0x5C)
- {
- char escapeValue = hexToEscapedChar(filterString, i + 1);
- i += 2; // Move to the next sequence.
- buffer.put(escapeValue);
- }
- else
- {
- buffer.put(filterString.charAt(i));
- }
- }
-
- char[] subAnyChars = new char[buffer.position()];
- buffer.flip();
- buffer.get(subAnyChars);
- subAny.add(new String(subAnyChars));
- }
- else
- {
- subAny.add(filterString.substring(firstPos + 1, firstPos
- + length + 1));
- }
-
- firstPos = asteriskPos;
- }
-
- // Finally, see if there is anything after the last asterisk,
- // which would be the subFinal value.
- String subFinal;
- if (firstPos == (endPos - 1))
- {
- subFinal = null;
- }
- else
- {
- int length = endPos - firstPos - 1;
-
- if (hasEscape)
- {
- CharBuffer buffer = CharBuffer.allocate(length);
- for (int i = firstPos + 1; i < endPos; i++)
- {
- if (filterString.charAt(i) == 0x5C)
- {
- char escapeValue = hexToEscapedChar(filterString, i + 1);
- i += 2; // Move to the next sequence.
- buffer.put(escapeValue);
- }
- else
- {
- buffer.put(filterString.charAt(i));
- }
- }
-
- char[] subFinalChars = new char[buffer.position()];
- buffer.flip();
- buffer.get(subFinalChars);
- subFinal = new String(subFinalChars);
- }
- else
- {
- subFinal =
- filterString.substring(firstPos + 1, length + firstPos
- + 1);
- }
- }
-
- return new Assertion(subInitial, subAny, subFinal);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ByteString normalizeAssertionValue(ByteSequence value)
- throws DecodeException
- {
- Assertion assertion = parseAssertion(value);
- String subInitial = assertion.getInitial();
-
- // Normalize the Values in the following format:
- // initialLength, initial, numberofany, anyLength1, any1,
- // anyLength2, any2, ..., anyLengthn, anyn, finalLength, final
- List<Integer> normalizedList = new ArrayList<Integer>();
-
- if (subInitial == null)
- {
- normalizedList.add(0);
- }
- else
- {
- addLengthAndBytes(subInitial, normalizedList);
- }
-
- List<String> subAny = assertion.getAny();
- if (subAny.isEmpty())
- {
- normalizedList.add(0);
- }
- else
- {
- normalizedList.add(subAny.size());
- for (String any : subAny)
- {
- addLengthAndBytes(any, normalizedList);
- }
- }
-
- String subFinal = assertion.getFinal();
- if (subFinal == null)
- {
- normalizedList.add(0);
- }
- else
- {
- addLengthAndBytes(subFinal, normalizedList);
- }
-
- byte[] normalizedBytes = new byte[normalizedList.size()];
- for (int i = 0; i < normalizedList.size(); i++)
- {
- normalizedBytes[i] = normalizedList.get(i).byteValue();
- }
- return ByteString.wrap(normalizedBytes);
- }
-
-
-
- private void addLengthAndBytes(String substring,
- List<Integer> normalizedList)
- {
- CollationKey key = collator.getCollationKey(substring);
- byte[] substrBytes = key.toByteArray();
-
- // Last 4 bytes are 0s with PRIMARY strength.
- int length = substrBytes.length - 4;
- normalizedList.add(length);
- for (int i = 0; i < length; i++)
- {
- normalizedList.add((int) substrBytes[i]);
- }
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConditionResult valuesMatch(ByteSequence attributeValue,
- ByteSequence assertionValue)
- { // FIXME Code similar to
- // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.matches()
- int valueLength = attributeValue.length() - 4;
- int valuePos = 0; // position in the value bytes array.
-
- // First byte is the length of subInitial.
- int subInitialLength = 0xFF & assertionValue.byteAt(0);
-
- if (subInitialLength != 0)
- {
- if (subInitialLength > valueLength)
- {
- return ConditionResult.FALSE;
- }
-
- for (; valuePos < subInitialLength; valuePos++)
- {
- if (attributeValue.byteAt(valuePos) != assertionValue
- .byteAt(valuePos + 1))
- {
- return ConditionResult.FALSE;
- }
- }
- }
-
- int assertPos = subInitialLength + 1;
- int anySize = 0xFF & assertionValue.byteAt(assertPos++);
- if (anySize != 0)
- {
- while (anySize-- > 0)
- {
- int anyLength = 0xFF & assertionValue.byteAt(assertPos++);
- int end = valueLength - anyLength;
- boolean match = false;
-
- for (; valuePos <= end; valuePos++)
- {
- if (assertionValue.byteAt(assertPos) == attributeValue
- .byteAt(valuePos))
- {
- boolean subMatch = true;
- for (int i = 1; i < anyLength; i++)
- {
- if (assertionValue.byteAt(assertPos + i) != attributeValue
- .byteAt(valuePos + i))
- {
- subMatch = false;
- break;
- }
- }
-
- if (subMatch)
- {
- match = subMatch;
- break;
- }
- }
- }
-
- if (match)
- {
- valuePos += anyLength;
- }
- else
- {
- return ConditionResult.FALSE;
- }
-
- assertPos = assertPos + anyLength;
- }
- }
-
- int finalLength = 0xFF & assertionValue.byteAt(assertPos++);
- if (finalLength != 0)
- {
- if ((valueLength - finalLength) < valuePos)
- {
- return ConditionResult.FALSE;
- }
-
- if (finalLength != assertionValue.length() - assertPos)
- {
- // Some issue with the encoding.
- return ConditionResult.FALSE;
- }
-
- valuePos = valueLength - finalLength;
- for (int i = 0; i < finalLength; i++, valuePos++)
- {
- if (assertionValue.byteAt(assertPos + i) != attributeValue
- .byteAt(valuePos))
- {
- return ConditionResult.FALSE;
- }
- }
- }
-
- return ConditionResult.TRUE;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public final Collection<ExtensibleIndexer> getIndexers()
- {
- List<ExtensibleIndexer> indexers = new ArrayList<ExtensibleIndexer>();
- if (subIndexer == null)
- {
- subIndexer = new CollationSubstringExtensibleIndexer(this);
- }
- if (indexer == null)
- {
- indexer = new CollationSharedExtensibleIndexer(this);
- }
-
- indexers.add(subIndexer);
- indexers.add(indexer);
-
- return indexers;
- }
-
-
- /**
- * Makes a byte array representing a substring index key for one
- * substring of a value.
- *
- * @param value
- * The String containing the value.
- * @param pos
- * The starting position of the substring.
- * @param len
- * The length of the substring.
- * @return A byte string containing a substring key.
- */
- private ByteString makeSubstringKey(String value, int pos, int len)
- {
- String sub = value.substring(pos, pos + len);
- CollationKey col = collator.getCollationKey(sub);
- byte[] key = col.toByteArray();
- // truncate the key
- return ByteString.wrap(key).subSequence(0, key.length - 4);
- }
-
-
-
- /**
- * Uses an equality index to retrieve the entry IDs that might
- * contain a given initial substring.
- *
- * @param bytes
- * A normalized initial substring of an attribute value.
- * @return The candidate entry IDs.
- */
- private <T> T matchInitialSubstring(String value,
- IndexQueryFactory<T> factory)
- {
- // Use the shared equality indexer.
- return createRangeMatchQuery(value, factory, this.indexer);
- }
-
- private <T> T createRangeMatchQuery(String value,
- IndexQueryFactory<T> factory, ExtensibleIndexer indexer)
- { // FIXME Code similar to
- // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.rangeMatch()
- ByteString lower = makeSubstringKey(value, 0, value.length());
- ByteStringBuilder upper = new ByteStringBuilder(lower);
- for (int i = upper.length() - 1; i >= 0; i--)
- {
- if (upper.byteAt(i) == 0xFF)
- {
- // We have to carry the overflow to the more significant byte.
- upper.setByte(i, (byte) 0);
- }
- else
- {
- // No overflow, we can stop.
- upper.setByte(i, (byte) (upper.byteAt(i) + 1));
- break;
- }
- }
- // Read the range: lower <= keys < upper.
- return factory.createRangeMatchQuery(
- indexer.getExtensibleIndexID(), lower, upper, true, false);
- }
-
- /**
- * Retrieves the Index Records that might contain a given substring.
- *
- * @param value
- * A String representing the attribute value.
- * @param factory
- * An IndexQueryFactory which issues calls to the backend.
- * @param substrLength
- * The length of the substring.
- * @return The candidate entry IDs.
- */
- private <T> T matchSubstring(String value, IndexQueryFactory<T> factory)
- { // FIXME Code similar to
- // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.substringMatch()
- int substrLength = factory.getIndexingOptions().substringKeySize();
- if (value.length() < substrLength)
- {
- return createRangeMatchQuery(value, factory, subIndexer);
- }
-
- List<T> queryList = new ArrayList<T>();
- Set<ByteString> set = new TreeSet<ByteString>();
- for (int first = 0, last = substrLength;
- last <= value.length();
- first++, last++)
- {
- set.add(makeSubstringKey(value, first, substrLength));
- }
-
- for (ByteString keyBytes : set)
- {
- queryList.add(factory.createExactMatchQuery(
- subIndexer.getExtensibleIndexID(), keyBytes));
- }
- return factory.createIntersectionQuery(queryList);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public <T> T createIndexQuery(ByteSequence assertionValue,
- IndexQueryFactory<T> factory) throws DecodeException
- { // FIXME Code similar to
- // AbstractSubstringMatchingRuleImpl.DefaultSubstringAssertion.createIndexQuery()?
- Assertion assertion = parseAssertion(assertionValue);
- String subInitial = assertion.getInitial();
- List<String> subAny = assertion.getAny();
- String subFinal = assertion.getFinal();
- List<T> queries = new ArrayList<T>();
-
- if (subInitial == null && subAny.isEmpty() && subFinal == null)
- {
- // Can happen with a filter like "cn:en.6:=*".
- // Just return an empty record.
- return factory.createMatchAllQuery();
- }
- List<String> elements = new ArrayList<String>();
- if (subInitial != null)
- {
- // Always use the shared indexer for initial match.
- queries.add(matchInitialSubstring(subInitial, factory));
- }
-
- if (subAny != null && subAny.size() > 0)
- {
- elements.addAll(subAny);
- }
-
- if (subFinal != null)
- {
- elements.add(subFinal);
- }
-
- for (String element : elements)
- {
- queries.add(matchSubstring(element, factory));
- }
- return factory.createIntersectionQuery(queries);
- }
- }
-
- /**
- * An abstract Collation rule for Ordering matching rule.
- */
- private abstract class CollationOrderingMatchingRule
- extends CollationMatchingRule
- implements OrderingMatchingRule
- {
-
- /**
- * The serial version identifier required to satisfy the compiler because
- * this class implements the <CODE>java.io.Serializable</CODE> interface.
- * This value was generated using the <CODE>serialver</CODE> command-line
- * utility included with the Java SDK.
- */
- private static final long serialVersionUID = 7354051060508436941L;
-
-
-
- /**
- * Constructs a new CollationOrderingMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationOrderingMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- super(nOID, names, locale);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ByteString normalizeAttributeValue(ByteSequence value)
- throws DecodeException
- {
- CollationKey key = collator.getCollationKey(value.toString());
- return ByteString.wrap(key.toByteArray());
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int compare(byte[] arg0, byte[] arg1)
- {
- return StaticUtils.compare(arg0, arg1);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int compareValues(ByteSequence value1, ByteSequence value2)
- {
- return value1.compareTo(value2);
- }
- }
-
- /**
- * Collation matching rule for Less-than matching rule.
- */
- private final class CollationLessThanMatchingRule extends
- CollationOrderingMatchingRule
- {
- /**
- * The serial version identifier required to satisfy the compiler because
- * this class implements the <CODE>java.io.Serializable</CODE> interface.
- * This value was generated using the <CODE>serialver</CODE> command-line
- * utility included with the Java SDK.
- */
- private static final long serialVersionUID = -7578406829946732713L;
-
-
-
- /**
- * Constructs a new CollationLessThanMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationLessThanMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- super(nOID, names, locale);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConditionResult valuesMatch(ByteSequence attributeValue,
- ByteSequence assertionValue)
- {
- int ret = attributeValue.compareTo(assertionValue);
- return ConditionResult.valueOf(ret < 0);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public <T> T createIndexQuery(ByteSequence assertionValue,
- IndexQueryFactory<T> factory) throws DecodeException
- {
- return factory.createRangeMatchQuery(indexer
- .getExtensibleIndexID(), ByteString.empty(),
- normalizeAttributeValue(assertionValue), false, false);
- }
- }
-
- /**
- * Collation rule for less-than-equal-to matching rule.
- */
- private final class CollationLessThanOrEqualToMatchingRule extends
- CollationOrderingMatchingRule
- {
- /**
- * The serial version identifier required to satisfy the compiler because
- * this class implements the <CODE>java.io.Serializable</CODE> interface.
- * This value was generated using the <CODE>serialver</CODE> command-line
- * utility included with the Java SDK.
- */
- private static final long serialVersionUID = 7222067708233629974L;
-
-
-
- /**
- * Constructs a new CollationLessThanOrEqualToMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationLessThanOrEqualToMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- super(nOID, names, locale);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConditionResult valuesMatch(ByteSequence attributeValue,
- ByteSequence assertionValue)
- {
- int ret = attributeValue.compareTo(assertionValue);
- return ConditionResult.valueOf(ret <= 0);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public <T> T createIndexQuery(ByteSequence assertionValue,
- IndexQueryFactory<T> factory) throws DecodeException
- {
- // Read the range: lower < keys <= upper.
- return factory.createRangeMatchQuery(indexer
- .getExtensibleIndexID(), ByteString.empty(),
- normalizeAttributeValue(assertionValue), false, true);
- }
- }
-
- /**
- * Collation rule for greater-than matching rule.
- */
- private final class CollationGreaterThanMatchingRule extends
- CollationOrderingMatchingRule
- {
- /**
- * The serial version identifier required to satisfy the compiler because
- * this class implements the <CODE>java.io.Serializable</CODE> interface.
- * This value was generated using the <CODE>serialver</CODE> command-line
- * utility included with the Java SDK.
- */
- private static final long serialVersionUID = 1204368277332957024L;
-
-
-
- /**
- * Constructs a new CollationGreaterThanMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationGreaterThanMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- super(nOID, names, locale);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConditionResult valuesMatch(ByteSequence attributeValue,
- ByteSequence assertionValue)
- {
- int ret = attributeValue.compareTo(assertionValue);
- return ConditionResult.valueOf(ret > 0);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public <T> T createIndexQuery(ByteSequence assertionValue,
- IndexQueryFactory<T> factory) throws DecodeException
- {
- return factory.createRangeMatchQuery(indexer
- .getExtensibleIndexID(), normalizeAttributeValue(assertionValue),
- ByteString.empty(), false, false);
- }
- }
-
- /**
- * Collation rule for greater-than-equal-to matching rule.
- */
- private final class CollationGreaterThanOrEqualToMatchingRule extends
- CollationOrderingMatchingRule
- {
- /**
- * The serial version identifier required to satisfy the compiler because
- * this class implements the <CODE>java.io.Serializable</CODE> interface.
- * This value was generated using the <CODE>serialver</CODE> command-line
- * utility included with the Java SDK.
- */
- private static final long serialVersionUID = -5212358378014047933L;
-
-
-
- /**
- * Constructs a new CollationGreaterThanOrEqualToMatchingRule.
- *
- * @param nOID
- * OID of the collation matching rule
- * @param names
- * names of this matching rule
- * @param locale
- * Locale of the collation matching rule
- */
- private CollationGreaterThanOrEqualToMatchingRule(String nOID,
- Collection<String> names, Locale locale)
- {
- super(nOID, names, locale);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ConditionResult valuesMatch(ByteSequence attributeValue,
- ByteSequence assertionValue)
- {
- int ret = attributeValue.compareTo(assertionValue);
- return ConditionResult.valueOf(ret >= 0);
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public <T> T createIndexQuery(ByteSequence assertionValue,
- IndexQueryFactory<T> factory) throws DecodeException
- {
- // Read the range: lower <= keys < upper.
- return factory.createRangeMatchQuery(indexer
- .getExtensibleIndexID(), normalizeAttributeValue(assertionValue),
- ByteString.empty(), true, false);
- }
- }
-
- /**
- * Extensible Indexer class for Collation Matching rules which share
- * the same index. This Indexer is shared by Equality and Ordering
- * Collation Matching Rules.
- */
- private final class CollationSharedExtensibleIndexer extends
- ExtensibleIndexer
- {
-
- /**
- * The Extensible Matching Rule.
- */
- private final CollationMatchingRule matchingRule;
-
-
-
- /**
- * Creates a new instance of CollationSharedExtensibleIndexer.
- *
- * @param matchingRule
- * The Collation Matching Rule.
- */
- private CollationSharedExtensibleIndexer(
- CollationMatchingRule matchingRule)
- {
- this.matchingRule = matchingRule;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String getExtensibleIndexID()
- {
- return EXTENSIBLE_INDEXER_ID_SHARED;
- }
-
-
-
- /**
- * {@inheritDoc}
- */
- @Override
- public final void createKeys(Schema schema, ByteSequence value,
- IndexingOptions options, Collection<ByteString> keys)
- throws DecodeException
- {
- keys.add(matchingRule.normalizeAttributeValue(value));
- }
-
- /** {@inheritDoc} */
- @Override
- public String getIndexID()
- {
- return matchingRule.getIndexName() + "." + getExtensibleIndexID();
- }
- }
-
- /**
- * Extensible Indexer class for Collation Substring Matching rules.
- * This Indexer is used by Substring Collation Matching Rules.
- */
- private final class CollationSubstringExtensibleIndexer extends
- ExtensibleIndexer
- {
- private final CollationSubstringMatchingRule matchingRule;
-
- /**
- * Creates a new instance of CollationSubstringExtensibleIndexer.
- *
- * @param matchingRule
- * The CollationSubstringMatching Rule.
- */
- private CollationSubstringExtensibleIndexer(
- CollationSubstringMatchingRule matchingRule)
- {
- this.matchingRule = matchingRule;
- }
-
- /** {@inheritDoc} */
- @Override
- public void createKeys(Schema schema, ByteSequence value,
- IndexingOptions options, Collection<ByteString> keys)
- { // TODO merge with AbstractSubstringMatchingRuleImpl.SubstringIndexer.createKeys();
- String normValue = value.toString();
- int keyLength = options.substringKeySize();
- for (int i = 0, remain = normValue.length(); remain > 0; i++, remain--)
- {
- int len = Math.min(keyLength, remain);
- keys.add(matchingRule.makeSubstringKey(normValue, i, len));
- }
- }
-
- /** {@inheritDoc} */
- @Override
- public String getIndexID()
- {
- return matchingRule.getIndexName() + "." + getExtensibleIndexID();
- }
-
- /** {@inheritDoc} */
- @Override
- public String getExtensibleIndexID()
- {
- return EXTENSIBLE_INDEXER_ID_SUBSTRING;
- }
-
- }
-
- /**
* A utility class for extracting the OID and Language Tag from the
* configuration entry.
*/
--
Gitblit v1.10.0