/* * [DVD.java] * * Summary: implements HTML DVD macro to sell a DVD. * * Copyright: (c) 2007-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.1 2007-03-25 add flags, B&N link, refactor */ package com.mindprod.stores; import com.mindprod.common18.EIO; import com.mindprod.common18.ST; import com.mindprod.fastcat.FastCat; import com.mindprod.htmlmacros.macro.Born; import com.mindprod.htmlmacros.macro.CountryFlag; import com.mindprod.htmlmacros.macro.GoogleCannedSearch; import com.mindprod.htmlmacros.macro.Macro; import com.mindprod.htmlmacros.support.BuildImage; import com.mindprod.htmlmacros.support.ImageAlignment; import com.mindprod.htmlmacros.support.Tools; import com.mindprod.hunkio.HunkIO; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.regex.Pattern; import static com.mindprod.htmlmacros.macro.Global.configuration; import static com.mindprod.stores.DStore.*; import static com.mindprod.stores.StockStatus.INSTOCK; import static java.lang.System.*; /** * implements HTML DVD macro to sell a DVD. *

* Expands HTML DVD macro. * Covers images names after asin. * file named after upc *

* Formats data in a table. Sell DVDs at electronic and other sources if UPC is known. * * @author Roedy Green, Canadian Mind Products * @version 1.1 2007-03-25 add flags, B&N link, refactor * @see DStore * @since 2007 */ @SuppressWarnings( { "ConstantConditions" } ) public class DVD extends Macro { // declarations /** * Google search modifier to ensure find only other DVD stores */ private static final String JUST_OTHER_DVDSTORES = "-amazon -site:barnesandnoble.com -site:powells.com"; /** * this item goes on the left side. */ private static final boolean ON_LEFT = false; /** * this item goes on the right side. */ private static final boolean ON_RIGHT = true; /** * how to use the macro */ private static final String USAGE = "\nDVD macro needs asin=, upc=, [isbn=,] title= author= " + "[birth=] [death=] [type=] [notes=]"; /** * list of image formats we support for DVD cover to display in HTML. */ private static final String[] SUPPORTED_EXTENSIONS = { ".png", ".jpg", ".gif" }; // /declarations // methods /** * split isbns at comma, gets rid of spaces around comma */ private static final Pattern SPLIT_ON_COMMA = Pattern.compile( "\\s*,\\s*" ); /** * true if one or more stores was grayed out for beingout of stock or not carried */ private boolean greyed = false; /** * find best isbn to display for this store, ideally instock * * @param dvdStore DVD store * @param preferredProducts list of isbns etc in preferred order * * @return bet fit isbn, null if no fit. will be NOTCARRIED, OUTOFSTOCK or INSTOCK. */ private static BestResult best( DStore dvdStore, String[] preferredProducts ) { String bestProduct = null; StockStatus bestStockStatus = StockStatus.UNKNOWN; for ( String product : preferredProducts ) { final StockStatus stockStatus = dvdStore.getProductStatus( product, true ); if ( stockStatus.compareTo( bestStockStatus ) > 0 ) { bestStockStatus = stockStatus; bestProduct = product; // if we find an isbn in stock, no point in continuing to search. if ( bestStockStatus == StockStatus.INSTOCK ) { break; } } } // end for // we might have fallen out the loop without finding anything if ( bestStockStatus.compareTo( StockStatus.NOTCARRIED ) < 0 ) { return new BestResult( dvdStore, null, bestStockStatus ); } else { return new BestResult( dvdStore, bestProduct, bestStockStatus ); } }// /method /** * Build a link to a DVD at dummy vendor. May be needed later. * * @param onRight true if inserting on right side of table. * * @return HTML for a filler, dummy vendor. */ @SuppressWarnings( { "SameParameterValue" } ) private static String buildDummy( boolean onRight ) { return onRight ? "\n" : ""; }// /method /** * Get filename of the dvdcover of the form B00005Y726.gif or B00005Y726.jpg * * @param asin item number without dashes or spaces. * * @return filename of cover image. Will return dummy "nodvdcover.png if there is no match. */ private static String getDVDCoverName( String asin ) { for ( String ext : SUPPORTED_EXTENSIONS ) { // ext includes the dot final String candidateDVDCoverImageName = asin + ext; if ( Tools.toFileFromUPath( "image/dvdcover/" + candidateDVDCoverImageName ).exists() ) { return candidateDVDCoverImageName; } } err.println( "Warning no image for DVD asin " + asin ); // could not find an image return "nodvdcover.png"; }// /method /** * is this electronic device stocked anywhere * * @param asin amazon part number * @param upc upc of dvd * * @return true if in stock in at most one store. */ private static boolean isStockedSomewhere( final String asin, final String upc, final String isbn13 ) { final String[] asins = splitProducts( asin ); for ( DStore d : AMAZON_DVDSTORES ) { if ( d.isAlive() ) { final BestResult br = best( d, asins ); if ( br.stockStatus == INSTOCK ) { return true; } } } DStore b = DStore.BN; if ( b.isAlive() ) { final BestResult br = best( b, splitProducts( upc ) ); if ( br.stockStatus == INSTOCK ) { return true; } } DStore p = DStore.POWELLS; if ( !p.isAlive() ) { final BestResult br = best( p, splitProducts( isbn13 ) ); if ( br.stockStatus == INSTOCK ) { return true; } } return false; }// /method /** * Create a little *.nostockers file to warn of products nobody stocks. * * @param product tHe product number * @param productCategory, i.e. DVD * @param pageUrl the url of the product description file * @param title title of the DVD */ private static void reportUnstockedProduct( final String productCategory, final String product, final String pageUrl, final String title ) { final String responseProblemFile = "C:\\temp\\" + productCategory.toUpperCase() + "_" + product + ".nostockers"; final FastCat sb = new FastCat( 9 ); sb.append( "N O S T O C K E R S\n" ); sb.append( productCategory, " not in stock at any store and not registered as rare in Configuration.isProductRare\n" ); sb.append( " pageUrl: ", pageUrl, "\n" ); sb.append( " title: ", title, "\n" ); final String problem = sb.toString(); try { HunkIO.writeEntireFile( new File( responseProblemFile ), problem, HunkIO.UTF8 ); } catch ( IOException e ) { err.println( "Unable to capture .nostockers file" ); } }// /method /** * create list of product ids * * @param productLists, comma-separated product list * * @return array of products, combining inputs */ private static String[] splitProducts( final String... productLists ) { ArrayList a = new ArrayList<>( 10 ); for ( String productList : productLists ) { if ( ST.isEmpty( productList ) ) { continue; } final String[] products = SPLIT_ON_COMMA.split( productList ); for ( String product : products ) { if ( ST.isEmpty( product ) || a.contains( product ) ) { // duplicate continue; } a.add( product ); } } return a.toArray( new String[ a.size() ] ); }// /method /** * build link with flag * * @param onright true if flag on right * @param dvdStore which DVD store * @param preferredProducts array of ISBNs in order to preference * @param title title of book * @param author author of book */ private String buildProductLinkWithFlag( boolean onright, DStore dvdStore, String[] preferredProducts, final String title, final String author ) { // flag comes from the store CountryFlag flag = dvdStore.getFlag(); final FastCat sb = new FastCat( 12 ); sb.append( leadingCountryFlag( onright, flag ) ); if ( dvdStore.isAlive() ) { final BestResult br = best( dvdStore, preferredProducts ); final String product = br.product; final StockStatus status = br.stockStatus; sb.append( "" ); greyed = true; break; case INSTOCK: if ( dvdStore.takesPayPal() ) { sb.append( " paypal" ); } sb.append( "\" rel=\"nofollow\" href=\"" ); sb.append( dvdStore.buildProductLink( product ), "\">" ); break; case NOTCARRIED: default: // unknown will show up here if there is no product number sb.append( "\" rel=\"nofollow\" href=\"" ); sb.append( dvdStore.buildQueryLink( title, author ), "\">" ); greyed = true; } sb.append( dvdStore.getDecoratedStoreName(), "" ); } else { sb.append( "", dvdStore.getDecoratedStoreName(), "" ); } sb.append( trailingCountryFlag( onright, flag ) ); return sb.toString(); }// /method /** * build link with flag * * @param onright true if flag on right * @param topic topic of search book title * * @return generated HTML to links to Google Search */ private String buildSearchLinkWithUNFlag( boolean onright, String topic, String upc ) { CountryFlag flag = CountryFlag.UN; // search web with google for this topic if ( upc != null ) { topic = topic + " " + upc; } final FastCat sb = new FastCat( 3 ); sb.append( leadingCountryFlag( onright, flag ) ); sb.append( GoogleCannedSearch.expand( false, JUST_OTHER_DVDSTORES, topic, "other stores", fileBeingDistributed ) ); sb.append( trailingCountryFlag( onright, flag ) ); return sb.toString(); }// /method /** * build grid of store buttons * * @param title title of DVD * @param upc12 product number * @param asin Amazon product number * @param isbn13 isbn * * @return generated HTML for store grid */ private String buildStoreButtons( final String title, final String author, final String upc12, final String asin, final String isbn13 ) { greyed = false; final String[] asins = splitProducts( asin ); final FastCat sb = new FastCat( 25 ); sb.append( "" ); sb.append( "\n" ); sb.append( "" ); sb.append( "" ); sb.append( "" ); sb.append( "" ); sb.append( "" ); sb.append( "" ); sb.append( "\n" ); /* L A Y O U T amazon.de amazon.uk amazon.es amazon.ca amazon.fr amazon.com amazon.it powells (isbn) junglee B&N (upc) other */ sb.append( Book.interleave( new String[] { // buildProductLinkWithFlag( ON_LEFT, AMAZONCN, asin, title, author ), buildProductLinkWithFlag( ON_LEFT, AMAZONDE, asins, title, author ), buildProductLinkWithFlag( ON_LEFT, AMAZONES, asins, title, author ), buildProductLinkWithFlag( ON_LEFT, AMAZONFR, asins, title, author ), buildProductLinkWithFlag( ON_LEFT, AMAZONIT, asins, title, author ), buildProductLinkWithFlag( ON_LEFT, JUNGLEE, asins, title, author ), buildSearchLinkWithUNFlag( ON_LEFT, title, upc12 ) }, new String[] { buildProductLinkWithFlag( ON_RIGHT, AMAZONUK, asins, title, author ), buildProductLinkWithFlag( ON_RIGHT, AMAZONCA, asins, title, author ), buildProductLinkWithFlag( ON_RIGHT, AMAZONCOM, asins, title, author ), buildProductLinkWithFlag( ON_RIGHT, POWELLS, splitProducts( isbn13 ), title, author ), // Powells proprietary ISBN buildProductLinkWithFlag( ON_RIGHT, BN, splitProducts( upc12 ), title, author ), buildDummy( ON_RIGHT ) } ) ); // end inner sellclassgrid table sb.append( "
" ); sb.append( "Online stores carrying " ); sb.append( Tools.stripHTMLTags( title ) ); sb.append( "
\n" ); sb.append( "Greyed out stores probably " + "do not have the item in stock\n" ); return sb.toString(); }// /method /** * Generate HTML for one DVD. Assumes all data is clean. All fields must be HTML, i.e. ampersands must be shown as * ampersand-amp; etc. Does NOT generate the \n" ); return sb.toString(); }// /method /** * Insert optional country flag on left of link. *

* * @param whichSide true if inserting on right side of table. * @param flag CountryFlag country. * * @return HTML to display the flag */ private String leadingCountryFlag( boolean whichSide, CountryFlag flag ) { return whichSide ? "" : "" + flag.getFlagRef( fileBeingDistributed ) + "\n"; }// /method /** * Insert optional country flag on right of link. * * @param whichSide true if inserting on right side of table. * @param flag CountryFlag country. * * @return HTML to display the flag */ private String trailingCountryFlag( boolean whichSide, CountryFlag flag ) { return whichSide ? "\n" + flag.getFlagRef( fileBeingDistributed ) + "\n" : "\n"; }// /method /** * Generate macro expansion for DVD macro * * @param parms parsed params for the macro. parms typically look like this: Parms may be in any order. * @param quiet true if want output suppressed. * @param verbose @return expanded text */ public String expandMacro( String[] parms, final boolean quiet, final boolean verbose ) { if ( parms.length < 3 * 2 ) { throw new IllegalArgumentException( USAGE ); } // must have even number of parms if ( ( parms.length & 1 ) != 0 ) { throw new IllegalArgumentException( "DVD macro syntax error: must have parm=value pairs\n" + USAGE ); } // set defaults String asin = ""; String upc12 = ""; String isbn13 = ""; String title = "unknown title"; String author = "anonymous"; String type = "DVD"; String notes = " "; String birth = ""; String death = ""; // extract data from parms for ( int i = 0; i < parms.length; i += 2 ) { String name = parms[ i ]; String value = parms[ i + 1 ]; switch ( name ) { case "asin": if ( value.length() != 10 ) { throw new IllegalArgumentException( "asin " + value + " must be 10 characters long" ); } asin = value; break; case "upc": if ( !( value.length() == 12 && ST.isNumeric( value ) ) ) { throw new IllegalArgumentException( "upc " + value + " must be 12 digits long" ); } upc12 = value;// don't check UPC check digit. break; case "isbn": // this is Powells ISBN. Do not validate isbn13 = value; break; case "title": title = value; break; case "author": author = value; break; case "birth": birth = value; break; case "death": death = value; break; case "type": type = value.toUpperCase(); // e.g. DVD CD software break; case "notes": notes = value; break; default: throw new IllegalArgumentException( "DVD macro syntax error: unknown parm: " + name + "\n" + USAGE ); } } // end for if ( ST.isEmpty( asin ) ) { throw new IllegalArgumentException( "DVD macro asin is missing\n" + USAGE ); } if ( ST.isEmpty( upc12 ) ) { throw new IllegalArgumentException( "DVD macro upc12 is missing\n" + USAGE ); } // generate the HTML to refer this dvd. return expand( asin, upc12, isbn13, title, author, birth, death, type, notes ); }// /method // /methods }