Package com.salas.bb.core

Source Code of com.salas.bb.core.HighlightsCalculator$Calculator

// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software Foundation;
// either version 2 of the License, or (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: HighlightsCalculator.java,v 1.38 2007/11/07 17:16:48 spyromus Exp $
//

package com.salas.bb.core;

import com.salas.bb.domain.IArticle;
import com.salas.bb.domain.IFeed;
import com.salas.bb.domain.utils.TextRange;
import com.salas.bb.utils.Constants;
import com.salas.bb.utils.StringUtils;
import com.salas.bb.utils.concurrency.CachingCalculator;
import com.salas.bb.utils.swinghtml.TextProcessor;

import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Calculator of highlighted areas for articles. Responsible for providing always actual
* information about what should be highlighted in articles.
*/
public class HighlightsCalculator
{

    private Map rangesCache = Collections.synchronizedMap(new WeakHashMap());
    private Map countsCache = Collections.synchronizedMap(new WeakHashMap());

    private String currentKeywords;

    private Pattern pattern = null;
    private final Object patternLock = new Object();

    private final Calculator calculator;

    /**
     * Creates highlights calculator.
     */
    public HighlightsCalculator()
    {
        calculator = new Calculator(1);
    }

    /**
     * Returns list of highlighted ranges in article.
     *
     * @param text text of article.
     *
     * @return list of regions.
     */
    public TextRange[] getHighlights(String text)
    {
        if (text == null) return Constants.EMPTY_TEXT_RANGE_LIST;

        // TODO !!!! read/write locking necessary !!!!

        CustomCacheKey key = CustomCacheKey.key(text);
        TextRange[] tranges = (TextRange[])rangesCache.get(key);

        if (tranges == null)
        {
            tranges = TextRange.findRanges(text, getKeywordsPattern());
            if (tranges != null) rangesCache.put(key, tranges);
        }

        return tranges;
    }

    /**
     * Returns number of highlights in text. Uses simplified scheme.
     *
     * @param text  text to process.
     *
     * @return number of highlights.
     */
    public int getHighlightsCount(String text)
    {
        if (text == null) return 0;
       
        int cnt = 0;

        String plainText = TextProcessor.toPlainText(text);
        Integer count = getCountFromCache(plainText);

        if (count == null)
        {
            Pattern pat = getKeywordsPattern();

            if (pat != null)
            {
                Matcher mat = pat.matcher(plainText);
                int st = 0;
                while (mat.find(st))
                {
                    cnt++;
                    st = mat.end(2);
                }
            }

            countsCache.put(CustomCacheKey.key(plainText), cnt);
        } else
        {
            cnt = count.intValue();
        }

        return cnt;
    }

    private Integer getCountFromCache(String text)
    {
        return (Integer)countsCache.get(CustomCacheKey.key(text));
    }

    /**
     * Returns regex pattern with keywords.
     *
     * @return pattern.
     */
    private Pattern getKeywordsPattern()
    {
        Pattern pat;

        synchronized (patternLock)
        {
            pat = pattern;
        }

        return pat;
    }

    /**
     * Called to notify that keywords has changed.
     *
     * @param newKeywords new keywords.
     */
    public void keywordsChanged(String newKeywords)
    {
        boolean invalidate = false;

        synchronized (patternLock)
        {
            if ((currentKeywords == null && newKeywords != null) ||
                (currentKeywords != null && !currentKeywords.equals(newKeywords)))
            {
                currentKeywords = newKeywords;

                String patternRegex = StringUtils.keywordsToPattern(currentKeywords);
                pattern = patternRegex == null ? null
                    : Pattern.compile(patternRegex, Pattern.CASE_INSENSITIVE);

                invalidate = true;
            }
        }

        if (invalidate) invalidateAll();
    }

    /**
     * Returns number of highlighted regions in articles of this feed.
     *
     * @param feed feed to check.
     *
     * @return number of highlights.
     */
    public int getHighlightsCount(IFeed feed)
    {
        return ((CompositeValue)calculator.getValue(feed)).highlights;
    }

    /**
     * Returns number of highlighted articles.
     *
     * @param feed   feed to examine.
     *
     * @return number of articles with highlights.
     */
    public int getHighlightedArticles(IFeed feed)
    {
        return ((CompositeValue)calculator.getValue(feed)).articlesWithHighlights;
    }

    /**
     * Invalidates all records stored in cache.
     */
    public void invalidateAll()
    {
        synchronized (this)
        {
            rangesCache.clear();
            countsCache.clear();
        }

        calculator.invalidateAll();
    }

    /**
     * Invalidates the feed stored in cache.
     *
     * @param feed feed to invalidate.
     */
    public void invalidateFeed(IFeed feed)
    {
        calculator.invalidateKey(feed);
    }

    /**
     * Removes the feed from cache.
     *
     * @param feed feed.
     */
    public void feedRemoved(IFeed feed)
    {
        calculator.removeKey(feed);
    }

    /**
     * Returns number of highlights for given article.
     *
     * @param aArticle article.
     *
     * @return number of highlights.
     */
    public int getHighlightsCount(IArticle aArticle)
    {
        return getHighlightsCount(aArticle.getPlainText());
    }

    /**
     * Simple composite value holder.
     */
    private static class CompositeValue
    {
        private volatile int highlights;
        private volatile int articlesWithHighlights;
    }

    private static class CustomCacheKey
    {
        private volatile int length;
        private volatile int hash;

        private CustomCacheKey(int length, int hash)
        {
            this.length = length;
            this.hash = hash;
        }

        public static CustomCacheKey key(String s)
        {
            return s == null ? new CustomCacheKey(0, 0) : new CustomCacheKey(s.length(), s.hashCode());
        }

        @Override
        public int hashCode()
        {
            return hash;
        }

        @Override
        public boolean equals(Object o)
        {
            boolean res = false;
            if (o instanceof CustomCacheKey)
            {
                CustomCacheKey other = (CustomCacheKey)o;
                res = length == other.length && hash == other.hash;
            }
            return res;
        }
    }

    /**
     * Calculator of highligts counts.
     */
    private class Calculator extends CachingCalculator
    {
        public Calculator(int threads)
        {
            super(threads);
            startThreads();
        }

        protected String getThreadsBaseName()
        {
            return "HC";
        }

        protected Object calculate(Object key)
        {
            final IFeed feed = (IFeed)key;

            CompositeValue value = new CompositeValue();
            int highlights = 0;
            int articlesWithHighlights = 0;

            IArticle[] articles = feed.getArticles();
            for (int i = 0; i < articles.length; i++)
            {
                IArticle article = articles[i];
                int highlightsInArticle = getHighlightsCount(article.getPlainText());

                highlights += highlightsInArticle;
                if (highlightsInArticle > 0) articlesWithHighlights++;
            }

            value.articlesWithHighlights = articlesWithHighlights;
            value.highlights = highlights;

            return value;
        }
    }
}
TOP

Related Classes of com.salas.bb.core.HighlightsCalculator$Calculator

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.