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 <subst key="<I>key</I>"></subst> 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 * <HTML> 026 * <HEAD> 027 * <SUBST KEY="HEAD"></SUBST> 028 * </HEAD> 029 * </HTML> 030 * <BODY BGCOLOR="Blue"> 031 * <CENTER> 032 * <IMG SRC="/adbanner.gif"> 033 * </CENTER> 034 * <P> 035 * <SUBST KEY="BODY"></SUBST> 036 * </BODY> 037 * </HTML> 038 * </PRE> 039 * Example Content Mapping File "filename.smap": 040 * <PRE> 041 * <HTML> 042 * <HEAD> 043 * <TITLE> 044 * Welcome to my home page! 045 * </TITLE> 046 * </HEAD> 047 * </HTML> 048 * <BODY> 049 * My Home page 050 * </BODY> 051 * </HTML> 052 * </PRE> 053 * Example Resultant HTML File "filename.html": 054 * <PRE> 055 * <HTML> 056 * <HEAD> 057 * <TITLE> 058 * Welcome to my home page! 059 * </TITLE> 060 * </HEAD> 061 * </HTML> 062 * <BODY BGCOLOR="Blue"> 063 * <CENTER> 064 * <IMG SRC="/adbanner.gif"> 065 * </CENTER> 066 * <P> 067 * My Home page 068 * </BODY> 069 * </HTML> 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 * "<SUBST KEY="HEAD"></SUBST> 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 <SUBST> and </SUBST> 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 }