/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.analysis.miscellaneous;

import java.io.Reader;
import java.util.Collections;
import java.util.Map;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.AnalyzerWrapper;
import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.tests.analysis.BaseTokenStreamTestCase;
import org.apache.lucene.tests.analysis.MockCharFilter;
import org.apache.lucene.tests.analysis.MockTokenizer;
import org.apache.lucene.util.IOUtils;

public class TestPerFieldAnalyzerWrapper extends BaseTokenStreamTestCase {
  public void testPerField() throws Exception {
    String text = "Qwerty";

    Map<String, Analyzer> analyzerPerField =
        Collections.<String, Analyzer>singletonMap("special", new SimpleAnalyzer());

    Analyzer defaultAnalyzer = new WhitespaceAnalyzer();

    PerFieldAnalyzerWrapper analyzer =
        new PerFieldAnalyzerWrapper(defaultAnalyzer, analyzerPerField);

    try (TokenStream tokenStream = analyzer.tokenStream("field", text)) {
      CharTermAttribute termAtt = tokenStream.getAttribute(CharTermAttribute.class);
      tokenStream.reset();

      assertTrue(tokenStream.incrementToken());
      assertEquals("WhitespaceAnalyzer does not lowercase", "Qwerty", termAtt.toString());
      assertFalse(tokenStream.incrementToken());
      tokenStream.end();
    }

    try (TokenStream tokenStream = analyzer.tokenStream("special", text)) {
      CharTermAttribute termAtt = tokenStream.getAttribute(CharTermAttribute.class);
      tokenStream.reset();

      assertTrue(tokenStream.incrementToken());
      assertEquals("SimpleAnalyzer lowercases", "qwerty", termAtt.toString());
      assertFalse(tokenStream.incrementToken());
      tokenStream.end();
    }
    // TODO: fix this about PFAW, this is crazy
    analyzer.close();
    defaultAnalyzer.close();
    IOUtils.close(analyzerPerField.values());
  }

  public void testReuseWrapped() throws Exception {
    final String text = "Qwerty";

    final Analyzer specialAnalyzer = new SimpleAnalyzer();
    final Analyzer defaultAnalyzer = new WhitespaceAnalyzer();

    TokenStream ts1, ts2, ts3, ts4;

    final PerFieldAnalyzerWrapper wrapper1 =
        new PerFieldAnalyzerWrapper(
            defaultAnalyzer,
            Collections.<String, Analyzer>singletonMap("special", specialAnalyzer));

    // test that the PerFieldWrapper returns the same instance as original Analyzer:
    ts1 = defaultAnalyzer.tokenStream("something", text);
    ts2 = wrapper1.tokenStream("something", text);
    ts3 = wrapper1.tokenStream("somethingElse", text);
    assertSame(ts1, ts2);
    assertSame(ts2, ts3);

    ts1 = specialAnalyzer.tokenStream("special", text);
    ts2 = wrapper1.tokenStream("special", text);
    assertSame(ts1, ts2);

    // Wrap with another wrapper, which does *not* extend DelegatingAnalyzerWrapper:
    final AnalyzerWrapper wrapper2 =
        new AnalyzerWrapper(wrapper1.getReuseStrategy()) {
          @Override
          protected Analyzer getWrappedAnalyzer(String fieldName) {
            return wrapper1;
          }

          @Override
          protected TokenStreamComponents wrapComponents(
              String fieldName, TokenStreamComponents components) {
            assertNotSame(
                specialAnalyzer.tokenStream("special", text), components.getTokenStream());
            TokenFilter filter = new ASCIIFoldingFilter(components.getTokenStream());
            return new TokenStreamComponents(components.getSource(), filter);
          }
        };
    ts3 = wrapper2.tokenStream("special", text);
    assertNotSame(ts1, ts3);
    assertTrue(ts3 instanceof ASCIIFoldingFilter);
    // check that cache did not get corrumpted:
    ts2 = wrapper1.tokenStream("special", text);
    assertSame(ts1, ts2);

    // Wrap PerField with another PerField. In that case all TokenStreams returned must be the same:
    final PerFieldAnalyzerWrapper wrapper3 =
        new PerFieldAnalyzerWrapper(
            wrapper1, Collections.<String, Analyzer>singletonMap("moreSpecial", specialAnalyzer));
    ts1 = specialAnalyzer.tokenStream("special", text);
    ts2 = wrapper3.tokenStream("special", text);
    assertSame(ts1, ts2);
    ts3 = specialAnalyzer.tokenStream("moreSpecial", text);
    ts4 = wrapper3.tokenStream("moreSpecial", text);
    assertSame(ts3, ts4);
    assertSame(ts2, ts3);
    IOUtils.close(wrapper3, wrapper2, wrapper1, specialAnalyzer, defaultAnalyzer);
  }

  public void testCharFilters() throws Exception {
    Analyzer a =
        new Analyzer() {
          @Override
          protected TokenStreamComponents createComponents(String fieldName) {
            return new TokenStreamComponents(new MockTokenizer());
          }

          @Override
          protected Reader initReader(String fieldName, Reader reader) {
            return new MockCharFilter(reader, 7);
          }
        };
    assertAnalyzesTo(a, "ab", new String[] {"aab"}, new int[] {0}, new int[] {2});

    // now wrap in PFAW
    PerFieldAnalyzerWrapper p =
        new PerFieldAnalyzerWrapper(a, Collections.<String, Analyzer>emptyMap());

    assertAnalyzesTo(p, "ab", new String[] {"aab"}, new int[] {0}, new int[] {2});
    p.close();
    a.close(); // TODO: fix this about PFAW, its a trap
  }
}
