/* * [Prices.java] * * Summary: generate CMP pricelist and downloadURL web pages. * * Copyright: (c) 1998-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.3 2005-06-30 stomp, switch to enum. * 1.4 2005-08-28 generate file names rather that have columns for * them. add RELEASE_DATE column. Generate pad reference, screenshot * ref, icon. * 1.5 2005-08-29 add pad, icon, screenshot, * Files of interest for an entry: * zips/converter01.zip : distributable * precis/converter.txt : precis * icon/converter.png : icon * screenshots/converter.screenshot.png : screenshot * pad/converter.xml : pad xml descriptor * applet/.manual.html : manual * applet/converter.html : run applet * webstart/startclock.jnlp : jnlp * utilities/cmossv.txt : manual * 1.6 2006-02-05 move icons to image/icon and screenshots to * image/screenshot * 1.7 2006-02-28 move add icons for purchase, better wordings to * describe what each includes, non-military restriction. * 1.8 2007-03-06 reformat with IntelliJ, add Javadoc. * 1.9 2007-04-12 tidy with code inspector, move the anchor to details. * 2.0 2009-04-24 generate allprograms.csv and allpads.txt */ package com.mindprod.prices; import com.mindprod.common18.BigDate; import com.mindprod.common18.Build; import com.mindprod.common18.EIO; import com.mindprod.common18.Misc; import com.mindprod.common18.ST; import com.mindprod.csv.CSVReader; import com.mindprod.csv.CSVWriter; import com.mindprod.fastcat.FastCat; import com.mindprod.hunkio.HunkIO; import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.EOFException; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.mindprod.common18.Common18.toZulu; import static java.lang.System.*; /** * generate CMP pricelist and downloadURL web pages. *

* snippets from mindprod/include directory to create E:/mindprod/include/products?guts.html Also sometimes generates * snippets, one per product in mindprod/include to include elsewhere outside the price list. It always generates code * in the code in the guts for the snippet. These guts files are included into E:\mindprod\products?.html guts = body of * group of items index = quilt menu to those items unanchored = list of references to those items. anchored = anchored * list of references to those items in another file. * * @author Roedy Green, Canadian Mind Products * @version 2.0 2009-04-24 generate allprograms.csv and allpads.txt * @since 1998 */ @SuppressWarnings( { "FieldCanBeLocal" } ) public final class Prices { // declarations private static final int FIRST_COPYRIGHT_YEAR = 1998; /** * what to generate for a br */ private static final String BR = "
"; /** * undisplayed copyright notice */ @SuppressWarnings( { "UnusedDeclaration" } ) private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 1998-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /** * make errors stand out */ private static final String ERRORINDICATOR = " <><><> "; /** * used to separate a click able link from its neighbours to make it stand out. light vertical bar. * If it won't render, will just give a little extra while space. *

* &VerticalSeparator in HTML 5 , ❘ in HTML 4 */ private static final String LINK_SEP = " ❘ "; private static final String PRODUCTS_INDEX_HEAD = "\n"; /** * table column headers for product details */ private static final String detailTableHead = DO_NOT_EDIT + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n" + "\n"; /** * table footer for product detail */ private static final String detailTableTail = "
CMP Downloadable programs
ProgramVerSize   Updated   StatusSourceDescriptionPopularity
"; /** * look for reference to appvisor site * match http://repository.appvisor.com/info/app-e4007bbb9bb0/Canadian_Sales_Tax_Calculator_pad.xml */ private static final Pattern APPVISOR_FINDER = Pattern.compile( "\\s*(http://repository.appvisor.com[\\-\\_\\+\\.:/\\w]+)\\s*" ); /** * Used to split a comma or colon list of languages into pieces. */ private static final Pattern SPLIT_ON_COLON_OR_COMMA = Pattern.compile( "[:,]" ); /** * where generated include snippet go */ private static String productsIncludePageDetailsFilename; /** * where products index for each page go. */ private static String productsPageIndexFilename; /** * root directory of website */ private static String webrootDirname; /** * one month ago, used for deciding if a program update still counts as new. */ private static BigDate aMonthAgo; /** * Where to write the all products csv file. */ private static File allproductsCSVFile; /** * Where to write the all pads csv file. */ private static File pad3List; /** * Where to write the all pads csv file. */ private static File pad4List; /** * name of the prices file */ private static File pricesCsvFile; /** * master products indexs include */ private static File productsIncludeMasterFile; /** * Bat file to redirect PAD4 to PADVisor */ private static File redirectPadsBtm; /** * Bat file to refresh local pads from AppVisor */ private static File refreshPadsBtm; private static File sourceDir; /** * root directory of website */ private static File webrootDir; /** * true if you want information about who generated what embedded in the output */ private static boolean DEBUGGING = false; /** * Measure of likelihood user will be interested in this program. obsolete, wallflower, current, nolfn, oldie, dos */ private static boolean distributeJet; /** * true if is new, modified within last month. Computed each time price.exe is run, not ever replacemindprod.exe. */ private static boolean isNew; /** * which page this entry belongs on */ private static int page; /** * who handles collecting money. paypal regnow regsoft free regsoft:nnnn */ private static String agent; /** * Where appInclude file goes e.g. E:\mindprod\include\bulk.htmlfrag */ private static String appIncludeHtml; /** * Measure of likelihood user will be interested in this program. obsolete, wallflower, current, nolfn, oldie, dos */ private static String category; /** * lower case name used in generating computer names, e.g. bulk. */ private static String computerName; /** * description */ private static String desc; /** * LOCAL_WEBROOT_WITH_BACKSLASHES-relative where downloads and about.txt/about.html is fon this entry e.g. * zips/java */ private static String downloadDir; /** * human name for the package, with a capital letter */ private static String aspName; /** * LOCAL_WEBROOT_WITH_BACKSLASHES relative directory on website where application icon is usually null meaning is no * icon, e.g. icon32/bulk.png. These are png files, not *.ico files Since we want them to to work on many * platforms. */ @Nullable private static String icon; /** * HTML to display the application icon, null if no icon. */ @Nullable private static String iconImgRef; /** * Default dir where Jet app will likely end up after install. */ private static String installedJetDir; /** * Default exe where Jet app will likely end up after install. */ private static String installedJetExe; /** * languages program is written in */ private static String language; /** * complete link and wording for non-military use only */ private static String nonMilitaryUseOnly; /** * shareware, free, custom, code for file. */ private static String pay; /** * long form of pay shareware, free, "unlimited redistribution licence" */ private static String payDescription; /** * additional information about what is included. */ private static String payDescriptionAux; /** * price is US $ */ private static String price; /** * numeric product id if this is sold through regsoft or regnow. */ private static String productID; /** * Which products file we are generating */ private static String productsFile; /** * upper case computer name for the package */ private static String ucComputerName; /** * LOCAL_WEBROOT_WITH_SLASHES relative name of the zip download. */ private static String uPathDownload; /** * LOCAL_WEBROOT_WITH_SLASHES relative name of the Jet -setup.exe download. */ private static String uPathDownloadJET; /** * LOCAL_WEBROOT_WITH_SLASHES relative directory on website where manual is, usually null meaning is no manual, * e.g. application/bulk.manual.html or utilities/cmossv.txt */ @Nullable private static String uPathManual; /** * LOCAL_WEBROOT_WITH_SLASHES relative directory on website where ASP pad.xml is, usually null meaning is no * xml, e.g. pad/bulk.xml */ @Nullable private static String uPathPadXml; /** * LOCAL_WEBROOT_WITH_BACKSLASHES relative filename of uses/converter.txt (precis) */ private static String uPathPrecis; /** * LOCAL_WEBROOT_WITH_SLASHES relative directory on website where screenshotURL is, usually null meaning is no * screenshotURL, e.g. image/screenshotURL/bulkst.png. */ @Nullable private static String uPathScreenShot; /** * version of form 1.10 */ private static String version; /** * enumerated type for the type of application it is. */ private static AppCat appCat; /** * Date this package was last revised. */ private static BigDate officialRELEASE_DATE; /** * reader for prices.txt raw info */ private static CSVReader r = null; /** * which pass we are on, ANCHORED_MASTER_INDEX, DETAILS, UNANCHORED_PAGE_INDEX */ private static Form pass; /** * how licenced, enum. */ private static Licence licence; /** * writer for generated HTML */ private static PrintWriter p = null; // /declarations /** * create include/products?index.htmlfrag. It will be include in products?.htmlfrag * * @param i which page we are working on * * @throws IOException if can't write output */ private static void buildAProductIndex( int i ) throws IOException { final File indexFile = new File( productsPageIndexFilename.replace( "%i", Integer.toString( i ) ) ); p = EIO.getPrintWriter( indexFile, 4 * 1024, EIO.UTF8 ); { here(); } p.write( "\n" ); p.write( PRODUCTS_INDEX_HEAD ); createPriceList( i, Form.UNANCHORED_PAGE_INDEX ); p.write( " -->" ); p.close(); } // /method /** * create include/productsindex.htmlfrag. It will be include in products.htmlfrag * * @throws IOException if trouble writing output */ private static void buildMasterProductIndex() throws IOException { p = EIO.getPrintWriter( productsIncludeMasterFile, 4 * 1024, EIO.UTF8 ); { here(); } p.write( PRODUCTS_INDEX_HEAD ); for ( int i = 1; i <= 4; i++ ) { createPriceList( i, Form.ANCHORED_MASTER_INDEX ); } p.write( " -->" ); p.close(); } // /method /** * create include/products?details.htmlfrag. It will be include in products?.htmlfrag * * @param i which page we are generating details for. * @param productsDetailsFile file where to build the product details. * * @throws IOException if trouble writing output */ private static void buildProductDetailsPage( int i, final String productsDetailsFile ) throws IOException { p = EIO.getPrintWriter( new File( productsDetailsFile ), 4 * 1024, EIO.UTF8 ); { here(); } p.write( detailTableHead ); createPriceList( i, Form.DETAILS ); p.write( detailTableTail ); p.close(); } // /method /** * get string to describe the shareware, free etc. */ private static void calcPayDescription() { payDescription = licence.getShortDesc(); payDescriptionAux = licence.getLongDesc(); } // /method /** * compute various directories and names from info in descriptor record. * * @param needDownload true if need download info. */ private static void calcPseudoFields( boolean needDownload ) { // build various pseudofields isNew = officialRELEASE_DATE.getOrdinal() > aMonthAgo.getOrdinal(); // LOCAL_WEBROOT_WITH_BACKSLASHES relative name of download zip. downloadDir = AppCat.getZipDir(); // 9.9 -> 99, 10.0 --> 100 { final FastCat sb = new FastCat( 6 ); sb.append( downloadDir ); sb.append( "/" ); sb.append( computerName ); sb.append( version.substring( 0, version.length() - 2 ) ); sb.append( version.charAt( version.length() - 1 ) ); sb.append( ".zip" ); uPathDownload = sb.toString(); } { final FastCat sb = new FastCat( 4 ); sb.append( downloadDir ); sb.append( "/" ); sb.append( computerName ); sb.append( "-setup.exe" ); uPathDownloadJET = sb.toString(); } { final FastCat sb = new FastCat( 4 ); sb.append( "C:\\Program Files (x86)\\Canadian Mind Products\\" ); sb.append( aspName ); sb.append( " " ); sb.append( version ); installedJetDir = sb.toString(); } { final FastCat sb = new FastCat( 4 ); sb.append( installedJetDir ); sb.append( "\\" ); sb.append( computerName ); sb.append( ".exe" ); installedJetExe = sb.toString(); } if ( needDownload && !new File( webrootDir, uPathDownload ).exists() ) { err.println( ERRORINDICATOR + "missing download " + uPathDownload ); // leave it. It may show up soon. } String precisDir = AppCat.getPrecisDir(); // display details: precis.htmlfrag in preference to precisL.txt/use uPathPrecis = precisDir + "/" + computerName + ".htmlfrag"; if ( !new File( webrootDir, uPathPrecis ).exists() ) { // no *.htmlfrag, try for a *.txt instead uPathPrecis = precisDir + "/" + computerName + ".txt"; if ( !new File( webrootDir, uPathPrecis ).exists() ) { err.println( ERRORINDICATOR + "missing precis " + uPathPrecis ); // leave it. It may show up soon. } } // manual has a name like application/bulk.manual.htmlfrag uPathManual = appCat.getJarDir() + "/" + computerName + ".manual.html"; if ( !new File( webrootDir, uPathManual ).exists() ) { // try for a utilities/cmossv.txt file instead uPathManual = appCat.getJarDir() + "/" + computerName + ".txt"; if ( !new File( webrootDir, uPathManual ).exists() ) { // may not exist uPathManual = null; } } // uPathScreenShot has a name like image/uPathScreenShot/bulk.png uPathScreenShot = AppCat.getScreenshotDir() + "/" + computerName + ".png"; if ( !new File( webrootDir, uPathScreenShot ).exists() ) { // was no uPathScreenShot after all, no sweat. uPathScreenShot = null; } // uPathPadXml has a name like pad/bulk.xml uPathPadXml = AppCat.getPadDir() + "/" + computerName + ".xml"; if ( !new File( webrootDir, uPathPadXml ).exists() ) { // was no uPathPadXml after all, no sweat. uPathPadXml = null; } nonMilitaryUseOnly = ""; // includeHtml has a name like include/bulk.html appIncludeHtml = webrootDirname + "/include/" + computerName + ".htmlfrag"; // use 64x64 icon in preference to 32x32 icon = AppCat.getIcon64Dir().substring( "image/".length() ) + "/" + computerName + ".png"; if ( !new File( webrootDir + "/image", icon ).exists() ) { icon = AppCat.getIcon32Dir().substring( "image/".length() ) + "/" + computerName + ".png"; } if ( new File( webrootDir + "/image", icon ).exists() ) { final FastCat sb = new FastCat( 5 ); sb.append( "" ); iconImgRef = sb.toString(); } else { // was no icon after all, no sweat. icon = null; iconImgRef = null; } ucComputerName = ST.trimTrailing( computerName.toUpperCase(), '!' ); } // /method /** * create allpdads.txt list of all Mindprod.com pad.xml files. */ private static void createAllPadsTxt() { // use global r r = null; // accumulate pads so we can sort before writing. ArrayList pads = new ArrayList<>( 150 ); try { try { r = new CSVReader( new BufferedReader( new FileReader( pricesCsvFile ) ) ); while ( true ) { // loop till eof // bypass page r.skip( 1 ); String command = r.get(); // bypass blank lines if ( command == null || command.length() == 0 ) { r.skipToNextLine(); continue; } if ( command.equals( "package" ) ) { // includes library, Applet, hybrid, webstart, // application etc. getRecord(); calcPseudoFields( true ); if ( uPathPadXml != null ) { // e.g. pad/cantax.xml (Pad 3 name ) pads.add( uPathPadXml ); } } r.skipToNextLine(); } // end while } catch ( EOFException e ) { // eof expected r.close(); } Collections.sort( pads ); // we are going to generate 4 files from the list of pads final PrintWriter p3 = EIO.getPrintWriter( pad3List, 4 * 1024, EIO.UTF8 ); final PrintWriter p4 = EIO.getPrintWriter( pad4List, 4 * 1024, EIO.UTF8 ); final PrintWriter refresh = EIO.getPrintWriter( refreshPadsBtm, 4 * 1024, EIO.UTF8 ); final PrintWriter redirect = EIO.getPrintWriter( redirectPadsBtm, 4 * 1024, EIO.UTF8 ); // header refresh.println( "rem refreshpads.btm refresh PAD 4.0 files, by downloading from ASP\n" + "rem The following script was generated from the local PAD files on " + BigDate.localToday().toString() + "\n" + "rem by com.mindprod.price.Prices.\n" + "rem D o N o t E d i t .\n" + "rem add to list by exporting pad from http://repository.appvisor.com to E:\\mindprod\\pad\\xxx.html\n" + "rem You do not need to know the url to do this.\n" + "rem mark as PAD40 in DefineAllProjects.\n" + "E:\n" + "E:\\mindprod\\pad\n" ); redirect.println( "#redirects for PADs for mindprod.com\n" + "#generated by Prices. D o N o t E d i t .\n" ); for ( String pad : pads ) { String contents = HunkIO.readEntireFile( new File( webrootDir, pad ), EIO.UTF8 ); final Matcher m = APPVISOR_FINDER.matcher( contents ); if ( m.find() ) { // PAD4 String appvisorURL = m.group( 1 ); p4.println( appvisorURL ); // download.exe http://repository.appvisor.com/info/app-0100a7100c41/Base64_pad.xml base64.xml refresh.println( "download.exe \"" + appvisorURL + "\" " + ST.chopLeadingString( pad, "pad/" ) ); // Redirect permanent /pad/base64.xml http://repository.appvisor.com/info/app-0100a7100c41/Base64_pad.xml redirect.println( "Redirect permanent /" + pad + " " + appvisorURL ); } else { // PAD3 String localPadURL = toAbsoluteURL( pad ); p3.println( localPadURL ); } } refresh.println( "\n" + "rem -30-\n" ); p3.close(); p4.close(); refresh.close(); redirect.close(); } catch ( IOException e ) { err.println(); e.printStackTrace( err ); err.println(); } } // /method /** * create allproducts.csv list of all Mindprod.com downloads */ private static void createAllProductsCsv() { // use global r r = null; CSVWriter w = null; try { try { r = new CSVReader( EIO.getBufferedReader( pricesCsvFile, 4 * 1024, EIO.UTF8 ) ); w = new CSVWriter( EIO.getPrintWriter( allproductsCSVFile, 4 * 1024, EIO.UTF8 ) ); w.nl( "mindprod.com downloadable files" ); w.nl( "shortName,longName,version,description,releaseDate,buildZuluTimestamp,downloadURL," + "[padChangeZuluTimestamp],[padURL]" ); while ( true ) { // loop till eof // bypass page r.skip( 1 ); String command = r.get(); // bypass blank lines if ( command == null || command.length() == 0 ) { r.skipToNextLine(); continue; } if ( command.equals( "package" ) ) { // includes library, Applet, hybrid, webstart, // application etc. getRecord(); calcPseudoFields( true ); if ( uPathDownload != null ) { w.put( computerName ); w.put( aspName ); w.put( version ); w.put( desc ); w.put( officialRELEASE_DATE.toString() ); w.put( toZulu( new File( webrootDir, uPathDownload ).lastModified() ) ); w.put( toAbsoluteURL( uPathDownload ) ); if ( uPathPadXml != null ) { w.put( toZulu( new File( webrootDir, uPathPadXml ).lastModified() ) ); w.put( toAbsoluteURL( uPathPadXml ) ); } else { w.put( "" ); w.put( "" ); } w.nl(); } } r.skipToNextLine(); } // end while } catch ( EOFException e ) { // eof expected if ( r != null ) { r.close(); } if ( w != null ) { w.close(); } } } catch ( IOException e ) { err.println(); e.printStackTrace( err ); err.println(); } } // /method /** * Create an appInclude file for the current record, e.g. E:\mindprod\include\bulk.htmlfrag to include in * E:\mindprod\applet\bulk.html This is not the code that generates the item in the prices, * just similar! This one includes a heading line. * * @throws java.io.FileNotFoundException if can't create the app snippet. * If you change this, change emitPackage too. */ private static void createAppInclude() throws FileNotFoundException { p = EIO.getPrintWriter( new File( appIncludeHtml ), 1024, EIO.UTF8 ); { here(); } // we now have all info about one product. Start // generating html // what no excess whitespace on head or tail of included text. final FastCat sb = new FastCat( 38 ); sb.append( "\n\n" ); sb.append( "\n" ); sb.append( "\n" ); sb.append( "" ); sb.append( "", "", "", "" ); sb.append( "", "", "" ); sb.append( "", "" ); sb.append( "\n" ); sb.append( "\n" ); sb.append( "\n" ); p.write( sb.toString() ); emitPay(); emitLanguages(); // Put target on details, tallest part of row so that when jump to it // all the row will be visible. p.write( "\n\n" ); emitCategoryIcons(); p.write( "\n" ); p.write( "" ); p.write( "
Download information for ", aspName, "
PackageVersionReleasedLicenceLanguageNotes 
" ); if ( iconImgRef != null ) { sb.append( iconImgRef ); sb.append( BR ); } sb.append( appCat.decoratedName( aspName ), "", version, "", officialRELEASE_DATE.toString(), "" ); emitDetailsAndManual(); emitDownload(); if ( distributeJet ) { emitDownloadForJet(); } emitPayment(); p.write( " 
 
\n" ); p.write( "" );// no final \n to confuse compactor p.close(); } // /method /** * Create various include file of the *.html for each webstart or Applet. */ @SuppressWarnings( { "InfiniteLoopStatement" } ) private static void createAppIncludes() { // code will will generate will end up one level deep in website, // e.g. E:\mindprod\applet // level = "../"; try { try { r = new CSVReader( new BufferedReader( new FileReader( pricesCsvFile ) ) ); while ( true ) { // loop till eof page = r.getInt(); String command = r.get(); // bypass blank lines while ( command == null || command.length() == 0 ) { r.skipToNextLine(); command = r.get(); } // end while if ( command.equals( "package" ) ) { // includes library, Applet, hybrid, webstart, // application etc. getRecord(); calcPseudoFields( true ); switch ( appCat ) { case APPLET: case APPLICATION: case HYBRID: case JWS: createAppInclude(); break; default: // ignore everything else break; } // end switch } // end if r.skipToNextLine(); } // end outer while } catch ( EOFException e ) { // eof expected r.close(); } } catch ( IOException e ) { err.println(); e.printStackTrace( err ); err.println(); } } // /method /** * @param listNumber 1 to 4 which page of the price list * @param form enum, which type guts, anchoredlist, unanchoredlist. */ @SuppressWarnings( { "InfiniteLoopStatement" } ) private static void createPriceList( int listNumber, Form form ) { // price page where this list will eventually be inserted, or which it points to //e.g. products3.html Prices.productsFile = "products" + listNumber + ".html"; Prices.pass = form; try { try { r = new CSVReader( new BufferedReader( new FileReader( pricesCsvFile ) ) ); if ( form == Form.DETAILS ) { { here(); } p.write( "\n\n" ); } // add nothing on front to confuse macro header while ( true ) { // loop till eof page = r.getInt(); if ( page == listNumber ) { String command = r.get(); // bypass blank lines if ( command == null || command.length() == 0 ) { r.skipToNextLine(); continue; } // three types of command commands switch ( command ) { case "include": makeInclude(); break; case "package": // includes library, Applet, hybrid, webstart, // application etc. makePackage(); break; case "licence": makeLicence(); break; default: err.println( ERRORINDICATOR + "unrecognisable command " + command ); break; } } r.skipToNextLine(); } // end while } catch ( EOFException e ) { /* normal end */ } // add nothing on end to confuse macro header r.close(); } catch ( Exception e ) { err.println(); e.printStackTrace( err ); err.println(); } } // /method /** * emit summary link of products2.htmlfrag item to go on products.html page */ private static void emitAnchored() { // we cannot insert debug info. We are inside a Quilt macro and it would confuse the macro processor. final FastCat sb = new FastCat( 9 ); sb.append( "{" ); sb.append( appCat.decoratedName( aspName ), "}\n" ); p.write( sb.toString() ); } // /method /** * emit icons in the last column for the various categories. More icons to come. */ private static void emitCategoryIcons() { { here(); } String[] categories = SPLIT_ON_COLON_OR_COMMA.split( category ); HashSet categorySet = new HashSet<>( Arrays .asList( categories ) ); p.write( "" ); if ( categorySet.contains( "obsolete" ) ) { p.write( "" ); } if ( categorySet.contains( "nolfn" ) ) { p.write( "" ); } p.write( "\n" ); } // /method /** * emit instructions to check out with subversion */ private static void emitCheckoutSource() { if ( new File( sourceDir, computerName ).isDirectory() ) { { here(); } final FastCat sb = new FastCat( 7 ); sb.append( "

To check out the corresponding source from the Subversion repository, use the " ); sb.append( " repo-browser to" ); sb.append( BR ); sb.append( ".

\n" ); p.write( sb.toString() ); } } // /method private static void emitDetails( final String filenameToInclude ) { { here(); } p.write( "\n" ); } // /method /** * Emit Details/manual/screenshotURL */ private static void emitDetailsAndManual() { { here(); } p.write( "
\n" ); String preposition = "on"; // repeat precis in case people don't recognise the button as clickable. p.write( LINK_SEP + "\n" ); preposition = "on"; if ( uPathManual != null ) { p.write( LINK_SEP + "\n" ); preposition = "on"; } if ( uPathScreenShot != null ) { // space out links a bit p.write( LINK_SEP + "\n" ); preposition = "for"; } if ( new File( sourceDir, computerName ).isDirectory() ) { p.write( LINK_SEP + "\n" ); preposition = "for"; } p.write( "
" + preposition + " the current version of " + appCat.decoratedName( aspName ) + "." + BR + "" + desc + "
\n
\n" ); } // /method /** * Emit download html */ private static void emitDownload() { // do the zip itself right off the bat { { here(); } final FastCat sb = new FastCat( 10 ); sb.append( "
" ); sb.append( "\n" ); sb.append( "\n" ); sb.append( "zip for " ); sb.append( appCat.decoratedName( aspName ) ); sb.append( " " ); p.write( sb.toString() ); } switch ( appCat ) { case ASMCOM: case ASMEXE: p.write( "ASM source and executable files to run on your own machine as a standalone utility." ); emitRunsOnDOS(); emitInstallZip(); emitCheckoutSource(); break; case BUNDLE: case SERVLET: break; case CUTILITY: p.write( "C source and executable files to run on your own machine as a standalone utility." ); emitRunsOnWindows(); emitInstallZip(); emitCheckoutSource(); break; case CPPUTILITY: p.write( "C++ source and executable files to run on your own machine as a standalone utility." ); emitRunsOnWindows(); emitInstallZip(); emitCheckoutSource(); break; case DOCUMENTATION: p.write( "documentation to view on your own machine." ); emitRunsOnJava(); emitInstallJava(); emitInstallZip(); emitCheckoutSource(); break; case HYBRID: p.write( "Java source, compiled class files, jar and documentation to run on your own machine either " + "as an application or an Applet." ); emitRunsOnJava(); emitInstallJava(); emitInstallZip(); emitCheckoutSource(); emitHowRunJar( "" ); break; case APPLET: p.write( "Java source, compiled class files, jar and documentation to run on your own machine as an " + "Applet." ); emitRunsOnJava(); emitInstallJava(); emitInstallZip(); emitCheckoutSource(); break; case APPLICATION: p.write( "Java source, compiled class files, jar and documentation to run on your own machine as an " + "application." ); emitRunsOnJava(); emitInstallJava(); emitInstallZip(); emitCheckoutSource(); emitHowRunJar( "parms" ); break; case JWS: p.write( "Java source, compiled class files, jar and documentation to run on your own machine as a " + "Java Web Start application." ); emitRunsOnJava(); emitInstallJava(); emitInstallZip(); emitCheckoutSource(); { final FastCat sb = new FastCat( 10 ); sb.append( "\n

" ); sb.append( "To run the JWS application, modify the jnlp file to look in the right place for its files, " + "then type:\n" ); sb.append( "

\n" ); sb.append( "javaws J:\\com\\mindprod\\" ); sb.append( computerName ); sb.append( "\\" ); sb.append( computerName ); sb.append( ".jnlp\n" ); sb.append( "
\n" ); sb.append( "

adjusting as necessary to account for where the jar file is.

\n" ); p.write( sb.toString() ); } break; case JAVA_CLASS_LIBRARY: p.write( "Java source and compiled class files to run on your own machine as a part of your own " + "program." ); emitRunsOnJava(); emitInstallJava(); emitInstallZip(); emitCheckoutSource(); break; case JAVA_SNIPPETS: p.write( "Sample Java source code snippets to study or modify." ); emitRunsOnJava(); emitInstallJava(); emitInstallZip(); emitCheckoutSource(); break; } if ( uPathPadXml != null ) { final FastCat sb = new FastCat( 6 ); sb.append( "

" ); sb.append( " program description for the current version of " ); sb.append( appCat.decoratedName( aspName ) ); sb.append( ".

\n" ); p.write( sb.toString() ); } p.write( "
\n" ); } // /method /** * Emit html to download JET application */ private static void emitDownloadForJet() { { here(); } final FastCat sb = new FastCat( 10 ); sb.append( "
\n" ); sb.append( "\n" ); sb.append( " installer exe for " ); sb.append( appCat.decoratedName( aspName ) ); sb.append( "\n" ); sb.append( "Windows only\n" ); p.write( sb.toString() ); switch ( appCat ) { case APPLICATION: p.write( "32-bit JET-compiled *.exe application, without source or documentation." ); emitInstallJETToThePath(); break; case HYBRID: p.write( "hybrid Applet run as a 32-bit JET-compiled *.exe application without using a browser. Does " + "not include source or documentation." ); break; case APPLET: case ASMCOM: case ASMEXE: case BUNDLE: case SERVLET: case CUTILITY: case CPPUTILITY: case DOCUMENTATION: case JWS: case JAVA_CLASS_LIBRARY: case JAVA_SNIPPETS: throw new IllegalArgumentException( "Install JET does not apply to " + appCat ); } p.write( "
\n" ); } // /method /** * Emit explanation why there is no download. */ private static void emitExplainNoDownload() { { here(); } if ( pay.equals( "custom" ) ) { final FastCat sb = new FastCat( 11 ); sb.append( "
There is no one-size-fits-all " ); sb.append( "download for " ); sb.append( appCat.decoratedName( aspName ) ); sb.append( ".\n" ); sb.append( "I will email or snail mail you a " ); sb.append( "customised version on CD\n" ); sb.append( "after we have talked about your special needs. " ); sb.append( "I will work with\n" ); sb.append( "you to get the program working on your machine. " ); sb.append( "That support is included\n" ); sb.append( "in the price.
\n" ); p.write( sb.toString() ); } else { p.write( "
Download and evaluate " + "the components separately.
\n" ); } } // /method /** * emit details of what free means */ private static void emitFree() { { here(); } final FastCat sb = new FastCat( 9 ); sb.append( "
\n" ); sb.append( "\n" ); // also generates total donated so far, and donate button. sb.append( "
" ); sb.append( appCat.decoratedName( aspName ) ); sb.append( " is free. " ); sb.append( payDescriptionAux ); sb.append( " " ); sb.append( nonMilitaryUseOnly ); sb.append( "
\n" ); p.write( sb.toString() ); } // /method /** * emit instructions to install and run a jar * * @param commandLineExample example parm for command line */ private static void emitHowRunJar( String commandLineExample ) { { here(); } final FastCat sb = new FastCat( 11 ); sb.append( "

After you have installed the jar, you can run it as an application. Type:" ); sb.append( "

\n
\n" ); sb.append( "java -jar J:\\com\\mindprod\\" ); sb.append( computerName ); sb.append( "\\" ); sb.append( computerName ); sb.append( ".jar " ); sb.append( commandLineExample ); sb.append( "\n
\n" ); sb.append( "

adjusting as necessary to account for where the jar file is.

\n" ); p.write( sb.toString() ); } // /method /** * emit instructions to install a JET application */ private static void emitInstallJET() { { here(); } final FastCat sb = new FastCat( 3 ); sb.append( "

To install, run the download file and follow your nose.\n" ); sb.append( "

" ); p.write( sb.toString() ); } // /method /** * emit instructions to install a JET application to the path */ private static void emitInstallJETToThePath() { { here(); } final FastCat sb = new FastCat( 31 ); sb.append( "

By default, the application will end up in " ); sb.append( installedJetDir ); sb.append( ".\n" ); sb.append( "To be useful, you will want the program on the path. There are three ways to do that:

\n" ); sb.append( "
    " ); sb.append( "
  1. Use regedit to create a " ); sb.append( "" ); sb.append( " of the form:\n
    " ); sb.append( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" ); sb.append( computerName ); sb.append( ".exe" ); sb.append( BR ); sb.append( "(default) = " ); sb.append( installedJetExe ); sb.append( "
    \nthat puts " ); sb.append( computerName ); sb.append( ".exe on the aux App Path.
  2. \n" ); sb.append( "
  3. Add " ); sb.append( installedJetDir ); sb.append( ".\n" ); sb.append( " to the variable in your system environment.
  4. \n" ); sb.append( "
  5. Copy " ); sb.append( "" ); sb.append( installedJetExe ); sb.append( "" ); sb.append( " to a directory somewhere already on the path.
  6. " ); sb.append( "
\n" ); p.write( sb.toString() ); } // /method /** * emit instructions to install the JRE */ private static void emitInstallJava() { { here(); } final FastCat sb = new FastCat( 2 ); sb.append( "\n

" ); sb.append( "First " + ".

\n" ); p.write( sb.toString() ); } // /method /** * emit instructions to install a jar, this will be inserted eventually in applet/xxxx.html or products?.html */ private static void emitInstallZip() { { here(); } final FastCat sb = new FastCat( 5 ); sb.append( "

To install, extract the zip download with ," + "\n" ); sb.append( "(or similar unzip utility) into any directory you please,\n" ); sb.append( "often J:\\ — ticking off the\n" ); sb.append( "use folder names option.

" ); p.write( sb.toString() ); } // /method /** * Emit list of languages in corresponding style. */ private static void emitLanguages() { /** * array of languages */ String[] languages = SPLIT_ON_COLON_OR_COMMA.split( language ); if ( DEBUGGING && languages.length > 0 ) { here(); } for ( String language1 : languages ) { final FastCat sb = new FastCat( 5 ); sb.append( "" ); sb.append( language1 ); sb.append( "\n" ); p.write( sb.toString() ); } } // /method /** * emit the column that displays the last modified date of some file. * * @param filename name of the file whose last modified date should be displayed. */ private static void emitLastMod( String filename ) { { here(); } p.write( "" ); if ( isNew ) { // Program was released within the last month. // We are in the webRoot so the filename is automatically // LOCAL_WEBROOT_WITH_BACKSLASHES-relative // Use last micro change date, what FLASTMOD would give, // rather than the official release date. // suppress the CSS updated class and the updated wording. final FastCat sb = new FastCat( 5 ); sb.append( "" ); sb.append( "" ); sb.append( "" ); p.write( sb.toString() ); } else { final FastCat sb = new FastCat( 3 ); sb.append( "" ); /* last serious revision date */ sb.append( officialRELEASE_DATE.toString() ); sb.append( "" ); p.write( sb.toString() ); } p.write( "\n" ); } // /method /** * emit HTML to describe one package * This is the key method. It pulls together all the parts to make up bundle of HTML to describe a package. * If you change this, also change createaAppInclude */ private static void emitPackage() { // we now have all info about one product. Start generating html { here(); } final FastCat sb = new FastCat( 21 ); sb.append( "\n" ); sb.append( "" );// don't put name= target here if ( iconImgRef != null ) { sb.append( iconImgRef ); sb.append( BR ); } sb.append( appCat.decoratedName( aspName ) ); sb.append( "\n" ); sb.append( "" ); sb.append( version ); sb.append( "\n" ); sb.append( "" ); // similar effect to SSI FSIZE sb.append( "" ); if ( distributeJet ) { sb.append( "
\n" ); sb.append( "\n" ); } sb.append( "\n" ); p.write( sb.toString() ); emitLastMod( uPathDownload ); emitPay(); emitLanguages(); // Put target on details, tallest part of row so that when jump to it // all the row will be visible. p.write( "\n" ); emitDetailsAndManual(); emitRun(); emitDownload(); if ( distributeJet ) { emitDownloadForJet(); } emitPayment(); p.write( "\n" ); emitCategoryIcons(); p.write( "\n" ); p.write( "\n" ); } // /method /** * emit html about shareware/free/custom */ private static void emitPay() { { here(); } final FastCat sb = new FastCat( 8 ); sb.append( "" ); sb.append( "" ); sb.append( payDescription ); sb.append( "\n" ); sb.append( "" ); p.write( sb.toString() ); } // /method /** * emit details to allow a paypal purchase */ private static void emitPayPalPurchase() { { here(); } err.println( " PayPal purchases are no longer supported." ); // final FastCat sb = new FastCat( 16 ); // sb.append( "
\n" ); // sb.append( "" ); // sb.append( "\n" ); // sb.append( "" ); // sb.append( aspName ); // sb.append( " " ); // sb.append( payDescription ); // sb.append( " registration. " ); // sb.append( payDescriptionAux ); // sb.append( " " ); // sb.append( nonMilitaryUseOnly ); // sb.append( "
\n" ); // p.write( sb.toString() ); } // /method /** * generate HTML to handle purchasing this product. free generates nothing. */ private static void emitPayment() { agent = agent.toLowerCase(); switch ( agent ) { case "paypal": emitPayPalPurchase(); break; case "free": emitFree(); break; default: String[] parts = SPLIT_ON_COLON_OR_COMMA.split( agent ); agent = parts[ 0 ]; productID = parts[ 1 ]; switch ( agent ) { case "regsoft": emitRegSoftPurchase(); break; case "regnow": emitRegNowPurchase(); break; default: throw new IllegalArgumentException( "invalid payment agent" + agent ); } break; } } // /method /** * emit details to allow a RegNow purchase */ private static void emitRegNowPurchase() { { here(); } final FastCat sb = new FastCat( 21 ); sb.append( "
" ); sb.append( "" ); sb.append( "buy via RegNow" ); sb.append( " " ); sb.append( "" ); sb.append( "" ); sb.append( BR ); sb.append( "" ); sb.append( appCat.decoratedName( aspName ) ); sb.append( " " ); sb.append( payDescription ); sb.append( " registration. " ); sb.append( payDescriptionAux ); sb.append( " " ); sb.append( nonMilitaryUseOnly ); sb.append( "
\n" ); p.write( sb.toString() ); } // /method /** * emit details to allow a RegSoft purchase */ private static void emitRegSoftPurchase() { { here(); } final FastCat sb = new FastCat( 21 ); sb.append( "
" ); sb.append( "" ); sb.append( "" ); sb.append( "buy via Regsoft" ); sb.append( " " ); sb.append( "" ); sb.append( "" ); sb.append( BR ); sb.append( "" ); sb.append( appCat.decoratedName( aspName ) ); sb.append( " " ); sb.append( payDescription ); sb.append( " registration. " ); sb.append( payDescriptionAux ); sb.append( " " ); sb.append( nonMilitaryUseOnly ); sb.append( "
\n" ); p.write( sb.toString() ); } // /method /** * Emit Go/run the code. */ private static void emitRun() { { here(); } final String jarTargetDir = appCat.getJarDir(); switch ( appCat ) { case HYBRID: case APPLET: { final FastCat sb = new FastCat( 7 ); sb.append( "
\n" ); sb.append( appCat.decoratedName( aspName ) ); sb.append( " online as an Applet in your browser, without installing it on your computer." + "
\n" ); p.write( sb.toString() ); } break; case JWS: { final FastCat sb = new FastCat( 25 ); sb.append( "
\n" ); sb.append( appCat.decoratedName( aspName ) ); sb.append( " online with Java Web Start.\n" ); sb.append( BR ); sb.append( "If the download fails, install " ); sb.append( appCat.decoratedName( aspName ) ); sb.append( " manually with:\n" ); sb.append( "
\n" ); sb.append( "rem On Windows" ); sb.append( BR ); sb.append( "javaws.exe " ); sb.append( "http://mindprod.com/webstart/" ); sb.append( computerName ); sb.append( ".jnlp" ); sb.append( BR ); sb.append( "# on Linux/BASH" ); sb.append( BR ); sb.append( "javaws " ); sb.append( "http://mindprod.com/webstart/" ); sb.append( computerName ); sb.append( ".jnlp
\n" ); p.write( sb.toString() ); } break; case APPLICATION: case ASMCOM: case ASMEXE: case JAVA_CLASS_LIBRARY: case JAVA_SNIPPETS: case CPPUTILITY: case DOCUMENTATION: break; case BUNDLE: break; case CUTILITY: break; case SERVLET: break; } } // /method /** * emit OS runs on DOS */ private static void emitRunsOnDOS() { { here(); } p.write( "\n

Runs on , e.g. from DOS thumbdrive, " + "tiny DOS partition or DOS compatibility box.

\n" ); } // /method /** * emit OS runs on JAVA */ private static void emitRunsOnJava() { { here(); } p.write( "\n

Runs on any OS that supports Java e.g. .

\n" ); } // /method /** * emit OS runs on Windows (for C/C++) */ private static void emitRunsOnWindows() { { here(); } p.write( "\n

Runs on .

\n" ); } // /method /** * emit summary link of products2.html item to go on products.html page */ private static void emitUnanchored() { // we cannot insert debug info. We are inside a Quilt macro and it would confuse the macro processor. final FastCat sb = new FastCat( 5 ); sb.append( "{" ); sb.append( appCat.decoratedName( aspName ) ); sb.append( "}\n" ); p.write( sb.toString() ); } // /method /** * read one record from prices file, describes one program to display * * @throws java.io.IOException if write fails. */ @SuppressWarnings( { "LocalVariableNamingConvention" } ) private static void getRecord() throws IOException { assert r != null; aspName = r.get(); // out.println( aspName ); computerName = r.get(); version = r.get(); String officialRELEASE_DATEString = r.get(); if ( !BigDate.isValid( officialRELEASE_DATEString ) ) { throw new IllegalArgumentException( "Bad officialRELEASE_DATE: " + officialRELEASE_DATEString ); } officialRELEASE_DATE = new BigDate( officialRELEASE_DATEString ); // downloadDir generated later // manual generated later // free, distribution, shareware pay = r.get(); licence = Licence.valueOfAlias( pay ); calcPayDescription(); price = r.get(); agent = r.get(); language = r.get(); appCat = AppCat.valueOfAlias( r.get() );// Applet application JWS distributeJet = r.getBoolean(); category = r.get(); // changed generated later desc = r.get(); } // /method /** * if debugging, puts a comment into the generated stream telling where in the program it was generated. */ private static void here() { if ( DEBUGGING ) { p.write( "" ); } } // /method /** * emit html to include a canned piece of boilerplate, such as the 4-part site download or the Abundance download. * * @throws IOException write fail * @see #createAppInclude */ private static void makeInclude() throws IOException { assert r != null : " input file not open"; aspName = r.get(); if ( aspName == null ) { throw new IllegalArgumentException( "missing human name" ); } computerName = r.get(); if ( computerName == null ) { throw new IllegalArgumentException( "missing computer name" ); } ucComputerName = computerName.toUpperCase(); // filename of the included text, relative to E:/mindprod/include String filenameToInclude = computerName + ".htmlfrag"; switch ( pass ) { case UNANCHORED_PAGE_INDEX: emitUnanchored(); break; case ANCHORED_MASTER_INDEX: emitAnchored(); break; case DETAILS: emitDetails( filenameToInclude ); break; } } // /method /** * emit html to describe a licence. No public downloadable. * * @throws IOException write fail */ private static void makeLicence() throws IOException { { here(); } getRecord(); calcPseudoFields( false ); switch ( pass ) { case UNANCHORED_PAGE_INDEX: emitUnanchored(); break; case ANCHORED_MASTER_INDEX: emitAnchored(); break; case DETAILS: // we now have all info about one license. Start generating html p.write( "\n" ); // don't put name= target here p.write( "\n" ); if ( iconImgRef != null ) { p.write( iconImgRef + BR ); } p.write( appCat.decoratedName( aspName ) + "\n" ); p.write( "" + version + "\n" ); p.write( " \n" ); emitLastMod( uPathPrecis ); emitPay(); emitLanguages(); // Put target on details, tallest part of row so that when jump to it // all the row will be visible. p.write( "\n" ); emitDetailsAndManual(); emitRun(); emitExplainNoDownload(); emitPayment(); p.write( "\n" ); emitCategoryIcons(); p.write( "\n" ); p.write( "\n" ); break; } } // /method // end makeLicence /** * emit html to describe a downloadable package which includes source. * * @throws IOException write fail. */ private static void makePackage() throws IOException { getRecord(); calcPseudoFields( true ); switch ( pass ) { case UNANCHORED_PAGE_INDEX: emitUnanchored(); break; case ANCHORED_MASTER_INDEX: emitAnchored(); break; case DETAILS: emitPackage(); break; } } // /method /** * convert webroot relative reference to absolute URL * * @param wrr webroot relative reference with \ * * @return http:// absolute URL */ private static String toAbsoluteURL( String wrr ) { return WEBSITE_URL + "/" + wrr.replace( '\\', '/' ); } // /method // /methods /** * Generate HTML from prices.txt assume current directory is E:\com\mindprod\prices * * @param args not used. * * @throws IOException if trouble writing output */ public static void main( String[] args ) throws IOException { // combine dirsWithMacros and dirsWithIncludes into dirsToProcess; sourceDir = new File( Build.MINDPROD_SOURCE ); webrootDirname = Build.MINDPROD_WEBROOT; webrootDir = new File( webrootDirname ); pad3List = new File( webrootDir, "pad/pad3.list" ); pad4List = new File( webrootDir, "pad/pad4.list" ); refreshPadsBtm = new File( "E:/env/refreshpads.btm" ); redirectPadsBtm = new File( "E:/env/redirectpads.txt" ); allproductsCSVFile = new File( webrootDir, "allproducts.csv" ); productsIncludeMasterFile = new File( webrootDir, "/include/productsindex.htmlfrag" ); // %i will be replaced by a small int to create /include/products2details.htmlfrag productsIncludePageDetailsFilename = webrootDirname + "/include/products%idetails.htmlfrag"; productsPageIndexFilename = webrootDirname + "/include/products%iindex.htmlfrag"; pricesCsvFile = new File( sourceDir, "prices/prices.csv" ); aMonthAgo = BigDate.localToday().addDays( -30 ); // We build various chunks to be included. // The productsdetails?.html pages contain the individual items dire8ctly. They do not incl/ude the // individivual app items. // master product index page, to be included. buildMasterProductIndex(); for ( int i = 1; i <= 4; i++ ) { // product index page, to be included buildAProductIndex( i ); // details of each product, to be included, popular etc. buildProductDetailsPage( i, productsIncludePageDetailsFilename.replace( "%i", Integer.toString( i ) ) ); } // for use by apps, to be included in manuals createAppIncludes(); // summary for website createAllProductsCsv(); // for padmaps createAllPadsTxt(); Misc.trackLastThread(); System.exit( 0 ); } // /method /** * ways of creating links to products *

* created with Intellij Idea * * @author Roedy Green, Canadian Mind Products */ @SuppressWarnings( { "EnumeratedClassNamingConvention" } ) enum Form { /** * link with name anchor (as on master index ) */ ANCHORED_MASTER_INDEX, /** * emit full whole entry */ @SuppressWarnings( { "EnumeratedConstantNamingConvention" } ) DETAILS, /** * link without name anchor (as on individual products pages ) */ UNANCHORED_PAGE_INDEX } }