Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
OsisParser |
|
| 7.6;7.6 |
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 or later | |
5 | * as published by the Free Software Foundation. This program is distributed | |
6 | * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even | |
7 | * the 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 | * © CrossWire Bible Society, 2005 - 2016 | |
18 | * | |
19 | */ | |
20 | package org.crosswire.jsword.passage; | |
21 | ||
22 | import java.util.ArrayList; | |
23 | import java.util.Arrays; | |
24 | import java.util.List; | |
25 | ||
26 | import org.crosswire.common.util.StringUtil; | |
27 | import org.crosswire.jsword.versification.BibleBook; | |
28 | import org.crosswire.jsword.versification.Versification; | |
29 | ||
30 | /** | |
31 | * The Osis ID parser simply assumes 1-3 parts divided by '.'. | |
32 | * Any deviation from the dot-delimited formatted yields nulls. | |
33 | * | |
34 | * OSIS Refs should be separated by a '-' if there are 2 refs signifying a range e.g. Gen.1-Gen.3. | |
35 | * | |
36 | * The current implementation doesn't support an OSIS ID or OSIS ref with a missing chapter, | |
37 | * as are currently returned by the getOsisRef() calls occasionally. | |
38 | * | |
39 | * Algorithm: | |
40 | * If ony 1 ID passed in then create ending id from it to enable a single flow through algorithm. | |
41 | * Missing chapter or verse of starting id will be set to 0/1. | |
42 | * Missing chapter or verse of ending id will be set to last chapter/verse. | |
43 | * | |
44 | * @author Chris Burrell, mjdenham | |
45 | */ | |
46 | 0 | public final class OsisParser { |
47 | ||
48 | // This could be 1 or 0 but for now I have used 1 | |
49 | private static final String START_CHAPTER_OR_VERSE = "1"; | |
50 | ||
51 | /** | |
52 | * String OSIS Ref parser, assumes a - separating two osis IDs | |
53 | * @param v11n the v11n | |
54 | * @param osisRef the ref | |
55 | * @return the equivalent verse range | |
56 | */ | |
57 | public VerseRange parseOsisRef(final Versification v11n, final String osisRef) { | |
58 | 0 | final String[] osisIDs = StringUtil.splitAll(osisRef, VerseRange.RANGE_OSIS_DELIM); |
59 | // Too many or few parts? | |
60 | 0 | if (osisIDs.length < 1 || osisIDs.length > 2) { |
61 | 0 | return null; |
62 | } | |
63 | ||
64 | // Parts must have content. | |
65 | 0 | if (osisIDs[0].length() == 0 || (osisIDs.length == 2 && osisIDs[1].length() == 0)) { |
66 | 0 | return null; |
67 | } | |
68 | ||
69 | // Ensure ending OSIS id exists to simplify future logic - yes it is okay to use the start id as the end id here | |
70 | 0 | String startOsisID = osisIDs[0]; |
71 | String endOsisID; | |
72 | 0 | if (osisIDs.length == 1) { |
73 | 0 | endOsisID = startOsisID; |
74 | } else { | |
75 | 0 | endOsisID = osisIDs[1]; |
76 | } | |
77 | ||
78 | // ensure no empty parts in osis id1 and not too many parts | |
79 | 0 | List<String> startOsisIDParts = splitOsisId(startOsisID); |
80 | 0 | if (isAnEmptyPart(startOsisIDParts) || startOsisIDParts.size() > 3) { |
81 | 0 | return null; |
82 | } | |
83 | ||
84 | // manipulate first osis id to 3 parts, padding with first chapter or verse | |
85 | 0 | while (startOsisIDParts.size() < 3) { |
86 | 0 | startOsisIDParts.add(START_CHAPTER_OR_VERSE); |
87 | } | |
88 | // now we have a full 3 part start OSIS id | |
89 | ||
90 | // Now let us manufacture a 3 part end OSIS id | |
91 | ||
92 | // First check no empty parts were passed in for osis id 2 | |
93 | 0 | List<String> endOsisIDParts = splitOsisId(endOsisID); |
94 | 0 | if (isAnEmptyPart(endOsisIDParts)) { |
95 | 0 | return null; |
96 | } | |
97 | ||
98 | // Add end chapter/verse if missing | |
99 | 0 | int endOsisIDPartCount = endOsisIDParts.size(); |
100 | 0 | if (endOsisIDPartCount < 3) { |
101 | // need to calculate chapter and verse for osis id 2 | |
102 | ||
103 | // there will always be a book | |
104 | 0 | String bookName = endOsisIDParts.get(0); |
105 | 0 | final BibleBook book = BibleBook.fromExactOSIS(bookName); |
106 | ||
107 | // can asssume last chapter if unspecified because this is the trailing osis Id | |
108 | int chapter; | |
109 | 0 | if (endOsisIDPartCount == 1) { |
110 | 0 | chapter = v11n.getLastChapter(book); |
111 | 0 | endOsisIDParts.add(Integer.toString(chapter)); |
112 | } else { | |
113 | 0 | chapter = Integer.parseInt(endOsisIDParts.get(1)); |
114 | } | |
115 | ||
116 | // can asssume last verse if unspecified because this is the trailing osis Id | |
117 | int verse; | |
118 | 0 | if (endOsisIDPartCount < 3) { |
119 | 0 | verse = v11n.getLastVerse(book, chapter); |
120 | 0 | endOsisIDParts.add(Integer.toString(verse)); |
121 | } | |
122 | } | |
123 | ||
124 | // Now there is exactly 1 beginning and 1 ending 3-part verse only beyond this point | |
125 | 0 | Verse start = parseOsisID(v11n, startOsisIDParts); |
126 | 0 | if (start == null) { |
127 | 0 | return null; |
128 | } | |
129 | ||
130 | 0 | Verse end = parseOsisID(v11n, endOsisIDParts); |
131 | 0 | if (end == null) { |
132 | 0 | return null; |
133 | } | |
134 | ||
135 | 0 | return new VerseRange(v11n, start, end); |
136 | } | |
137 | ||
138 | /** | |
139 | * Strict OSIS ID parsers, case-sensitive | |
140 | * @param v11n the versification to use when constructing the verse | |
141 | * @param osisID the ID we want to parse | |
142 | * @return the verse that matches the OSIS ID | |
143 | */ | |
144 | public Verse parseOsisID(final Versification v11n, final String osisID) { | |
145 | 0 | if (osisID == null) { |
146 | 0 | return null; |
147 | } | |
148 | ||
149 | 0 | final List<String> osisIDParts = splitOsisId(osisID); |
150 | ||
151 | 0 | if (osisIDParts.size() != 3 || isAnEmptyPart(osisIDParts)) { |
152 | 0 | return null; |
153 | } | |
154 | ||
155 | 0 | return parseOsisID(v11n, osisIDParts); |
156 | } | |
157 | ||
158 | private Verse parseOsisID(final Versification v11n, final List<String> osisIDParts) { | |
159 | ||
160 | 0 | final BibleBook b = BibleBook.fromExactOSIS(osisIDParts.get(0)); |
161 | 0 | if (b == null) { |
162 | 0 | return null; |
163 | } | |
164 | ||
165 | // Allow a Verse to have a sub identifier on the last part. | |
166 | // We should use it, but throwing it away for now. | |
167 | 0 | String[] endParts = StringUtil.splitAll(osisIDParts.get(2), Verse.VERSE_OSIS_SUB_PREFIX); |
168 | 0 | String subIdentifier = null; |
169 | 0 | if (endParts.length == 2 && endParts[1].length() > 0) { |
170 | 0 | subIdentifier = endParts[1]; |
171 | } | |
172 | 0 | return new Verse(v11n, b, Integer.parseInt(osisIDParts.get(1)), Integer.parseInt(endParts[0]), subIdentifier); |
173 | } | |
174 | ||
175 | /** | |
176 | * Split string like 'Gen.1.1' into a 3 element list | |
177 | */ | |
178 | private List<String> splitOsisId(String osisID1) { | |
179 | 0 | String[] partArray = StringUtil.splitAll(osisID1, Verse.VERSE_OSIS_DELIM); |
180 | ||
181 | // add to an appropriately sized editable list | |
182 | 0 | List<String> list = new ArrayList(3); |
183 | 0 | list.addAll(Arrays.asList(partArray)); |
184 | 0 | return list; |
185 | } | |
186 | ||
187 | /** | |
188 | * Check no part of the Osis ref is empty | |
189 | */ | |
190 | private static boolean isAnEmptyPart(List<String> parts) { | |
191 | 0 | for (String part : parts) { |
192 | 0 | if (part.length() == 0) { |
193 | 0 | return true; |
194 | } | |
195 | } | |
196 | 0 | return false; |
197 | } | |
198 | } |