/*
* 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.exoplatform.services.jcr.impl.core.query.lucene;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermEnum;
import org.apache.lucene.search.FilteredTermEnum;
import org.apache.lucene.search.MultiTermQuery;
/**
* <code>CaseTermQuery</code> implements a term query which convert the term from the index either
* to upper or lower case before it is matched.
*/
abstract class CaseTermQuery
extends MultiTermQuery
implements TransformConstants
{
/**
* Indicates whether terms from the index should be lower-cased or upper-cased.
*/
protected final int transform;
CaseTermQuery(Term term, int transform)
{
super(term);
this.transform = transform;
}
/**
* {@inheritDoc}
*/
protected FilteredTermEnum getEnum(IndexReader reader) throws IOException
{
return new CaseTermEnum(reader);
}
static final class Upper
extends CaseTermQuery
{
Upper(Term term)
{
super(term, TRANSFORM_UPPER_CASE);
}
}
static final class Lower
extends CaseTermQuery
{
Lower(Term term)
{
super(term, TRANSFORM_LOWER_CASE);
}
}
private final class CaseTermEnum
extends FilteredTermEnum
{
private final int nameLength;
private final OffsetCharSequence termText;
private final OffsetCharSequence currentTerm;
CaseTermEnum(IndexReader reader) throws IOException
{
// gather all terms that match
// keep them in order and remember the doc frequency as value
final Map orderedTerms = new LinkedHashMap();
Term term = getTerm();
// there are always two range scanse: one with an initial
// lower case character and another one with an initial upper case
// character
List rangeScans = new ArrayList(2);
nameLength = FieldNames.getNameLength(term.text());
String propName = term.text().substring(0, nameLength);
this.termText = new OffsetCharSequence(nameLength, term.text());
this.currentTerm = new OffsetCharSequence(nameLength, term.text(), transform);
try
{
// start with a term using the lower case character for the first
// character of the value.
if (term.text().length() > nameLength)
{
// start with initial lower case
StringBuffer lowerLimit = new StringBuffer(propName);
lowerLimit.append(termText.toString().toUpperCase());
lowerLimit.setCharAt(nameLength, Character.toLowerCase(lowerLimit.charAt(nameLength)));
StringBuffer upperLimit = new StringBuffer(propName);
upperLimit.append(termText.toString().toLowerCase());
rangeScans.add(new RangeScan(reader, new Term(term.field(), lowerLimit.toString()), new Term(term
.field(), upperLimit.toString())));
// second scan with upper case start
lowerLimit = new StringBuffer(propName);
lowerLimit.append(termText.toString().toUpperCase());
upperLimit = new StringBuffer(propName);
upperLimit.append(termText.toString().toLowerCase());
upperLimit.setCharAt(nameLength, Character.toUpperCase(upperLimit.charAt(nameLength)));
rangeScans.add(new RangeScan(reader, new Term(term.field(), lowerLimit.toString()), new Term(term
.field(), upperLimit.toString())));
}
else
{
// use term as is
rangeScans.add(new RangeScan(reader, term, term));
}
for (Iterator it = rangeScans.iterator(); it.hasNext();)
{
TermEnum terms = (TermEnum) it.next();
do
{
Term t = terms.term();
if (t != null)
{
currentTerm.setBase(t.text());
int compare = currentTerm.compareTo(termText);
if (compare == 0)
{
orderedTerms.put(t, new Integer(terms.docFreq()));
}
else if (compare < 0)
{
// try next one
}
else
{
// compare > 0
}
}
else
{
break;
}
}
while (terms.next());
}
}
finally
{
for (Iterator it = rangeScans.iterator(); it.hasNext();)
{
TermEnum terms = (TermEnum) it.next();
try
{
terms.close();
}
catch (IOException e)
{
// ignore
}
}
}
final Iterator it = orderedTerms.keySet().iterator();
setEnum(new TermEnum()
{
private Term current;
{
getNext();
}
public boolean next()
{
getNext();
return current != null;
}
public Term term()
{
return current;
}
public int docFreq()
{
Integer docFreq = (Integer) orderedTerms.get(current);
return docFreq != null ? docFreq.intValue() : 0;
}
public void close()
{
// nothing to close
}
private void getNext()
{
current = it.hasNext() ? (Term) it.next() : null;
}
});
}
protected boolean termCompare(Term term)
{
// they all match
return true;
}
public float difference()
{
return 1.0f;
}
protected boolean endEnum()
{
return false;
}
}
}