/* * [Prepare.java] * * Summary: prepare predigested resource of information about http response codes so we can rapidly. * * Copyright: (c) 2012-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.0 2012-11-12 initial version */ package com.mindprod.brokenlinks; import com.mindprod.common18.EIO; import com.mindprod.common18.ST; import com.mindprod.csv.CSVReader; import java.io.BufferedReader; import java.io.EOFException; import java.io.File; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.PrintWriter; import java.util.zip.GZIPOutputStream; import static java.lang.System.*; /** * prepare predigested resource of information about http response codes so we can rapidly. *

* classify status codes and find corresponding descriptions. * * @author Roedy Green, Canadian Mind Products * @version 1.0 2012-11-12 initial version * @since 2012-11-12 */ public class Prepare { /** * highest response code we cover. * If change, must change in Prepare, StatusKind, responsecodes.csv, recompile, run Prepare and rebuild. */ private static final int HIGHEST_CODE = 12200; /** * lowest response code we cover. * If change, must change in Prepare, StatusKind, responsecodes.csv, recompile, run Prepare and rebuild. */ private static final int LOWEST_CODE = -20; /** * how many codes track. */ private static final int N = HIGHEST_CODE - LOWEST_CODE + 1; /** * array of names Java uses for codes, interned */ private static final String[] javaNames = new String[ N ]; /** * array of English descriptions for codes, interned */ private static final String[] statusWordings = new String[ N ]; /** * true if this code has been accounted for in the responsecodes.csv file */ private static final boolean[] accountedFors = new boolean[ N ]; /** * letter b=bad g=good i=ignore p=permanent redirect t=temp redirect u=unknown */ private static final char[] kindLetters = new char[ N ]; /** * marks version of file */ private static final long serialVersionUID = 1; /** * Like assert, but works even when assertions turned off * * @param assertion boolean expression that must be true. * @param reason error message to display if assertion fails. * @param r optional CSVReader in use at time of failure */ private static void must( final boolean assertion, final String reason, final CSVReader r ) { if ( assertion ) { return; } if ( r != null ) { err.print( "<><>Error<><> on line " + r.lineCount() + " " ); } else { err.print( "<><>Error<><> " ); } err.println( reason ); } /** * read list of response codes from csv file * * @throws IOException if problems reading csv file */ private static void readResponseCodes() throws IOException { final CSVReader r = new CSVReader( new BufferedReader( new FileReader( "responsecodes.csv" ) ) ); try { int prevLow = LOWEST_CODE - 1; while ( true ) { // # Xenu status code meanings. -20 to 12100 // # low, high, kind, JavaName, statusWording // -10, -7, unknown, XENU_UNUSED, unknown // read one line of csv file final int low = r.getInt(); int high = r.getInt(); if ( high == 0 ) { high = low; } final String kind = r.get().intern(); final String javaName = r.get().intern(); final String statusWording = r.get().intern(); r.skipToNextLine(); // validate the line must( LOWEST_CODE <= low && low <= HIGHEST_CODE, "low out of range: " + low, r ); must( LOWEST_CODE <= high && high <= HIGHEST_CODE, "high out of range: " + low, r ); must( low <= high, "low must be less than high " + low + " " + high, r ); must( !ST.isEmpty( kind ) && ( kind.equals( "bad" ) || kind.equals( "good" ) || kind.equals( "ignore" ) || kind.equals( "perm_redirect" ) || kind.equals( "temp_redirect" ) || kind.equals( "unknown" ) ) , "bad kind " + kind, r ); must( low > prevLow, "out of order near code " + low, r ); prevLow = low; // apply to each code in range. for ( int i = low; i <= high; i++ ) { // i = human code // j = internal index int j = i - LOWEST_CODE; if ( accountedFors[ j ] ) { err.println( "<><>Error<><> code " + i + " duplicated" ); } else { accountedFors[ j ] = true; } kindLetters[ j ] = kind.charAt( 0 ); javaNames[ j ] = javaName; statusWordings[ j ] = statusWording; } } // end while } catch ( EOFException e ) { r.close(); } // check that no codes missed for ( int i = LOWEST_CODE; i <= HIGHEST_CODE; i++ ) { // i = human code // j = internal index final int j = i - LOWEST_CODE; must( accountedFors[ j ], "code " + i + " not accounted for", null ); } // dump tables we just got from the resource for ( int i = LOWEST_CODE; i <= HIGHEST_CODE; i++ ) { final int j = i - LOWEST_CODE; out.println( i + " " + kindLetters[ j ] + " " + javaNames[ j ] + " " + statusWordings[ j ] ); } } /** * write serialized list of default apps * * @throws IOException if problems writing serialised file */ private static void writeResponseCodes() throws IOException { // O P E N final FileOutputStream fos = new FileOutputStream( "responsecodes.ser", false ); final GZIPOutputStream gzos = new GZIPOutputStream( fos, 4 * 1024 /* buffsize in bytes */ ); final ObjectOutputStream oos = new ObjectOutputStream( gzos ); // W R I T E oos.writeObject( serialVersionUID ); oos.writeObject( LOWEST_CODE ); oos.writeObject( HIGHEST_CODE ); // write out as separate complete arrays indexed by code rather than array of objects. oos.writeObject( kindLetters ); // will be converted to enums after load. oos.writeObject( javaNames ); oos.writeObject( statusWordings ); // C L O S E oos.close(); } /** * write abbreviated list for use as code, not resource is com.mindprod.http.HTTP * * @throws IOException if problems writing file */ private static void writeResponseCodesForHTTP() throws IOException { // O P E N final PrintWriter prw = EIO.getPrintWriter( new File( "responsecodes.javafrag" ), 10 * 1024, EIO.UTF8 ); prw.println( "// Do not edit. Generated by BrokenLinks.Prepare from responsecodes.csv" ); // W R I T E for ( int i = LOWEST_CODE; i <= HIGHEST_CODE; i++ ) { // i = human code // j = internal index final int j = i - LOWEST_CODE; char kind = kindLetters[ j ]; switch ( kind ) { case 'i': case 'u': // not http code break; case 'b': case 'g': case 'p': case 't': String javaName = javaNames[ j ]; if ( javaName.startsWith( "HTTP" ) ) { // e.g. means( 501, "not implemented" ); prw.println( "means ( " + i + ", \"" + statusWordings[ j ] + "\" );" ); } break; default: err.println( "invalid kind " + kind ); } } // C L O S E prw.close(); } public static void main( String[] args ) throws IOException { // write out in compressed serialised format: readResponseCodes(); // write out in compressed serialised format: writeResponseCodes(); // export a list of response codes is Java source code for HTTP writeResponseCodesForHTTP(); } }