/* * @copyright 2011 Philip Warner * @license GNU General Public License * * This file is part of Book Catalogue. * * Book Catalogue 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. * * Book Catalogue 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 Book Catalogue. If not, see <http://www.gnu.org/licenses/>. */ package com.eleybourn.bookcatalogue; import java.io.Serializable; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.os.Parcel; import android.os.Parcelable; import com.eleybourn.bookcatalogue.utils.Utils; /** * Class to hold book-related series data. Used in lists and import/export. * * @author Philip Warner */ public class Series implements Serializable, Utils.ItemWithIdFixup { private static final long serialVersionUID = 1L; public long id; public String name; public String num; private java.util.regex.Pattern mPattern = java.util.regex.Pattern.compile("^(.*)\\s*\\((.*)\\)\\s*$"); public Series(String name) { java.util.regex.Matcher m = mPattern.matcher(name); if (m.find()) { this.name = m.group(1).trim(); this.num = cleanupSeriesPosition(m.group(2)); } else { this.name = name.trim(); this.num = ""; } this.id = 0L; } Series(long id, String name) { this(id, name, ""); } public Series(String name, String num) { this(0L, name, num); } Series(long id, String name, String num) { this.id = id; this.name = name.trim(); this.num = cleanupSeriesPosition(num); } public String getDisplayName() { if (num != null && num.length() > 0) return name + " (" + num + ")"; else return name; } public String getSortName() { return getDisplayName(); } public String toString() { return getDisplayName(); } /** * Replace local details from another series * * @param source Author to copy */ void copyFrom(Series source) { name = source.name; num = source.num; id = source.id; } /** * Support for creation via Parcelable */ public static final Parcelable.Creator<Series> CREATOR = new Parcelable.Creator<Series>() { public Series createFromParcel(Parcel in) { return new Series(in); } public Series[] newArray(int size) { return new Series[size]; } }; private Series(Parcel in) { name = in.readString(); num = in.readString(); id = in.readLong(); } @Override public long fixupId(CatalogueDBAdapter db) { this.id = db.lookupSeriesId(this); return this.id; } @Override public long getId() { return id; } /** * Each position in a series ('Elric(1)', 'Elric(2)' etc) will have the same * ID, so they are not unique by ID. */ @Override public boolean isUniqueById() { return false; } /** * Data class giving resulting series info after parsing a series name * * @author Philip Warner */ public static class SeriesDetails { public String name; public String position = null; public int startChar; } /** Pattern used to recognize series numbers embedded in names */ private static Pattern mSeriesPat = null; private static final String mSeriesNumberPrefixes = "(#|number|num|num.|no|no.|nr|nr.|book|bk|bk.|volume|vol|vol.|tome|part|pt.|)"; /** * Try to extract a series from a book title. * * @param title Book title to parse * @return */ public static SeriesDetails findSeries(String title) { SeriesDetails details = null; int last = title.lastIndexOf("("); if (last >= 1) { // We want a title that does not START with a bracket! int close = title.lastIndexOf(")"); if (close > -1 && last < close) { details = new SeriesDetails(); details.name = title.substring((last+1), close); details.startChar = last; if (mSeriesPat == null) { // NOTE: Changes to this pattern should be mirrored in cleanupSeriesPosition(). String seriesExp = "(.*?)(,|\\s)\\s*" + mSeriesNumberPrefixes + "\\s*([0-9\\.\\-]+|[ivxlcm\\.\\-]+)\\s*$"; // Compile and get a reference to a Pattern object. <br> mSeriesPat = Pattern.compile(seriesExp, Pattern.CASE_INSENSITIVE|Pattern.UNICODE_CASE); } Matcher matcher = mSeriesPat.matcher(details.name); if (matcher.find()) { details.name = matcher.group(1); details.position = matcher.group(4); } } } return details; } /** Pattern used to remove extraneous text from series positions */ private static Pattern mSeriesPosCleanupPat = null; private static Pattern mSeriesIntegerPat = null; /** * Try to cleanup a series position number by removing superfluous text. * * @param position Position name to cleanup * @return */ public static String cleanupSeriesPosition(String position) { if (position == null) return ""; position = position.trim(); if (mSeriesPosCleanupPat == null) { // NOTE: Changes to this pattern should be mirrored in findSeries(). String seriesExp = "^\\s*" + mSeriesNumberPrefixes + "\\s*([0-9\\.\\-]+|[ivxlcm\\.\\-]+)\\s*$"; // Compile and get a reference to a Pattern object. <br> mSeriesPosCleanupPat = Pattern.compile(seriesExp, Pattern.CASE_INSENSITIVE|Pattern.UNICODE_CASE); } if (mSeriesIntegerPat == null) { String numericExp = "^[0-9]+$"; mSeriesIntegerPat = Pattern.compile(numericExp); } Matcher matcher = mSeriesPosCleanupPat.matcher(position); if (matcher.find()) { // Try to remove leading zeros. String pos = matcher.group(2); Matcher intMatch = mSeriesIntegerPat.matcher(pos); if (intMatch.find()) { return Long.parseLong(pos) + ""; } else { return pos; } } else { return position; } } }