Android Open Source - RssReader Rss Atom Parser






From Project

Back to project page RssReader.

License

The source code is released under:

GNU General Public License

If you think the Android project RssReader listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * RssReader/*ww  w  .  ja v  a2 s  .  c o  m*/
 *
 * Copyright (c) 2013-2014 teejoe
 *
 *     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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 * Some parts of this software are based on "Sparse rss" under the MIT license (see
 * below). Please refers to the original project to identify which parts are under the
 * MIT license.
 *
 * Copyright (c) 2010-2012 Stefan Handschuh
 *
 *     Permission is hereby granted, free of charge, to any person obtaining a copy
 *     of this software and associated documentation files (the "Software"), to deal
 *     in the Software without restriction, including without limitation the rights
 *     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *     copies of the Software, and to permit persons to whom the Software is
 *     furnished to do so, subject to the following conditions:
 *
 *     The above copyright notice and this permission notice shall be included in
 *     all copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *     THE SOFTWARE.
 */

package org.m2x.rssreader.util;

import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.text.Html;
import android.text.TextUtils;

import org.m2x.rssreader.Constants;
import org.m2x.rssreader.MainApplication;
import org.m2x.rssreader.provider.FeedData;
import org.m2x.rssreader.provider.FeedData.EntryColumns;
import org.m2x.rssreader.provider.FeedData.FeedColumns;
import org.m2x.rssreader.provider.FeedData.FilterColumns;
import org.m2x.rssreader.service.FetcherService;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RssAtomParser extends DefaultHandler {

    private static final String AND_SHARP = "&#";
    private static final String HTML_TEXT = "text/html";
    private static final String HTML_TAG_REGEX = "<(.|\n)*?>";

    private static final String TAG_RSS = "rss";
    private static final String TAG_RDF = "rdf";
    private static final String TAG_FEED = "feed";
    private static final String TAG_ENTRY = "entry";
    private static final String TAG_ITEM = "item";
    private static final String TAG_UPDATED = "updated";
    private static final String TAG_TITLE = "title";
    private static final String TAG_LINK = "link";
    private static final String TAG_DESCRIPTION = "description";
    private static final String TAG_MEDIA_DESCRIPTION = "media:description";
    private static final String TAG_CONTENT = "content";
    private static final String TAG_MEDIA_CONTENT = "media:content";
    private static final String TAG_ENCODED_CONTENT = "encoded";
    private static final String TAG_SUMMARY = "summary";
    private static final String TAG_PUBDATE = "pubDate";
    private static final String TAG_PUBLISHED = "published";
    private static final String TAG_DATE = "date";
    private static final String TAG_LAST_BUILD_DATE = "lastBuildDate";
    private static final String TAG_ENCLOSURE = "enclosure";
    private static final String TAG_GUID = "guid";
    private static final String TAG_AUTHOR = "author";
    private static final String TAG_CREATOR = "creator";
    private static final String TAG_NAME = "name";
    private static final String TAG_COMMENTS = "comments";

    private static final String ATTRIBUTE_URL = "url";
    private static final String ATTRIBUTE_HREF = "href";
    private static final String ATTRIBUTE_TYPE = "type";
    private static final String ATTRIBUTE_LENGTH = "length";
    private static final String ATTRIBUTE_REL = "rel";

    private static final String[][] TIMEZONES_REPLACE = {
        {"MEST", "+0200"}, {"EST", "-0500"}, {"PST", "-0800"}};

    private static final DateFormat[] PUBDATE_DATE_FORMATS = {
        new SimpleDateFormat("d' 'MMM' 'yyyy' 'HH:mm:ss", Locale.CHINA),
            new SimpleDateFormat("d' 'MMM' 'yyyy' 'HH:mm:ss' 'Z", Locale.CHINA), 
            new SimpleDateFormat("d' 'MMM' 'yyyy' 'HH:mm:ss' 'z", Locale.CHINA)};

    private static final DateFormat[] UPDATE_DATE_FORMATS = {
        new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss", Locale.CHINA),
            new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ssZ", Locale.CHINA), 
            new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss.SSSz", Locale.CHINA),
            new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)};

    private final Date mRealLastUpdateDate;
    private long mNewRealLastUpdate;

    private final String mId;

    private boolean mEntryTagEntered = false;
    private boolean mTitleTagEntered = false;
    private boolean mUpdatedTagEntered = false;
    private boolean mLinkTagEntered = false;
    private boolean mDescriptionTagEntered = false;
    private boolean mPubDateTagEntered = false;
    private boolean mPublishedTagEntered = false;
    private boolean mDateTagEntered = false;
    private boolean mLastBuildDateTagEntered = false;
    private boolean mGuidTagEntered = false;
    private boolean mAuthorTagEntered = false;
    private boolean mCommentsTagEntered = false;
    
    private StringBuilder mTitle;
    private StringBuilder mDateStringBuilder;
    private String mFeedLink;
    private Date mEntryDate;
    private Date mEntryUpdateDate;
    private Date mPreviousEntryDate;
    private Date mPreviousEntryUpdateDate;
    private StringBuilder mEntryLink;
    private StringBuilder mDescription;
    private StringBuilder mEnclosure;
    private StringBuilder mComments;
    private final Uri mFeedEntriesUri;
    private int mNewCount = 0;
    private final String mFeedName;
    private String mFeedTitle;
    private String mFeedBaseUrl;
    private boolean mDone = false;
    private final Date mKeepDateBorder;
    private boolean mFetchImages = false;
    private boolean mCancelled = false;
    private long mNow = System.currentTimeMillis();
    private StringBuilder mGuid;
    private StringBuilder mAuthor;
    private StringBuilder mTmpAuthor;

    private final FeedFilters mFilters;

    private final ArrayList<ContentProviderOperation> mInserts = 
        new ArrayList<ContentProviderOperation>();
    private final ArrayList<ArrayList<String>> mEntriesImages = 
        new ArrayList<ArrayList<String>>();

    public RssAtomParser(Date realLastUpdateDate, final String id, String feedName, 
        String url, boolean retrieveFullText) {
      
        long keepTime = Long.parseLong(PrefUtils.getString(PrefUtils.KEEP_TIME, "4")) * 86400000l;
        long keepDateBorderTime = keepTime > 0 ? System.currentTimeMillis() - keepTime : 0;

        mKeepDateBorder = new Date(keepDateBorderTime);
        this.mRealLastUpdateDate = realLastUpdateDate;
        mNewRealLastUpdate = realLastUpdateDate.getTime();
        this.mId = id;
        this.mFeedName = feedName;
        mFeedEntriesUri = EntryColumns.ENTRIES_FOR_FEED_CONTENT_URI(id);

        mFilters = new FeedFilters(id);

        // Remove old stuffs
        final String where = EntryColumns.DATE + '<' + keepDateBorderTime + Constants.DB_AND 
            + EntryColumns.WHERE_NOT_FAVORITE;
        NetworkUtils.deleteFeedImagesCache(mFeedEntriesUri, where);
        MainApplication.getContext().getContentResolver().delete(mFeedEntriesUri, where, null);

        mFeedBaseUrl = NetworkUtils.getBaseUrl(url);
    }

    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) 
        throws SAXException {

        if (TAG_UPDATED.equals(localName)) {
            mUpdatedTagEntered = true;
            mDateStringBuilder = new StringBuilder();
        } else if (TAG_ENTRY.equals(localName) || TAG_ITEM.equals(localName)) {
            mEntryTagEntered = true;
            mDescription = null;
            mEntryLink = null;
            mComments = null;

            // Save the previous (if no date are found for this entry)
            mPreviousEntryDate = mEntryDate;
            mPreviousEntryUpdateDate = mEntryUpdateDate;
            mEntryDate = null;
            mEntryUpdateDate = null;

            // This is the retrieved feed mTitle
            if (mFeedTitle == null && mTitle != null && mTitle.length() > 0) {
                mFeedTitle = mTitle.toString();
            }
            mTitle = null;
        } else if (TAG_TITLE.equals(localName)) {
            if (mTitle == null) {
                mTitleTagEntered = true;
                mTitle = new StringBuilder();
            }
        } else if (TAG_LINK.equals(localName)) {
            if (mAuthorTagEntered) {
                return;
            }
            if (TAG_ENCLOSURE.equals(attributes.getValue("", ATTRIBUTE_REL))) {
                startEnclosure(attributes, attributes.getValue("", ATTRIBUTE_HREF));
            } else {
                // Get the link only if we don't have one or if its the good one (html)
                if (mEntryLink == null || HTML_TEXT.equals(attributes.getValue("", ATTRIBUTE_TYPE))) {
                    mEntryLink = new StringBuilder();

                    boolean foundLink = false;
                    String href = attributes.getValue("", ATTRIBUTE_HREF);
                    if (!TextUtils.isEmpty(href)) {
                        mEntryLink.append(href);
                        foundLink = true;
                        mLinkTagEntered = false;
                    } else {
                        mLinkTagEntered = true;
                    }

                    if (!foundLink) {
                        mLinkTagEntered = true;
                    }
                }
            }
        } else if ((TAG_DESCRIPTION.equals(localName) && !TAG_MEDIA_DESCRIPTION.equals(qName))
                || (TAG_CONTENT.equals(localName) && !TAG_MEDIA_CONTENT.equals(qName))) {
            mDescriptionTagEntered = true;
            mDescription = new StringBuilder();
        } else if (TAG_SUMMARY.equals(localName)) {
            if (mDescription == null) {
                mDescriptionTagEntered = true;
                mDescription = new StringBuilder();
            }
        } else if (TAG_PUBDATE.equals(localName)) {
            mPubDateTagEntered = true;
            mDateStringBuilder = new StringBuilder();
        } else if (TAG_PUBLISHED.equals(localName)) {
            mPublishedTagEntered = true;
            mDateStringBuilder = new StringBuilder();
        } else if (TAG_DATE.equals(localName)) {
            mDateTagEntered = true;
            mDateStringBuilder = new StringBuilder();
        } else if (TAG_LAST_BUILD_DATE.equals(localName)) {
            mLastBuildDateTagEntered = true;
            mDateStringBuilder = new StringBuilder();
        } else if (TAG_ENCODED_CONTENT.equals(localName)) {
            mDescriptionTagEntered = true;
            mDescription = new StringBuilder();
        } else if (TAG_ENCLOSURE.equals(localName)) {
            startEnclosure(attributes, attributes.getValue("", ATTRIBUTE_URL));
        } else if (TAG_COMMENTS.equals(localName)) {
          mCommentsTagEntered = true;
          mComments = new StringBuilder();
        } else if (TAG_GUID.equals(localName)) {
            mGuidTagEntered = true;
            mGuid = new StringBuilder();
        } else if (TAG_NAME.equals(localName) || TAG_AUTHOR.equals(localName) 
            || TAG_CREATOR.equals(localName)) {
            mAuthorTagEntered = true;
            if (mTmpAuthor == null) {
                mTmpAuthor = new StringBuilder();
            }
        }
    }

    private void startEnclosure(Attributes attributes, String url) {
        if (mEnclosure == null && url != null) { // fetch the first mEnclosure only
            mEnclosure = new StringBuilder(url);
            mEnclosure.append(Constants.ENCLOSURE_SEPARATOR);

            String value = attributes.getValue("", ATTRIBUTE_TYPE);

            if (value != null) {
                mEnclosure.append(value);
            }
            mEnclosure.append(Constants.ENCLOSURE_SEPARATOR);
            value = attributes.getValue("", ATTRIBUTE_LENGTH);
            if (value != null) {
                mEnclosure.append(value);
            }
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (mTitleTagEntered) {
            mTitle.append(ch, start, length);
        } else if (mLinkTagEntered) {
            mEntryLink.append(ch, start, length);
        } else if (mCommentsTagEntered) {
          mComments.append(ch, start, length);
        } else if (mDescriptionTagEntered) {
            mDescription.append(ch, start, length);
        } else if (mUpdatedTagEntered || mPubDateTagEntered || mPublishedTagEntered 
            || mDateTagEntered || mLastBuildDateTagEntered) {
            mDateStringBuilder.append(ch, start, length);
        } else if (mGuidTagEntered) {
            mGuid.append(ch, start, length);
        } else if (mAuthorTagEntered) {
            mTmpAuthor.append(ch, start, length);
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {

        if (TAG_TITLE.equals(localName)) {
            mTitleTagEntered = false;
        } else if ((TAG_DESCRIPTION.equals(localName) && !TAG_MEDIA_DESCRIPTION.equals(qName)) 
            || TAG_SUMMARY.equals(localName) || (TAG_CONTENT.equals(localName) 
            && !TAG_MEDIA_CONTENT.equals(qName)) || TAG_ENCODED_CONTENT.equals(localName)) {
            mDescriptionTagEntered = false;
        } else if (TAG_LINK.equals(localName)) {
            mLinkTagEntered = false;

            if (mFeedLink == null && !mEntryTagEntered && TAG_LINK.equals(qName)) { // Skip <atom10:link> tags
                mFeedLink = mEntryLink.toString();
            }
        } else if (TAG_COMMENTS.equals(localName)) {
          mCommentsTagEntered = false;
        } else if (TAG_UPDATED.equals(localName)) {
            mEntryUpdateDate = parseUpdateDate(mDateStringBuilder.toString());
            mUpdatedTagEntered = false;
        } else if (TAG_PUBDATE.equals(localName)) {
            mEntryDate = parsePubdateDate(mDateStringBuilder.toString());
            mPubDateTagEntered = false;
        } else if (TAG_PUBLISHED.equals(localName)) {
            mEntryDate = parsePubdateDate(mDateStringBuilder.toString());
            mPublishedTagEntered = false;
        } else if (TAG_LAST_BUILD_DATE.equals(localName)) {
            mEntryDate = parsePubdateDate(mDateStringBuilder.toString());
            mLastBuildDateTagEntered = false;
        } else if (TAG_DATE.equals(localName)) {
            mEntryDate = parseUpdateDate(mDateStringBuilder.toString());
            mDateTagEntered = false;
        } else if (TAG_ENTRY.equals(localName) || TAG_ITEM.equals(localName)) {
            mEntryTagEntered = false;

            boolean updateOnly = false;
            // Old mEntryDate but recent update date => we need to not insert it!
            if (mEntryUpdateDate != null && mEntryDate != null && (mEntryDate.before(
                mRealLastUpdateDate) || mEntryDate.before(mKeepDateBorder))) {
                updateOnly = true;
                if (mEntryUpdateDate.after(mEntryDate)) {
                    mEntryDate = mEntryUpdateDate;
                }
            } else if (mEntryDate == null && mEntryUpdateDate != null) { // only one updateDate, copy it into mEntryDate
                mEntryDate = mEntryUpdateDate;
            } else if (mEntryDate == null && mEntryUpdateDate == null) { // nothing, we need to retrieve the previous date
                mEntryDate = mPreviousEntryDate;
                mEntryUpdateDate = mPreviousEntryUpdateDate;
            }

            if (mTitle != null && (mEntryDate == null || (mEntryDate.after(mRealLastUpdateDate)
                && mEntryDate.after(mKeepDateBorder)))) {
                ContentValues values = new ContentValues();

                if (mEntryDate != null && mEntryDate.getTime() > mNewRealLastUpdate) {
                    mNewRealLastUpdate = mEntryDate.getTime();
                }

                String improvedTitle = unescapeTitle(mTitle.toString().trim());
                values.put(EntryColumns.TITLE, improvedTitle);

                String improvedContent = null;
                if (mDescription != null) {
                    // Improve the mDescription
                    improvedContent = HtmlUtils.improveHtmlContent(mDescription.toString(), 
                        mFeedBaseUrl);
                    if (mFetchImages) {
                        mEntriesImages.add(HtmlUtils.getImageURLs(improvedContent));
                    }
                    if (improvedContent != null) {
                        values.put(EntryColumns.ABSTRACT, improvedContent);
                    }
                }

                // Try to find if the entry is not filtered and need to be processed
                if (!mFilters.isEntryFiltered(improvedTitle, improvedContent)) {

                    if (mAuthor != null) {
                        values.put(EntryColumns.AUTHOR, mAuthor.toString());
                    }
                    
                    if (mComments != null) {
                      values.put(EntryColumns.COMMENTS, mComments.toString());
                    }

                    String enclosureString = null;
                    StringBuilder existanceStringBuilder = new StringBuilder(EntryColumns.LINK)
                        .append(Constants.DB_ARG);

                    if (mEnclosure != null && mEnclosure.length() > 0) {
                        enclosureString = mEnclosure.toString();
                        values.put(EntryColumns.ENCLOSURE, enclosureString);
                        existanceStringBuilder.append(Constants.DB_AND).append(EntryColumns
                            .ENCLOSURE).append(Constants.DB_ARG);
                    }

                    String guidString = null;

                    if (mGuid != null && mGuid.length() > 0) {
                        guidString = mGuid.toString();
                        values.put(EntryColumns.GUID, guidString);
                        existanceStringBuilder.append(Constants.DB_AND).append(EntryColumns.GUID)
                            .append(Constants.DB_ARG);
                    }

                    String entryLinkString = ""; // don't set this to null as we need *some* value

                    if (mEntryLink != null && mEntryLink.length() > 0) {
                        entryLinkString = mEntryLink.toString().trim();
                        if (mFeedBaseUrl != null && !entryLinkString.startsWith(Constants
                            .HTTP_SCHEME) && !entryLinkString.startsWith(Constants.HTTPS_SCHEME)) {
                            entryLinkString = mFeedBaseUrl + (entryLinkString.startsWith("/") 
                                ? entryLinkString : "/" + entryLinkString);
                        }
                    }

                    String[] existanceValues = enclosureString != null ? (guidString != null ? new String[]{entryLinkString, enclosureString,
                            guidString} : new String[]{entryLinkString, enclosureString}) : (guidString != null ? new String[]{entryLinkString,
                            guidString} : new String[]{entryLinkString});

                    // First, try to update the feed
                    ContentResolver cr = MainApplication.getContext().getContentResolver();
                    boolean isUpdated = ((entryLinkString.length() != 0) || guidString != null)
                            && cr.update(mFeedEntriesUri, values, existanceStringBuilder.toString(), existanceValues) != 0;

                    // Insert it only if necessary
                    if (!isUpdated && !updateOnly) {
                        // We put the date only for new entry (no need to change the past, you may already read it)
                        if (mEntryDate != null) {
                            values.put(EntryColumns.DATE, mEntryDate.getTime());
                        } else {
                            values.put(EntryColumns.DATE, mNow--); // -1 to keep the good entries order
                        }

                        values.put(EntryColumns.LINK, entryLinkString);

                        // We cannot update, we need to insert it
                        mInserts.add(ContentProviderOperation.newInsert(mFeedEntriesUri)
                            .withValues(values).build());

                        mNewCount++;
                    }

                    // No date, but we managed to update an entry => we already parsed the following entries and don't need to continue
                    if (isUpdated && mEntryDate == null) {
                        cancel();
                    }
                }
            } else {
                cancel();
            }
            mDescription = null;
            mTitle = null;
            mEnclosure = null;
            mGuid = null;
            mAuthor = null;
            mComments = null;
        } else if (TAG_RSS.equals(localName) || TAG_RDF.equals(localName) 
            || TAG_FEED.equals(localName)) {
            mDone = true;
        } else if (TAG_GUID.equals(localName)) {
            mGuidTagEntered = false;
        } else if (TAG_NAME.equals(localName) || TAG_AUTHOR.equals(localName) 
            || TAG_CREATOR.equals(localName)) {
            mAuthorTagEntered = false;

            if (mTmpAuthor != null && mTmpAuthor.indexOf("@") == -1) { // no email
                if (mAuthor == null) {
                    mAuthor = new StringBuilder(mTmpAuthor);
                } else { // this indicates multiple authors
                    boolean found = false;
                    for (String previousAuthor : mAuthor.toString().split(",")) {
                        if (previousAuthor.equals(mTmpAuthor.toString())) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        mAuthor.append(Constants.COMMA_SPACE);
                        mAuthor.append(mTmpAuthor);
                    }
                }
            }

            mTmpAuthor = null;
        }
    }

    public String getFeedLink() {
        return mFeedLink;
    }

    public int getNewCount() {
        return mNewCount;
    }

    public boolean isDone() {
        return mDone;
    }

    public boolean isCancelled() {
        return mCancelled;
    }

    private void cancel() throws SAXException {
        if (!mCancelled) {
            mCancelled = true;
            mDone = true;
            endDocument();

            throw new SAXException("Finished");
        }
    }

    public void setFetchImages(boolean fetchImages) {
        this.mFetchImages = fetchImages;
    }

    private Date parseUpdateDate(String dateStr) {
        dateStr = improveDateString(dateStr);
        return parseUpdateDate(dateStr, true);
    }

    private Date parseUpdateDate(String dateStr, boolean tryAllFormat) {
        for (DateFormat format : UPDATE_DATE_FORMATS) {
            try {
                Date result = format.parse(dateStr);
                return (result.getTime() > mNow ? new Date(mNow) : result);
            } catch (ParseException ignored) {
            } // just do nothing
        }

        if (tryAllFormat)
            return parsePubdateDate(dateStr, false);
        else
            return null;
    }

    private Date parsePubdateDate(String dateStr) {
        dateStr = improveDateString(dateStr);
        return parsePubdateDate(dateStr, true);
    }

    private Date parsePubdateDate(String dateStr, boolean tryAllFormat) {
        for (DateFormat format : PUBDATE_DATE_FORMATS) {
            try {
                Date result = format.parse(dateStr);
                return (result.getTime() > mNow ? new Date(mNow) : result);
            } catch (ParseException ignored) {
            } // just do nothing
        }

        if (tryAllFormat)
            return parseUpdateDate(dateStr, false);
        else
            return null;
    }

    private String improveDateString(String dateStr) {
        // We remove the first part if necessary (the day display)
        int coma = dateStr.indexOf(", ");
        if (coma != -1) {
            dateStr = dateStr.substring(coma + 2);
        }

        dateStr = dateStr.replace("T", " ").replace("Z", "").replaceAll("  ", " ").trim(); // fix useless char

        // Replace bad timezones
        for (String[] timezoneReplace : TIMEZONES_REPLACE) {
            dateStr = dateStr.replace(timezoneReplace[0], timezoneReplace[1]);
        }

        return dateStr;
    }

    private static String unescapeTitle(String title) {
        String result = title.replace(Constants.AMP_SG, Constants.AMP)
            .replaceAll(HTML_TAG_REGEX, "").replace(Constants.HTML_LT, Constants.LT)
                .replace(Constants.HTML_GT, Constants.GT)
                .replace(Constants.HTML_QUOT, Constants.QUOT)
                .replace(Constants.HTML_APOSTROPHE, Constants.APOSTROPHE);

        if (result.contains(AND_SHARP)) {
            return Html.fromHtml(result, null, null).toString();
        } else {
            return result;
        }
    }

    @Override
    public void warning(SAXParseException e) throws SAXException {
        // ignore warnings
    }

    @Override
    public void error(SAXParseException e) throws SAXException {
        // ignore small errors
    }

    @Override
    public void endDocument() throws SAXException {
        ContentResolver cr = MainApplication.getContext().getContentResolver();

        try {
            if (!mInserts.isEmpty()) {
                ContentProviderResult[] results = cr.applyBatch(FeedData.AUTHORITY, mInserts);

                if (mFetchImages) {
                    for (int i = 0; i < results.length; ++i) {
                        ArrayList<String> images = mEntriesImages.get(i);
                        if (images != null) {
                            FetcherService.addImagesToDownload(results[i].uri.getLastPathSegment(), 
                                images);
                        }
                    }
                }
            }
        } catch (Exception ignored) {
        }

        ContentValues values = new ContentValues();
        if (mFeedName == null && mFeedTitle != null) {
            values.put(FeedColumns.NAME, mFeedTitle.trim());
        }
        values.putNull(FeedColumns.ERROR);
        values.put(FeedColumns.LAST_UPDATE, System.currentTimeMillis() - 3000); // by precaution to not miss some feeds
        values.put(FeedData.FeedColumns.REAL_LAST_UPDATE, mNewRealLastUpdate);
        cr.update(FeedColumns.CONTENT_URI(mId), values, null, null);

        super.endDocument();
    }

    private class FeedFilters {

        private class Rule {
            public String filterText;
            public boolean isRegex;
            public boolean isAppliedToTitle;
            public boolean isAcceptRule;
        }

        private final ArrayList<Rule> mFilters = new ArrayList<Rule>();
    
        public FeedFilters(String feedId) {
            ContentResolver cr = MainApplication.getContext().getContentResolver();
            Cursor c = cr.query(FilterColumns.FILTERS_FOR_FEED_CONTENT_URI(feedId), 
                new String[]{FilterColumns.FILTER_TEXT, FilterColumns.IS_REGEX,
                    FilterColumns.IS_APPLIED_TO_TITLE, FilterColumns.IS_ACCEPT_RULE}, 
                    null, null, null);
            while (c.moveToNext()) {
                Rule r = new Rule();
                r.filterText = c.getString(0);
                r.isRegex = c.getInt(1) == 1;
                r.isAppliedToTitle = c.getInt(2) == 1;
                r.isAcceptRule = c.getInt(3) == 1;
                mFilters.add(r);
            }
            c.close();

        }

        public boolean isEntryFiltered(String title, String content) {

            boolean isFiltered = false;

            for (Rule r : mFilters) {

                boolean isMatch = false;
                if (r.isRegex) {
                    Pattern p = Pattern.compile(r.filterText);
                    if (r.isAppliedToTitle) {
                        Matcher m = p.matcher(title);
                        isMatch = m.find();
                    } else if (content != null) {
                        Matcher m = p.matcher(content);
                        isMatch = m.find();
                    }
                } else if ((r.isAppliedToTitle && title.contains(r.filterText)) 
                    || (!r.isAppliedToTitle && content != null && content.contains(r.filterText))) {
                    isMatch = true;
                }

                if (r.isAcceptRule) {
                    if (isMatch) {
                        // accept rules override reject rules, the rest of the rules must be ignored
                        isFiltered = false;
                        break;
                    }
                } else if (isMatch) {
                    isFiltered = true;
                    // no break, there might be an accept rule later
                }
            }

            return isFiltered;
        }
    }
}




Java Source Code List

org.m2x.rssreader.Constants.java
org.m2x.rssreader.MainApplication.java
org.m2x.rssreader.activity.AddChannelActivity.java
org.m2x.rssreader.activity.EditChannelActivity.java
org.m2x.rssreader.activity.MainActivity.java
org.m2x.rssreader.activity.PreferenceActivity.java
org.m2x.rssreader.activity.RssArticleActivity.java
org.m2x.rssreader.activity.RssItemListActivity.java
org.m2x.rssreader.adapter.ChannelListAdapter.java
org.m2x.rssreader.adapter.RssArticlePagerAdapter.java
org.m2x.rssreader.adapter.RssItemListAdapter.java
org.m2x.rssreader.fragment.ChannelListFragment.java
org.m2x.rssreader.fragment.RssArticleFragment.java
org.m2x.rssreader.fragment.RssItemListFragment.java
org.m2x.rssreader.provider.DatabaseHelper.java
org.m2x.rssreader.provider.FeedDataProvider.java
org.m2x.rssreader.provider.FeedData.java
org.m2x.rssreader.service.FetcherService.java
org.m2x.rssreader.service.RefreshService.java
org.m2x.rssreader.util.HtmlUtils.java
org.m2x.rssreader.util.NetworkUtils.java
org.m2x.rssreader.util.OPML.java
org.m2x.rssreader.util.PrefUtils.java
org.m2x.rssreader.util.RssAtomParser.java
org.m2x.rssreader.util.StringUtils.java
org.m2x.rssreader.util.UiUtils.java
org.m2x.rssreader.view.RssArticleView.java