Coverage Report - org.crosswire.jsword.passage.Verse
 
Classes in this File Line Coverage Branch Coverage Complexity
Verse
0%
0/148
0%
0/58
2.044
 
 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.io.IOException;
 23  
 import java.io.ObjectInputStream;
 24  
 import java.io.ObjectOutputStream;
 25  
 import java.util.Iterator;
 26  
 
 27  
 import org.crosswire.common.icu.NumberShaper;
 28  
 import org.crosswire.common.util.ItemIterator;
 29  
 import org.crosswire.jsword.JSMsg;
 30  
 import org.crosswire.jsword.JSOtherMsg;
 31  
 import org.crosswire.jsword.versification.BibleBook;
 32  
 import org.crosswire.jsword.versification.BibleNames;
 33  
 import org.crosswire.jsword.versification.Versification;
 34  
 import org.crosswire.jsword.versification.system.Versifications;
 35  
 import org.slf4j.Logger;
 36  
 import org.slf4j.LoggerFactory;
 37  
 
 38  
 /**
 39  
  * A Verse is a pointer to a single verse. Externally its unique identifier is
 40  
  * a String of the form "Gen 1:1" Internally we use
 41  
  * <code>( v11n, book, chapter, verse )</code>
 42  
  * 
 43  
  * <p>
 44  
  * A Verse is designed to be immutable. This is a necessary from a collections
 45  
  * point of view. A Verse should always be valid, although some versions may not
 46  
  * return any text for verses that they consider to be untranslated in some
 47  
  * way.
 48  
  * </p>
 49  
  * 
 50  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 51  
  * @author Joe Walker
 52  
  * @author DM Smith
 53  
  */
 54  0
 public final class Verse implements VerseKey<Verse> {
 55  
     /**
 56  
      * Create a Verse from book, chapter and verse numbers, throwing up if the
 57  
      * specified Verse does not exist.
 58  
      * 
 59  
      * @param v11n
 60  
      *            The versification to which this verse belongs
 61  
      * @param book
 62  
      *            The book number (Genesis = 1)
 63  
      * @param chapter
 64  
      *            The chapter number
 65  
      * @param verse
 66  
      *            The verse number
 67  
      */
 68  
     public Verse(Versification v11n, BibleBook book, int chapter, int verse) {
 69  0
         this(v11n, book, chapter, verse, null);
 70  0
     }
 71  
 
 72  
     /**
 73  
      * Create a Verse from book, chapter and verse numbers, throwing up if the
 74  
      * specified Verse does not exist.
 75  
      * 
 76  
      * @param v11n
 77  
      *            The versification to which this verse belongs
 78  
      * @param book
 79  
      *            The book number (Genesis = 1)
 80  
      * @param chapter
 81  
      *            The chapter number
 82  
      * @param verse
 83  
      *            The verse number
 84  
      * @param subIdentifier
 85  
      *            The optional sub identifier
 86  
      */
 87  0
     public Verse(Versification v11n, BibleBook book, int chapter, int verse, String subIdentifier) {
 88  0
         this.v11n = v11n;
 89  0
         this.book = book;
 90  0
         this.chapter = chapter;
 91  0
         this.verse = verse;
 92  0
         this.subIdentifier = subIdentifier;
 93  0
         this.ordinal = v11n.getOrdinal(this);
 94  0
     }
 95  
 
 96  
     /**
 97  
      * Create a Verse from book, chapter and verse numbers, patching up if the
 98  
      * specified verse does not exist.
 99  
      * <p>
 100  
      * The actual value of the boolean is ignored. However for future proofing
 101  
      * you should only use 'true'. Do not use patch_up=false, use
 102  
      * <code>Verse(int, int, int)</code> This so that we can declare this
 103  
      * constructor to not throw an exception. Is there a better way of doing
 104  
      * this?
 105  
      * 
 106  
      * @param v11n
 107  
      *            The versification to which this verse belongs
 108  
      * @param book
 109  
      *            The book number (Genesis = 1)
 110  
      * @param chapter
 111  
      *            The chapter number
 112  
      * @param verse
 113  
      *            The verse number
 114  
      * @param patchUp
 115  
      *            True to trigger reference fixing
 116  
      */
 117  0
     public Verse(Versification v11n, BibleBook book, int chapter, int verse, boolean patchUp) {
 118  0
         if (!patchUp) {
 119  0
             throw new IllegalArgumentException(JSOtherMsg.lookupText("Use patchUp=true."));
 120  
         }
 121  
 
 122  0
         this.v11n = v11n;
 123  0
         Verse patched = this.v11n.patch(book, chapter, verse);
 124  0
         this.book = patched.book;
 125  0
         this.chapter = patched.chapter;
 126  0
         this.verse = patched.verse;
 127  0
         this.ordinal = patched.ordinal;
 128  0
     }
 129  
 
 130  
     /**
 131  
      * Set a Verse using a verse ordinal number - WARNING Do not use this method
 132  
      * unless you really know the dangers of doing so. Ordinals are not always
 133  
      * going to be the same. So you should use Versification, Book, Chapter and Verse
 134  
      * in preference to an int ordinal whenever possible. Ordinal numbers are 1
 135  
      * based and not 0 based.
 136  
      * 
 137  
      * @param v11n
 138  
      *            The versification to which this verse belongs
 139  
      * @param ordinal
 140  
      *            The verse id
 141  
      */
 142  0
     public Verse(Versification v11n, int ordinal) {
 143  0
         Verse decoded = v11n.decodeOrdinal(ordinal);
 144  0
         this.v11n = v11n;
 145  0
         this.book = decoded.book;
 146  0
         this.chapter = decoded.chapter;
 147  0
         this.verse = decoded.verse;
 148  0
         this.ordinal = decoded.ordinal;
 149  0
     }
 150  
 
 151  
     /* (non-Javadoc)
 152  
      * @see org.crosswire.jsword.passage.VerseKey#isWhole()
 153  
      */
 154  
     public boolean isWhole() {
 155  0
         return subIdentifier == null || subIdentifier.length() == 0;
 156  
     }
 157  
 
 158  
     /* (non-Javadoc)
 159  
      * @see org.crosswire.jsword.passage.VerseKey#getWhole()
 160  
      */
 161  
     public Verse getWhole() {
 162  0
         if (isWhole()) {
 163  0
             return this;
 164  
         }
 165  0
         return new Verse(v11n, book, chapter, verse);
 166  
     }
 167  
 
 168  
     @Override
 169  
     public String toString() {
 170  0
         return getName();
 171  
     }
 172  
 
 173  
     /* (non-Javadoc)
 174  
      * @see org.crosswire.jsword.passage.Key#getName()
 175  
      */
 176  
     public String getName() {
 177  0
         return getName(null);
 178  
     }
 179  
 
 180  
     /* (non-Javadoc)
 181  
      * @see org.crosswire.jsword.passage.Key#getName(org.crosswire.jsword.passage.Key)
 182  
      */
 183  
     public String getName(Key base) {
 184  0
         if (base != null && !(base instanceof Verse)) {
 185  0
             return getName();
 186  
         }
 187  
 
 188  0
         String verseName = doGetName((Verse) base);
 189  
         // Only shape it if it can be unshaped.
 190  0
         if (shaper.canUnshape()) {
 191  0
             return shaper.shape(verseName);
 192  
         }
 193  
 
 194  0
         return verseName;
 195  
     }
 196  
 
 197  
     /* (non-Javadoc)
 198  
      * @see org.crosswire.jsword.passage.Key#getRootName()
 199  
      */
 200  
     public String getRootName() {
 201  0
         return BibleNames.instance().getShortName(book);
 202  
     }
 203  
 
 204  
     /* (non-Javadoc)
 205  
      * @see org.crosswire.jsword.passage.Key#getOsisRef()
 206  
      */
 207  
     public String getOsisRef() {
 208  0
         return getOsisID();
 209  
     }
 210  
 
 211  
     /* (non-Javadoc)
 212  
      * @see org.crosswire.jsword.passage.Key#getOsisID()
 213  
      */
 214  
     public String getOsisID() {
 215  0
         final StringBuilder buf = getVerseIdentifier();
 216  0
         if (subIdentifier != null && subIdentifier.length() > 0) {
 217  0
             buf.append(VERSE_OSIS_SUB_PREFIX);
 218  0
             buf.append(subIdentifier);
 219  
         }
 220  0
         return buf.toString();
 221  
     }
 222  
 
 223  
     /* (non-Javadoc)
 224  
      * @see org.crosswire.jsword.passage.Key#getOsisID()
 225  
      */
 226  
     public String getOsisIDNoSubIdentifier() {
 227  0
         return getVerseIdentifier().toString();
 228  
     }
 229  
 
 230  
     /**
 231  
      * Gets the common name of the verse, excluding any !abc sub-identifier
 232  
      * @return the verse OSIS-ID, excluding the sub-identifier
 233  
      */
 234  
     private StringBuilder getVerseIdentifier() {
 235  0
         StringBuilder buf = new StringBuilder();
 236  0
         buf.append(book.getOSIS());
 237  0
         buf.append(Verse.VERSE_OSIS_DELIM);
 238  0
         buf.append(chapter);
 239  0
         buf.append(Verse.VERSE_OSIS_DELIM);
 240  0
         buf.append(verse);
 241  0
         return buf;
 242  
     }
 243  
 
 244  
     @Override
 245  
     public Verse clone() {
 246  0
         Verse copy = null;
 247  
         try {
 248  0
             copy = (Verse) super.clone();
 249  0
             copy.v11n = this.v11n;
 250  0
             copy.book = this.book;
 251  0
             copy.chapter = this.chapter;
 252  0
             copy.verse = this.verse;
 253  0
             copy.ordinal = this.ordinal;
 254  0
             copy.subIdentifier = this.subIdentifier;
 255  0
         } catch (CloneNotSupportedException e) {
 256  0
             assert false : e;
 257  0
         }
 258  
 
 259  0
         return copy;
 260  
     }
 261  
 
 262  
     @Override
 263  
     public boolean equals(Object obj) {
 264  
         // Since this can not be null
 265  0
         if (!(obj instanceof Verse)) {
 266  0
             return false;
 267  
         }
 268  
 
 269  0
         Verse that = (Verse) obj;
 270  
 
 271  
         // The real tests
 272  0
         return this.ordinal == that.ordinal
 273  
                 && this.v11n.equals(that.v11n)
 274  
                 && bothNullOrEqual(this.subIdentifier, that.subIdentifier);
 275  
     }
 276  
 
 277  
     @Override
 278  
     public int hashCode() {
 279  0
         int result = 31 + ordinal;
 280  0
         result = 31 * result + ((v11n == null) ? 0 : v11n.hashCode());
 281  0
         return 31 * result + ((subIdentifier == null) ? 0 : subIdentifier.hashCode());
 282  
     }
 283  
 
 284  
     /* (non-Javadoc)
 285  
      * @see java.lang.Comparable#compareTo(java.lang.Object)
 286  
      */
 287  
     public int compareTo(Key obj) {
 288  0
         return this.ordinal - ((Verse) obj).ordinal;
 289  
     }
 290  
 
 291  
     /* (non-Javadoc)
 292  
      * @see org.crosswire.jsword.passage.VerseKey#getVersification()
 293  
      */
 294  
     public Versification getVersification() {
 295  0
         return v11n;
 296  
     }
 297  
 
 298  
     /* (non-Javadoc)
 299  
      * @see org.crosswire.jsword.passage.Passage#reversify(org.crosswire.jsword.versification.Versification)
 300  
      */
 301  
     public Verse reversify(Versification newVersification) {
 302  0
         if (v11n.equals(newVersification)) {
 303  0
             return this;
 304  
         }
 305  
 
 306  
         try {
 307  
             //check the v11n supports this key, otherwise this leads to all sorts of issues
 308  0
             if (newVersification.validate(book, chapter, verse, true)) {
 309  0
                 return new Verse(newVersification, book, chapter, verse);
 310  
             }
 311  0
         } catch (NoSuchVerseException ex) {
 312  
             // will never happen
 313  0
             log.error("Contract for validate was changed to thrown an exception when silent mode is true", ex);
 314  0
         }
 315  0
         return null;
 316  
     }
 317  
 
 318  
     /**
 319  
      * Return the book that we refer to
 320  
      * 
 321  
      * @return The book of the Bible
 322  
      */
 323  
     public BibleBook getBook() {
 324  0
         return book;
 325  
     }
 326  
 
 327  
     /**
 328  
      * Return the chapter that we refer to
 329  
      * 
 330  
      * @return The chapter number
 331  
      */
 332  
     public int getChapter() {
 333  0
         return chapter;
 334  
     }
 335  
 
 336  
     /**
 337  
      * Return the verse that we refer to
 338  
      * 
 339  
      * @return The verse number
 340  
      */
 341  
     public int getVerse() {
 342  0
         return verse;
 343  
     }
 344  
 
 345  
     /**
 346  
      * Return the sub identifier if any
 347  
      * @return The optional OSIS sub identifier
 348  
      */
 349  
     public String getSubIdentifier() {
 350  0
         return subIdentifier;
 351  
     }
 352  
 
 353  
     /**
 354  
      * Return the ordinal value of the verse in its versification.
 355  
      * 
 356  
      * @return The verse number
 357  
      */
 358  
     public int getOrdinal() {
 359  0
         return ordinal;
 360  
     }
 361  
 
 362  
     /**
 363  
      * Create an array of Verses
 364  
      * 
 365  
      * @return The array of verses that this makes up
 366  
      */
 367  
     public Verse[] toVerseArray() {
 368  0
         return new Verse[] {
 369  
             this
 370  
         };
 371  
     }
 372  
 
 373  
     /* (non-Javadoc)
 374  
      * @see org.crosswire.jsword.passage.Key#getParent()
 375  
      */
 376  
     public Key getParent() {
 377  0
         return null;
 378  
     }
 379  
 
 380  
     /**
 381  
      * Determine whether two objects are equal, allowing nulls
 382  
      * @param x
 383  
      * @param y
 384  
      * @return true if both are null or the two are equal
 385  
      */
 386  
     public static boolean bothNullOrEqual(Object x, Object y) {
 387  0
         return x == y || (x != null && x.equals(y));
 388  
     }
 389  
 
 390  
     /**
 391  
      * Compute the verse representation given the context.
 392  
      * 
 393  
      * @param verseBase
 394  
      *            the context or null if there is none
 395  
      * @return the verse representation
 396  
      */
 397  
     private String doGetName(Verse verseBase) {
 398  0
         StringBuilder buf = new StringBuilder();
 399  
         // To cope with thing like Jude 2...
 400  0
         if (book.isShortBook()) {
 401  0
             if (verseBase == null || verseBase.book != book) {
 402  0
                 buf.append(BibleNames.instance().getPreferredName(book));
 403  0
                 buf.append(Verse.VERSE_PREF_DELIM1);
 404  0
                 buf.append(verse);
 405  0
                 return buf.toString();
 406  
             }
 407  
 
 408  0
             return Integer.toString(verse);
 409  
         }
 410  
 
 411  0
         if (verseBase == null || verseBase.book != book) {
 412  0
             buf.append(BibleNames.instance().getPreferredName(book));
 413  0
             buf.append(Verse.VERSE_PREF_DELIM1);
 414  0
             buf.append(chapter);
 415  0
             buf.append(Verse.VERSE_PREF_DELIM2);
 416  0
             buf.append(verse);
 417  0
             return buf.toString();
 418  
         }
 419  
 
 420  0
         if (verseBase.chapter != chapter) {
 421  0
             buf.append(chapter);
 422  0
             buf.append(Verse.VERSE_PREF_DELIM2);
 423  0
             buf.append(verse);
 424  0
             return buf.toString();
 425  
         }
 426  
 
 427  0
         return Integer.toString(verse);
 428  
     }
 429  
 
 430  
     /**
 431  
      * This is simply a convenience function to wrap Integer.parseInt() and give
 432  
      * us a reasonable exception on failure. It is called by VerseRange hence
 433  
      * protected, however I would prefer private
 434  
      * 
 435  
      * @param text
 436  
      *            The string to be parsed
 437  
      * @return The correctly parsed chapter or verse
 438  
      */
 439  
     protected static int parseInt(String text) throws NoSuchVerseException {
 440  
         try {
 441  0
             return Integer.parseInt(shaper.unshape(text));
 442  0
         } catch (NumberFormatException ex) {
 443  
             // TRANSLATOR: The chapter or verse number is actually not a number, but something else.
 444  
             // {0} is a placeholder for what the user supplied.
 445  0
             throw new NoSuchVerseException(JSMsg.gettext("Cannot understand {0} as a chapter or verse.", text));
 446  
         }
 447  
     }
 448  
 
 449  
     /**
 450  
      * Write out the object to the given ObjectOutputStream
 451  
      * 
 452  
      * @param out
 453  
      *            The stream to write our state to
 454  
      * @throws IOException
 455  
      *             if the read fails
 456  
      * @serialData Write the ordinal number of this verse
 457  
      */
 458  
     private void writeObject(ObjectOutputStream out) throws IOException {
 459  0
         out.defaultWriteObject();
 460  0
         out.writeUTF(v11n.getName());
 461  0
     }
 462  
 
 463  
     /**
 464  
      * Write out the object to the given ObjectOutputStream
 465  
      * 
 466  
      * @param in
 467  
      *            The stream to read our state from
 468  
      * @throws IOException
 469  
      *             if the read fails
 470  
      * @throws ClassNotFoundException
 471  
      *             If the read data is incorrect
 472  
      * @serialData Write the ordinal number of this verse
 473  
      */
 474  
     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
 475  0
         in.defaultReadObject();
 476  0
         String v11nName = in.readUTF();
 477  0
         v11n = Versifications.instance().getVersification(v11nName);
 478  0
         Verse decoded = v11n.decodeOrdinal(ordinal);
 479  
 
 480  0
         this.book = decoded.book;
 481  0
         this.chapter = decoded.chapter;
 482  0
         this.verse = decoded.verse;
 483  0
     }
 484  
 
 485  
     /* (non-Javadoc)
 486  
      * @see org.crosswire.jsword.passage.Key#canHaveChildren()
 487  
      */
 488  
     public boolean canHaveChildren() {
 489  0
         return false;
 490  
     }
 491  
 
 492  
     /* (non-Javadoc)
 493  
      * @see org.crosswire.jsword.passage.Key#getChildCount()
 494  
      */
 495  
     public int getChildCount() {
 496  0
         return 0;
 497  
     }
 498  
 
 499  
     /* (non-Javadoc)
 500  
      * @see org.crosswire.jsword.passage.Key#getCardinality()
 501  
      */
 502  
     public int getCardinality() {
 503  0
         return 1;
 504  
     }
 505  
 
 506  
     /* (non-Javadoc)
 507  
      * @see org.crosswire.jsword.passage.Key#isEmpty()
 508  
      */
 509  
     public boolean isEmpty() {
 510  0
         return false;
 511  
     }
 512  
 
 513  
     /* (non-Javadoc)
 514  
      * @see org.crosswire.jsword.passage.Key#contains(org.crosswire.jsword.passage.Key)
 515  
      */
 516  
     public boolean contains(Key key) {
 517  0
         return this.equals(key);
 518  
     }
 519  
 
 520  
     /* (non-Javadoc)
 521  
      * @see java.lang.Iterable#iterator()
 522  
      */
 523  
     public Iterator<Key> iterator() {
 524  0
         return new ItemIterator<Key>(this);
 525  
     }
 526  
 
 527  
     /* (non-Javadoc)
 528  
      * @see org.crosswire.jsword.passage.Key#addAll(org.crosswire.jsword.passage.Key)
 529  
      */
 530  
     public void addAll(Key key) {
 531  0
         throw new UnsupportedOperationException();
 532  
     }
 533  
 
 534  
     /* (non-Javadoc)
 535  
      * @see org.crosswire.jsword.passage.Key#removeAll(org.crosswire.jsword.passage.Key)
 536  
      */
 537  
     public void removeAll(Key key) {
 538  0
         throw new UnsupportedOperationException();
 539  
     }
 540  
 
 541  
     /* (non-Javadoc)
 542  
      * @see org.crosswire.jsword.passage.Key#retainAll(org.crosswire.jsword.passage.Key)
 543  
      */
 544  
     public void retainAll(Key key) {
 545  0
         throw new UnsupportedOperationException();
 546  
     }
 547  
 
 548  
     /* (non-Javadoc)
 549  
      * @see org.crosswire.jsword.passage.Key#clear()
 550  
      */
 551  
     public void clear() {
 552  
         // do nothing
 553  0
     }
 554  
 
 555  
     /* (non-Javadoc)
 556  
      * @see org.crosswire.jsword.passage.Key#get(int)
 557  
      */
 558  
     public Key get(int index) {
 559  0
         if (index == 0) {
 560  0
             return this;
 561  
         }
 562  0
         return null;
 563  
     }
 564  
 
 565  
     /* (non-Javadoc)
 566  
      * @see org.crosswire.jsword.passage.Key#indexOf(org.crosswire.jsword.passage.Key)
 567  
      */
 568  
     public int indexOf(Key that) {
 569  0
         if (this.equals(that)) {
 570  0
             return 0;
 571  
         }
 572  0
         return -1;
 573  
     }
 574  
 
 575  
     /* (non-Javadoc)
 576  
      * @see org.crosswire.jsword.passage.Key#blur(int, org.crosswire.jsword.passage.RestrictionType)
 577  
      */
 578  
     public void blur(int by, RestrictionType restrict) {
 579  0
         throw new UnsupportedOperationException();
 580  
     }
 581  
 
 582  
     /**
 583  
      * What characters should we use to separate parts of an OSIS verse
 584  
      * reference
 585  
      */
 586  
     public static final char VERSE_OSIS_DELIM = '.';
 587  
 
 588  
     /**
 589  
      * What characters should we use to start an OSIS sub identifier
 590  
      */
 591  
     public static final char VERSE_OSIS_SUB_PREFIX = '!';
 592  
 
 593  
     /**
 594  
      * What characters should we use to separate the book from the chapter
 595  
      */
 596  
     public static final char VERSE_PREF_DELIM1 = ' ';
 597  
 
 598  
     /**
 599  
      * What characters should we use to separate the chapter from the verse
 600  
      */
 601  
     public static final char VERSE_PREF_DELIM2 = ':';
 602  
 
 603  
     /**
 604  
      * The default verse
 605  
      */
 606  0
     public static final Verse DEFAULT = new Verse(Versifications.instance().getVersification("KJV"), BibleBook.GEN, 1, 1);
 607  
 
 608  
     /**
 609  
      * Allow the conversion to and from other number representations.
 610  
      */
 611  0
     private static NumberShaper shaper = new NumberShaper();
 612  
 
 613  
     /**
 614  
      * The versification for this verse.
 615  
      */
 616  
     private transient Versification v11n;
 617  
 
 618  
     /**
 619  
      * The ordinal value for this verse within its versification.
 620  
      */
 621  
     private int ordinal;
 622  
 
 623  
     /**
 624  
      * The book of the Bible.
 625  
      */
 626  
     private transient BibleBook book;
 627  
 
 628  
     /**
 629  
      * The chapter number
 630  
      */
 631  
     private transient int chapter;
 632  
 
 633  
     /**
 634  
      * The verse number
 635  
      */
 636  
     private transient int verse;
 637  
 
 638  
     /**
 639  
      * The OSIS Sub-identifier if present.
 640  
      * This should be a string that allows for the likes of:
 641  
      * a.xy.asdf.qr
 642  
      */
 643  
     private String subIdentifier;
 644  
 
 645  0
     private static final Logger log = LoggerFactory.getLogger(Verse.class);
 646  
 
 647  
     /**
 648  
      * To make serialization work across new versions
 649  
      */
 650  
     private static final long serialVersionUID = -4033921076023185171L;
 651  
 }