/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2006-2010 Sun Microsystems, Inc.
 *      Portions Copyright 2011-2015 ForgeRock AS
 */
package org.opends.server.schema;

import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.SearchScope;
import org.forgerock.opendj.ldap.schema.MatchingRule;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.schema.Syntax;
import org.opends.server.TestCaseUtils;
import org.opends.server.api.AttributeSyntax;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.protocols.internal.SearchRequest;
import org.opends.server.types.AttributeType;
import org.opends.server.util.RemoveOnceSDKSchemaIsUsed;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import static org.opends.server.protocols.internal.InternalClientConnection.*;
import static org.opends.server.protocols.internal.Requests.*;
import static org.testng.Assert.*;

/**
 * Test the AttributeTypeSyntax.
 */
@RemoveOnceSDKSchemaIsUsed
public class AttributeTypeSyntaxTest extends AttributeSyntaxTest
{

  /** {@inheritDoc} */
  @Override
  protected AttributeSyntax getRule()
  {
    return new AttributeTypeSyntax();
  }

  /** {@inheritDoc} */
  @Override
  @DataProvider(name="acceptableValues")
  public Object[][] createAcceptableValues()
  {
    return new Object [][] {
        {"(1.2.8.5 NAME 'testtype' DESC 'full type' OBSOLETE SUP cn " +
          " EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch" +
          " SUBSTR caseIgnoreSubstringsMatch" +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" +
          " USAGE userApplications )",
          true},
        {"(1.2.8.5 NAME 'testtype' DESC 'full type' OBSOLETE " +
          " EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch" +
          " SUBSTR caseIgnoreSubstringsMatch" +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" +
          " COLLECTIVE USAGE userApplications )",
          true},
          {"(1.2.8.5 NAME 'testtype')", true},
        {"(1.2.8.5 NAME 'testtype' DESC 'full type')",
              true},
        {"(1.2.8.5 NAME 'testType' DESC 'full type' EQUALITY caseIgnoreMatch " +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15)",
              true},
        {"(1.2.8.5 NAME 'testType' DESC 'full type' EQUALITY caseIgnoreMatch " +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'test' )",
              true},
        {"(1.2.8.5 NAME 'testType' DESC 'full type' EQUALITY caseIgnoreMatch " +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'test')",
              true},
        {"(1.2.8.5 NAME 'testType' DESC 'full type' EQUALITY caseIgnoreMatch " +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'test' " +
          " X-SCHEMA-FILE '33-test.ldif' )",
              true},
        {"(1.2.8.5 USAGE directoryOperation )",
              true},
        {"(1.2.8.5 NAME 'testtype' DESC 'full type' OBSOLETE SUP cn " +
          " EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch" +
          " SUBSTR caseIgnoreSubstringsMatch" +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" +
          " COLLECTIVE USAGE userApplications )",
          true}, // Collective can inherit from non-collective
        {"(1.2.8.5 NAME 'testtype' DESC 'full type' OBSOLETE " +
          " EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch" +
          " SUBSTR caseIgnoreSubstringsMatch" +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE" +
          " COLLECTIVE USAGE directoryOperation )",
          true}, // Collective can be operational
        {"(1.2.8.5 NAME 'testType' DESC 'full type' EQUALITY caseIgnoreMatch " +
          " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'test' " +
          " X-SCHEMA-FILE '33-test.ldif' X-NAME )",
          false}, // X-NAME is invalid extension (no value)
    };
  }



  /**
   * Tests the use of the "X-APPROX" extension to specify a particular
   * approximate matching rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test
  public void testXAPPROXExtension() throws Exception
  {
    MatchingRule mrule = Schema.getCoreSchema().getMatchingRule("ds-mr-double-metaphone-approx");

    // Get a reference to the attribute type syntax implementation in the
    // server.
    Syntax attrTypeSyntax = DirectoryServer.getSchema().getSyntax("1.3.6.1.4.1.1466.115.121.1.3", false);
    assertNotNull(attrTypeSyntax);


    // Create an attribute type definition and verify that it is acceptable.
    ByteString definition = ByteString.valueOfUtf8(
      "( testxapproxtype-oid NAME 'testXApproxType' " +
           "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " +
           "X-APPROX 'ds-mr-double-metaphone-approx' )");
    LocalizableMessageBuilder invalidReason = new LocalizableMessageBuilder();
    assertTrue(attrTypeSyntax.valueIsAcceptable(definition, invalidReason),
            invalidReason.toString());


    // Verify that we can decode the attribute type and that it has the
    // correct approximate matching rule.
    AttributeType attrType =
         AttributeTypeSyntax.decodeAttributeType(definition,
                                                 DirectoryServer.getSchema(),
                                                 false);
    assertNotNull(attrType);
    assertNotNull(attrType.getApproximateMatchingRule());
    assertEquals(attrType.getApproximateMatchingRule(), mrule);
  }



  /**
   * Tests a situation when two radically different equality and substring
   * matching rules (such as Case Ignore and Case Exact) are used to define an
   * attribute description. For more information, look at the issue# 4468 in
   * Issue Tracker.
   *
   * @throws Exception In case of an error.
   */
  @Test
  public void testMixedEqualityAndSubstringMatchingRules() throws Exception
  {
    //Add an attribute with directory string syntax.
    int  resultCode = TestCaseUtils.applyModifications(true,
      "dn: cn=schema",
      "changetype: modify",
      "add: attributetypes",
      "attributeTypes: ( gvRights-oid NAME 'gvRights' DESC 'x attr' EQUALITY " +
      "caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR " +
      "caseExactSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 " +
      "USAGE userApplications )",
      "-",
      "add: objectclasses",
      "objectClasses: ( gvRightsTest-oid NAME 'gvRightsTest' DESC 'Test' SUP top AUXILIARY " +
      "MUST ( objectClass $ gvRights ) )");
    assertEquals(resultCode, 0);
    TestCaseUtils.initializeTestBackend(true);
    //add the test entry.
    TestCaseUtils.addEntry(
      "dn: cn=gvrightstest,o=test",
      "objectclass: person",
      "objectclass: gvRightsTest",
      "cn: gvrightstest",
      "sn: test",
      "gvRights: gvApplId=test2,ou=Applications,dc=bla$test2-T");

    //Search for the entry using substring matching rule filter.
    String filter = "(&(gvrights=*ApplId=test2,ou=*)" + "(gvrights=*test2,ou=A*))";
    SearchRequest request = newSearchRequest("cn=gvrightstest,o=test", SearchScope.WHOLE_SUBTREE, filter);
    InternalSearchOperation searchOperation = getRootConnection().processSearch(request);
    assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
    assertEquals(searchOperation.getEntriesSent(), 1);
  }
}

