1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.jct.utils.mapmerge; 28 29 import java.io.BufferedReader; 30 import java.io.BufferedWriter; 31 import java.io.File; 32 import java.io.FileReader; 33 import java.io.FileWriter; 34 import java.io.IOException; 35 import java.io.PrintStream; 36 import java.io.PrintWriter; 37 import java.io.Reader; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Iterator; 41 import java.util.List; 42 import java.util.Map; 43 import java.util.TreeMap; 44 import org.apache.tools.ant.BuildException; 45 import org.apache.tools.ant.FileScanner; 46 import org.apache.tools.ant.taskdefs.MatchingTask; 47 import org.apache.tools.ant.types.FileSet; 48 49 /** 50 * A utility to merge JavaHelp map files. 51 */ 52 public class Main 53 { 54 /** 55 * An exception to report bad command line arguments. 56 */ 57 public static class BadArgs extends Exception { 58 BadArgs(String msg) { 59 super(msg); 60 } 61 } 62 63 /** 64 * Command line entry point.<br> 65 * @param args Command line arguments, per the usage as described. 66 */ 67 public static void main(String[] args) { 68 try { 69 if (args.length == 0) 70 usage(System.err); 71 else { 72 Main m = new Main(args); 73 m.run(); 74 } 75 } 76 catch (BadArgs e) { 77 System.err.println(e); 78 usage(System.err); 79 System.exit(1); 80 } 81 catch (Throwable t) { 82 t.printStackTrace(); 83 System.exit(2); 84 } 85 } 86 87 /** 88 * Write out short command line help. 89 * @param out A stream to which to write the help. 90 */ 91 private static void usage(PrintStream out) { 92 String program = System.getProperty("program", "java " + Main.class.getName()); 93 out.println("Usage:"); 94 out.println(" " + program + " options files..."); 95 out.println(""); 96 out.println("Arguments:"); 97 out.println("-o file"); 98 out.println(" Output file."); 99 out.println("files..."); 100 out.println(" Input files to be merged."); 101 } 102 103 public Main() { } 104 105 /** 106 * Create an object based on command line args. 107 * It is an error if no input files or no output file is given. 108 * @param args Command line args. 109 * @see #main 110 * @throws Main.BadArgs if problems are found in the given arguments. 111 */ 112 public Main(String[] args) throws BadArgs { 113 for (int i = 0; i < args.length; i++) { 114 if (args[i].equals("-o") && i + 1 < args.length) { 115 outFile = new File(args[++i]); 116 } 117 else { 118 inFiles = new File[args.length - i]; 119 for (int j = 0; j < inFiles.length; j++) 120 inFiles[j] = new File(args[i++]); 121 } 122 123 } 124 } 125 126 public static class Ant extends MatchingTask { 127 private Main m = new Main(); 128 private List<FileSet> fileSets = new ArrayList<FileSet>(); 129 130 public void setOutFile(File file) { 131 m.outFile = file; 132 } 133 134 public void addFileSet(FileSet fs) { 135 fileSets.add(fs); 136 } 137 138 public void execute() { 139 for (Iterator iter = fileSets.iterator(); iter.hasNext(); ) { 140 FileSet fs = (FileSet) iter.next(); 141 FileScanner s = fs.getDirectoryScanner(getProject()); 142 m.addFiles(s.getBasedir(), s.getIncludedFiles()); 143 } 144 try { 145 m.run(); 146 } catch (BadArgs e) { 147 throw new BuildException(e.getMessage()); 148 } catch (IOException e) { 149 throw new BuildException(e); 150 } 151 } 152 } 153 154 public void addFiles(File baseDir, String[] paths) { 155 if (paths == null) 156 return; 157 List<File> files = new ArrayList<File>(); 158 if (inFiles != null) 159 files.addAll(Arrays.asList(inFiles)); 160 for (int i = 0; i < paths.length; i++) 161 files.add(new File(baseDir, paths[i])); 162 inFiles = files.toArray(new File[files.size()]); 163 } 164 165 private void run() throws BadArgs, IOException 166 { 167 168 if (inFiles == null || inFiles.length == 0) 169 throw new BadArgs("no input files specified"); 170 171 if (outFile == null) 172 throw new BadArgs("no output file specified"); 173 174 map = new TreeMap<>(); 175 176 for (int i = 0; i < inFiles.length; i++) 177 read(inFiles[i]); 178 179 PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(outFile))); 180 out.println("<?xml version='1.0' encoding='ISO-8859-1' ?>"); 181 out.println("<!DOCTYPE map"); 182 out.println(" PUBLIC \"-//Sun Microsystems Inc.//DTD JavaHelp Map Version 1.0//EN\""); 183 out.println(" \"http://java.sun.com/products/javahelp/map_1_0.dtd\">"); 184 out.println("<map version=\"1.0\">"); 185 186 int maxLen = 0; 187 for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) { 188 Map.Entry e = (Map.Entry) (iter.next()); 189 String target = (String) (e.getKey()); 190 String url = (String) (e.getValue()); 191 maxLen = Math.max(maxLen, target.length()); 192 } 193 194 for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) { 195 Map.Entry e = (Map.Entry) (iter.next()); 196 String target = (String) (e.getKey()); 197 String url = (String) (e.getValue()); 198 out.print(" <mapID target=\"" + target + "\" "); 199 for (int i = target.length(); i < maxLen; i++) 200 out.print(' '); 201 out.println(" url=\"" + url + "\" />"); 202 } 203 204 out.println("</map>"); 205 out.close(); 206 } 207 208 private void read(File f) throws IOException { 209 Reader in = new BufferedReader(new FileReader(f)); 210 currFile = f; 211 read(in); 212 in.close(); 213 } 214 215 private void read(Reader in) throws IOException { 216 this.in = in; 217 line = 1; 218 nextCh(); 219 while (c >= 0) { 220 if (c == '<') { 221 nextCh(); 222 skipSpace(); 223 switch (c) { 224 case '!': 225 nextCh(); 226 if (c == '-') { 227 nextCh(); 228 if (c == '-') { 229 nextCh(); 230 skipComment(); 231 } 232 } 233 break; 234 235 case '?': 236 nextCh(); 237 skipTag(); 238 break; 239 240 case '/': 241 nextCh(); 242 skipTag(); 243 break; 244 245 default: 246 String startTag = scanIdentifier(); 247 if (isMapID(startTag)) 248 scanMapID(true); 249 else 250 skipTag(); 251 } 252 } 253 else { 254 nextCh(); 255 } 256 } 257 258 } 259 260 private boolean isMapID(String tag) { 261 return tag.equals("mapID"); 262 } 263 264 private void scanMapID(boolean start) throws IOException { 265 String target = null; 266 String url = null; 267 268 skipSpace(); 269 while (c != '>') { 270 if (c == '/') { 271 nextCh(); 272 if (c == '>') 273 break; 274 else 275 throw new IOException("error parsing HTML input (" + currFile + ":" + line + ")"); 276 } 277 278 String att = scanIdentifier(); 279 if (att.equals("target")) { 280 target = scanValue(); 281 } 282 else if (att.equals("url")) { 283 url = scanValue(); 284 } 285 else 286 scanValue(); 287 skipSpace(); 288 } 289 map.put(target, url); 290 nextCh(); 291 } 292 293 /** 294 * Read an identifier 295 */ 296 private String scanIdentifier() throws IOException { 297 StringBuffer buf = new StringBuffer(); 298 while (true) { 299 if ((c >= 'a') && (c <= 'z')) { 300 buf.append((char)c); 301 nextCh(); 302 } else if ((c >= 'A') && (c <= 'Z')) { 303 buf.append((char)c); 304 nextCh(); 305 } else if ((c >= '0') && (c <= '9')) { 306 buf.append((char)c); 307 nextCh(); 308 } else if (c == '-') { // needed for <META HTTP-EQUIV ....> 309 buf.append((char)c); 310 nextCh(); 311 } else 312 if (buf.length() == 0) 313 throw new IOException("Identifier expected (" + currFile + ":" + line + ")"); 314 else 315 return buf.toString(); 316 } 317 } 318 319 /** 320 * Read the value of an HTML attribute, which may be quoted. 321 */ 322 private String scanValue() throws IOException { 323 skipSpace(); 324 if (c != '=') 325 return ""; 326 327 int quote = -1; 328 nextCh(); 329 skipSpace(); 330 if ((c == '\'') || (c == '\"')) { 331 quote = c; 332 nextCh(); 333 skipSpace(); 334 } 335 StringBuffer buf = new StringBuffer(); 336 while (((quote < 0) && (c != ' ') && (c != '\t') && 337 (c != '\n') && (c != '\r') && (c != '>')) || 338 ((quote >= 0) && (c != quote))) { 339 if (c == -1 || c == '\n' || c == '\r') { 340 throw new IOException("mismatched quotes (" + currFile + ":" + line + ")"); 341 } 342 buf.append((char)c); 343 nextCh(); 344 } 345 if (c == quote) 346 nextCh(); 347 skipSpace(); 348 return buf.toString(); 349 } 350 351 /** 352 * Skip an HTML comment <!-- ... --> 353 */ 354 private void skipComment() throws IOException { 355 // a comment sequence is "<!--" ... "-->" 356 // at the time this is called, "<!--" has been read; 357 int numHyphens = 0; 358 while (c != -1 && (numHyphens < 2 || c != '>')) { 359 if (c == '-') 360 numHyphens++; 361 else 362 numHyphens = 0; 363 nextCh(); 364 //System.out.print((char)c); 365 } 366 nextCh(); 367 } 368 369 /** 370 * Skip whitespace. 371 */ 372 private void skipSpace() throws IOException { 373 while ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')) { 374 nextCh(); 375 } 376 } 377 378 379 /** 380 * Skip the contents of a tag i.e. <...> 381 */ 382 private void skipTag() throws IOException { 383 skipSpace(); 384 while (c != '>') { 385 if (c == '/' || c == '?') { 386 nextCh(); 387 if (c == '>') 388 break; 389 else 390 throw new IOException("error parsing HTML input (" + currFile + ":" + line + ")"); 391 } 392 393 String att = scanIdentifier(); 394 if (att == "") 395 throw new IOException("error parsing HTML input (" + currFile + ":" + line + ")"); 396 String value = scanValue(); 397 skipSpace(); 398 } 399 nextCh(); 400 } 401 402 403 /** 404 * Read the next character. 405 */ 406 private void nextCh() throws IOException { 407 c = in.read(); 408 if (c == '\n') 409 line++; 410 } 411 412 413 private File[] inFiles; 414 private File outFile; 415 private Map<String, String> map; 416 417 private Reader in; 418 private int c; 419 private File currFile; 420 private int line; 421 422 }