001         package com.croftsoft.apps.template;
002    
003         import java.io.*;
004         import java.util.*;
005    
006         import com.croftsoft.core.lang.StringLib;
007    
008         /*********************************************************************
009         * Creates web pages from a template file and substitution map files.
010         *
011         * <P>
012         *
013         * Wherever the &lt;subst key="<I>key</I>"&gt;&lt;/subst&gt; occurs in
014         * the template file, it is replaced with a value parsed from the
015         * content mapping file to create a new *.html file.
016         *
017         * <P>
018         *
019         * Note that the key name is case-senstive.
020         *
021         * <P>
022         *
023         * Example Template File "template.html":
024         * <PRE>
025         * &lt;HTML&gt;
026         * &lt;HEAD&gt;
027         * &lt;SUBST KEY="HEAD"&gt;&lt;/SUBST&gt;
028         * &lt;/HEAD&gt;
029         * &lt;/HTML&gt;
030         * &lt;BODY BGCOLOR="Blue"&gt;
031         * &lt;CENTER&gt;
032         * &lt;IMG SRC="/adbanner.gif"&gt;
033         * &lt;/CENTER&gt;
034         * &lt;P&gt;
035         * &lt;SUBST KEY="BODY"&gt;&lt;/SUBST&gt;
036         * &lt;/BODY&gt;
037         * &lt;/HTML&gt;
038         * </PRE>
039         * Example Content Mapping File "filename.smap":
040         * <PRE>
041         * &lt;HTML&gt;
042         * &lt;HEAD&gt;
043         * &lt;TITLE&gt;
044         * Welcome to my home page!
045         * &lt;/TITLE&gt;
046         * &lt;/HEAD&gt;
047         * &lt;/HTML&gt;
048         * &lt;BODY&gt;
049         * My Home page
050         * &lt;/BODY&gt;
051         * &lt;/HTML&gt;
052         * </PRE>
053         * Example Resultant HTML File "filename.html":
054         * <PRE>
055         * &lt;HTML&gt;
056         * &lt;HEAD&gt;
057         * &lt;TITLE&gt;
058         * Welcome to my home page!
059         * &lt;/TITLE&gt;
060         * &lt;/HEAD&gt;
061         * &lt;/HTML&gt;
062         * &lt;BODY BGCOLOR="Blue"&gt;
063         * &lt;CENTER&gt;
064         * &lt;IMG SRC="/adbanner.gif"&gt;
065         * &lt;/CENTER&gt;
066         * &lt;P&gt;
067         * My Home page
068         * &lt;/BODY&gt;
069         * &lt;/HTML&gt;
070         * </PRE>
071         *
072         * <P>
073         *
074         * Execute with the document root directory name as the command-line
075         * argument (defaults to ".").  It will traverse
076         * subdirectories as needed to create corresponding *.html files for
077         * each *.smap file.  The template file in the current directory will
078         * be used.  Subdirectories will use their parent template files unless
079         * they have template files of their own.
080         *
081         * <P>
082         *
083         * Intended to operate like the JavaWebServer templating mechanism
084         * except that the files are preprocessed.  Note that I am using
085         * SUBST KEY="..." instead of SUBST DATA="..." as I intend to make
086         * this more general-purpose over time.
087         *
088         * <P>
089         *
090         * <FONT COLOR="Red">
091         * Current implementation limitations to be fixed later:
092         *
093         * <UL>
094         * <LI> Weak parsing:  must be exactly
095         *      "&lt;SUBST KEY="HEAD"&gt;&lt;/SUBST&gt; to work
096         *      with no extra white space, no lower case letters (except for
097         *      the case-sensitive key name), and no replaceable content
098         *      between &lt;SUBST&gt; and &lt;/SUBST&gt; allowed.
099         *      [fix using javax.swing.text.html parser?]
100         * </UL>
101         * </FONT>
102         *
103         * <P>
104         *
105         * @version
106         *   2000-05-05
107         * @author
108         *   <A HREF="http://www.alumni.caltech.edu/~croft/">David W. Croft</A>
109         *********************************************************************/
110    
111         public class  Template
112         //////////////////////////////////////////////////////////////////////
113         //////////////////////////////////////////////////////////////////////
114         {
115    
116         private static final String  TEMPLATE_FILE_NAME = "template.html";
117    
118         private static final String  SMAP_EXTENSION     = ".smap";
119    
120         private final String      templateText;
121    
122         private       String [ ]  keys;
123    
124         //////////////////////////////////////////////////////////////////////
125         //////////////////////////////////////////////////////////////////////
126    
127         /*********************************************************************
128         * Recursively creates *.html files for each *.smap file.
129         *
130         * <P>
131         *
132         * @param  args
133         *   First command-line argument is the document root directory;
134         *   defaults to ".".
135         *********************************************************************/
136         public static void  main ( String [ ]  args ) throws Exception
137         //////////////////////////////////////////////////////////////////////
138         {
139           String  dirName = ".";
140    
141           if ( args.length > 0 )
142           {
143             dirName = args [ 0 ];
144           }
145    
146           File  dir = new File ( dirName );
147    
148           if ( !dir.isDirectory ( ) )
149           {
150             throw new IllegalArgumentException (
151               "\"" + dirName + "\" not a directory" );
152           }
153    
154           generateFilesInBranch ( null, dir );
155         }
156    
157         public static void  generateFilesInBranch (
158           Template  parentTemplate,
159           File      dir )
160           throws IOException
161         //////////////////////////////////////////////////////////////////////
162         {
163           if ( dir == null )
164           {
165             throw new IllegalArgumentException ( "null dir" );
166           }
167    
168           Template  template = parentTemplate;
169    
170           File  templateFile = new File ( dir, TEMPLATE_FILE_NAME );
171    
172           if ( templateFile.exists ( ) )
173           {
174             System.out.println ( "Loading template from " + templateFile );
175    
176             String  templateText = com.orbs.open1.mit.io.FileLib.readFile (
177               templateFile.toString ( ) );
178    
179             template = new Template ( templateText );
180           }
181    
182           String [ ]  fileNames = dir.list ( );
183    
184           for ( int  i = 0; i < fileNames.length; i++ )
185           {
186             String  fileName = fileNames [ i ];
187    
188             File  sdatFile = new File ( dir, fileName );
189    
190             if ( sdatFile.isDirectory ( ) )
191             {
192               // recursive
193    
194               generateFilesInBranch ( template, sdatFile );
195    
196               continue;
197             }
198    
199             if ( template == null )
200             {
201               continue;
202             }
203    
204             if ( !fileName.endsWith ( SMAP_EXTENSION ) )
205             {
206               continue;
207             }
208    
209             String  sdatText = com.orbs.open1.mit.io.FileLib.readFile (
210               new File ( dir, fileName ).toString ( ) );
211    
212             Map  substitutionMap = new HashMap ( );
213    
214             String [ ]  keys = template.getKeys ( );
215    
216             for ( int  keyIndex = 0; keyIndex < keys.length; keyIndex++ )
217             {
218               String  value = extractTag ( sdatText, keys [ keyIndex ] );
219    
220               if ( value != null )
221               {
222                 substitutionMap.put ( keys [ keyIndex ], value );
223               }
224             }
225    
226             String  htmlText = template.generate ( substitutionMap );
227    
228             String  htmlFileName
229               = com.croftsoft.core.io.FileLib.pareExtension ( fileName )
230               + ".html";
231    
232             File  htmlFile = new File ( dir, htmlFileName );
233    
234             System.out.println ( "Creating " + htmlFile );
235    
236             OutputStream  out = new FileOutputStream ( htmlFile );
237    
238             out.write ( htmlText.getBytes ( ) );
239    
240             out.close ( );
241           }
242         }
243    
244         //////////////////////////////////////////////////////////////////////
245         //////////////////////////////////////////////////////////////////////
246    
247         public  Template ( String  templateText )
248         //////////////////////////////////////////////////////////////////////
249         {
250           if ( templateText == null )
251           {
252             throw new IllegalArgumentException ( "null templateText" );
253           }
254    
255           this.templateText = templateText;
256    
257           parse ( );
258         }
259    
260         //////////////////////////////////////////////////////////////////////
261         //////////////////////////////////////////////////////////////////////
262    
263         public String [ ]  getKeys ( )
264         //////////////////////////////////////////////////////////////////////
265         {
266           return keys;
267         }
268    
269         public String  generate ( Map  substitutionMap )
270         //////////////////////////////////////////////////////////////////////
271         {
272           String  newText = templateText;
273    
274           for ( int  i = 0; i < keys.length; i++ )
275           {
276             String  key = keys [ i ];
277    
278             String  value = ( String ) substitutionMap.get ( key );
279    
280             if ( value != null )
281             {
282    // this is weak:  case-sensitive and white-space sensitive
283    
284    // also does not allow replaceable content between <SUBST> and </SUBST>
285    
286               newText = StringLib.replace ( newText,
287                 "<SUBST KEY=\"" + key + "\"></SUBST>", value );
288             }
289           }
290    
291           return newText;
292         }
293    
294         //////////////////////////////////////////////////////////////////////
295         // private methods
296         //////////////////////////////////////////////////////////////////////
297    
298         /*********************************************************************
299         * Finds all of the substitution keys in the template.
300         *********************************************************************/
301         private void  parse ( )
302         //////////////////////////////////////////////////////////////////////
303         {
304           System.out.println ( "  Extracting keys" );
305    
306    // this is weak:  case-sensitive and white-space sensitive
307    
308           String  lead = "<SUBST KEY=\"";
309    
310           Set  keySet = new HashSet ( );
311    
312           int  index0 = 0;
313    
314           while ( true )
315           {
316             index0 = templateText.indexOf ( lead, index0 );
317    
318             if ( index0 < 0 )
319             {
320               break;
321             }
322    
323             index0 += lead.length ( );
324    
325             int  index1 = templateText.indexOf ( "\"", index0 );
326    
327             if ( index1 < 0 )
328             {
329               break;
330             }
331    
332             String  key = templateText.substring ( index0, index1 );
333    
334             System.out.println ( "    key = \"" + key + "\"" );
335    
336             keySet.add ( key );
337           }
338    
339           keys = ( String [ ] ) keySet.toArray ( new String [ ] { } );
340         }
341    
342         private static String  extractTag (
343           String  sdatText, String  tagName )
344         //////////////////////////////////////////////////////////////////////
345         {
346    // this is weak:  case-sensitive and white-space sensitive
347    
348    
349           int  startIndex = sdatText.indexOf ( "<" + tagName + ">" );
350    
351           if ( startIndex < 0 )
352           {
353             return null;
354           }
355    
356           startIndex += 2 + tagName.length ( );
357    
358           int  endIndex
359             = sdatText.indexOf ( "</" + tagName + ">", startIndex );
360    
361           if ( endIndex < 0 )
362           {
363             return null;
364           }
365    
366           return sdatText.substring ( startIndex, endIndex );
367         }
368    
369         //////////////////////////////////////////////////////////////////////
370         //////////////////////////////////////////////////////////////////////
371         }