//: CodePackager.java ////////////////////////////////////////////////// // Copyright (c) Bruce Eckel, 1998 // Source code file from the book "Thinking in Java" // All rights reserved EXCEPT as allowed by the // following statements: You can freely use this file // for your own work (personal or commercial), // including modifications and distribution in // executable form only. Permission is granted to use // this file in classroom situations, including its // use in presentation materials, as long as the book // "Thinking in Java" is cited as the source. // Except in classroom situations, you cannot copy // and distribute this code; instead, the sole // distribution point is http://www.BruceEckel.com // (and official mirror sites) where it is // freely available. You cannot remove this // copyright and notice. You cannot distribute // modified versions of the source code in this // package. You cannot use this file in printed // media without the express permission of the // author. Bruce Eckel makes no representation about // the suitability of this software for any purpose. // It is provided "as is" without express or implied // warranty of any kind, including any implied // warranty of merchantability, fitness for a // particular purpose or non-infringement. The entire // risk as to the quality and performance of the // software is with you. Bruce Eckel and the // publisher shall not be liable for any damages // suffered by you or any third party as a result of // using or distributing software. In no event will // Bruce Eckel or the publisher be liable for any // lost revenue, profit, or data, or for direct, // indirect, special, consequential, incidental, or // punitive damages, however caused and regardless of // the theory of liability, arising out of the use of // or inability to use software, even if Bruce Eckel // and the publisher have been advised of the // possibility of such damages. Should the software // prove defective, you assume the cost of all // necessary servicing, repair, or correction. If you // think you've found an error, please email all // modified files with clearly commented changes to: // Bruce@EckelObjects.com. (Please use the same // address for non-code errors found in the book.) ///////////////////////////////////////////////// // "Packs" and "unpacks" the code in "Thinking // in Java" for cross-platform distribution. /* Commented so CodePackager sees it and starts a new chapter directory, but so you don't have to worry about the directory where this program lives: package c17; */ import java.util.*; import java.io.*; class Pr { static void error(String e) { System.err.println("ERROR: " + e); System.exit(1); } } class IO { static BufferedReader disOpen(File f) { BufferedReader in = null; try { in = new BufferedReader( new FileReader(f)); } catch(IOException e) { Pr.error("could not open " + f); } return in; } static BufferedReader disOpen(String fname) { return disOpen(new File(fname)); } static DataOutputStream dosOpen(File f) { DataOutputStream in = null; try { in = new DataOutputStream( new BufferedOutputStream( new FileOutputStream(f))); } catch(IOException e) { Pr.error("could not open " + f); } return in; } static DataOutputStream dosOpen(String fname) { return dosOpen(new File(fname)); } static PrintWriter psOpen(File f) { PrintWriter in = null; try { in = new PrintWriter( new BufferedWriter( new FileWriter(f))); } catch(IOException e) { Pr.error("could not open " + f); } return in; } static PrintWriter psOpen(String fname) { return psOpen(new File(fname)); } static void close(Writer os) { try { os.close(); } catch(IOException e) { Pr.error("closing " + os); } } static void close(DataOutputStream os) { try { os.close(); } catch(IOException e) { Pr.error("closing " + os); } } static void close(Reader os) { try { os.close(); } catch(IOException e) { Pr.error("closing " + os); } } } class SourceCodeFile { public static final String startMarker = "//:", // Start of source file endMarker = "} ///:~", // End of source endMarker2 = "}; ///:~", // C++ file end beginContinue = "} ///:Continued", endContinue = "///:Continuing", packMarker = "###", // Packed file header tag eol = // Line separator on current system System.getProperty("line.separator"), filesep = // System's file path separator System.getProperty("file.separator"); public static String copyright = ""; static { try { BufferedReader cr = new BufferedReader( new FileReader("Copyright.txt")); String crin; while((crin = cr.readLine()) != null) copyright += crin + "\n"; cr.close(); } catch(Exception e) { copyright = ""; } } private String filename, dirname, contents = new String(); private static String chapter = "c02"; // The file name separator from the old system: public static String oldsep; public String toString() { return dirname + filesep + filename; } // Constructor for parsing from document file: public SourceCodeFile(String firstLine, BufferedReader in) { dirname = chapter; // Skip past marker: filename = firstLine.substring( startMarker.length()).trim(); // Find space that terminates file name: if(filename.indexOf(' ') != -1) filename = filename.substring( 0, filename.indexOf(' ')); System.out.println("found: " + filename); contents = firstLine + eol; if(copyright.length() != 0) contents += copyright + eol; String s; boolean foundEndMarker = false; try { while((s = in.readLine()) != null) { if(s.startsWith(startMarker)) Pr.error("No end of file marker for " + filename); // For this program, no spaces before // the "package" keyword are allowed // in the input source code: else if(s.startsWith("package")) { // Extract package name: String pdir = s.substring( s.indexOf(' ')).trim(); pdir = pdir.substring( 0, pdir.indexOf(';')).trim(); // Capture the chapter from the package // ignoring the 'com' subdirectories: if(!pdir.startsWith("com")) { int firstDot = pdir.indexOf('.'); if(firstDot != -1) chapter = pdir.substring(0,firstDot); else chapter = pdir; } // Convert package name to path name: pdir = pdir.replace( '.', filesep.charAt(0)); System.out.println("package " + pdir); dirname = pdir; } contents += s + eol; // Move past continuations: if(s.startsWith(beginContinue)) while((s = in.readLine()) != null) if(s.startsWith(endContinue)) { contents += s + eol; break; } // Watch for end of code listing: if(s.startsWith(endMarker) || s.startsWith(endMarker2)) { foundEndMarker = true; break; } } if(!foundEndMarker) Pr.error( "End marker not found before EOF"); System.out.println("Chapter: " + chapter); } catch(IOException e) { Pr.error("Error reading line"); } } // For recovering from a packed file: public SourceCodeFile(BufferedReader pFile) { try { String s = pFile.readLine(); if(s == null) return; if(!s.startsWith(packMarker)) Pr.error("Can't find " + packMarker + " in " + s); s = s.substring( packMarker.length()).trim(); dirname = s.substring(0, s.indexOf('#')); filename = s.substring(s.indexOf('#') + 1); dirname = dirname.replace( oldsep.charAt(0), filesep.charAt(0)); filename = filename.replace( oldsep.charAt(0), filesep.charAt(0)); System.out.println("listing: " + dirname + filesep + filename); while((s = pFile.readLine()) != null) { // Watch for end of code listing: if(s.startsWith(endMarker) || s.startsWith(endMarker2)) { contents += s; break; } contents += s + eol; } } catch(IOException e) { System.err.println("Error reading line"); } } public boolean hasFile() { return filename != null; } public String directory() { return dirname; } public String filename() { return filename; } public String contents() { return contents; } // To write to a packed file: public void writePacked(DataOutputStream out) { try { out.writeBytes( packMarker + dirname + "#" + filename + eol); out.writeBytes(contents); } catch(IOException e) { Pr.error("writing " + dirname + filesep + filename); } } // To generate the actual file: public void writeFile(String rootpath) { File path = new File(rootpath, dirname); path.mkdirs(); PrintWriter p = IO.psOpen(new File(path, filename)); p.print(contents); IO.close(p); } } class DirMap { private Hashtable t = new Hashtable(); private String rootpath; DirMap() { rootpath = System.getProperty("user.dir"); } DirMap(String alternateDir) { rootpath = alternateDir; } public void add(SourceCodeFile f){ String path = f.directory(); if(!t.containsKey(path)) t.put(path, new Vector()); ((Vector)t.get(path)).addElement(f); } public void writePackedFile(String fname) { DataOutputStream packed = IO.dosOpen(fname); try { packed.writeBytes("###Old Separator:" + SourceCodeFile.filesep + "###\n"); } catch(IOException e) { Pr.error("Writing separator to " + fname); } Enumeration e = t.keys(); while(e.hasMoreElements()) { String dir = (String)e.nextElement(); System.out.println( "Writing directory " + dir); Vector v = (Vector)t.get(dir); for(int i = 0; i < v.size(); i++) { SourceCodeFile f = (SourceCodeFile)v.elementAt(i); f.writePacked(packed); } } IO.close(packed); } // Write all the files in their directories: public void write() { Enumeration e = t.keys(); while(e.hasMoreElements()) { String dir = (String)e.nextElement(); Vector v = (Vector)t.get(dir); for(int i = 0; i < v.size(); i++) { SourceCodeFile f = (SourceCodeFile)v.elementAt(i); f.writeFile(rootpath); } // Add file indicating file quantity // written to this directory as a check: IO.close(IO.dosOpen( new File(new File(rootpath, dir), Integer.toString(v.size())+".files"))); } } } public class CodePackager { private static final String usageString = "usage: java CodePackager packedFileName" + "\nExtracts source code files from packed \n" + "version of Tjava.doc sources into " + "directories off current directory\n" + "java CodePackager packedFileName newDir\n" + "Extracts into directories off newDir\n" + "java CodePackager -p source.txt packedFile" + "\nCreates packed version of source files" + "\nfrom text version of Tjava.doc"; private static void usage() { System.err.println(usageString); System.exit(1); } public static void main(String[] args) { if(args.length == 0) usage(); if(args[0].equals("-p")) { if(args.length != 3) usage(); createPackedFile(args); } else { if(args.length > 2) usage(); extractPackedFile(args); } } private static String currentLine; private static BufferedReader in; private static DirMap dm; private static void createPackedFile(String[] args) { dm = new DirMap(); in = IO.disOpen(args[1]); try { while((currentLine = in.readLine()) != null) { if(currentLine.startsWith( SourceCodeFile.startMarker)) { dm.add(new SourceCodeFile( currentLine, in)); } else if(currentLine.startsWith( SourceCodeFile.endMarker)) Pr.error("file has no start marker"); // Else ignore the input line } } catch(IOException e) { Pr.error("Error reading " + args[1]); } IO.close(in); dm.writePackedFile(args[2]); } private static void extractPackedFile(String[] args) { if(args.length == 2) // Alternate directory dm = new DirMap(args[1]); else // Current directory dm = new DirMap(); in = IO.disOpen(args[0]); String s = null; try { s = in.readLine(); } catch(IOException e) { Pr.error("Cannot read from " + in); } // Capture the separator used in the system // that packed the file: if(s.indexOf("###Old Separator:") != -1 ) { String oldsep = s.substring( "###Old Separator:".length()); oldsep = oldsep.substring( 0, oldsep. indexOf('#')); SourceCodeFile.oldsep = oldsep; } SourceCodeFile sf = new SourceCodeFile(in); while(sf.hasFile()) { dm.add(sf); sf = new SourceCodeFile(in); } dm.write(); } } ///:~