Mon, 18 Jul 2016 23:53:12 +0300
8138725: Add options for Javadoc generation
Reviewed-by: jjg
1.1 --- a/src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java Tue Jul 12 14:52:08 2016 -0700 1.2 +++ b/src/share/classes/com/sun/tools/doclets/formats/html/ConfigurationImpl.java Mon Jul 18 23:53:12 2016 +0300 1.3 @@ -1,5 +1,5 @@ 1.4 /* 1.5 - * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved. 1.6 + * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved. 1.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.8 * 1.9 * This code is free software; you can redistribute it and/or modify it 1.10 @@ -38,6 +38,7 @@ 1.11 import com.sun.tools.javac.file.JavacFileManager; 1.12 import com.sun.tools.javac.util.Context; 1.13 import com.sun.tools.javac.util.StringUtils; 1.14 +import com.sun.tools.javadoc.JavaScriptScanner; 1.15 import com.sun.tools.javadoc.RootDocImpl; 1.16 1.17 /** 1.18 @@ -181,6 +182,11 @@ 1.19 public Set<String> doclintOpts = new LinkedHashSet<String>(); 1.20 1.21 /** 1.22 + * Whether or not to check for JavaScript in doc comments. 1.23 + */ 1.24 + private boolean allowScriptInComments; 1.25 + 1.26 + /** 1.27 * Unique Resource Handler for this package. 1.28 */ 1.29 public final MessageRetriever standardmessage; 1.30 @@ -283,8 +289,11 @@ 1.31 doclintOpts.add(null); 1.32 } else if (opt.startsWith("-xdoclint:")) { 1.33 doclintOpts.add(opt.substring(opt.indexOf(":") + 1)); 1.34 + } else if (opt.equals("--allow-script-in-comments")) { 1.35 + allowScriptInComments = true; 1.36 } 1.37 } 1.38 + 1.39 if (root.specifiedClasses().length > 0) { 1.40 Map<String,PackageDoc> map = new HashMap<String,PackageDoc>(); 1.41 PackageDoc pd; 1.42 @@ -301,9 +310,30 @@ 1.43 1.44 if (root instanceof RootDocImpl) { 1.45 ((RootDocImpl) root).initDocLint(doclintOpts, tagletManager.getCustomTagNames()); 1.46 + JavaScriptScanner jss = ((RootDocImpl) root).initJavaScriptScanner(isAllowScriptInComments()); 1.47 + if (jss != null) { 1.48 + // In a more object-oriented world, this would be done by methods on the Option objects. 1.49 + // Note that -windowtitle silently removes any and all HTML elements, and so does not need 1.50 + // to be handled here. 1.51 + checkJavaScript(jss, "-header", header); 1.52 + checkJavaScript(jss, "-footer", footer); 1.53 + checkJavaScript(jss, "-top", top); 1.54 + checkJavaScript(jss, "-bottom", bottom); 1.55 + checkJavaScript(jss, "-doctitle", doctitle); 1.56 + checkJavaScript(jss, "-packagesheader", packagesheader); 1.57 + } 1.58 } 1.59 } 1.60 1.61 + private void checkJavaScript(JavaScriptScanner jss, final String opt, String value) { 1.62 + jss.parse(value, new JavaScriptScanner.Reporter() { 1.63 + public void report() { 1.64 + root.printError(getText("doclet.JavaScript_in_option", opt)); 1.65 + throw new FatalError(); 1.66 + } 1.67 + }); 1.68 + } 1.69 + 1.70 /** 1.71 * Returns the "length" of a given option. If an option takes no 1.72 * arguments, its length is one. If it takes one argument, it's 1.73 @@ -337,7 +367,8 @@ 1.74 option.equals("-nonavbar") || 1.75 option.equals("-nooverview") || 1.76 option.equals("-xdoclint") || 1.77 - option.startsWith("-xdoclint:")) { 1.78 + option.startsWith("-xdoclint:") || 1.79 + option.equals("--allow-script-in-comments")) { 1.80 return 1; 1.81 } else if (option.equals("-help")) { 1.82 // Uugh: first, this should not be hidden inside optionLength, 1.83 @@ -595,4 +626,13 @@ 1.84 public Content newContent() { 1.85 return new ContentBuilder(); 1.86 } 1.87 + 1.88 + /** 1.89 + * Returns whether or not to allow JavaScript in comments. 1.90 + * Default is off; can be set true from a command line option. 1.91 + * @return the allowScriptInComments 1.92 + */ 1.93 + public boolean isAllowScriptInComments() { 1.94 + return allowScriptInComments; 1.95 + } 1.96 }
2.1 --- a/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java Tue Jul 12 14:52:08 2016 -0700 2.2 +++ b/src/share/classes/com/sun/tools/doclets/formats/html/HtmlDoclet.java Mon Jul 18 23:53:12 2016 +0300 2.3 @@ -1,5 +1,5 @@ 2.4 /* 2.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 2.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 2.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 2.8 * 2.9 * This code is free software; you can redistribute it and/or modify it 2.10 @@ -190,6 +190,8 @@ 2.11 } 2.12 } catch (IOException e) { 2.13 throw new DocletAbortException(e); 2.14 + } catch (FatalError fe) { 2.15 + throw fe; 2.16 } catch (DocletAbortException de) { 2.17 throw de; 2.18 } catch (Exception e) {
3.1 --- a/src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java Tue Jul 12 14:52:08 2016 -0700 3.2 +++ b/src/share/classes/com/sun/tools/doclets/formats/html/markup/HtmlWriter.java Mon Jul 18 23:53:12 2016 +0300 3.3 @@ -1,5 +1,5 @@ 3.4 /* 3.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 3.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 3.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.8 * 3.9 * This code is free software; you can redistribute it and/or modify it 3.10 @@ -164,7 +164,9 @@ 3.11 3.12 public final Content descfrmInterfaceLabel; 3.13 3.14 - private final Writer writer; 3.15 + private final DocFile file; 3.16 + 3.17 + private Writer writer; 3.18 3.19 private Content script; 3.20 3.21 @@ -180,7 +182,7 @@ 3.22 */ 3.23 public HtmlWriter(Configuration configuration, DocPath path) 3.24 throws IOException, UnsupportedEncodingException { 3.25 - writer = DocFile.createFileForOutput(configuration, path).openWriter(); 3.26 + file = DocFile.createFileForOutput(configuration, path); 3.27 this.configuration = configuration; 3.28 this.memberDetailsListPrinted = false; 3.29 profileTableHeader = new String[] { 3.30 @@ -239,6 +241,7 @@ 3.31 } 3.32 3.33 public void write(Content c) throws IOException { 3.34 + writer = file.openWriter(); 3.35 c.write(writer, true); 3.36 } 3.37
4.1 --- a/src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java Tue Jul 12 14:52:08 2016 -0700 4.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/AbstractDoclet.java Mon Jul 18 23:53:12 2016 +0300 4.3 @@ -1,5 +1,5 @@ 4.4 /* 4.5 - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 4.6 + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. 4.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.8 * 4.9 * This code is free software; you can redistribute it and/or modify it 4.10 @@ -83,6 +83,8 @@ 4.11 } catch (Configuration.Fault f) { 4.12 root.printError(f.getMessage()); 4.13 return false; 4.14 + } catch (FatalError fe) { 4.15 + return false; 4.16 } catch (DocletAbortException e) { 4.17 Throwable cause = e.getCause(); 4.18 if (cause != null) {
5.1 --- a/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AbstractBuilder.java Tue Jul 12 14:52:08 2016 -0700 5.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/builders/AbstractBuilder.java Mon Jul 18 23:53:12 2016 +0300 5.3 @@ -1,5 +1,5 @@ 5.4 /* 5.5 - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. 5.6 + * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. 5.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.8 * 5.9 * This code is free software; you can redistribute it and/or modify it 5.10 @@ -140,7 +140,14 @@ 5.11 configuration.root.printError("Unknown element: " + component); 5.12 throw new DocletAbortException(e); 5.13 } catch (InvocationTargetException e) { 5.14 - throw new DocletAbortException(e.getCause()); 5.15 + Throwable cause = e.getCause(); 5.16 + if (cause instanceof FatalError) { 5.17 + throw (FatalError) cause; 5.18 + } else if (cause instanceof DocletAbortException) { 5.19 + throw (DocletAbortException) cause; 5.20 + } else { 5.21 + throw new DocletAbortException(cause); 5.22 + } 5.23 } catch (Exception e) { 5.24 e.printStackTrace(); 5.25 configuration.root.printError("Exception " +
6.1 --- a/src/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties Tue Jul 12 14:52:08 2016 -0700 6.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/resources/doclets.properties Mon Jul 18 23:53:12 2016 +0300 6.3 @@ -29,6 +29,8 @@ 6.4 doclet.Building_Tree=Building tree for all the packages and classes... 6.5 doclet.Building_Index=Building index for all the packages and classes... 6.6 doclet.Building_Index_For_All_Classes=Building index for all classes... 6.7 +doclet.JavaScript_in_option=Argument for {0} contains JavaScript.\n\ 6.8 +Use --allow-script-in-comments to allow use of JavaScript. 6.9 doclet.sourcetab_warning=The argument for -sourcetab must be an integer greater than 0. 6.10 doclet.Packages=Packages 6.11 doclet.Profiles=Profiles
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/src/share/classes/com/sun/tools/doclets/internal/toolkit/util/FatalError.java Mon Jul 18 23:53:12 2016 +0300 7.3 @@ -0,0 +1,39 @@ 7.4 +/* 7.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 7.7 + * 7.8 + * This code is free software; you can redistribute it and/or modify it 7.9 + * under the terms of the GNU General Public License version 2 only, as 7.10 + * published by the Free Software Foundation. Oracle designates this 7.11 + * particular file as subject to the "Classpath" exception as provided 7.12 + * by Oracle in the LICENSE file that accompanied this code. 7.13 + * 7.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 7.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 7.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 7.17 + * version 2 for more details (a copy is included in the LICENSE file that 7.18 + * accompanied this code). 7.19 + * 7.20 + * You should have received a copy of the GNU General Public License version 7.21 + * 2 along with this work; if not, write to the Free Software Foundation, 7.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 7.23 + * 7.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 7.25 + * or visit www.oracle.com if you need additional information or have any 7.26 + * questions. 7.27 + */ 7.28 + 7.29 +package com.sun.tools.doclets.internal.toolkit.util; 7.30 + 7.31 +/** 7.32 + * <p><b>This is NOT part of any supported API. 7.33 + * If you write code that depends on this, you do so at your own risk. 7.34 + * This code and its internal interfaces are subject to change or 7.35 + * deletion without notice.</b> 7.36 + */ 7.37 +@Deprecated 7.38 +public class FatalError extends Error { 7.39 + private static final long serialVersionUID = -9131058909576418984L; 7.40 + 7.41 + public FatalError() { } 7.42 +}
8.1 --- a/src/share/classes/com/sun/tools/doclint/Checker.java Tue Jul 12 14:52:08 2016 -0700 8.2 +++ b/src/share/classes/com/sun/tools/doclint/Checker.java Mon Jul 18 23:53:12 2016 +0300 8.3 @@ -1,5 +1,5 @@ 8.4 /* 8.5 - * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. 8.6 + * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved. 8.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 8.8 * 8.9 * This code is free software; you can redistribute it and/or modify it 8.10 @@ -126,7 +126,7 @@ 8.11 } 8.12 } 8.13 8.14 - private Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well 8.15 + private final Deque<TagStackItem> tagStack; // TODO: maybe want to record starting tree as well 8.16 private HtmlTag currHeaderTag; 8.17 8.18 private final int implicitHeaderLevel; 8.19 @@ -401,7 +401,16 @@ 8.20 break; 8.21 8.22 case OTHER: 8.23 - env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName); 8.24 + switch (t) { 8.25 + case SCRIPT: 8.26 + // <script> may or may not be allowed, depending on --allow-script-in-comments 8.27 + // but we allow it here, and rely on a separate scanner to detect all uses 8.28 + // of JavaScript, including <script> tags, and use in attributes, etc. 8.29 + break; 8.30 + 8.31 + default: 8.32 + env.messages.error(HTML, tree, "dc.tag.not.allowed", treeName); 8.33 + } 8.34 return; 8.35 } 8.36 8.37 @@ -519,22 +528,27 @@ 8.38 if (!first) 8.39 env.messages.error(HTML, tree, "dc.attr.repeated", name); 8.40 } 8.41 - AttrKind k = currTag.getAttrKind(name); 8.42 - switch (k) { 8.43 - case OK: 8.44 - break; 8.45 8.46 - case INVALID: 8.47 - env.messages.error(HTML, tree, "dc.attr.unknown", name); 8.48 - break; 8.49 + // for now, doclint allows all attribute names beginning with "on" as event handler names, 8.50 + // without checking the validity or applicability of the name 8.51 + if (!name.toString().startsWith("on")) { 8.52 + AttrKind k = currTag.getAttrKind(name); 8.53 + switch (k) { 8.54 + case OK: 8.55 + break; 8.56 8.57 - case OBSOLETE: 8.58 - env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete", name); 8.59 - break; 8.60 + case INVALID: 8.61 + env.messages.error(HTML, tree, "dc.attr.unknown", name); 8.62 + break; 8.63 8.64 - case USE_CSS: 8.65 - env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete.use.css", name); 8.66 - break; 8.67 + case OBSOLETE: 8.68 + env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete", name); 8.69 + break; 8.70 + 8.71 + case USE_CSS: 8.72 + env.messages.warning(ACCESSIBILITY, tree, "dc.attr.obsolete.use.css", name); 8.73 + break; 8.74 + } 8.75 } 8.76 8.77 if (attr != null) { 8.78 @@ -643,6 +657,9 @@ 8.79 } 8.80 8.81 private void checkURI(AttributeTree tree, String uri) { 8.82 + // allow URIs beginning with javascript:, which would otherwise be rejected by the URI API. 8.83 + if (uri.startsWith("javascript:")) 8.84 + return; 8.85 try { 8.86 URI u = new URI(uri); 8.87 } catch (URISyntaxException e) {
9.1 --- a/src/share/classes/com/sun/tools/doclint/HtmlTag.java Tue Jul 12 14:52:08 2016 -0700 9.2 +++ b/src/share/classes/com/sun/tools/doclint/HtmlTag.java Mon Jul 18 23:53:12 2016 +0300 9.3 @@ -1,5 +1,5 @@ 9.4 /* 9.5 - * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 9.6 + * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. 9.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 9.8 * 9.9 * This code is free software; you can redistribute it and/or modify it 9.10 @@ -183,7 +183,8 @@ 9.11 } 9.12 }, 9.13 9.14 - SCRIPT(BlockType.OTHER, EndKind.REQUIRED), 9.15 + SCRIPT(BlockType.OTHER, EndKind.REQUIRED, 9.16 + attrs(AttrKind.OK, SRC)), 9.17 9.18 SMALL(BlockType.INLINE, EndKind.REQUIRED, 9.19 EnumSet.of(Flag.EXPECT_CONTENT)),
10.1 --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Tue Jul 12 14:52:08 2016 -0700 10.2 +++ b/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Mon Jul 18 23:53:12 2016 +0300 10.3 @@ -1,5 +1,5 @@ 10.4 /* 10.5 - * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved. 10.6 + * Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved. 10.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10.8 * 10.9 * This code is free software; you can redistribute it and/or modify it 10.10 @@ -585,7 +585,7 @@ 10.11 /** 10.12 * Ident = IDENTIFIER 10.13 */ 10.14 - Name ident() { 10.15 + public Name ident() { 10.16 if (token.kind == IDENTIFIER) { 10.17 Name name = token.name(); 10.18 nextToken();
11.1 --- a/src/share/classes/com/sun/tools/javadoc/DocEnv.java Tue Jul 12 14:52:08 2016 -0700 11.2 +++ b/src/share/classes/com/sun/tools/javadoc/DocEnv.java Mon Jul 18 23:53:12 2016 +0300 11.3 @@ -1,5 +1,5 @@ 11.4 /* 11.5 - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. 11.6 + * Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved. 11.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 11.8 * 11.9 * This code is free software; you can redistribute it and/or modify it 11.10 @@ -86,7 +86,7 @@ 11.11 JavadocEnter enter; 11.12 11.13 /** The name table. */ 11.14 - Names names; 11.15 + private final Names names; 11.16 11.17 /** The encoding name. */ 11.18 private String encoding; 11.19 @@ -109,6 +109,7 @@ 11.20 JavaFileManager fileManager; 11.21 Context context; 11.22 DocLint doclint; 11.23 + JavaScriptScanner javaScriptScanner; 11.24 11.25 WeakHashMap<JCTree, TreePath> treePaths = new WeakHashMap<JCTree, TreePath>(); 11.26 11.27 @@ -834,6 +835,15 @@ 11.28 doclint.init(t, doclintOpts.toArray(new String[doclintOpts.size()]), false); 11.29 } 11.30 11.31 + JavaScriptScanner initJavaScriptScanner(boolean allowScriptInComments) { 11.32 + if (allowScriptInComments) { 11.33 + javaScriptScanner = null; 11.34 + } else { 11.35 + javaScriptScanner = new JavaScriptScanner(); 11.36 + } 11.37 + return javaScriptScanner; 11.38 + } 11.39 + 11.40 boolean showTagMessages() { 11.41 return (doclint == null); 11.42 }
12.1 --- a/src/share/classes/com/sun/tools/javadoc/DocImpl.java Tue Jul 12 14:52:08 2016 -0700 12.2 +++ b/src/share/classes/com/sun/tools/javadoc/DocImpl.java Mon Jul 18 23:53:12 2016 +0300 12.3 @@ -1,5 +1,5 @@ 12.4 /* 12.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 12.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 12.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 12.8 * 12.9 * This code is free software; you can redistribute it and/or modify it 12.10 @@ -36,6 +36,7 @@ 12.11 12.12 import com.sun.javadoc.*; 12.13 import com.sun.source.util.TreePath; 12.14 +import com.sun.tools.doclets.internal.toolkit.util.FatalError; 12.15 import com.sun.tools.javac.tree.JCTree; 12.16 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 12.17 import com.sun.tools.javac.util.Position; 12.18 @@ -127,6 +128,15 @@ 12.19 Comment comment() { 12.20 if (comment == null) { 12.21 String d = documentation(); 12.22 + if (env.javaScriptScanner != null) { 12.23 + env.javaScriptScanner.parse(d, new JavaScriptScanner.Reporter() { 12.24 + @Override 12.25 + public void report() { 12.26 + env.error(DocImpl.this, "javadoc.JavaScript_in_comment"); 12.27 + throw new FatalError(); 12.28 + } 12.29 + }); 12.30 + } 12.31 if (env.doclint != null 12.32 && treePath != null 12.33 && d.equals(getCommentText(treePath))) {
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/src/share/classes/com/sun/tools/javadoc/JavaScriptScanner.java Mon Jul 18 23:53:12 2016 +0300 13.3 @@ -0,0 +1,1103 @@ 13.4 +/* 13.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 13.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 13.7 + * 13.8 + * This code is free software; you can redistribute it and/or modify it 13.9 + * under the terms of the GNU General Public License version 2 only, as 13.10 + * published by the Free Software Foundation. Oracle designates this 13.11 + * particular file as subject to the "Classpath" exception as provided 13.12 + * by Oracle in the LICENSE file that accompanied this code. 13.13 + * 13.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 13.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13.17 + * version 2 for more details (a copy is included in the LICENSE file that 13.18 + * accompanied this code). 13.19 + * 13.20 + * You should have received a copy of the GNU General Public License version 13.21 + * 2 along with this work; if not, write to the Free Software Foundation, 13.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 13.23 + * 13.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 13.25 + * or visit www.oracle.com if you need additional information or have any 13.26 + * questions. 13.27 + */ 13.28 + 13.29 +package com.sun.tools.javadoc; 13.30 + 13.31 +import java.util.Arrays; 13.32 +import java.util.HashMap; 13.33 +import java.util.HashSet; 13.34 +import java.util.Locale; 13.35 +import java.util.Map; 13.36 +import java.util.Set; 13.37 + 13.38 +import com.sun.tools.javadoc.JavaScriptScanner.TagParser.Kind; 13.39 + 13.40 +import static com.sun.tools.javac.util.LayoutCharacters.EOI; 13.41 + 13.42 +/** 13.43 + * Parser to detect use of JavaScript in documentation comments. 13.44 + */ 13.45 +@Deprecated 13.46 +public class JavaScriptScanner { 13.47 + public static interface Reporter { 13.48 + void report(); 13.49 + } 13.50 + 13.51 + static class ParseException extends Exception { 13.52 + private static final long serialVersionUID = 0; 13.53 + ParseException(String key) { 13.54 + super(key); 13.55 + } 13.56 + } 13.57 + 13.58 + private Reporter reporter; 13.59 + 13.60 + /** The input buffer, index of most recent character read, 13.61 + * index of one past last character in buffer. 13.62 + */ 13.63 + protected char[] buf; 13.64 + protected int bp; 13.65 + protected int buflen; 13.66 + 13.67 + /** The current character. 13.68 + */ 13.69 + protected char ch; 13.70 + 13.71 + private boolean newline = true; 13.72 + 13.73 + Map<String, TagParser> tagParsers; 13.74 + Set<String> eventAttrs; 13.75 + Set<String> uriAttrs; 13.76 + 13.77 + public JavaScriptScanner() { 13.78 + initTagParsers(); 13.79 + initEventAttrs(); 13.80 + initURIAttrs(); 13.81 + } 13.82 + 13.83 + public void parse(String comment, Reporter r) { 13.84 + reporter = r; 13.85 + String c = comment; 13.86 + buf = new char[c.length() + 1]; 13.87 + c.getChars(0, c.length(), buf, 0); 13.88 + buf[buf.length - 1] = EOI; 13.89 + buflen = buf.length - 1; 13.90 + bp = -1; 13.91 + newline = true; 13.92 + nextChar(); 13.93 + 13.94 + blockContent(); 13.95 + blockTags(); 13.96 + } 13.97 + 13.98 + private void checkHtmlTag(String tag) { 13.99 + if (tag.equalsIgnoreCase("script")) { 13.100 + reporter.report(); 13.101 + } 13.102 + } 13.103 + 13.104 + private void checkHtmlAttr(String name, String value) { 13.105 + String n = name.toLowerCase(Locale.ENGLISH); 13.106 + if (eventAttrs.contains(n) 13.107 + || uriAttrs.contains(n) 13.108 + && value != null && value.toLowerCase(Locale.ENGLISH).trim().startsWith("javascript:")) { 13.109 + reporter.report(); 13.110 + } 13.111 + } 13.112 + 13.113 + void nextChar() { 13.114 + ch = buf[bp < buflen ? ++bp : buflen]; 13.115 + switch (ch) { 13.116 + case '\f': case '\n': case '\r': 13.117 + newline = true; 13.118 + } 13.119 + } 13.120 + 13.121 + /** 13.122 + * Read block content, consisting of text, html and inline tags. 13.123 + * Terminated by the end of input, or the beginning of the next block tag: 13.124 + * i.e. @ as the first non-whitespace character on a line. 13.125 + */ 13.126 + @SuppressWarnings("fallthrough") 13.127 + protected void blockContent() { 13.128 + 13.129 + loop: 13.130 + while (bp < buflen) { 13.131 + switch (ch) { 13.132 + case '\n': case '\r': case '\f': 13.133 + newline = true; 13.134 + // fallthrough 13.135 + 13.136 + case ' ': case '\t': 13.137 + nextChar(); 13.138 + break; 13.139 + 13.140 + case '&': 13.141 + entity(null); 13.142 + break; 13.143 + 13.144 + case '<': 13.145 + html(); 13.146 + break; 13.147 + 13.148 + case '>': 13.149 + newline = false; 13.150 + nextChar(); 13.151 + break; 13.152 + 13.153 + case '{': 13.154 + inlineTag(null); 13.155 + break; 13.156 + 13.157 + case '@': 13.158 + if (newline) { 13.159 + break loop; 13.160 + } 13.161 + // fallthrough 13.162 + 13.163 + default: 13.164 + newline = false; 13.165 + nextChar(); 13.166 + } 13.167 + } 13.168 + } 13.169 + 13.170 + /** 13.171 + * Read a series of block tags, including their content. 13.172 + * Standard tags parse their content appropriately. 13.173 + * Non-standard tags are represented by {@link UnknownBlockTag}. 13.174 + */ 13.175 + protected void blockTags() { 13.176 + while (ch == '@') 13.177 + blockTag(); 13.178 + } 13.179 + 13.180 + /** 13.181 + * Read a single block tag, including its content. 13.182 + * Standard tags parse their content appropriately. 13.183 + * Non-standard tags are represented by {@link UnknownBlockTag}. 13.184 + */ 13.185 + protected void blockTag() { 13.186 + int p = bp; 13.187 + try { 13.188 + nextChar(); 13.189 + if (isIdentifierStart(ch)) { 13.190 + String name = readTagName(); 13.191 + TagParser tp = tagParsers.get(name); 13.192 + if (tp == null) { 13.193 + blockContent(); 13.194 + } else { 13.195 + switch (tp.getKind()) { 13.196 + case BLOCK: 13.197 + tp.parse(p); 13.198 + return; 13.199 + case INLINE: 13.200 + return; 13.201 + } 13.202 + } 13.203 + } 13.204 + blockContent(); 13.205 + } catch (ParseException e) { 13.206 + blockContent(); 13.207 + } 13.208 + } 13.209 + 13.210 + protected void inlineTag(Void list) { 13.211 + newline = false; 13.212 + nextChar(); 13.213 + if (ch == '@') { 13.214 + inlineTag(); 13.215 + } 13.216 + } 13.217 + 13.218 + /** 13.219 + * Read a single inline tag, including its content. 13.220 + * Standard tags parse their content appropriately. 13.221 + * Non-standard tags are represented by {@link UnknownBlockTag}. 13.222 + * Malformed tags may be returned as {@link Erroneous}. 13.223 + */ 13.224 + protected void inlineTag() { 13.225 + int p = bp - 1; 13.226 + try { 13.227 + nextChar(); 13.228 + if (isIdentifierStart(ch)) { 13.229 + String name = readTagName(); 13.230 + TagParser tp = tagParsers.get(name); 13.231 + 13.232 + if (tp == null) { 13.233 + skipWhitespace(); 13.234 + inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); 13.235 + nextChar(); 13.236 + } else { 13.237 + skipWhitespace(); 13.238 + if (tp.getKind() == TagParser.Kind.INLINE) { 13.239 + tp.parse(p); 13.240 + } else { // handle block tags (ex: @see) in inline content 13.241 + inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip content 13.242 + nextChar(); 13.243 + } 13.244 + } 13.245 + } 13.246 + } catch (ParseException e) { 13.247 + } 13.248 + } 13.249 + 13.250 + private static enum WhitespaceRetentionPolicy { 13.251 + RETAIN_ALL, 13.252 + REMOVE_FIRST_SPACE, 13.253 + REMOVE_ALL 13.254 + } 13.255 + 13.256 + /** 13.257 + * Read plain text content of an inline tag. 13.258 + * Matching pairs of { } are skipped; the text is terminated by the first 13.259 + * unmatched }. It is an error if the beginning of the next tag is detected. 13.260 + */ 13.261 + private void inlineText(WhitespaceRetentionPolicy whitespacePolicy) throws ParseException { 13.262 + switch (whitespacePolicy) { 13.263 + case REMOVE_ALL: 13.264 + skipWhitespace(); 13.265 + break; 13.266 + case REMOVE_FIRST_SPACE: 13.267 + if (ch == ' ') 13.268 + nextChar(); 13.269 + break; 13.270 + case RETAIN_ALL: 13.271 + default: 13.272 + // do nothing 13.273 + break; 13.274 + 13.275 + } 13.276 + int pos = bp; 13.277 + int depth = 1; 13.278 + 13.279 + loop: 13.280 + while (bp < buflen) { 13.281 + switch (ch) { 13.282 + case '\n': case '\r': case '\f': 13.283 + newline = true; 13.284 + break; 13.285 + 13.286 + case ' ': case '\t': 13.287 + break; 13.288 + 13.289 + case '{': 13.290 + newline = false; 13.291 + depth++; 13.292 + break; 13.293 + 13.294 + case '}': 13.295 + if (--depth == 0) { 13.296 + return; 13.297 + } 13.298 + newline = false; 13.299 + break; 13.300 + 13.301 + case '@': 13.302 + if (newline) 13.303 + break loop; 13.304 + newline = false; 13.305 + break; 13.306 + 13.307 + default: 13.308 + newline = false; 13.309 + break; 13.310 + } 13.311 + nextChar(); 13.312 + } 13.313 + throw new ParseException("dc.unterminated.inline.tag"); 13.314 + } 13.315 + 13.316 + /** 13.317 + * Read Java class name, possibly followed by member 13.318 + * Matching pairs of {@literal < >} are skipped. The text is terminated by the first 13.319 + * unmatched }. It is an error if the beginning of the next tag is detected. 13.320 + */ 13.321 + // TODO: boolean allowMember should be enum FORBID, ALLOW, REQUIRE 13.322 + // TODO: improve quality of parse to forbid bad constructions. 13.323 + // TODO: update to use ReferenceParser 13.324 + @SuppressWarnings("fallthrough") 13.325 + protected void reference(boolean allowMember) throws ParseException { 13.326 + int pos = bp; 13.327 + int depth = 0; 13.328 + 13.329 + // scan to find the end of the signature, by looking for the first 13.330 + // whitespace not enclosed in () or <>, or the end of the tag 13.331 + loop: 13.332 + while (bp < buflen) { 13.333 + switch (ch) { 13.334 + case '\n': case '\r': case '\f': 13.335 + newline = true; 13.336 + // fallthrough 13.337 + 13.338 + case ' ': case '\t': 13.339 + if (depth == 0) 13.340 + break loop; 13.341 + break; 13.342 + 13.343 + case '(': 13.344 + case '<': 13.345 + newline = false; 13.346 + depth++; 13.347 + break; 13.348 + 13.349 + case ')': 13.350 + case '>': 13.351 + newline = false; 13.352 + --depth; 13.353 + break; 13.354 + 13.355 + case '}': 13.356 + if (bp == pos) 13.357 + return; 13.358 + newline = false; 13.359 + break loop; 13.360 + 13.361 + case '@': 13.362 + if (newline) 13.363 + break loop; 13.364 + // fallthrough 13.365 + 13.366 + default: 13.367 + newline = false; 13.368 + 13.369 + } 13.370 + nextChar(); 13.371 + } 13.372 + 13.373 + if (depth != 0) 13.374 + throw new ParseException("dc.unterminated.signature"); 13.375 + } 13.376 + 13.377 + /** 13.378 + * Read Java identifier 13.379 + * Matching pairs of { } are skipped; the text is terminated by the first 13.380 + * unmatched }. It is an error if the beginning of the next tag is detected. 13.381 + */ 13.382 + @SuppressWarnings("fallthrough") 13.383 + protected void identifier() throws ParseException { 13.384 + skipWhitespace(); 13.385 + int pos = bp; 13.386 + 13.387 + if (isJavaIdentifierStart(ch)) { 13.388 + readJavaIdentifier(); 13.389 + return; 13.390 + } 13.391 + 13.392 + throw new ParseException("dc.identifier.expected"); 13.393 + } 13.394 + 13.395 + /** 13.396 + * Read a quoted string. 13.397 + * It is an error if the beginning of the next tag is detected. 13.398 + */ 13.399 + @SuppressWarnings("fallthrough") 13.400 + protected void quotedString() { 13.401 + int pos = bp; 13.402 + nextChar(); 13.403 + 13.404 + loop: 13.405 + while (bp < buflen) { 13.406 + switch (ch) { 13.407 + case '\n': case '\r': case '\f': 13.408 + newline = true; 13.409 + break; 13.410 + 13.411 + case ' ': case '\t': 13.412 + break; 13.413 + 13.414 + case '"': 13.415 + nextChar(); 13.416 + // trim trailing white-space? 13.417 + return; 13.418 + 13.419 + case '@': 13.420 + if (newline) 13.421 + break loop; 13.422 + 13.423 + } 13.424 + nextChar(); 13.425 + } 13.426 + } 13.427 + 13.428 + /** 13.429 + * Read a term ie. one word. 13.430 + * It is an error if the beginning of the next tag is detected. 13.431 + */ 13.432 + @SuppressWarnings("fallthrough") 13.433 + protected void inlineWord() { 13.434 + int pos = bp; 13.435 + int depth = 0; 13.436 + loop: 13.437 + while (bp < buflen) { 13.438 + switch (ch) { 13.439 + case '\n': 13.440 + newline = true; 13.441 + // fallthrough 13.442 + 13.443 + case '\r': case '\f': case ' ': case '\t': 13.444 + return; 13.445 + 13.446 + case '@': 13.447 + if (newline) 13.448 + break loop; 13.449 + 13.450 + case '{': 13.451 + depth++; 13.452 + break; 13.453 + 13.454 + case '}': 13.455 + if (depth == 0 || --depth == 0) 13.456 + return; 13.457 + break; 13.458 + } 13.459 + newline = false; 13.460 + nextChar(); 13.461 + } 13.462 + } 13.463 + 13.464 + /** 13.465 + * Read general text content of an inline tag, including HTML entities and elements. 13.466 + * Matching pairs of { } are skipped; the text is terminated by the first 13.467 + * unmatched }. It is an error if the beginning of the next tag is detected. 13.468 + */ 13.469 + @SuppressWarnings("fallthrough") 13.470 + private void inlineContent() { 13.471 + 13.472 + skipWhitespace(); 13.473 + int pos = bp; 13.474 + int depth = 1; 13.475 + 13.476 + loop: 13.477 + while (bp < buflen) { 13.478 + 13.479 + switch (ch) { 13.480 + case '\n': case '\r': case '\f': 13.481 + newline = true; 13.482 + // fall through 13.483 + 13.484 + case ' ': case '\t': 13.485 + nextChar(); 13.486 + break; 13.487 + 13.488 + case '&': 13.489 + entity(null); 13.490 + break; 13.491 + 13.492 + case '<': 13.493 + newline = false; 13.494 + html(); 13.495 + break; 13.496 + 13.497 + case '{': 13.498 + newline = false; 13.499 + depth++; 13.500 + nextChar(); 13.501 + break; 13.502 + 13.503 + case '}': 13.504 + newline = false; 13.505 + if (--depth == 0) { 13.506 + nextChar(); 13.507 + return; 13.508 + } 13.509 + nextChar(); 13.510 + break; 13.511 + 13.512 + case '@': 13.513 + if (newline) 13.514 + break loop; 13.515 + // fallthrough 13.516 + 13.517 + default: 13.518 + nextChar(); 13.519 + break; 13.520 + } 13.521 + } 13.522 + 13.523 + } 13.524 + 13.525 + protected void entity(Void list) { 13.526 + newline = false; 13.527 + entity(); 13.528 + } 13.529 + 13.530 + /** 13.531 + * Read an HTML entity. 13.532 + * {@literal &identifier; } or {@literal &#digits; } or {@literal &#xhex-digits; } 13.533 + */ 13.534 + protected void entity() { 13.535 + nextChar(); 13.536 + String name = null; 13.537 + if (ch == '#') { 13.538 + int namep = bp; 13.539 + nextChar(); 13.540 + if (isDecimalDigit(ch)) { 13.541 + nextChar(); 13.542 + while (isDecimalDigit(ch)) 13.543 + nextChar(); 13.544 + name = new String(buf, namep, bp - namep); 13.545 + } else if (ch == 'x' || ch == 'X') { 13.546 + nextChar(); 13.547 + if (isHexDigit(ch)) { 13.548 + nextChar(); 13.549 + while (isHexDigit(ch)) 13.550 + nextChar(); 13.551 + name = new String(buf, namep, bp - namep); 13.552 + } 13.553 + } 13.554 + } else if (isIdentifierStart(ch)) { 13.555 + name = readIdentifier(); 13.556 + } 13.557 + 13.558 + if (name != null) { 13.559 + if (ch != ';') 13.560 + return; 13.561 + nextChar(); 13.562 + } 13.563 + } 13.564 + 13.565 + /** 13.566 + * Read the start or end of an HTML tag, or an HTML comment 13.567 + * {@literal <identifier attrs> } or {@literal </identifier> } 13.568 + */ 13.569 + protected void html() { 13.570 + int p = bp; 13.571 + nextChar(); 13.572 + if (isIdentifierStart(ch)) { 13.573 + String name = readIdentifier(); 13.574 + checkHtmlTag(name); 13.575 + htmlAttrs(); 13.576 + if (ch == '/') { 13.577 + nextChar(); 13.578 + } 13.579 + if (ch == '>') { 13.580 + nextChar(); 13.581 + return; 13.582 + } 13.583 + } else if (ch == '/') { 13.584 + nextChar(); 13.585 + if (isIdentifierStart(ch)) { 13.586 + readIdentifier(); 13.587 + skipWhitespace(); 13.588 + if (ch == '>') { 13.589 + nextChar(); 13.590 + return; 13.591 + } 13.592 + } 13.593 + } else if (ch == '!') { 13.594 + nextChar(); 13.595 + if (ch == '-') { 13.596 + nextChar(); 13.597 + if (ch == '-') { 13.598 + nextChar(); 13.599 + while (bp < buflen) { 13.600 + int dash = 0; 13.601 + while (ch == '-') { 13.602 + dash++; 13.603 + nextChar(); 13.604 + } 13.605 + // Strictly speaking, a comment should not contain "--" 13.606 + // so dash > 2 is an error, dash == 2 implies ch == '>' 13.607 + // See http://www.w3.org/TR/html-markup/syntax.html#syntax-comments 13.608 + // for more details. 13.609 + if (dash >= 2 && ch == '>') { 13.610 + nextChar(); 13.611 + return; 13.612 + } 13.613 + 13.614 + nextChar(); 13.615 + } 13.616 + } 13.617 + } 13.618 + } 13.619 + 13.620 + bp = p + 1; 13.621 + ch = buf[bp]; 13.622 + } 13.623 + 13.624 + /** 13.625 + * Read a series of HTML attributes, terminated by {@literal > }. 13.626 + * Each attribute is of the form {@literal identifier[=value] }. 13.627 + * "value" may be unquoted, single-quoted, or double-quoted. 13.628 + */ 13.629 + protected void htmlAttrs() { 13.630 + skipWhitespace(); 13.631 + 13.632 + loop: 13.633 + while (isIdentifierStart(ch)) { 13.634 + int namePos = bp; 13.635 + String name = readAttributeName(); 13.636 + skipWhitespace(); 13.637 + StringBuilder value = new StringBuilder(); 13.638 + if (ch == '=') { 13.639 + nextChar(); 13.640 + skipWhitespace(); 13.641 + if (ch == '\'' || ch == '"') { 13.642 + char quote = ch; 13.643 + nextChar(); 13.644 + while (bp < buflen && ch != quote) { 13.645 + if (newline && ch == '@') { 13.646 + // No point trying to read more. 13.647 + // In fact, all attrs get discarded by the caller 13.648 + // and superseded by a malformed.html node because 13.649 + // the html tag itself is not terminated correctly. 13.650 + break loop; 13.651 + } 13.652 + value.append(ch); 13.653 + nextChar(); 13.654 + } 13.655 + nextChar(); 13.656 + } else { 13.657 + while (bp < buflen && !isUnquotedAttrValueTerminator(ch)) { 13.658 + value.append(ch); 13.659 + nextChar(); 13.660 + } 13.661 + } 13.662 + skipWhitespace(); 13.663 + } 13.664 + checkHtmlAttr(name, value.toString()); 13.665 + } 13.666 + } 13.667 + 13.668 + protected void attrValueChar(Void list) { 13.669 + switch (ch) { 13.670 + case '&': 13.671 + entity(list); 13.672 + break; 13.673 + 13.674 + case '{': 13.675 + inlineTag(list); 13.676 + break; 13.677 + 13.678 + default: 13.679 + nextChar(); 13.680 + } 13.681 + } 13.682 + 13.683 + protected boolean isIdentifierStart(char ch) { 13.684 + return Character.isUnicodeIdentifierStart(ch); 13.685 + } 13.686 + 13.687 + protected String readIdentifier() { 13.688 + int start = bp; 13.689 + nextChar(); 13.690 + while (bp < buflen && Character.isUnicodeIdentifierPart(ch)) 13.691 + nextChar(); 13.692 + return new String(buf, start, bp - start); 13.693 + } 13.694 + 13.695 + protected String readAttributeName() { 13.696 + int start = bp; 13.697 + nextChar(); 13.698 + while (bp < buflen && (Character.isUnicodeIdentifierPart(ch) || ch == '-')) 13.699 + nextChar(); 13.700 + return new String(buf, start, bp - start); 13.701 + } 13.702 + 13.703 + protected String readTagName() { 13.704 + int start = bp; 13.705 + nextChar(); 13.706 + while (bp < buflen 13.707 + && (Character.isUnicodeIdentifierPart(ch) || ch == '.' 13.708 + || ch == '-' || ch == ':')) { 13.709 + nextChar(); 13.710 + } 13.711 + return new String(buf, start, bp - start); 13.712 + } 13.713 + 13.714 + protected boolean isJavaIdentifierStart(char ch) { 13.715 + return Character.isJavaIdentifierStart(ch); 13.716 + } 13.717 + 13.718 + protected String readJavaIdentifier() { 13.719 + int start = bp; 13.720 + nextChar(); 13.721 + while (bp < buflen && Character.isJavaIdentifierPart(ch)) 13.722 + nextChar(); 13.723 + return new String(buf, start, bp - start); 13.724 + } 13.725 + 13.726 + protected boolean isDecimalDigit(char ch) { 13.727 + return ('0' <= ch && ch <= '9'); 13.728 + } 13.729 + 13.730 + protected boolean isHexDigit(char ch) { 13.731 + return ('0' <= ch && ch <= '9') 13.732 + || ('a' <= ch && ch <= 'f') 13.733 + || ('A' <= ch && ch <= 'F'); 13.734 + } 13.735 + 13.736 + protected boolean isUnquotedAttrValueTerminator(char ch) { 13.737 + switch (ch) { 13.738 + case '\f': case '\n': case '\r': case '\t': 13.739 + case ' ': 13.740 + case '"': case '\'': case '`': 13.741 + case '=': case '<': case '>': 13.742 + return true; 13.743 + default: 13.744 + return false; 13.745 + } 13.746 + } 13.747 + 13.748 + protected boolean isWhitespace(char ch) { 13.749 + return Character.isWhitespace(ch); 13.750 + } 13.751 + 13.752 + protected void skipWhitespace() { 13.753 + while (isWhitespace(ch)) { 13.754 + nextChar(); 13.755 + } 13.756 + } 13.757 + 13.758 + /** 13.759 + * @param start position of first character of string 13.760 + * @param end position of character beyond last character to be included 13.761 + */ 13.762 + String newString(int start, int end) { 13.763 + return new String(buf, start, end - start); 13.764 + } 13.765 + 13.766 + static abstract class TagParser { 13.767 + enum Kind { INLINE, BLOCK } 13.768 + 13.769 + final Kind kind; 13.770 + final String name; 13.771 + 13.772 + 13.773 + TagParser(Kind k, String tk) { 13.774 + kind = k; 13.775 + name = tk; 13.776 + } 13.777 + 13.778 + TagParser(Kind k, String tk, boolean retainWhiteSpace) { 13.779 + this(k, tk); 13.780 + } 13.781 + 13.782 + Kind getKind() { 13.783 + return kind; 13.784 + } 13.785 + 13.786 + String getName() { 13.787 + return name; 13.788 + } 13.789 + 13.790 + abstract void parse(int pos) throws ParseException; 13.791 + } 13.792 + 13.793 + /** 13.794 + * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/tools/solaris/javadoc.html#javadoctags">Javadoc Tags</a> 13.795 + */ 13.796 + @SuppressWarnings("deprecation") 13.797 + private void initTagParsers() { 13.798 + TagParser[] parsers = { 13.799 + // @author name-text 13.800 + new TagParser(Kind.BLOCK, "author") { 13.801 + @Override 13.802 + public void parse(int pos) { 13.803 + blockContent(); 13.804 + } 13.805 + }, 13.806 + 13.807 + // {@code text} 13.808 + new TagParser(Kind.INLINE, "code", true) { 13.809 + @Override 13.810 + public void parse(int pos) throws ParseException { 13.811 + inlineText(WhitespaceRetentionPolicy.REMOVE_FIRST_SPACE); 13.812 + nextChar(); 13.813 + } 13.814 + }, 13.815 + 13.816 + // @deprecated deprecated-text 13.817 + new TagParser(Kind.BLOCK, "deprecated") { 13.818 + @Override 13.819 + public void parse(int pos) { 13.820 + blockContent(); 13.821 + } 13.822 + }, 13.823 + 13.824 + // {@docRoot} 13.825 + new TagParser(Kind.INLINE, "docRoot") { 13.826 + @Override 13.827 + public void parse(int pos) throws ParseException { 13.828 + if (ch == '}') { 13.829 + nextChar(); 13.830 + return; 13.831 + } 13.832 + inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip unexpected content 13.833 + nextChar(); 13.834 + throw new ParseException("dc.unexpected.content"); 13.835 + } 13.836 + }, 13.837 + 13.838 + // @exception class-name description 13.839 + new TagParser(Kind.BLOCK, "exception") { 13.840 + @Override 13.841 + public void parse(int pos) throws ParseException { 13.842 + skipWhitespace(); 13.843 + reference(false); 13.844 + blockContent(); 13.845 + } 13.846 + }, 13.847 + 13.848 + // @hidden hidden-text 13.849 + new TagParser(Kind.BLOCK, "hidden") { 13.850 + @Override 13.851 + public void parse(int pos) { 13.852 + blockContent(); 13.853 + } 13.854 + }, 13.855 + 13.856 + // @index search-term options-description 13.857 + new TagParser(Kind.INLINE, "index") { 13.858 + @Override 13.859 + public void parse(int pos) throws ParseException { 13.860 + skipWhitespace(); 13.861 + if (ch == '}') { 13.862 + throw new ParseException("dc.no.content"); 13.863 + } 13.864 + if (ch == '"') quotedString(); else inlineWord(); 13.865 + skipWhitespace(); 13.866 + if (ch != '}') { 13.867 + inlineContent(); 13.868 + } else { 13.869 + nextChar(); 13.870 + } 13.871 + } 13.872 + }, 13.873 + 13.874 + // {@inheritDoc} 13.875 + new TagParser(Kind.INLINE, "inheritDoc") { 13.876 + @Override 13.877 + public void parse(int pos) throws ParseException { 13.878 + if (ch == '}') { 13.879 + nextChar(); 13.880 + return; 13.881 + } 13.882 + inlineText(WhitespaceRetentionPolicy.REMOVE_ALL); // skip unexpected content 13.883 + nextChar(); 13.884 + throw new ParseException("dc.unexpected.content"); 13.885 + } 13.886 + }, 13.887 + 13.888 + // {@link package.class#member label} 13.889 + new TagParser(Kind.INLINE, "link") { 13.890 + @Override 13.891 + public void parse(int pos) throws ParseException { 13.892 + reference(true); 13.893 + inlineContent(); 13.894 + } 13.895 + }, 13.896 + 13.897 + // {@linkplain package.class#member label} 13.898 + new TagParser(Kind.INLINE, "linkplain") { 13.899 + @Override 13.900 + public void parse(int pos) throws ParseException { 13.901 + reference(true); 13.902 + inlineContent(); 13.903 + } 13.904 + }, 13.905 + 13.906 + // {@literal text} 13.907 + new TagParser(Kind.INLINE, "literal", true) { 13.908 + @Override 13.909 + public void parse(int pos) throws ParseException { 13.910 + inlineText(WhitespaceRetentionPolicy.REMOVE_FIRST_SPACE); 13.911 + nextChar(); 13.912 + } 13.913 + }, 13.914 + 13.915 + // @param parameter-name description 13.916 + new TagParser(Kind.BLOCK, "param") { 13.917 + @Override 13.918 + public void parse(int pos) throws ParseException { 13.919 + skipWhitespace(); 13.920 + 13.921 + boolean typaram = false; 13.922 + if (ch == '<') { 13.923 + typaram = true; 13.924 + nextChar(); 13.925 + } 13.926 + 13.927 + identifier(); 13.928 + 13.929 + if (typaram) { 13.930 + if (ch != '>') 13.931 + throw new ParseException("dc.gt.expected"); 13.932 + nextChar(); 13.933 + } 13.934 + 13.935 + skipWhitespace(); 13.936 + blockContent(); 13.937 + } 13.938 + }, 13.939 + 13.940 + // @return description 13.941 + new TagParser(Kind.BLOCK, "return") { 13.942 + @Override 13.943 + public void parse(int pos) { 13.944 + blockContent(); 13.945 + } 13.946 + }, 13.947 + 13.948 + // @see reference | quoted-string | HTML 13.949 + new TagParser(Kind.BLOCK, "see") { 13.950 + @Override 13.951 + public void parse(int pos) throws ParseException { 13.952 + skipWhitespace(); 13.953 + switch (ch) { 13.954 + case '"': 13.955 + quotedString(); 13.956 + skipWhitespace(); 13.957 + if (ch == '@' 13.958 + || ch == EOI && bp == buf.length - 1) { 13.959 + return; 13.960 + } 13.961 + break; 13.962 + 13.963 + case '<': 13.964 + blockContent(); 13.965 + return; 13.966 + 13.967 + case '@': 13.968 + if (newline) 13.969 + throw new ParseException("dc.no.content"); 13.970 + break; 13.971 + 13.972 + case EOI: 13.973 + if (bp == buf.length - 1) 13.974 + throw new ParseException("dc.no.content"); 13.975 + break; 13.976 + 13.977 + default: 13.978 + if (isJavaIdentifierStart(ch) || ch == '#') { 13.979 + reference(true); 13.980 + blockContent(); 13.981 + } 13.982 + } 13.983 + throw new ParseException("dc.unexpected.content"); 13.984 + } 13.985 + }, 13.986 + 13.987 + // @serialData data-description 13.988 + new TagParser(Kind.BLOCK, "@serialData") { 13.989 + @Override 13.990 + public void parse(int pos) { 13.991 + blockContent(); 13.992 + } 13.993 + }, 13.994 + 13.995 + // @serialField field-name field-type description 13.996 + new TagParser(Kind.BLOCK, "serialField") { 13.997 + @Override 13.998 + public void parse(int pos) throws ParseException { 13.999 + skipWhitespace(); 13.1000 + identifier(); 13.1001 + skipWhitespace(); 13.1002 + reference(false); 13.1003 + if (isWhitespace(ch)) { 13.1004 + skipWhitespace(); 13.1005 + blockContent(); 13.1006 + } 13.1007 + } 13.1008 + }, 13.1009 + 13.1010 + // @serial field-description | include | exclude 13.1011 + new TagParser(Kind.BLOCK, "serial") { 13.1012 + @Override 13.1013 + public void parse(int pos) { 13.1014 + blockContent(); 13.1015 + } 13.1016 + }, 13.1017 + 13.1018 + // @since since-text 13.1019 + new TagParser(Kind.BLOCK, "since") { 13.1020 + @Override 13.1021 + public void parse(int pos) { 13.1022 + blockContent(); 13.1023 + } 13.1024 + }, 13.1025 + 13.1026 + // @throws class-name description 13.1027 + new TagParser(Kind.BLOCK, "throws") { 13.1028 + @Override 13.1029 + public void parse(int pos) throws ParseException { 13.1030 + skipWhitespace(); 13.1031 + reference(false); 13.1032 + blockContent(); 13.1033 + } 13.1034 + }, 13.1035 + 13.1036 + // {@value package.class#field} 13.1037 + new TagParser(Kind.INLINE, "value") { 13.1038 + @Override 13.1039 + public void parse(int pos) throws ParseException { 13.1040 + reference(true); 13.1041 + skipWhitespace(); 13.1042 + if (ch == '}') { 13.1043 + nextChar(); 13.1044 + return; 13.1045 + } 13.1046 + nextChar(); 13.1047 + throw new ParseException("dc.unexpected.content"); 13.1048 + } 13.1049 + }, 13.1050 + 13.1051 + // @version version-text 13.1052 + new TagParser(Kind.BLOCK, "version") { 13.1053 + @Override 13.1054 + public void parse(int pos) { 13.1055 + blockContent(); 13.1056 + } 13.1057 + }, 13.1058 + }; 13.1059 + 13.1060 + tagParsers = new HashMap<>(); 13.1061 + for (TagParser p: parsers) 13.1062 + tagParsers.put(p.getName(), p); 13.1063 + 13.1064 + } 13.1065 + 13.1066 + private void initEventAttrs() { 13.1067 + eventAttrs = new HashSet<>(Arrays.asList( 13.1068 + // See https://www.w3.org/TR/html-markup/global-attributes.html#common.attrs.event-handler 13.1069 + "onabort", "onblur", "oncanplay", "oncanplaythrough", 13.1070 + "onchange", "onclick", "oncontextmenu", "ondblclick", 13.1071 + "ondrag", "ondragend", "ondragenter", "ondragleave", 13.1072 + "ondragover", "ondragstart", "ondrop", "ondurationchange", 13.1073 + "onemptied", "onended", "onerror", "onfocus", "oninput", 13.1074 + "oninvalid", "onkeydown", "onkeypress", "onkeyup", 13.1075 + "onload", "onloadeddata", "onloadedmetadata", "onloadstart", 13.1076 + "onmousedown", "onmousemove", "onmouseout", "onmouseover", 13.1077 + "onmouseup", "onmousewheel", "onpause", "onplay", 13.1078 + "onplaying", "onprogress", "onratechange", "onreadystatechange", 13.1079 + "onreset", "onscroll", "onseeked", "onseeking", 13.1080 + "onselect", "onshow", "onstalled", "onsubmit", "onsuspend", 13.1081 + "ontimeupdate", "onvolumechange", "onwaiting", 13.1082 + 13.1083 + // See https://www.w3.org/TR/html4/sgml/dtd.html 13.1084 + // Most of the attributes that take a %Script are also defined as event handlers 13.1085 + // in HTML 5. The one exception is onunload. 13.1086 + // "onchange", "onclick", "ondblclick", "onfocus", 13.1087 + // "onkeydown", "onkeypress", "onkeyup", "onload", 13.1088 + // "onmousedown", "onmousemove", "onmouseout", "onmouseover", 13.1089 + // "onmouseup", "onreset", "onselect", "onsubmit", 13.1090 + "onunload" 13.1091 + )); 13.1092 + } 13.1093 + 13.1094 + private void initURIAttrs() { 13.1095 + uriAttrs = new HashSet<>(Arrays.asList( 13.1096 + // See https://www.w3.org/TR/html4/sgml/dtd.html 13.1097 + // https://www.w3.org/TR/html5/ 13.1098 + // These are all the attributes that take a %URI or a valid URL potentially surrounded 13.1099 + // by spaces 13.1100 + "action", "cite", "classid", "codebase", "data", 13.1101 + "datasrc", "for", "href", "longdesc", "profile", 13.1102 + "src", "usemap" 13.1103 + )); 13.1104 + } 13.1105 + 13.1106 +}
14.1 --- a/src/share/classes/com/sun/tools/javadoc/RootDocImpl.java Tue Jul 12 14:52:08 2016 -0700 14.2 +++ b/src/share/classes/com/sun/tools/javadoc/RootDocImpl.java Mon Jul 18 23:53:12 2016 +0300 14.3 @@ -1,5 +1,5 @@ 14.4 /* 14.5 - * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 14.6 + * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 14.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 14.8 * 14.9 * This code is free software; you can redistribute it and/or modify it 14.10 @@ -381,6 +381,10 @@ 14.11 env.initDoclint(opts, customTagNames); 14.12 } 14.13 14.14 + public JavaScriptScanner initJavaScriptScanner(boolean allowScriptInComments) { 14.15 + return env.initJavaScriptScanner(allowScriptInComments); 14.16 + } 14.17 + 14.18 public boolean isFunctionalInterface(AnnotationDesc annotationDesc) { 14.19 return annotationDesc.annotationType().qualifiedName().equals( 14.20 env.syms.functionalInterfaceType.toString()) && env.source.allowLambda();
15.1 --- a/src/share/classes/com/sun/tools/javadoc/resources/javadoc.properties Tue Jul 12 14:52:08 2016 -0700 15.2 +++ b/src/share/classes/com/sun/tools/javadoc/resources/javadoc.properties Mon Jul 18 23:53:12 2016 +0300 15.3 @@ -1,5 +1,5 @@ 15.4 # 15.5 -# Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 15.6 +# Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. 15.7 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 15.8 # 15.9 # This code is free software; you can redistribute it and/or modify it 15.10 @@ -110,6 +110,8 @@ 15.11 javadoc.Body_missing_from_html_file=Body tag missing from HTML file 15.12 javadoc.End_body_missing_from_html_file=Close body tag missing from HTML file 15.13 javadoc.Multiple_package_comments=Multiple sources of package comments found for package "{0}" 15.14 +javadoc.JavaScript_in_comment=JavaScript found in documentation comment.\n\ 15.15 + Use --allow-script-in-comments to allow use of JavaScript. 15.16 javadoc.class_not_found=Class {0} not found. 15.17 javadoc.error=error 15.18 javadoc.warning=warning
16.1 --- a/test/tools/doclint/html/OtherTagsTest.out Tue Jul 12 14:52:08 2016 -0700 16.2 +++ b/test/tools/doclint/html/OtherTagsTest.out Mon Jul 18 23:53:12 2016 +0300 16.3 @@ -19,10 +19,7 @@ 16.4 OtherTagsTest.java:20: error: element not allowed in documentation comments: <noframes> 16.5 * <noframes> </noframes> 16.6 ^ 16.7 -OtherTagsTest.java:21: error: element not allowed in documentation comments: <script> 16.8 - * <script> </script> 16.9 - ^ 16.10 OtherTagsTest.java:22: error: element not allowed in documentation comments: <title> 16.11 * <title> </title> 16.12 ^ 16.13 -9 errors 16.14 +8 errors
17.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 17.2 +++ b/test/tools/javadoc/TestScriptInComment.java Mon Jul 18 23:53:12 2016 +0300 17.3 @@ -0,0 +1,314 @@ 17.4 +/* 17.5 + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 17.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 17.7 + * 17.8 + * This code is free software; you can redistribute it and/or modify it 17.9 + * under the terms of the GNU General Public License version 2 only, as 17.10 + * published by the Free Software Foundation. Oracle designates this 17.11 + * particular file as subject to the "Classpath" exception as provided 17.12 + * by Oracle in the LICENSE file that accompanied this code. 17.13 + * 17.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 17.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 17.17 + * version 2 for more details (a copy is included in the LICENSE file that 17.18 + * accompanied this code). 17.19 + * 17.20 + * You should have received a copy of the GNU General Public License version 17.21 + * 2 along with this work; if not, write to the Free Software Foundation, 17.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 17.23 + * 17.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 17.25 + * or visit www.oracle.com if you need additional information or have any 17.26 + * questions. 17.27 + */ 17.28 + 17.29 +/** 17.30 + * @test 17.31 + * @bug 8138725 17.32 + * @summary test --allow-script-in-comments 17.33 + * @run main TestScriptInComment 17.34 + */ 17.35 + 17.36 +import java.io.File; 17.37 +import java.io.FileWriter; 17.38 +import java.io.IOException; 17.39 +import java.io.PrintStream; 17.40 +import java.io.PrintWriter; 17.41 +import java.io.StringWriter; 17.42 +import java.util.ArrayList; 17.43 +import java.util.Arrays; 17.44 +import java.util.Collections; 17.45 +import java.util.List; 17.46 +import java.util.regex.Matcher; 17.47 +import java.util.regex.Pattern; 17.48 + 17.49 +/** 17.50 + * Combo-style test, exercising combinations of different HTML fragments that may contain 17.51 + * JavaScript, different places to place those fragments, and whether or not to allow the use 17.52 + * of JavaScript. 17.53 + */ 17.54 +public class TestScriptInComment { 17.55 + public static void main(String... args) throws Exception { 17.56 + new TestScriptInComment().run(); 17.57 + } 17.58 + 17.59 + /** 17.60 + * Representative samples of different fragments of HTML that may contain JavaScript. 17.61 + * To facilitate checking the output manually in a browser, the text "#ALERT" will be 17.62 + * replaced by a JavaScript call of "alert(msg)", using a message string that is specific 17.63 + * to the test case. 17.64 + */ 17.65 + enum Comment { 17.66 + LC("<script>#ALERT</script>", true), // script tag in Lower Case 17.67 + UC("<SCRIPT>#ALERT</script>", true), // script tag in Upper Case 17.68 + WS("< script >#ALERT</script>", false, "-Xdoclint:none"), // script tag with invalid white space 17.69 + SA("<script src=\"file\"> #ALERT </script>", true), // script tag with an attribute 17.70 + ON("<a onclick='#ALERT'>x</a>", true), // event handler attribute 17.71 + URI("<a href='javascript:#ALERT'>x</a>", true); // javadcript URI 17.72 + 17.73 + /** 17.74 + * Creates an HTML fragment to be injected into a template. 17.75 + * @param text the HTML fragment to put into a doc comment or option. 17.76 + * @param hasScript whether or not this fragment does contain legal JavaScript 17.77 + * @param opts any additional options to be specified when javadoc is run 17.78 + */ 17.79 + Comment(String text, boolean hasScript, String... opts) { 17.80 + this.text = text; 17.81 + this.hasScript = hasScript; 17.82 + this.opts = Arrays.asList(opts); 17.83 + } 17.84 + 17.85 + final String text; 17.86 + final boolean hasScript; 17.87 + final List<String> opts; 17.88 + }; 17.89 + 17.90 + /** 17.91 + * Representative samples of positions in which javadoc may find JavaScript. 17.92 + * Each template contains a series of strings, which are written to files or inferred as options. 17.93 + * The first source file implies a corresponding output file which should not be written 17.94 + * if the comment contains JavaScript and JavaScript is not allowed. 17.95 + */ 17.96 + enum Template { 17.97 + OVR("<html><body> overview #COMMENT </body></html>", "package p; public class C { }"), 17.98 + PKGINFO("#COMMENT package p;", "package p; public class C { }"), 17.99 + PKGHTML("<html><body>#COMMENT package p;</body></html>", "package p; public class C { }"), 17.100 + CLS("package p; #COMMENT public class C { }"), 17.101 + CON("package p; public class C { #COMMENT public C() { } }"), 17.102 + FLD("package p; public class C { #COMMENT public int f; }"), 17.103 + MTH("package p; public class C { #COMMENT public void m() { } }"), 17.104 + TOP("-top", "lorem #COMMENT ipsum", "package p; public class C { }"), 17.105 + HDR("-header", "lorem #COMMENT ipsum", "package p; public class C { }"), 17.106 + FTR("-footer", "lorem #COMMENT ipsum", "package p; public class C { }"), 17.107 + BTM("-bottom", "lorem #COMMENT ipsum", "package p; public class C { }"), 17.108 + DTTL("-doctitle", "lorem #COMMENT ipsum", "package p; public class C { }"), 17.109 + PHDR("-packagesheader", "lorem #COMMENT ipsum", "package p; public class C { }"); 17.110 + 17.111 + Template(String... args) { 17.112 + opts = new ArrayList<String>(); 17.113 + sources = new ArrayList<String>(); 17.114 + int i = 0; 17.115 + while (args[i].startsWith("-")) { 17.116 + // all options being tested have a single argument that follow the option 17.117 + opts.add(args[i++]); 17.118 + opts.add(args[i++]); 17.119 + } 17.120 + while(i < args.length) { 17.121 + sources.add(args[i++]); 17.122 + } 17.123 + } 17.124 + 17.125 + // groups: 1 <html> or not; 2: package name; 3: class name 17.126 + private final Pattern pat = 17.127 + Pattern.compile("(?i)(<html>)?.*?(?:package ([a-z]+);.*?(?:class ([a-z]+).*)?)?"); 17.128 + 17.129 + /** 17.130 + * Infer the file in which to write the given source. 17.131 + * @param dir the base source directory 17.132 + * @param src the source text 17.133 + * @return the file in which the source should be written 17.134 + */ 17.135 + File getSrcFile(File srcDir, String src) { 17.136 + String f; 17.137 + Matcher m = pat.matcher(src); 17.138 + if (!m.matches()) 17.139 + throw new Error("match failed"); 17.140 + if (m.group(3) != null) { 17.141 + f = m.group(2) + "/" + m.group(3) + ".java"; 17.142 + } else if (m.group(2) != null) { 17.143 + f = m.group(2) + "/" + (m.group(1) == null ? "package-info.java" : "package.html"); 17.144 + } else { 17.145 + f = "overview.html"; 17.146 + } 17.147 + return new File(srcDir, f); 17.148 + } 17.149 + 17.150 + /** 17.151 + * Get the options to give to javadoc. 17.152 + * @param srcDir the srcDir to use -overview is needed 17.153 + * @return 17.154 + */ 17.155 + List<String> getOpts(File srcDir) { 17.156 + if (!opts.isEmpty()) { 17.157 + return opts; 17.158 + } else if (sources.get(0).contains("overview")) { 17.159 + return Arrays.asList("-overview", getSrcFile(srcDir, sources.get(0)).getPath()); 17.160 + } else { 17.161 + return Collections.emptyList(); 17.162 + } 17.163 + } 17.164 + 17.165 + /** 17.166 + * Gets the output file corresponding to the first source file. 17.167 + * This file should not be written if the comment contains JavaScript and JavaScripot is 17.168 + * not allowed. 17.169 + * @param dir the base output directory 17.170 + * @return the output file 17.171 + */ 17.172 + File getOutFile(File outDir) { 17.173 + String f; 17.174 + Matcher m = pat.matcher(sources.get(0)); 17.175 + if (!m.matches()) 17.176 + throw new Error("match failed"); 17.177 + if (m.group(3) != null) { 17.178 + f = m.group(2) + "/" + m.group(3) + ".html"; 17.179 + } else if (m.group(2) != null) { 17.180 + f = m.group(2) + "/package-summary.html"; 17.181 + } else { 17.182 + f = "overview-summary.html"; 17.183 + } 17.184 + return new File(outDir, f); 17.185 + } 17.186 + 17.187 + final List<String> opts; 17.188 + final List<String> sources; 17.189 + }; 17.190 + 17.191 + enum Option { 17.192 + OFF(null), 17.193 + ON("--allow-script-in-comments"); 17.194 + 17.195 + Option(String text) { 17.196 + this.text = text; 17.197 + } 17.198 + 17.199 + final String text; 17.200 + }; 17.201 + 17.202 + private PrintStream out = System.err; 17.203 + 17.204 + public void run() throws Exception { 17.205 + int count = 0; 17.206 + for (Template template: Template.values()) { 17.207 + for (Comment comment: Comment.values()) { 17.208 + for (Option option: Option.values()) { 17.209 + if (test(template, comment, option)) { 17.210 + count++; 17.211 + } 17.212 + } 17.213 + } 17.214 + } 17.215 + 17.216 + out.println(count + " test cases run"); 17.217 + if (errors > 0) { 17.218 + throw new Exception(errors + " errors occurred"); 17.219 + } 17.220 + } 17.221 + 17.222 + boolean test(Template template, Comment comment, Option option) throws IOException { 17.223 + if (option == Option.ON && !comment.hasScript) { 17.224 + // skip --allowScriptInComments if comment does not contain JavaScript 17.225 + return false; 17.226 + } 17.227 + 17.228 + String test = template + "-" + comment + "-" + option; 17.229 + out.println("Test: " + test); 17.230 + 17.231 + File dir = new File(test); 17.232 + dir.mkdirs(); 17.233 + File srcDir = new File(dir, "src"); 17.234 + File outDir = new File(dir, "out"); 17.235 + 17.236 + String alert = "alert(\"" + test + "\");"; 17.237 + for (String src: template.sources) { 17.238 + writeFile(template.getSrcFile(srcDir, src), 17.239 + src.replace("#COMMENT", 17.240 + "/** " + comment.text.replace("#ALERT", alert) + " **/")); 17.241 + } 17.242 + 17.243 + List<String> opts = new ArrayList<String>(); 17.244 + opts.add("-sourcepath"); 17.245 + opts.add(srcDir.getPath()); 17.246 + opts.add("-d"); 17.247 + opts.add(outDir.getPath()); 17.248 + if (option.text != null) 17.249 + opts.add(option.text); 17.250 + for (String opt: template.getOpts(srcDir)) { 17.251 + opts.add(opt.replace("#COMMENT", comment.text.replace("#ALERT", alert))); 17.252 + } 17.253 + opts.addAll(comment.opts); 17.254 + opts.add("-noindex"); // index not required; save time/space writing files 17.255 + opts.add("p"); 17.256 + 17.257 + StringWriter sw = new StringWriter(); 17.258 + PrintWriter pw = new PrintWriter(sw); 17.259 + int rc = javadoc(opts, pw); 17.260 + pw.close(); 17.261 + String log = sw.toString(); 17.262 + writeFile(new File(dir, "log.txt"), log); 17.263 + 17.264 + out.println("opts: " + opts); 17.265 + out.println(" rc: " + rc); 17.266 + out.println(" log:"); 17.267 + out.println(log); 17.268 + 17.269 + String ERROR = "Use --allow-script-in-comment"; 17.270 + File outFile = template.getOutFile(outDir); 17.271 + 17.272 + boolean expectErrors = comment.hasScript && (option == Option.OFF); 17.273 + 17.274 + if (expectErrors) { 17.275 + check(rc != 0, "unexpected exit code: " + rc); 17.276 + check(log.contains(ERROR), "expected error message not found"); 17.277 + check(!outFile.exists(), "output file found unexpectedly"); 17.278 + } else { 17.279 + check(rc == 0, "unexpected exit code: " + rc); 17.280 + check(!log.contains(ERROR), "error message found"); 17.281 + check(outFile.exists(), "output file not found"); 17.282 + } 17.283 + 17.284 + out.println(); 17.285 + return true; 17.286 + } 17.287 + 17.288 + int javadoc(List<String> opts, PrintWriter pw) { 17.289 + return com.sun.tools.javadoc.Main.execute("javadoc", pw, pw, pw, 17.290 + "com.sun.tools.doclets.standard.Standard", opts.toArray(new String[opts.size()])); 17.291 + } 17.292 + 17.293 + File writeFile(File f, String text) throws IOException { 17.294 + f.getParentFile().mkdirs(); 17.295 + FileWriter fw = new FileWriter(f); 17.296 + try { 17.297 + fw.write(text); 17.298 + } finally { 17.299 + fw.close(); 17.300 + } 17.301 + return f; 17.302 + } 17.303 + 17.304 + void check(boolean cond, String errMessage) { 17.305 + if (!cond) { 17.306 + error(errMessage); 17.307 + } 17.308 + } 17.309 + 17.310 + void error(String message) { 17.311 + out.println("Error: " + message); 17.312 + errors++; 17.313 + } 17.314 + 17.315 + int errors = 0; 17.316 +} 17.317 +