1   /**
2    * Distribution License:
3    * JSword is free software; you can redistribute it and/or modify it under
4    * the terms of the GNU Lesser General Public License, version 2.1 as published by
5    * the Free Software Foundation. This program is distributed in the hope
6    * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7    * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8    * See the GNU Lesser General Public License for more details.
9    *
10   * The License is available on the internet at:
11   *       http://www.gnu.org/copyleft/lgpl.html
12   * or by writing to:
13   *      Free Software Foundation, Inc.
14   *      59 Temple Place - Suite 330
15   *      Boston, MA 02111-1307, USA
16   *
17   * Copyright: 2005
18   *     The copyright to this program is held by it's authors.
19   *
20   * ID: $Id: VerseRangeFactory.java 2226 2012-02-02 19:25:21Z dmsmith $
21   */
22  package org.crosswire.jsword.passage;
23  
24  import org.crosswire.common.util.StringUtil;
25  import org.crosswire.jsword.JSMsg;
26  import org.crosswire.jsword.versification.Versification;
27  
28  /**
29   * A factory that creates VerseRanges from user input.
30   * 
31   * @see gnu.lgpl.License for license details.<br>
32   *      The copyright to this program is held by it's authors.
33   * @author Joe Walker [joe at eireneh dot com]
34   * @author DM Smith [dmsmith555 at yahoo dot com]
35   */
36  public final class VerseRangeFactory {
37  
38      /**
39       * prevent instantiation
40       */
41      private VerseRangeFactory() {
42      }
43  
44      /**
45       * Construct a VerseRange from a human readable string. For example
46       * "Gen 1:1-3" in case the user does not want to have their typing 'fixed'
47       * by a meddling patronizing computer.
48       * 
49       * @param orginal
50       *            The textual representation
51       * @exception NoSuchVerseException
52       *                If the text can not be understood
53       * @deprecated  use {@link #fromString(Versification, String)} instead
54       */
55      @Deprecated
56      public static VerseRange fromString(String orginal) throws NoSuchVerseException {
57          return fromString(null, orginal, null);
58      }
59  
60      public static VerseRange fromString(Versification v11n, String orginal) throws NoSuchVerseException {
61          return fromString(v11n, orginal, null);
62      }
63  
64      /**
65       * Construct a VerseRange from a String and a VerseRange. For example given
66       * "2:2" and a basis of Gen 1:1-2 the result would be range of 1 verse
67       * starting at Gen 2:2. Also given "2:2-5" and a basis of Gen 1:1-2 the
68       * result would be a range of 5 verses starting at Gen 1:1.
69       * <p>
70       * This constructor is different from the (String, Verse) constructor in
71       * that if the basis is a range that exactly covers a chapter and the string
72       * is a single number, then we assume that the number refers to a chapter
73       * and not to a verse. This allows us to have a Passage like "Gen 1,2" and
74       * have the 2 understood as chapter 2 and not verse 2 of Gen 1, which would
75       * have occurred otherwise.
76       * 
77       * @param original
78       *            The string describing the verse e.g "2:2"
79       * @param basis
80       *            The verse that forms the basis by which to understand the
81       *            original.
82       * @exception NoSuchVerseException
83       *                If the reference is illegal
84       * @deprecated  use {@link #fromString(Versification, String, VerseRange)} instead
85       */
86      @Deprecated
87      public static VerseRange fromString(String original, VerseRange basis) throws NoSuchVerseException {
88          return fromString(null, original, basis);
89      }
90  
91      public static VerseRange fromString(Versification v11n, String original, VerseRange basis) throws NoSuchVerseException {
92          String[] parts = StringUtil.split(original, VerseRange.RANGE_ALLOWED_DELIMS);
93  
94          switch (parts.length) {
95          case 1:
96              return fromText(v11n, original, parts[0], parts[0], basis);
97  
98          case 2:
99              return fromText(v11n, original, parts[0], parts[1], basis);
100 
101         default:
102             // TRANSLATOR: The user specified a verse range with too many separators. {0} is a placeholder for the allowable separators.
103             throw new NoSuchVerseException(JSMsg.gettext("A verse range cannot have more than 2 parts. (Parts are separated by {0}) Given {1}", VerseRange.RANGE_ALLOWED_DELIMS, original));
104         }
105     }
106 
107     private static VerseRange fromText(Versification v11n, String original, String startVerseDesc, String endVerseDesc, VerseRange basis) throws NoSuchVerseException {
108         String[] startParts = AccuracyType.tokenize(startVerseDesc);
109         AccuracyType accuracyStart = AccuracyType.fromText(v11n, original, startParts, basis);
110         Verse start = accuracyStart.createStartVerse(v11n, startVerseDesc, basis, startParts);
111         v11n.validate(start.getBook(), start.getChapter(), start.getVerse());
112 
113         String[] endParts;
114         if (startVerseDesc.equals(endVerseDesc)) {
115             endParts = startParts;
116         } else {
117             endParts = AccuracyType.tokenize(endVerseDesc);
118         }
119 
120         AccuracyType accuracyEnd = AccuracyType.fromText(v11n, original, endParts, accuracyStart, basis);
121         Verse end = accuracyEnd.createEndVerse(v11n, endVerseDesc, start, endParts);
122         v11n.validate(end.getBook(), end.getChapter(), end.getVerse());
123 
124         return new VerseRange(v11n, original, start, end);
125     }
126 }
127