/* * Copyright (2005-2012) Schibsted ASA * This file is part of Possom. * * Possom is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Possom is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Possom. If not, see <http://www.gnu.org/licenses/>. */ package no.sesat.search.query.token; import java.lang.ref.Reference; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; import no.sesat.commons.ref.ReferenceMap; /** Used by VeryFastTokenEvaluator for matches against part of the query to a fast list. * * <b>Immutable</b> * * * @version $Id$ **/ final class TokenMatch{ // Constants ----------------------------------------------------- /** General properties to regular expressions configured. **/ private static final int REG_EXP_OPTIONS = Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE; private static final int WEAK_CACHE_INITIAL_CAPACITY = 2000; private static final float WEAK_CACHE_LOAD_FACTOR = 0.5f; private static final int WEAK_CACHE_CONCURRENCY_LEVEL = 16; private static final ReferenceMap<Integer,TokenMatch> WEAK_CACHE = new ReferenceMap<Integer,TokenMatch>( ReferenceMap.Type.WEAK, new ConcurrentHashMap<Integer,Reference<TokenMatch>>( WEAK_CACHE_INITIAL_CAPACITY, WEAK_CACHE_LOAD_FACTOR, WEAK_CACHE_CONCURRENCY_LEVEL)); // Static -------------------------------------------------------- /** Hands out an instance given the 'constructing arguments'. * We use the flyweight pattern since instances are immutable. * * @param token * @param match * @param value any synonym for the match. may be null * @return */ public static TokenMatch instanceOf( final String token, final String match, final String value) { final int hashCode = computeHashCode(token, match, value); TokenMatch tm = WEAK_CACHE.get(hashCode); if(null == tm){ tm = new TokenMatch(token, match, value); WEAK_CACHE.put(hashCode, tm); } return tm; } private static int computeHashCode( final String token, final String match, final String value) { int result = 17; result = 37*result + token.hashCode(); result = 37*result + match.hashCode(); if(null != value){ result = 37*result + value.hashCode(); } return result; } // Attributes ---------------------------------------------------- private final String token; private final String match; private final String value; private final Pattern matcher; // Constructors ------------------------------------------------- private TokenMatch(final String token, final String match, final String value) { this.token = token; this.match = match; this.value = value; // (^|\s) or ($|\s) is neccessary to avoid matching fragments of words. matcher = Pattern.compile("(^|\\s)" + match + "($|\\s)", REG_EXP_OPTIONS); } // Public -------------------------------------------------------- /** * Get the match. * * @return the match. */ public String getMatch() { return match; } /** * Get the regular expression Matcher to use to find a sub-match. * * @param string * @return the match. */ public Matcher getMatcher(final String string) { return matcher.matcher(string); } /** * Get the token. * * @return the token. */ public String getToken() { return token; } /** * Get the Fast value. * * @return the value. may be null. */ public String getValue() { return value; } @Override public String toString() { return "token=\"" + token + "\"; match=\"" + match + "\"; value=" + (value == null ? "null" : "\"" + value + "\"") + "; matcher=" + matcher + ";"; } @Override public boolean equals(Object obj) { return obj instanceof TokenMatch && obj.hashCode() == hashCode(); } @Override public int hashCode() { return computeHashCode(token, match, value); } }