All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.lucene.util.ReaderCloneFactory Maven / Gradle / Ivy

Go to download

The Combo Analyzer plugin for ElasticSearch provides with a new analyzer type that combines the output of multiple analyzers into one.

The newest version!
/*
 * 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.util;

import org.apache.lucene.analysis.ReusableStringReaderCloner;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.ESLoggerFactory;

import javax.io.StringReaderCloner;
import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.FilterReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.lang.ref.WeakReference;
import java.util.WeakHashMap;

/**
 * Duplicates {@link Reader}s in order to feed multiple consumers.
 *
 * This class registers multiple implementations, and tries to resolve which one to use,
 * looking at the actual class of the Reader to clone, and matching with the most bond
 * handled classes for each {@link ReaderCloner} implementation.
 *
 * By default, a few {@link Reader} implementations are handled, including the
 * most used inside Lucene ({@link StringReader}), and a default, fallback implementation
 * that merely reads all the available content, and creates a String out of it.
 *
 * Therefore you should understand the importance of having a proper implementation for
 * any optimizable {@link Reader}. For instance, {@link javax.io.StringReaderCloner} gains access
 * to the underlying String in order to avoid copies. A generic BufferedReader
 */
public class ReaderCloneFactory {

    private static final ESLogger logger = ESLoggerFactory.getLogger(ReaderCloneFactory.class.getSimpleName());

    /**
     * Interface for a utility class, able to unwrap a {@link java.io.Reader}
     * inside another {@link Reader}.
     * @param  The base class handled.
     */
    public static interface ReaderUnwrapper {
        /**
         * Unwraps a {@link Reader} from another, simplifying an eventual chain.
         */
        public Reader unwrap(T originalReader) throws IllegalArgumentException;
    }

    /**
     * Interface for a utility class, able to clone the content of a {@link java.io.Reader},
     * possibly in an optimized way (such as gaining access to a package private field,
     * or through reflection using {@link java.lang.reflect.Field#setAccessible(boolean)}).
     * @param  The base class handled.
     */
    public static interface ReaderCloner {
        /**
         * Initialize or reinitialize the cloner with the given reader.
         * The implementing class should have a default no arguments constructor.
         *
         * 

Remark: The given Reader is now controlled by this ReaderCloner, it may * be closed during a call to this method, or it may be returned * at first call to {@link #giveAClone()}. * @see #giveAClone() */ public void init(T originalReader) throws IOException; /** * Returns a new {@link Reader}. *

Remark: The returned Reader should be closed. * The original Reader, if not consumed by the {@link #init(java.io.Reader)} method, * should be returned at first call. Therefore it is important to * call this method at least once, or to be prepared to face possible * exceptions when closing the original Reader. */ public Reader giveAClone(); } /** Map storing the mapping between a handled class and a handling class, for {@link ReaderCloner}s */ private static final WeakHashMap, WeakReference>> typeMap = new WeakHashMap, WeakReference>>(); /** Map storing the mapping between a handled class and a handling instance, for {@link ReaderUnwrapper}s */ private static final WeakHashMap, ReaderUnwrapper> unwrapperTypeMap = new WeakHashMap, ReaderUnwrapper>(); /** * Add the association between a (handled) class and its handling {@link ReaderCloner}. * @param handledClass The base class that is handled by clonerImplClass. * Using this parameter, you can further restrict the usage of a more generic cloner. * @param clonerImplClass The class of the associated cloner. * @param The base handled class of the ReaderCloner. * @return The previously associated ReaderCloner for the handledClass. */ public static WeakReference> bindCloner( Class handledClass, Class> clonerImplClass) { return typeMap.put(handledClass, new WeakReference>(clonerImplClass)); } /** * Add the association between a (handled) class and its handling {@link ReaderUnwrapper} instance. * @param handledClass The base class that is handled by clonerImplClass. * Using this parameter, you can further restrict the usage of a more generic cloner. * @param unwrapperImpl The instance of the associated unwrapper. * @param The base handled class of the ReaderUnwrapper. * @return The previously associated ReaderUnwrapper instance for the handledClass. */ public static ReaderUnwrapper bindUnwrapper( Class handledClass, ReaderUnwrapper unwrapperImpl) { return unwrapperTypeMap.put(handledClass, unwrapperImpl); } /** * Static initialization registering default associations */ static { // General purpose Reader handling bindCloner(Reader.class, ReaderClonerDefaultImpl.class); bindUnwrapper(BufferedReader.class, new BufferedReaderUnwrapper()); bindUnwrapper(FilterReader.class, new FilterReaderUnwrapper()); // Often used Java Readers bindCloner(StringReader.class, StringReaderCloner.class); // very, very used inside Lucene bindCloner(CharArrayReader.class, CharArrayReaderCloner.class); // Lucene specific handling ReusableStringReaderCloner.registerCloner(); } /** * (Expert) Returns the ReaderUnwrapper associated with the exact given class. * @param forClass The handled class bond to the ReaderUnwrapper to return. * @param The base handled class of the ReaderUnwrapper to return. * @return The bond ReaderUnwrapper, or null. */ public static ReaderUnwrapper getUnwrapperStrict(Class forClass) { return unwrapperTypeMap.get(forClass); } /** * Returns the ReaderCloner associated with the exact given class. * @param forClass The handled class bond to the ReaderCloner to return. * @param The base handled class of the ReaderCloner to return. * @return The bond ReaderCloner, or null. */ public static ReaderCloner getClonerStrict(Class forClass) { WeakReference> refClonerClass = typeMap.get(forClass); if (refClonerClass != null) { Class clazz = refClonerClass.get(); if (clazz != null) { try { ReaderCloner cloner = (ReaderCloner) clazz.newInstance(); return cloner; } catch (Throwable ignored) { } } } return null; } /** * (Advanced) Returns an initialized ReaderCloner, associated with the exact class of the given Reader. * If the initialization fails, this function returns null. * @param forReader The handled class bond to the ReaderCloner to return. * @param The base handled class of the ReaderCloner to return. * @return The bond, initialized ReaderCloner, or null. */ public static ReaderCloner getClonerStrict(T forReader) { ReaderCloner rtn = ReaderCloneFactory.getClonerStrict((Class) forReader.getClass()); if (rtn != null) { try { rtn.init(forReader); } catch (Throwable fail) { return null; } } return rtn; } /** * (Advanced) Returns an initialized ReaderCloner, associated with the given base class, for the given Reader. * If the initialization fails, this function returns null. * * The function first tries to match the exact class of forReader, and initialize the ReaderCloner. * If no ReaderCloner or (tested second) ReaderUnwrapper matches, the resolution continues with the super class, * until the baseClass is reached, and tested. * * If this process is not successful, null is returned. * * @param baseClass The baseClass, above which the resolution will not try to continue with the super class. * @param forClass The class to start with, should be the class of forReader (but the latter can be null, hence this parameter) * @param forReader The Reader instance to return and initialize a ReaderCloner for. Can be null. * @param The base handled class of the ReaderCloner to return * @param The class of the given Reader to handle * @return An initialized ReaderCloner suitable for the givenReader, or null. */ public static ReaderCloner getCloner(Class baseClass, Class forClass, final S forReader) { final Class originalForClass = forClass; // Loop through each super class while (forClass != null) { // Try first a matching cloner ReaderCloner cloner = ReaderCloneFactory.getClonerStrict(forClass); if (cloner != null) { if (forReader != null) { try { cloner.init(forReader); } catch (Exception e) { logger.debug("Error while initializing [{}]", e, cloner.getClass().getCanonicalName()); cloner = null; } } if (cloner != null) return cloner; } // Try then a matching unwrapper, for better suitability of the used cloner if (forReader != null) { ReaderUnwrapper unwrapper = ReaderCloneFactory.getUnwrapperStrict(forClass); if (unwrapper != null) try { // Recursive resolution Reader unwrapped = unwrapper.unwrap(forReader); if (unwrapped != null) return (ReaderCloner)ReaderCloneFactory.getCloner(Reader.class, (Class)unwrapped.getClass(), unwrapped); } catch (Throwable ignore) { // in case of errors, simply continue the began process and forget about this failed attempt logger.debug("Error while cloning [{}] with [{}]", ignore, unwrapper.getClass().getCanonicalName(), cloner.getClass().getCanonicalName()); } } // Continue resolution with super class... Class clazz = forClass.getSuperclass(); // ... checking ancestry with the given base class if (baseClass.isAssignableFrom(clazz)) forClass = clazz; else forClass = null; } if (forReader != null) logger.debug("Could not find a suitable ReaderCloner for [{}]", forReader.getClass().getCanonicalName()); else logger.debug("Could not find a suitable ReaderCloner for class [{}]", originalForClass.getCanonicalName()); return null; } /** * Returns a ReaderCloner suitable for handling general Ss instances (inheriting T, itself * inheriting {@link java.io.Reader}). * * Resolution starts on forClass (S), and does not go further than baseClass. * * Not all optimizations can be ran, like unwrapping and failing initialization fallback. * However, for standard cases, when performance is really critical, * using this function can reduce a possible resolution overhead * because ReaderCloner are reusable. * * @param baseClass The baseClass, above which the resolution will not try to continue with the super class. * @param forClass The class to start with, should be the class of forReader (but the latter can be null, hence this parameter) * @param The base handled class of the ReaderCloner to return * @param The class of the given Reader to handle * @return An uninitialized ReaderCloner suitable for any T Readers, or null. */ public static ReaderCloner getCloner(Class baseClass, Class forClass) { return ReaderCloneFactory.getCloner(baseClass, forClass, null); } /** * Returns a ReaderCloner suitable for handling general Ss instances (inheriting {@link java.io.Reader}). * * Calls ReaderCloneFactory.getCloner(Reader.class, forClass, (S)null). * * Not all optimizations can be ran, like unwrapping and failing initialization fallback. * However, for standard cases, when performance is really critical, * using this function can reduce a possible resolution overhead * because ReaderCloner are reusable. * * @param forClass The class to start with, should be the class of forReader (but the latter can be null, hence this parameter) * @param The class of the given Reader to handle * @return An uninitialized ReaderCloner suitable for any S, or null. */ public static ReaderCloner getCloner(Class forClass) { return ReaderCloneFactory.getCloner(Reader.class, forClass, null); } /** * Returns an initialized ReaderCloner, for the given Reader. * * Calls ReaderCloneFactory.getCloner(Reader.class, (Class)forReader.getClass(), forReader). * If forReader is null, works as {@link ReaderCloneFactory#getGenericCloner()}. * * @param forReader The Reader instance to return and initialize a ReaderCloner for. Can be null. * @param The class of the given Reader * @return An initialized ReaderCloner suitable for given Reader, or null. */ public static ReaderCloner getCloner(S forReader) { if (forReader != null) return ReaderCloneFactory.getCloner(Reader.class, (Class)forReader.getClass(), forReader); else return ReaderCloneFactory.getGenericCloner(); } /** * Returns a {@link ReaderCloner} suitable for any {@link java.io.Reader} instance. */ public static ReaderCloner getGenericCloner() { return ReaderCloneFactory.getCloner(Reader.class, Reader.class, null); } }