avatar

906

Don't allow unknown currency amounts to get set as max bid. [#733 tagged:committed state:resolved] by mrs, 17 May, 2009 01:20 AM
Diff this changeset:
ebayAuction.java
cyberfox 1   package com.jbidwatcher.auction.server.ebay;
cyberfox 2   
mrs      3   import com.jbidwatcher.auction.AuctionEntry;
cyberfox 4   import com.jbidwatcher.auction.SpecificAuction;
mrs      5   import com.jbidwatcher.util.config.*;
mrs      6   import com.jbidwatcher.util.Externalized;
mrs      7   import com.jbidwatcher.util.queue.MQFactory;
mrs      8   import com.jbidwatcher.util.queue.PlainMessageQueue;
cyberfox 9   import com.jbidwatcher.util.*;
cyberfox 10  import com.jbidwatcher.util.html.JHTML;
cyberfox 11  import com.jbidwatcher.util.html.htmlToken;
mrs      12  import com.jbidwatcher.util.Constants;
cyberfox 13  
mrs      14  import java.util.Date;
mrs      15  import java.util.regex.Matcher;
mrs      16  import java.util.regex.Pattern;
cyberfox 17  
cyberfox 18  /**
mrs      19   * User: Morgan
mrs      20   * Date: Feb 25, 2007
mrs      21   * Time: 4:08:52 PM
mrs      22   * The core eBay auction parsing class.
mrs      23   * Nearly everything about the item is set by code in this class.
mrs      24   */
cyberfox 25  class ebayAuction extends SpecificAuction {
mrs      26    private static Currency zeroDollars = new Currency("$0.00");
mrs      27    String mBidCountScript = null;
mrs      28    String mStartComment = null;
cyberfox 29    private static final int TITLE_LENGTH = 60;
cyberfox 30    private static final int HIGH_BIT_SET = 0x80;
cyberfox 31    private final Pattern p = Pattern.compile(Externalized.getString("ebayServer.thumbSearch"), Pattern.DOTALL | Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
cyberfox 32    private final Pattern p2 = Pattern.compile(Externalized.getString("ebayServer.thumbSearch2"), Pattern.DOTALL | Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
cyberfox 33    private String potentialThumbnail = null;
mrs      34    private TT T;
cyberfox 35  
mrs      36    protected ebayAuction(TT countryProperties) {
mrs      37      super();
mrs      38      T = countryProperties;
mrs      39    }
mrs      40  
cyberfox 41    private void checkThumb(StringBuffer sb) {
cyberfox 42      Matcher imgMatch = p.matcher(sb);
cyberfox 43      if(imgMatch.find()) {
cyberfox 44        potentialThumbnail = imgMatch.group(1);
cyberfox 45      } else {
cyberfox 46        imgMatch = p2.matcher(sb);
cyberfox 47        if(imgMatch.find()) {
cyberfox 48          potentialThumbnail = imgMatch.group(1);
cyberfox 49        }
cyberfox 50      }
cyberfox 51    }
cyberfox 52  
cyberfox 53    /**
cyberfox 54     * @brief Delete the 'description' portion of a page, all scripts, and comments.
cyberfox 55     *
cyberfox 56     * @param sb - The StringBuffer to clean of description and scripts.
cyberfox 57     */
cyberfox 58    public void cleanup(StringBuffer sb) {
cyberfox 59      checkThumb(sb);
mrs      60  
cyberfox 61      //  We ignore the result of this, because it's just useful if it
cyberfox 62      //  works, it's not critical.
cyberfox 63      StringTools.deleteFirstToLast(sb, Externalized.getString("ebayServer.description"), Externalized.getString("ebayServer.descriptionMotors"), Externalized.getString("ebayServer.descriptionEnd"), Externalized.getString("ebayServer.descriptionClosedEnd"));
cyberfox 64      StringTools.deleteFirstToLast(sb, Externalized.getString("ebayServer.descStart"), Externalized.getString("ebayServer.descriptionMotors"), Externalized.getString("ebayServer.descEnd"), Externalized.getString("ebayServer.descriptionClosedEnd"));
cyberfox 65  
cyberfox 66      String skimOver = sb.toString();
cyberfox 67  
cyberfox 68      Matcher startCommentSearch = Pattern.compile(Externalized.getString("ebayServer.startedRegex")).matcher(skimOver);
mrs      69      if(startCommentSearch.find())
mrs      70        mStartComment = startCommentSearch.group(1);
mrs      71      else
mrs      72        mStartComment = "";
cyberfox 73  
mrs      74      Matcher bidCountSearch = Pattern.compile(T.s("ebayServer.bidCountRegex")).matcher(skimOver);
mrs      75      if(bidCountSearch.find())
mrs      76        mBidCountScript = bidCountSearch.group(1);
mrs      77      else
mrs      78        mBidCountScript = "";
cyberfox 79  
cyberfox 80      //  Use eBay's cleanup method to finish up with.
cyberfox 81      new ebayCleaner().cleanup(sb);
cyberfox 82    }
cyberfox 83  
mrs      84    boolean checkValidTitle(String auctionTitle) {
cyberfox 85      String[] eBayTitles = new String[]{
mrs      86          T.s("ebayServer.titleEbay"),
mrs      87          T.s("ebayServer.titleEbay2"),
mrs      88          T.s("ebayServer.titleEbay3"),
mrs      89          T.s("ebayServer.titleMotors"),
mrs      90          T.s("ebayServer.titleMotors2"),
mrs      91          T.s("ebayServer.titleDisney"),
mrs      92          T.s("ebayServer.titleCollections")};
cyberfox 93  
cyberfox 94      for (String eBayTitle : eBayTitles) {
mrs      95        if(auctionTitle.matches(eBayTitle)) return true;
cyberfox 96      }
cyberfox 97  
cyberfox 98      return false;
cyberfox 99    }
cyberfox 100 
mrs      101   private Currency getUSCurrency(Currency val, JHTML htmlDoc) {
mrs      102     Currency newCur = zeroDollars;
cyberfox 103 
cyberfox 104     if(val != null && !val.isNull()) {
mrs      105       if (val.getCurrencyType() == Currency.US_DOLLAR) {
cyberfox 106         newCur = val;
cyberfox 107       } else {
mrs      108         newCur = walkForUSCurrency(htmlDoc);
mrs      109       }
mrs      110     }
mrs      111 
mrs      112     return newCur;
mrs      113   }
mrs      114 
mrs      115   /**
mrs      116    * If the next text doesn't contain a USD amount, it's separated somehow.
mrs      117    * Skim forward until we either find something, or give up.  (6 steps for now.)
mrs      118    *
mrs      119    * @param html   - The document to search.
mrs      120    * @return - Either zeroDollars or the approximate USD equivalent of the value of the item.
mrs      121    */
mrs      122   private Currency walkForUSCurrency(JHTML html) {
mrs      123     Currency newCur = zeroDollars;
mrs      124     int count = 0;
mrs      125 
mrs      126     String usdPattern = Externalized.getString("ebayServer.USD");
mrs      127     do {
mrs      128       String approxAmount = html.getNextContent();
mrs      129       approxAmount = StringTools.stripHigh(approxAmount, "");
mrs      130       approxAmount = approxAmount.replaceAll("\\s+", " ");
mrs      131       int matchAtIndex = approxAmount.indexOf(usdPattern);
mrs      132       if (matchAtIndex != -1) {
mrs      133         approxAmount = approxAmount.substring(matchAtIndex); //$NON-NLS-1$
mrs      134         newCur = Currency.getCurrency(approxAmount);
mrs      135         if (newCur.getCurrencyType() != Currency.US_DOLLAR) newCur = zeroDollars;
cyberfox 136       }
mrs      137     } while (count++ < 6 && newCur == zeroDollars);
cyberfox 138 
cyberfox 139     return newCur;
cyberfox 140   }
cyberfox 141 
cyberfox 142   private Pattern digits = Pattern.compile("([0-9]+)");
cyberfox 143 
cyberfox 144   int getDigits(String digitsStarting) {
cyberfox 145     Matcher m = digits.matcher(digitsStarting);
mrs      146     if(m.find()) {
mrs      147       String rawCount = m.group();
mrs      148       if (rawCount != null) {
mrs      149         return Integer.parseInt(rawCount);
mrs      150       }
cyberfox 151     }
cyberfox 152     return -1;
cyberfox 153   }
cyberfox 154 
cyberfox 155   /**
cyberfox 156    * @brief Check the title for unavailable or 'removed item' messages.
cyberfox 157    *
mrs      158    * @param title - The title from the web page, to check.
cyberfox 159    */
mrs      160   private void handleBadTitle(String title) {
mrs      161     if(title.indexOf(T.s("ebayServer.unavailable")) != -1) {
cyberfox 162       MQFactory.getConcrete("Swing").enqueue("LINK DOWN eBay (or the link to eBay) appears to be down.");
cyberfox 163       MQFactory.getConcrete("Swing").enqueue("eBay (or the link to eBay) appears to be down for the moment.");
mrs      164     } else if(title.indexOf(T.s("ebayServer.invalidItem")) != -1) {
mrs      165       JConfig.log().logDebug("Found bad/deleted item.");
cyberfox 166     } else {
mrs      167       JConfig.log().logDebug("Failed to load auction title from header: \"" + title + '\"');
cyberfox 168     }
cyberfox 169   }
cyberfox 170 
cyberfox 171   /**
cyberfox 172    * @brief Build the title from the data on the web page, pulling HTML tokens out as it goes.
cyberfox 173    *
cyberfox 174    * @param doc - The document to pull the title from.
cyberfox 175    *
cyberfox 176    * @return - A string consisting of just the title part of the page, with tags stripped.
cyberfox 177    */
cyberfox 178   private String buildTitle(JHTML doc) {
cyberfox 179     //  This is an HTML title...  Suck.
cyberfox 180     doc.reset();
cyberfox 181     doc.getNextTag();
cyberfox 182     StringBuffer outTitle = new StringBuffer(TITLE_LENGTH);
cyberfox 183     //  Iterate over the tokens, adding all content to the
cyberfox 184     //  title tag until the end of the title.
cyberfox 185     htmlToken jh;
cyberfox 186     do {
cyberfox 187       jh = doc.nextToken();
cyberfox 188       if(jh.getTokenType() == htmlToken.HTML_CONTENT) {
cyberfox 189         outTitle.append(jh.getToken());
cyberfox 190       }
cyberfox 191     } while(!(jh.getTokenType() == htmlToken.HTML_ENDTAG &&
cyberfox 192               jh.getToken().equalsIgnoreCase("/title")));
cyberfox 193 
cyberfox 194     return outTitle.toString();
cyberfox 195   }
cyberfox 196 
mrs      197   private Pattern amountPat = Pattern.compile("([0-9]+\\.[0-9]+|(?i)free)");
cyberfox 198 
mrs      199   private void loadShippingInsurance(Currency sampleAmount) {
mrs      200     String shipString = mDocument.getNextContentAfterRegex(T.s("ebayServer.shipping"));
cyberfox 201     //  Sometimes the next content might not be the shipping amount, it might be the next-next.
cyberfox 202     Matcher amount = null;
cyberfox 203     boolean amountFound = false;
cyberfox 204     if(shipString != null) {
cyberfox 205       amount = amountPat.matcher(shipString);
cyberfox 206       amountFound = amount.find();
cyberfox 207       if (!amountFound) {
mrs      208         shipString = mDocument.getNextContent();
cyberfox 209         amount = amountPat.matcher(shipString);
cyberfox 210         if (shipString != null) amountFound = amount.find();
cyberfox 211       }
cyberfox 212     }
cyberfox 213     //  This will result in either 'null' or the amount.
cyberfox 214     if(shipString != null && amountFound) shipString = amount.group();
cyberfox 215 
cyberfox 216     //  Step back two contents, to check if it's 'Payment
cyberfox 217     //  Instructions', in which case, the shipping and handling
cyberfox 218     //  came from their instructions box, not the
cyberfox 219     //  standard-formatted data.
mrs      220     String shipStringCheck = mDocument.getPrevContent(2);
cyberfox 221 
mrs      222     String insureString = mDocument.getNextContentAfterRegex(T.s("ebayServer.shippingInsurance"));
mrs      223     String insuranceOptionalCheck = mDocument.getNextContent();
cyberfox 224 
cyberfox 225     //  Default to thinking it's optional if the word 'required' isn't found.
cyberfox 226     //  You don't want to make people think it's required if it's not.
mrs      227     setInsuranceOptional(insuranceOptionalCheck == null || (insuranceOptionalCheck.toLowerCase().indexOf(T.s("ebayServer.requiredInsurance")) == -1));
cyberfox 228 
cyberfox 229     if(insureString != null) {
cyberfox 230       if(insureString.equals("-") || insureString.equals("--")) {
cyberfox 231         insureString = null;
cyberfox 232       } else {
cyberfox 233         insureString = insureString.trim();
cyberfox 234       }
cyberfox 235     }
cyberfox 236 
mrs      237     if(shipStringCheck != null && !shipStringCheck.equals(T.s("ebayServer.paymentInstructions"))) {
cyberfox 238       if(shipString != null) {
cyberfox 239         if(shipString.equals("-")) {
cyberfox 240           shipString = null;
cyberfox 241         } else {
cyberfox 242           shipString = shipString.trim();
cyberfox 243         }
cyberfox 244       }
cyberfox 245     } else {
cyberfox 246       shipString = null;
cyberfox 247     }
cyberfox 248 
cyberfox 249     if(shipString != null) {
cyberfox 250       if(shipString.equalsIgnoreCase("free")) {
mrs      251         setShipping(Currency.getCurrency(sampleAmount.fullCurrencyName(), "0.0"));
cyberfox 252       } else {
cyberfox 253         try {
mrs      254           setShipping(Currency.getCurrency(sampleAmount.fullCurrencyName(), shipString));
cyberfox 255         } catch(NumberFormatException nfe) {
mrs      256           setShipping(Currency.NoValue());
cyberfox 257         }
cyberfox 258       }
cyberfox 259     } else {
mrs      260       setShipping(Currency.NoValue());
cyberfox 261     }
cyberfox 262     try {
mrs      263       setInsurance(Currency.getCurrency(insureString));
cyberfox 264     } catch(NumberFormatException nfe) {
mrs      265       setInsurance(Currency.NoValue());
cyberfox 266     }
cyberfox 267   }
cyberfox 268 
mrs      269   private void loadBuyNow() {
mrs      270     setBuyNow(Currency.NoValue());
mrs      271     setBuyNowUS(zeroDollars);
mrs      272 
mrs      273     String altBuyNowString1 = mDocument.getNextContentAfterRegexIgnoring(T.s("ebayServer.price"), "([Ii]tem.[Nn]umber|^\\s*[0-9]+\\s*$)");
mrs      274     if(altBuyNowString1 != null) {
mrs      275       altBuyNowString1 = altBuyNowString1.trim();
cyberfox 276     }
mrs      277     if(altBuyNowString1 != null && altBuyNowString1.length() != 0) {
mrs      278       setBuyNow(Currency.getCurrency(altBuyNowString1));
mrs      279       if(getBuyNow().isNull()) {
mrs      280         altBuyNowString1 = mDocument.getNextContentAfterContent("Buy It Now:");
mrs      281         setBuyNow(Currency.getCurrency(altBuyNowString1));
mrs      282       }
mrs      283       setBuyNowUS(getUSCurrency(getBuyNow(), mDocument));
cyberfox 284     }
cyberfox 285   }
cyberfox 286 
cyberfox 287   private String getEndDate(String inTitle) {
cyberfox 288     String result = null;
cyberfox 289 
mrs      290     String dateMatch = T.s("title.end_time");
mrs      291     Pattern datePat = Pattern.compile(dateMatch);
mrs      292 
mrs      293     Matcher when = datePat.matcher(inTitle);
mrs      294     if(when.find()) result = when.group(2);
cyberfox 295 
cyberfox 296     return result;
cyberfox 297   }
cyberfox 298 
cyberfox 299   /**
cyberfox 300    * A utility function to check the provided preferred object against an arbitrary 'bad' value,
cyberfox 301    * and return the preferred object if it's not bad, and an alternative object if it the preferred
cyberfox 302    * object is bad.
cyberfox 303    *
cyberfox 304    * @param preferred - The preferred object (to be compared against the 'bad' value)
cyberfox 305    * @param alternate - The alternative object, if the first object is bad.
cyberfox 306    * @param bad - The bad object to validate the preferred object against.
cyberfox 307    *
cyberfox 308    * @return - preferred if it's not bad, alternate if the preferred object is bad.
cyberfox 309    * @noinspection ObjectEquality
cyberfox 310    **/
cyberfox 311   private Object ensureSafeValue(Object preferred, Object alternate, Currency bad) {
cyberfox 312     return (preferred == bad)?alternate:preferred;
cyberfox 313   }
cyberfox 314 
cyberfox 315   private String getResult(JHTML doc, String regex, int match) {
cyberfox 316     String rval = doc.grep(regex);
cyberfox 317     if(rval != null) {
cyberfox 318       if(match == 0) return rval;
cyberfox 319       Pattern searcher = Pattern.compile(regex);
cyberfox 320       Matcher matcher = searcher.matcher(rval);
cyberfox 321       if(matcher.matches()) return matcher.group(match);
cyberfox 322     }
cyberfox 323 
cyberfox 324     return null;
cyberfox 325   }
cyberfox 326 
mrs      327   private void loadOptionalInformation(JHTML doc) {
cyberfox 328     try {
mrs      329       loadFeedback(doc);
cyberfox 330 
mrs      331       String location = doc.getNextContentAfterRegex(T.s("ebayServer.itemLocationRegex"));
cyberfox 332       if(location != null) {
mrs      333         setItemLocation(StringTools.decode(location, doc.getCharset()));
cyberfox 334       }
cyberfox 335 
mrs      336       loadPaypal(doc);
cyberfox 337     } catch(Throwable t) {
cyberfox 338       //  I don't actually CARE about any of this data, or any errors that occur on loading it, so don't mess things up on errors.
mrs      339       String msg = t.getMessage();
mrs      340       if(msg != null) {
mrs      341         JConfig.log().logDebug(msg);
mrs      342       } else {
mrs      343         JConfig.log().handleException("A weird error occurred", t);
mrs      344       }
cyberfox 345     }
cyberfox 346   }
cyberfox 347 
mrs      348   private void loadPaypal(JHTML doc) {
mrs      349     String pbp = getResult(doc, T.s("ebayServer.paypalMatcherRegex"), 0);
mrs      350     if(pbp != null) {
mrs      351       setPaypal(true);
mrs      352     } else {
mrs      353       String preferred = doc.getNextContentAfterRegex("PayPal.?");
mrs      354       if(preferred != null) {
mrs      355         if(preferred.indexOf("preferred") != -1) setPaypal(true);
mrs      356         if(preferred.indexOf("accepted") != -1) setPaypal(true);
mrs      357       }
mrs      358       String methods = doc.getNextContentAfterRegex("Payment methods:?");
mrs      359       //  If it's not the first payment method...
mrs      360       //  It might be the second.
mrs      361       int i=0;
mrs      362       while (i<3 && !hasPaypal()) {
mrs      363         if (methods != null && methods.equalsIgnoreCase("paypal")) setPaypal(true);
mrs      364         else methods = doc.getNextContent();
mrs      365         i++;
mrs      366       }
mrs      367     }
mrs      368   }
mrs      369 
mrs      370   private void loadFeedback(JHTML doc) {
mrs      371     String score = doc.getContentBeforeContent(T.s("ebayServer.feedback"));
mrs      372     if(score != null && StringTools.isNumberOnly(score)) {
mrs      373       mSeller.setFeedback(Integer.parseInt(score));
mrs      374     }
mrs      375 
mrs      376     String percentage = doc.getNextContentAfterContent(T.s("ebayServer.feedback"));
mrs      377     if(percentage != null) mSeller.setPositivePercentage(percentage);
mrs      378   }
mrs      379 
cyberfox 380   /**
cyberfox 381    * @brief - Not brief at all; wow...this is way too long.
cyberfox 382    *
cyberfox 383    * @param ae - The auction entry to update or null if it's a new auction.
cyberfox 384    *
cyberfox 385    * @return - false if the parse failed, true if it succeeded.  This needs
cyberfox 386    * to be turned into a set of enums.
cyberfox 387    */
mrs      388   public ParseErrors parseAuction(AuctionEntry ae) {
cyberfox 389     //  Verify the title (in case it's an invalid page, the site is
cyberfox 390     //  down for maintenance, etc).
mrs      391     String prelimTitle;
mrs      392     try {
mrs      393       prelimTitle = checkTitle();
mrs      394     } catch (ParseException e) {
mrs      395       finish();
mrs      396       return e.getError();
mrs      397     }
mrs      398 
mrs      399     if(prelimTitle == null) {
mrs      400       finish();
mrs      401       return ParseErrors.BAD_TITLE;
mrs      402     }
mrs      403 
mrs      404     if(getIdentifier() == null && (ae == null || ae.getIdentifier() == null)) {
mrs      405       parseIdentifier();
mrs      406     }
mrs      407 
mrs      408     Integer quant = getNumberFromLabel(mDocument, T.s("ebayServer.quantity"), T.s("ebayServer.postTitleIgnore"));
mrs      409 
mrs      410     //  Get the integer values (Quantity, Bidcount)
mrs      411     setQuantity(quant == null ? 1 : quant);
mrs      412 
mrs      413     setFixedPrice(false);
mrs      414     setNumBids(getBidCount(mDocument, getQuantity()));
mrs      415     if(!isFixedPrice() && quant != null) setDutch(true);
mrs      416 
mrs      417     try {
mrs      418       loadBuyNow();
mrs      419     } catch(Exception e) {
mrs      420       JConfig.log().handleException("Buy It Now Loading error", e);
mrs      421     }
mrs      422 
mrs      423     if (isFixedPrice()) {
mrs      424       establishCurrentBidFixedPrice(ae);
mrs      425     } else {
mrs      426       Currency maxBid = establishCurrentBid(ae);
mrs      427       setOutbid(mDocument.grep(T.s("ebayServer.outbid")) != null);
mrs      428       setMaxBidFromServer(ae, maxBid);
mrs      429     }
mrs      430 
mrs      431     if(getMinBid() == null && getBuyNow() != null && !getBuyNow().isNull()) {
mrs      432       setMinBid(getBuyNow());
mrs      433     }
mrs      434     try {
mrs      435       Currency sample = getCurBid();
mrs      436       if(sample.isNull()) sample = getMinBid();
mrs      437       loadShippingInsurance(sample);
mrs      438     } catch(Exception e) {
mrs      439       JConfig.log().handleException("Shipping / Insurance Loading Failed", e);
mrs      440     }
mrs      441 
mrs      442     if (checkSeller(ae)) return ParseErrors.SELLER_AWAY;
mrs      443 
mrs      444     checkDates(prelimTitle, ae);
mrs      445     checkHighBidder();
mrs      446     checkReserve();
mrs      447     checkPrivate();
mrs      448 
mrs      449     loadOptionalInformation(mDocument);
mrs      450     checkThumbnail();
mrs      451 
mrs      452     finish();
mrs      453     this.saveDB();
mrs      454     return ParseErrors.SUCCESS;
mrs      455   }
mrs      456 
mrs      457   private void parseIdentifier() {
mrs      458     String itemNumberContent = mDocument.grep(T.s("ebayServer.itemNumber"));
mrs      459     if(itemNumberContent != null) {
mrs      460       Pattern p = Pattern.compile(T.s("ebayServer.itemNumber"));
mrs      461       Matcher m = p.matcher(itemNumberContent);
mrs      462       if (m.matches()) {
mrs      463         setIdentifier(m.group(1));
mrs      464       }
mrs      465     } else {
mrs      466       itemNumberContent = mDocument.getNextContentAfterRegex("(?i).*item.number.*");
mrs      467       if(itemNumberContent != null && StringTools.isNumberOnly(itemNumberContent)) {
mrs      468         setIdentifier(itemNumberContent);
mrs      469       }
mrs      470     }
mrs      471   }
mrs      472 
mrs      473   private static class ParseException extends Exception {
mrs      474     private ParseErrors mError;
mrs      475     public ParseException(ParseErrors error) {
mrs      476       mError = error;
mrs      477     }
mrs      478 
mrs      479     public ParseErrors getError() {
mrs      480       return mError;
mrs      481     }
mrs      482   }
mrs      483 
mrs      484   /**
mrs      485    * Sets title, and possibly end.
mrs      486    *
mrs      487    * @return - The preliminary extraction of the title, in its entirety, for later parsing.  null if a failure occurred.
mrs      488    * @throws com.jbidwatcher.auction.server.ebay.ebayAuction.ParseException - An exception that describes what's wrong with the title.
mrs      489    */
mrs      490   private String checkTitle() throws ParseException {
mrs      491     String prelimTitle = mDocument.getTitle();
cyberfox 492     if( prelimTitle == null) {
mrs      493       prelimTitle = T.s("ebayServer.unavailable");
cyberfox 494     }
mrs      495     if(prelimTitle.equals(T.s("ebayServer.adultPageTitle")) || prelimTitle.indexOf("Terms of Use") != -1) {
mrs      496       throw new ParseException(ParseErrors.NOT_ADULT);
cyberfox 497     }
cyberfox 498 
mrs      499     if(prelimTitle.equals(T.s("ebayServer.invalidItem"))) {
mrs      500       String realListing = mDocument.getLinkForContent(T.s("view.original.listing"));
mrs      501       if(realListing != null) {
mrs      502         setURL(realListing);
mrs      503         throw new ParseException(ParseErrors.WRONG_SITE);        
mrs      504       }
mrs      505       throw new ParseException(ParseErrors.DELETED);
mrs      506     }
mrs      507 
mrs      508     if(prelimTitle.equals("Security Measure")) {
mrs      509       throw new ParseException(ParseErrors.CAPTCHA);
mrs      510     }
mrs      511 
cyberfox 512     //  Is this a valid eBay item page?
mrs      513     if(prelimTitle != null && !checkValidTitle(prelimTitle)) {
mrs      514       handleBadTitle(prelimTitle);
mrs      515       if(getURL() != null) {
mrs      516         throw new ParseException(ParseErrors.WRONG_SITE);
mrs      517       } else {
mrs      518         throw new ParseException(ParseErrors.BAD_TITLE);
mrs      519       }
cyberfox 520     }
cyberfox 521 
mrs      522     if(prelimTitle != null) {
mrs      523       //  If we got a valid title, mark the link as up, because it worked...
mrs      524       MQFactory.getConcrete("Swing").enqueue("LINK UP");
cyberfox 525 
mrs      526       boolean ebayMotors = false;
mrs      527       if(prelimTitle.matches(T.s("ebayServer.ebayMotorsTitle"))) ebayMotors = true;
mrs      528       //  This is mostly a hope, not a guarantee, as eBay might start
mrs      529       //  cross-advertising eBay Motors in their normal pages, or
mrs      530       //  something.
mrs      531       if(doesLabelExist(T.s("ebayServer.ebayMotorsTitle"))) ebayMotors = true;
cyberfox 532 
mrs      533       setEnd(null);
mrs      534       setTitle(null);
cyberfox 535 
mrs      536       //  This sucks.  They changed to: eBay: {title} (item # end time {endtime})
mrs      537       if(prelimTitle.matches(T.s("ebayServer.titleEbay2")) ||
mrs      538          prelimTitle.matches(T.s("ebayServer.titleMotors2"))) {
mrs      539         //  Handle the new titles.
mrs      540         Pattern newTitlePat = Pattern.compile(T.s("ebayServer.titleMatch"));
mrs      541         Matcher newTitleMatch = newTitlePat.matcher(prelimTitle);
mrs      542         if (newTitleMatch.find()) {
mrs      543           setTitle(StringTools.decode(newTitleMatch.group(1), mDocument.getCharset()));
mrs      544           String endDate = newTitleMatch.group(4);
mrs      545           if(getEnd() != null) setEnd(StringTools.figureDate(endDate, T.s("ebayServer.dateFormat")).getDate());
mrs      546         }
cyberfox 547       }
cyberfox 548 
mrs      549       if(getTitle() == null) {
mrs      550         boolean htmlTitle = false;
mrs      551         //  The first element after the title is always the description.  Unfortunately, it's in HTML-encoded format,
mrs      552         //  so there are &lt;'s, and such.  While I could translate that, that's something I can wait on.  --  HACKHACK
mrs      553         //      title = (String)contentFields.get(1);
mrs      554         //  For now, just load from the title, everything after ') - '.
mrs      555         int titleIndex = prelimTitle.indexOf(") - ");
mrs      556         if(titleIndex == -1) {
mrs      557           titleIndex = prelimTitle.indexOf(") -");
mrs      558           //  This is an HTML title...  Suck.
mrs      559           htmlTitle = true;
mrs      560         }
cyberfox 561 
mrs      562         //  Always convert, at this point, from iso-8859-1 (iso latin-1) to UTF-8.
mrs      563         if(htmlTitle) {
mrs      564           setTitle(StringTools.decode(buildTitle(mDocument), mDocument.getCharset()));
mrs      565         } else {
mrs      566           setTitle(StringTools.decode(prelimTitle.substring(titleIndex+4).trim(), mDocument.getCharset()));
mrs      567         }
cyberfox 568       }
cyberfox 569 
mrs      570       if(getTitle().length() == 0) setTitle("(bad title)");
mrs      571       setTitle(JHTML.deAmpersand(getTitle()));
cyberfox 572 
mrs      573       // eBay Motors titles are really a combination of the make/model,
mrs      574       // and the user's own text.  Under BIBO, the user's own text is
mrs      575       // below the 'description' fold.  For now, we don't get the user
mrs      576       // text.
mrs      577       if(ebayMotors) {
mrs      578         extractMotorsTitle();
mrs      579       }
cyberfox 580     }
mrs      581     return prelimTitle;
mrs      582   }
cyberfox 583 
mrs      584   /**
mrs      585    * Sets start and end.
mrs      586    *
mrs      587    * @param prelimTitle - The preliminary title block, because sometimes it has date information in it.
mrs      588    * @param ae - The old auction, in case we need to fall back because we can't figure out the ending date.
mrs      589    */
mrs      590   private void checkDates(String prelimTitle, AuctionEntry ae) {
mrs      591     setStart(StringTools.figureDate(mDocument.getNextContentAfterRegexIgnoring(T.s("ebayServer.startTime"), T.s("ebayServer.postTitleIgnore")), T.s("ebayServer.dateFormat")).getDate());
mrs      592     if (getStart() == null) {
mrs      593       setStart(StringTools.figureDate(mStartComment, T.s("ebayServer.dateFormat")).getDate());
cyberfox 594     }
mrs      595     setStart((Date) ensureSafeValue(getStart(), ae != null ? ae.getStartDate() : null, null));
cyberfox 596 
mrs      597     if (getEnd() == null) {
cyberfox 598       String endDate = getEndDate(prelimTitle);
mrs      599       setEnd(StringTools.figureDate(endDate, T.s("ebayServer.dateFormat")).getDate());
cyberfox 600     }
cyberfox 601 
mrs      602     //  Handle odd case...
mrs      603     if (getEnd() == null) {
mrs      604       setEnd(StringTools.figureDate(mDocument.getNextContentAfterRegex(T.s("ebayServer.endsPrequel")), T.s("ebayServer.dateFormat")).getDate());
mrs      605       if (getEnd() == null) {
mrs      606         String postContent = mDocument.getNextContent().replaceAll("[()]", "");
mrs      607         setEnd(StringTools.figureDate(postContent, T.s("ebayServer.dateFormat")).getDate());
cyberfox 608       }
cyberfox 609     }
cyberfox 610 
mrs      611     setEnd((Date) ensureSafeValue(getEnd(), ae != null ? ae.getEndDate() : null, null));
mrs      612     if(getEnd() != null) {
mrs      613       if (getEnd().getTime() > System.currentTimeMillis()) {
mrs      614         //  Item is not ended yet.
mrs      615         if (ae != null) {
mrs      616           ae.setComplete(false);
mrs      617           ae.setSticky(false);
mrs      618         }
mrs      619       }
mrs      620     } else {
mrs      621       if(ae != null) setEnd(ae.getEndDate());
mrs      622       if(mDocument.grep(T.s("ebayServer.ended")) != null) {
mrs      623         if(ae != null) ae.setComplete(true);
mrs      624         setEnd(new Date());
mrs      625       } else {
mrs      626         if(isFixedPrice()) {
mrs      627           String durationRaw = mDocument.getNextContentAfterContent("Duration:");
mrs      628           if(durationRaw != null) {
mrs      629             String duration = durationRaw.replaceAll("[^0-9]", "");
mrs      630             long days = Long.parseLong(duration);
mrs      631             if(getStart() != null && !getStart().equals(Constants.LONG_AGO)) {
mrs      632               long endTime = getStart().getTime() + Constants.ONE_DAY * days;
mrs      633               setEnd(new Date(endTime));
mrs      634             } else {
mrs      635               setEnd(Constants.FAR_FUTURE);
mrs      636             }
mrs      637           } else {
mrs      638             JConfig.log().logMessage("Setting auction #" + getIdentifier() + " to be a 'Far Future' listing, as it has no date info.");
mrs      639             setEnd(Constants.FAR_FUTURE);
mrs      640           }
mrs      641         }
mrs      642       }
cyberfox 643     }
cyberfox 644 
mrs      645     if (getStart() == null) setStart(Constants.LONG_AGO);
mrs      646     if (getEnd() == null) setEnd(Constants.FAR_FUTURE);
mrs      647   }
cyberfox 648 
mrs      649   /**
mrs      650    * Sets the user's max bid, based on what eBay thinks it is, to catch out-of-JBidwatcher bidding.
mrs      651    *
mrs      652    * @param ae - The auction entry, so we can set the max bid value.
mrs      653    * @param maxBid - The max bid extracted from eBay.
mrs      654    */
mrs      655   private void setMaxBidFromServer(AuctionEntry ae, Currency maxBid) {
mrs      656     // This is dangerously intimate with the AuctionEntry class,
mrs      657     // and it won't work the first time, since the first time ae
mrs      658     // is null.
mrs      659     if(ae != null && !maxBid.isNull()) {
mrs      660       try {
mrs      661         if(!ae.isBidOn() || ae.getBid().less(maxBid)) ae.setBid(maxBid);
mrs      662       } catch(Currency.CurrencyTypeException cte) {
mrs      663         JConfig.log().handleException("eBay says my max bid is a different type of currency than I have stored!", cte);
cyberfox 664       }
mrs      665     }
mrs      666   }
cyberfox 667 
mrs      668   private Currency establishCurrentBid(AuctionEntry ae) {
mrs      669     //  The set of tags that indicate the current/starting/lowest/winning
mrs      670     //  bid are 'Current bid', 'Starting bid', 'Lowest bid',
mrs      671     //  'Winning bid' so far.
mrs      672     String cvtCur = mDocument.getNextContentAfterRegex(T.s("ebayServer.currentBid"));
mrs      673     setCurBid(Currency.getCurrency(cvtCur));
mrs      674     setUSCur(getUSCurrency(getCurBid(), mDocument));
cyberfox 675 
mrs      676     if(getCurBid() == null || getCurBid().isNull()) {
mrs      677       if(getQuantity() > 1) {
mrs      678         setCurBid(Currency.getCurrency(mDocument.getNextContentAfterContent(T.s("ebayServer.lowestBid"))));
mrs      679         setUSCur(getUSCurrency(getCurBid(), mDocument));
cyberfox 680       }
mrs      681     }
cyberfox 682 
mrs      683     setMinBid(Currency.getCurrency(mDocument.getNextContentAfterContent(T.s("ebayServer.firstBid"))));
mrs      684     Currency maxBid = Currency.getCurrency(mDocument.getNextContentAfterContent(T.s("ebayServer.yourMaxBid")));
mrs      685     if(maxBid != null && maxBid.getCurrencyType() == Currency.NONE) maxBid = Currency.NoValue();
cyberfox 686 
mrs      687     setMinBid((Currency)ensureSafeValue(getMinBid(), ae!=null?ae.getMinBid()  :Currency.NoValue(), Currency.NoValue()));
mrs      688     setCurBid((Currency)ensureSafeValue(getCurBid(), ae!=null?ae.getCurBid()  :Currency.NoValue(), Currency.NoValue()));
mrs      689     setUSCur((Currency)ensureSafeValue(getUSCur(), ae!=null?ae.getUSCurBid():zeroDollars, Currency.NoValue()));
cyberfox 690 
mrs      691     if(getNumBids() == 0 && (getMinBid() == null || getMinBid().isNull())) setMinBid(getCurBid());
cyberfox 692 
mrs      693     if(getMinBid() == null || getMinBid().isNull()) {
mrs      694       String original = mDocument.grep(T.s("ebayServer.originalBid"));
mrs      695       if(original != null) {
mrs      696         Pattern bidPat = Pattern.compile(T.s("ebayServer.originalBid"));
mrs      697         Matcher bidMatch = bidPat.matcher(original);
mrs      698         if (bidMatch.find()) {
mrs      699           setMinBid(Currency.getCurrency(bidMatch.group(1)));
cyberfox 700         }
cyberfox 701       }
cyberfox 702     }
mrs      703     return maxBid;
mrs      704   }
cyberfox 705 
mrs      706   private void establishCurrentBidFixedPrice(AuctionEntry ae) {
mrs      707     if(getBuyNow() != null && !getBuyNow().isNull()) {
mrs      708       setMinBid(getBuyNow());
mrs      709       setCurBid(getBuyNow());
mrs      710       setUSCur(getBuyNowUS());
mrs      711     } else {
mrs      712       //  The set of tags that indicate the current/starting/lowest/winning
mrs      713       //  bid are 'Starts at', 'Current bid', 'Starting bid', 'Lowest bid',
mrs      714       //  'Winning bid' so far.  'Starts at' is mainly for live auctions!
mrs      715       String cvtCur = mDocument.getNextContentAfterRegex(T.s("ebayServer.currentBid"));
mrs      716       setCurBid(Currency.getCurrency(cvtCur));
mrs      717       setUSCur(getUSCurrency(getCurBid(), mDocument));
mrs      718 
mrs      719       setCurBid((Currency)ensureSafeValue(getCurBid(), ae!=null?ae.getCurBid()  :Currency.NoValue(), Currency.NoValue()));
mrs      720       setUSCur((Currency)ensureSafeValue(getUSCur(), ae!=null?ae.getUSCurBid():zeroDollars, Currency.NoValue()));
cyberfox 721     }
mrs      722   }
cyberfox 723 
mrs      724   private boolean checkSeller(AuctionEntry ae) {
mrs      725     String sellerName = mDocument.getNextContentAfterRegex(T.s("ebayServer.seller"));
mrs      726     if(sellerName == null) {
mrs      727       sellerName = mDocument.getNextContentAfterRegex(T.s("ebayServer.sellerInfoPrequel"));
cyberfox 728     }
mrs      729 
mrs      730     if(sellerName == null) {
mrs      731       if(mDocument.grep(T.s("ebayServer.sellerAwayRegex")) != null) {
cyberfox 732         if(ae != null) {
cyberfox 733           ae.setLastStatus("Seller away - item unavailable.");
cyberfox 734         }
cyberfox 735         finish();
mrs      736         return true;
cyberfox 737       } else {
mrs      738         if(ae == null)
mrs      739           sellerName = "(unknown)";
mrs      740         else
mrs      741           sellerName = ae.getSeller();
cyberfox 742       }
cyberfox 743     }
mrs      744     setSellerName(sellerName);
cyberfox 745 
mrs      746     return false;
mrs      747   }
mrs      748 
mrs      749   private void checkThumbnail() {
mrs      750     try {
mrs      751       if(JConfig.queryConfiguration("show.images", "true").equals("true")) {
mrs      752         if(!hasNoThumbnail() && !hasThumbnail()) {
mrs      753           ((PlainMessageQueue)MQFactory.getConcrete("thumbnail")).enqueueObject(this);
mrs      754         }
cyberfox 755       }
mrs      756     } catch(Exception e) {
mrs      757       JConfig.log().handleException("Error handling thumbnail loading", e);
cyberfox 758     }
mrs      759   }
mrs      760 
mrs      761   private void checkPrivate() {
mrs      762     if(getHighBidder().indexOf(T.s("ebayServer.keptPrivate")) != -1) { //$NON-NLS-1$
mrs      763       setPrivate(true);
mrs      764       setHighBidder("(private)"); //$NON-NLS-1$
mrs      765     }
mrs      766   }
mrs      767 
mrs      768   private void checkReserve() {
mrs      769     if(doesLabelExist(T.s("ebayServer.reserveNotMet1")) || //$NON-NLS-1$
mrs      770        doesLabelExist(T.s("ebayServer.reserveNotMet2"))) { //$NON-NLS-1$
mrs      771       setReserve(true);
mrs      772       setReserveMet(false);
mrs      773     } else {
mrs      774       if(doesLabelExist(T.s("ebayServer.reserveMet1")) || //$NON-NLS-1$
mrs      775          doesLabelExist(T.s("ebayServer.reserveMet2"))) { //$NON-NLS-1$
mrs      776         setReserve(true);
mrs      777         setReserveMet(true);
mrs      778       }
mrs      779     }
mrs      780   }
mrs      781 
mrs      782   private void checkHighBidder() {
mrs      783     String bidder = null;
mrs      784     String email = null;
mrs      785 
mrs      786     if(mDocument.grep("This is a private listing.*") != null) {
mrs      787       bidder = "(private)";
mrs      788       setPrivate(true);
mrs      789     } else {
mrs      790       if (isFixedPrice()) {
mrs      791         bidder = mDocument.getNextContentAfterRegex(T.s("ebayServer.buyer"));
mrs      792         if (bidder != null) {
mrs      793           setNumBids(1);
mrs      794           bidder = bidder.trim();
mrs      795           email = findHighBidderEmail(bidder);
cyberfox 796         }
cyberfox 797       } else {
mrs      798         if (getQuantity() > 1 || isDutch()) {
mrs      799           setDutch(true);
mrs      800           bidder = "(dutch)";
mrs      801         } else {
mrs      802           if (getNumBids() != 0) {
mrs      803             bidder = mDocument.getNextContentAfterRegex(T.s("ebayServer.highBidder"));
mrs      804             if (bidder != null) {
mrs      805               bidder = bidder.trim();
mrs      806               email = findHighBidderEmail(bidder);
mrs      807             } else {
mrs      808               bidder = "(unknown)"; //  ...but present.
cyberfox 809             }
cyberfox 810           }
cyberfox 811         }
cyberfox 812       }
cyberfox 813     }
mrs      814 
mrs      815     setHighBidder(bidder == null ? "" : bidder);
mrs      816     setHighBidderEmail(email == null ? "(unknown)" : email);
cyberfox 817   }
cyberfox 818 
mrs      819   private String findHighBidderEmail(String bidder) {
mrs      820     String email = null;
mrs      821 
mrs      822     if(bidder != null) {
mrs      823       email = mDocument.getNextContentAfterContent(bidder, true, false);
mrs      824       if (email != null) {
mrs      825         email = email.trim();
mrs      826         if (email.charAt(0) == '(' && email.charAt(email.length() - 1) == ')' && email.indexOf('@') != -1) {
mrs      827           email = (email.substring(1, email.length() - 1));
mrs      828         }
mrs      829         if (email.equals("(")) email = null;
mrs      830       }
mrs      831     }
mrs      832 
mrs      833     return email;
mrs      834   }
mrs      835 
cyberfox 836   private int getBidCount(JHTML doc, int quantity) {
mrs      837     String rawBidCount = doc.getNextContentAfterRegex(T.s("ebayServer.bidCount"));
mrs      838     if (rawBidCount == null) {
mrs      839       rawBidCount = doc.getContentBeforeContent("See history");
mrs      840       if(rawBidCount != null && rawBidCount.matches("^(Purchased|Bid).*")) {
mrs      841         if (rawBidCount.matches("^Purchased.*")) setFixedPrice(true);
mrs      842         rawBidCount = doc.getPrevContent();
mrs      843       }
mrs      844       if(rawBidCount != null && !StringTools.isNumberOnly(rawBidCount)) rawBidCount = null;
mrs      845     }
mrs      846 
cyberfox 847     int bidCount = 0;
cyberfox 848     if(rawBidCount != null) {
mrs      849       if(rawBidCount.equals(T.s("ebayServer.purchasesBidCount")) ||
mrs      850          rawBidCount.matches(T.s("ebayServer.offerRecognition"))) {
mrs      851         setFixedPrice(true);
cyberfox 852         bidCount = -1;
cyberfox 853       } else {
mrs      854         if(rawBidCount.matches(T.s("ebayServer.bidderListCount"))) {
mrs      855           bidCount = Integer.parseInt(mBidCountScript);
mrs      856           mBidCountScript = null;
cyberfox 857         } else {
cyberfox 858           bidCount = getDigits(rawBidCount);
cyberfox 859         }
cyberfox 860       }
cyberfox 861     }
cyberfox 862 
cyberfox 863     //  If we can't match any digits in the bidcount, or there is no match for the eBayBidCount regex, then
cyberfox 864     //  this is a store or FP item.  Still true under BIBO?
mrs      865     if (rawBidCount == null || bidCount == -1) {
mrs      866       setHighBidder(T.s("ebayServer.fixedPrice"));
mrs      867       setFixedPrice(true);
cyberfox 868 
mrs      869       if (doesLabelExist(T.s("ebayServer.hasBeenPurchased")) ||
mrs      870           doesLabelPrefixExist(T.s("ebayServer.endedEarly"))) {
cyberfox 871         bidCount = quantity;
mrs      872         Date now = new Date();
mrs      873         setEnd(now);
mrs      874         if(getStart() == null) setStart(now);
cyberfox 875       } else {
cyberfox 876         bidCount = 0;
cyberfox 877       }
cyberfox 878     }
cyberfox 879 
cyberfox 880     return bidCount;
cyberfox 881   }
cyberfox 882 
mrs      883   public String getThumbnailURL() {
mrs      884     if(potentialThumbnail != null) return potentialThumbnail;
mrs      885     return getThumbnailById(getIdentifier());
cyberfox 886   }
cyberfox 887 
mrs      888   public String getAlternateSiteThumbnail() {
cyberfox 889     return getThumbnailById(getIdentifier() + "6464");
cyberfox 890   }
cyberfox 891 
mrs      892   private String getThumbnailById(String id) {
mrs      893     return "http://thumbs.ebaystatic.com/pict/" + id + ".jpg";
cyberfox 894   }
cyberfox 895 
mrs      896   private Integer getNumberFromLabel(JHTML doc, String label, String ignore) {
cyberfox 897     String rawQuantity;
cyberfox 898     if(ignore == null) {
cyberfox 899       rawQuantity = doc.getNextContentAfterRegex(label);
cyberfox 900     } else {
cyberfox 901       rawQuantity = doc.getNextContentAfterRegexIgnoring(label, ignore);
cyberfox 902     }
mrs      903     Integer quant2;
cyberfox 904     if(rawQuantity != null) {
cyberfox 905       quant2 = getDigits(rawQuantity);
cyberfox 906     } else {
mrs      907       quant2 = null;
cyberfox 908     }
cyberfox 909     return quant2;
cyberfox 910   }
cyberfox 911 
cyberfox 912   private void extractMotorsTitle() {
mrs      913     String motorsTitle = mDocument.getContentBeforeContent(T.s("ebayServer.itemNum"));
cyberfox 914     if(motorsTitle != null) {
cyberfox 915       motorsTitle = motorsTitle.trim();
cyberfox 916     }
mrs      917     if(motorsTitle != null && motorsTitle.length() != 0 && !getTitle().equals(motorsTitle)) {
cyberfox 918       if(motorsTitle.length() != 1 || motorsTitle.charAt(0) < HIGH_BIT_SET) {
mrs      919         if(getTitle().length() == 0) {
mrs      920           setTitle(StringTools.decodeLatin(motorsTitle));
cyberfox 921         } else {
mrs      922           setTitle(StringTools.decodeLatin(motorsTitle + " (" + getTitle() + ')'));
cyberfox 923         }
cyberfox 924       }
cyberfox 925     }
cyberfox 926   }
cyberfox 927 }

Check out the code: svn co jbidwatcher/trunk/src/com/jbidwatcher/auction/server/ebay/ebayAuction.java