/*
* [Book.java]
*
* Summary: Implements Book Macro to create references for a book to various book stores.
*
* 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.9 2007-03-20 new code for Powell's, Barnes and Noble,
* AMAZON.com, AMAZON.ca, AMAZON.co.uk, AMAZON.ca, AMAZON.de
* 2.0 2007-03-24 uses buildGenericAmazon, adds flags, AMAZON.fr.
* 2.1 2011-09-28 add AMAZON.es and AMAZON.it. Drop impression counters. new style links.
* 2.2 2011-09-30 add eBook support in Safari and Sony
* 2.3 2011-10-17 add support for audio
* 2.4 2014-03-21 drop Sony
* 2.5 2016-07-09 allow isbn fields to be comma lists
*/
package com.mindprod.stores;
import com.mindprod.common18.BigDate;
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 com.mindprod.isbn.ISBNValidate;
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.BStore.*;
import static com.mindprod.stores.StockStatus.INSTOCK;
import static java.lang.System.*;
/**
* Implements Book Macro to create references for a book to various book stores.
*
* Expands HTML Book macro, formats data in a table. expand is called by both com.mindprod.htmlmacros.Book to regenerate
* html from \n" );
return sb.toString();
}// /method
/**
* get the preferred ISBNs for this store to sell this book
*
* @param bookStore which online store
*
* @return array of ISBNsu
*/
private String[] getPreferredISBNs( BStore bookstore )
{
if ( bookstore.isOnlyEbooks() )
{
return preferredEbookISBNs;
}
else
{
return preferredPaperISBNs;
}
}
/**
* is this book stocked somewhere?
*
* @param isbn13 main isbn, not a commolist
* @param eBook eBook isbn
*
* @return true if book in stock in at least one bookstore
*/
private boolean isStockedSomewhere( final String isbn13, final String title )
{
assert !isbn13.contains( "," ) : "primary isbn " + isbn13 + " contains comma.";
boolean stockedSomewhere = false;
for ( String isbn : preferredPaperISBNs )
{
for ( BStore bookStore : BStore.values() )
{
if ( !bookStore.isAlive() )
{
continue;
}
if ( bookStore.getProductStatus( isbn, true ) == INSTOCK )
{
return true;
}
}
}
// report unstocked books, unless marked as rare in config
if ( !configuration.isProductRare( isbn13 ) )
{
// isbn13 is a single field
reportUnstockedProduct( "book", isbn13, EIO.getCanOrAbsPath( fileBeingProcessed ), title );
}
return false;
}// /method
/**
* Insert optional country flag on left of link.
*
*
* @param onRight true if inserting on right side of table.
* @param flag CountryFlag country.
*
* @return HTML to display the flag
*/
private String leadingCountryFlag( boolean onRight, CountryFlag flag )
{
return onRight ? "" : " | " + flag.getFlagRef( fileBeingDistributed ) + " | \n";
}// /method
/**
* Insert optional country flag on right of link.
*
* @param onRight true if inserting on right side of table.
* @param flag CountryFlag country.
*
* @return HTML to display the flag
*/
private String trailingCountryFlag( boolean onRight, CountryFlag flag )
{
return onRight ? " | \n" + flag.getFlagRef( fileBeingDistributed ) + " |
\n" :
"\n";
}// /method
/**
* Generate macro expansion for Book macro. primarily parse list of parms.
*
* @param parms parsed params for the macro. parms typically look like this: Parms may be in any order and already entity-encoded.
* @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 < 6 )
{
throw new IllegalArgumentException( USAGE );
}
// must have even number of parms
if ( ( parms.length & 1 ) != 0 )
{
throw new IllegalArgumentException( "Book macro syntax error: must have parm=value pairs" );
}
// set defaults
String audio = "";
String author = "anonymous";
String birth = "";
String death = "";
String eBook = "";
String hardcover13 = "";
String isbn13 = "";
String kindle = "";
String notes = "";
String paperback13 = "";
String published = "";
String publisher = "";
String title = "unknown title";
String webbook = "";
// 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 "isbn":
ISBNValidate.ensureISBN13Valid( value );
isbn13 = value;
break;
case "paperback":
paperback13 = value;
break;
case "hardcover":
hardcover13 = value;
break;
case "kindle":
kindle = value.toUpperCase();/* e.g. -000OZ0N5I */
break;
case "ebook":
eBook = value;
break;
case "audio":
audio = value;
break;
case "title":
title = value;
break;
case "author":
author = value;
break;
case "birth":
birth = value;
break;
case "death":
death = value;
break;
case "notes":
notes = value;
break;
case "price":
// ignore it. AMAZON does not want us displaying prices, especially out of date prices. Further Each
// vendor has a different price.
break;
case "publisher":
publisher = value;
break;
case "published":
published = value;
switch ( published.length() )
{
case 1:
if ( !published.equals( "?" ) )
{
throw new IllegalArgumentException( "[published] date must be ? for unknown" );
}
break;
case 10:
if ( !BigDate.isValid( published ) )
{
throw new IllegalArgumentException(
"[published] date must be a valid date of form yyyy-mm-dd" );
}
break;
case 7:
if ( !BigDate.isValid( published + "-01" ) )
{
throw new IllegalArgumentException( "[published] date must be a valid date of form " +
"yyyy-mm" );
}
break;
case 4:
if ( !BigDate.isValid( published + "-01-01" ) )
{
throw new IllegalArgumentException( "[published] date must be a valid date of form yyyy" );
}
break;
default:
throw new IllegalArgumentException( "[published] date must be a valid date of form " +
"yyyy-mm-dd" );
}
break;
case "webbook":
ISBNValidate.ensureISBN13Valid( value );
webbook = value.toUpperCase();
break;
default:
throw new IllegalArgumentException( "Book macro syntax error: unknown parm: [" + name + "]" );
}
} // end for
if ( isbn13.length() == 0 )
{
if ( paperback13.length() > 0 )
{
isbn13 = paperback13;
}
else if ( hardcover13.length() > 0 )
{
isbn13 = hardcover13;
}
else
{
throw new IllegalArgumentException( "no ISBN specified by [isbn], [paperback] or [hardcover]" );
}
}
if ( !( paperback13.length() > 0 || hardcover13.length() > 0 ) )
{
throw new IllegalArgumentException( "Book macro must have either [paperback] or [hardcover] or both" );
}
if ( !( paperback13.contains( isbn13 ) || hardcover13.contains( isbn13 ) ) )
{
throw new IllegalArgumentException( "Book macro [isbn] must match either [paperback] or [hardcover]" );
}
// generate the HTML to refer this book.
return expand( isbn13,
paperback13,
hardcover13,
kindle,
eBook,
webbook,
audio,
title,
publisher,
published,
author,
birth,
death,
notes );
}// /method
// /methods
}
// end Book
/**
* used to allow methods to return 2 or 3 values
*/
class BestResult
{
/**
* constructor
*/
final OnlineStore onlineStore;
final String product;
final StockStatus stockStatus;
BestResult( OnlineStore onlineStore, String product, StockStatus stockStatus )
{
this.onlineStore = onlineStore;
this.product = product;
this.stockStatus = stockStatus;
}
}