Package xbird.storage.indexer

Source Code of xbird.storage.indexer.LikeIndexQuery

/*
* @(#)$Id$
*
* Copyright 2006-2008 Makoto YUI
*
* Licensed 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.
*
* Contributors:
*     Makoto YUI - initial implementation
*/
package xbird.storage.indexer;

import java.util.ArrayList;

import xbird.storage.index.Value;
import xbird.storage.indexer.BasicIndexQuery.IndexConditionSW;
import xbird.util.collections.ints.IntStack;
import xbird.util.primitive.Primitives;
import xbird.util.string.StringUtils;

/**
*
* <DIV lang="en"></DIV>
* <DIV lang="ja"></DIV>
*
* @author Makoto YUI (yuin405+xbird@gmail.com)
*/
public final class LikeIndexQuery extends IndexConditionSW {

    private static final char DEFAULT_ESCAPE = '\\';
    private static final int MATCH = 0, ONE = 1, ANY = 2;

    private final int escapeChar;

    private char[][] _patterns;
    private int[] _types;

    public LikeIndexQuery(Value prefix, String suffix) {
        this(prefix, suffix, DEFAULT_ESCAPE);
    }

    public LikeIndexQuery(Value prefix, String suffix, char escape) {
        super(prefix);
        if(suffix == null) {
            throw new IllegalArgumentException("Illegal null suffix");
        }
        this.escapeChar = escape;
        initPattern(suffix);
    }

    private void initPattern(String p) {
        final int ptnlen = p.length();
        final IntStack typeStack = new IntStack(12);
        final ArrayList<char[]> patternStack = new ArrayList<char[]>();
        final StringBuilder pending = new StringBuilder(64);
        for(int i = 0; i < ptnlen; i++) {
            char c = p.charAt(i);
            if(c == escapeChar) {
                if(i >= (ptnlen - 1)) {
                    throw new IllegalArgumentException("Illegal like expression: " + p);
                }
                c = p.charAt(++i);
                if(c != '%' && c != '_' && c != escapeChar) {
                    throw new IllegalArgumentException("Illegal like expression: " + p);
                }
                pending.append(c);
            } else if(c == '%') {
                if(!typeStack.isEmpty()) {
                    int lastType = typeStack.peek();
                    if(lastType == '%') {
                        continue;
                    }
                }
                if(pending.length() > 0) {
                    String s = pending.toString();
                    typeStack.push(MATCH);
                    patternStack.add(StringUtils.getChars(s));
                    pending.setLength(0);
                }
                typeStack.push(ANY);
                patternStack.add(null);
            } else if(c == '_') {
                if(pending.length() > 0) {
                    String s = pending.toString();
                    typeStack.push(MATCH);
                    patternStack.add(StringUtils.getChars(s));
                    pending.setLength(0);
                }
                typeStack.push(ONE);
                patternStack.add(null);
            } else {
                pending.append(c);
            }
        }
        if(pending.length() > 0) {
            String s = pending.toString();
            typeStack.push(MATCH);
            patternStack.add(StringUtils.getChars(s));
        }
        this._patterns = patternStack.toArray(new char[patternStack.size()][]);
        this._types = typeStack.toArray();
    }

    @Override
    public boolean testValue(Value value) {
        boolean sw = value.startsWith(_operands[0]);
        if(!sw) {
            return false;
        }
        if(_types.length == 0) {
            return true;
        }
        byte[] data = value.getData();
        int offset = _operands[0].getLength();
        int length = data.length - offset;
        char[] target = Primitives.toChars(data, offset, length);
        return match(target, _patterns, _types, 0, 0);
    }

    private static boolean match(char[] target, char[][] verifyPatterns, int[] verifyType, int ti, int pi) {
        final int round = verifyType.length;
        final int tlimit = target.length;
        for(; pi < round; pi++) {
            final int type = verifyType[pi];
            final char[] ptn = verifyPatterns[pi];
            switch(type) {
                case MATCH:
                    final int ptnlen = ptn.length;
                    if((ti + ptnlen) > tlimit) {
                        return false;
                    }
                    for(int j = 0; j < ptnlen; j++) {
                        if(target[ti++] != ptn[j]) {
                            return false;
                        }
                    }
                    break;
                case ONE:
                    if(ti++ >= tlimit) {
                        return false;
                    }
                    break;
                case ANY:
                    if(++pi >= round) {
                        return true;
                    }
                    // there are more rounds
                    for(; ti < tlimit; ti++) {// recursive trick
                        if(match(target, verifyPatterns, verifyType, ti, pi)) {
                            return true;
                        }
                        // backtrack to ANY
                    }
                    return false;
                default:
                    throw new IllegalStateException("Illegal type: " + type);
            }
        }
        return ti == tlimit;
    }

    @Override
    public String toString() {
        StringBuilder buf = new StringBuilder(64);
        buf.append("prefix: ");
        buf.append(_operands[0].toString());
        buf.append(", suffix: ");
        for(int i = 0; i < _types.length; i++) {
            int type = _types[i];
            if(type == ANY) {
                buf.append('%');
            } else if(type == ONE) {
                buf.append('_');
            } else if(type == MATCH) {
                char[] pattern = _patterns[i];
                buf.append(pattern);
            } else {
                throw new IllegalStateException("Unexpected type: " + type);
            }
        }
        return buf.toString();
    }

}
TOP

Related Classes of xbird.storage.indexer.LikeIndexQuery

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.