Coverage Report - org.crosswire.jsword.book.study.StrongsNumber
 
Classes in this File Line Coverage Branch Coverage Complexity
StrongsNumber
0%
0/64
0%
0/49
3.231
 
 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, 2007 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.jsword.book.study;
 21  
 
 22  
 import java.text.DecimalFormat;
 23  
 import java.util.regex.Matcher;
 24  
 import java.util.regex.Pattern;
 25  
 
 26  
 /**
 27  
  * A Strong's Number is either Greek or Hebrew, where the actual numbers for
 28  
  * each start at 1. This class can parse Strong's Numbers that begin with G, g,
 29  
  * H or h and are immediately followed by a number. That number can have leading
 30  
  * 0's. It can be followed by an OSISref extension of !a, !b, which is ignored.
 31  
  * 
 32  
  * <p>
 33  
  * The canonical representation of the number is a G or H followed by 4 digits,
 34  
  * with leading 0's as needed.
 35  
  * </p>
 36  
  * 
 37  
  * <p>
 38  
  * Numbers that exist:
 39  
  * </p>
 40  
  * <ul>
 41  
  * <li>Hebrew: 1-8674</li>
 42  
  * <li>Greek: 1-5624 (but not 1418, 2717, 3203-3302, 4452)</li>
 43  
  * </ul>
 44  
  * 
 45  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 46  
  * @author DM Smith
 47  
  */
 48  
 public class StrongsNumber {
 49  
     /**
 50  
      * Build an immutable Strong's Number.
 51  
      * 
 52  
      * @param input
 53  
      *            a string that needs to be parsed.
 54  
      */
 55  0
     public StrongsNumber(String input) {
 56  0
         valid = parse(input);
 57  0
     }
 58  
 
 59  
     /**
 60  
      * Build an immutable Strong's Number. 
 61  
      * 
 62  
      * @param language
 63  
      * @param strongsNumber
 64  
      */
 65  
     public StrongsNumber(char language, short strongsNumber) {
 66  0
         this(language, strongsNumber, null);
 67  0
     }
 68  
 
 69  
     /**
 70  
      * Build an immutable Strong's Number.
 71  
      * 
 72  
      * @param language
 73  
      * @param strongsNumber
 74  
      * @param part 
 75  
      */
 76  0
     public StrongsNumber(char language, short strongsNumber, String part) {
 77  0
         this.language = language;
 78  0
         this.strongsNumber = strongsNumber;
 79  0
         this.part = part;
 80  0
         valid = isValid();
 81  0
     }
 82  
 
 83  
     /**
 84  
      * Return the canonical form of a Strong's Number, without the part.
 85  
      * 
 86  
      * @return the strongsNumber
 87  
      */
 88  
     public String getStrongsNumber() {
 89  0
         StringBuilder buf = new StringBuilder(5);
 90  0
         buf.append(language);
 91  0
         buf.append(ZERO_PAD.format(strongsNumber));
 92  0
         return buf.toString();
 93  
     }
 94  
 
 95  
     /**
 96  
      * Return the canonical form of a Strong's Number, with the part, if any
 97  
      * 
 98  
      * @return the strongsNumber
 99  
      */
 100  
     public String getFullStrongsNumber() {
 101  0
         StringBuilder buf = new StringBuilder(5);
 102  0
         buf.append(language);
 103  0
         buf.append(ZERO_PAD.format(strongsNumber));
 104  0
         if (part != null) {
 105  0
             buf.append(part);
 106  
         }
 107  0
         return buf.toString();
 108  
     }
 109  
 
 110  
     /**
 111  
      * @return true if the Strong's number is for Greek
 112  
      */
 113  
     public boolean isGreek() {
 114  0
         return language == 'G';
 115  
     }
 116  
 
 117  
     /**
 118  
      * @return true if the Strong's number is for Hebrew
 119  
      */
 120  
     public boolean isHebrew() {
 121  0
         return language == 'H';
 122  
     }
 123  
 
 124  
     /**
 125  
      * @return true if this Strong's number is identified by a sub part
 126  
      */
 127  
     public boolean isPart() {
 128  0
         return part != null;
 129  
     }
 130  
 
 131  
     /**
 132  
      * Validates the number portion of this StrongsNumber.
 133  
      * <ul>
 134  
      * <li>Hebrew Strong's numbers are in the range of: 1-8674.</li>
 135  
      * <li>Greek Strong's numbers in the range of: 1-5624
 136  
      * (but not 1418, 2717, 3203-3302, 4452).</li>
 137  
      * </ul>
 138  
      * 
 139  
      * @return true if the Strong's number is in range.
 140  
      */
 141  
     public boolean isValid() {
 142  0
         if (!valid) {
 143  0
             return false;
 144  
         }
 145  
 
 146  
         // Dig deeper.
 147  
         // The valid flag when set by parse indicates whether it had problems.
 148  0
         if (language == 'H'
 149  
                 && strongsNumber >= 1
 150  
                 && strongsNumber <= 8674)
 151  
         {
 152  0
             return true;
 153  
         }
 154  
 
 155  0
         if (language == 'G'
 156  
                 && ((strongsNumber >= 1 && strongsNumber < 1418)
 157  
                         || (strongsNumber > 1418 && strongsNumber < 2717)
 158  
                         || (strongsNumber > 2717 && strongsNumber < 3203)
 159  
                         || (strongsNumber > 3302 && strongsNumber <= 5624)))
 160  
         {
 161  0
             return true;
 162  
         }
 163  
 
 164  0
         valid = false;
 165  0
         return false;
 166  
     }
 167  
 
 168  
     /*
 169  
      * (non-Javadoc)
 170  
      * 
 171  
      * @see java.lang.Object#hashCode()
 172  
      */
 173  
     @Override
 174  
     public int hashCode() {
 175  0
         int result = 31 + language;
 176  0
         return 31 * result + strongsNumber;
 177  
     }
 178  
 
 179  
     /*
 180  
      * (non-Javadoc)
 181  
      * 
 182  
      * @see java.lang.Object#equals(java.lang.Object)
 183  
      */
 184  
     @Override
 185  
     public boolean equals(Object obj) {
 186  0
         if (this == obj) {
 187  0
             return true;
 188  
         }
 189  
 
 190  0
         if (obj == null || getClass() != obj.getClass()) {
 191  0
             return false;
 192  
         }
 193  
 
 194  0
         final StrongsNumber other = (StrongsNumber) obj;
 195  
 
 196  0
         return language == other.language && strongsNumber == other.strongsNumber;
 197  
     }
 198  
 
 199  
     /*
 200  
      * (non-Javadoc)
 201  
      * 
 202  
      * @see java.lang.Object#toString()
 203  
      */
 204  
     @Override
 205  
     public String toString() {
 206  0
         return getStrongsNumber();
 207  
     }
 208  
 
 209  
     /**
 210  
      * Do the actual parsing.
 211  
      * 
 212  
      * @param input
 213  
      * @return true when the input looks like a Strong's Number
 214  
      */
 215  
     private boolean parse(String input) {
 216  0
         String text = input;
 217  0
         language = 'U';
 218  0
         strongsNumber = 9999;
 219  0
         part = "";
 220  
 
 221  
         // Does it match
 222  0
         Matcher m = STRONGS_PATTERN.matcher(text);
 223  0
         if (!m.lookingAt()) {
 224  0
             return false;
 225  
         }
 226  
 
 227  0
         String lang = m.group(1);
 228  0
         language = lang.charAt(0);
 229  0
         switch (language) {
 230  
         case 'g':
 231  0
             language = 'G';
 232  0
             break;
 233  
         case 'h':
 234  0
             language = 'H';
 235  0
             break;
 236  
         default:
 237  
             // pass through
 238  
         }
 239  
 
 240  
         // Get the number after the G or H
 241  
         try {
 242  0
             strongsNumber = Integer.parseInt(m.group(2));
 243  0
         } catch (NumberFormatException e) {
 244  0
             strongsNumber = 0; // An invalid Strong's Number
 245  0
             return false;
 246  0
         }
 247  
 
 248  
         // FYI: OSIS refers to what follows a ! as a grain
 249  0
         part = m.group(3);
 250  0
         return true;
 251  
     }
 252  
 
 253  
     /**
 254  
      * Whether it is Greek (G) or Hebrew (H).
 255  
      */
 256  
     private char language;
 257  
 
 258  
     /**
 259  
      * The Strong's Number.
 260  
      */
 261  
     private int strongsNumber;
 262  
 
 263  
     /**
 264  
      * The part if any.
 265  
      */
 266  
     private String part;
 267  
 
 268  
     /*
 269  
      * The value is valid.
 270  
      */
 271  
     private boolean valid;
 272  
 
 273  
     /**
 274  
      * The pattern of an acceptable Strong's number.
 275  
      */
 276  0
     private static final Pattern STRONGS_PATTERN = Pattern.compile("([GgHh])([0-9]*)!?([A-Za-z]+)?");
 277  0
     private static final DecimalFormat ZERO_PAD = new DecimalFormat("0000");
 278  
 }