1 /* 2 * Copyright (c) 2001, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.javadoc.internal.tool; 27 28 29 import java.io.File; 30 import java.util.ArrayList; 31 import java.util.HashSet; 32 import java.util.LinkedHashSet; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 37 import javax.tools.JavaFileObject; 38 import javax.tools.StandardJavaFileManager; 39 40 import com.sun.tools.javac.code.ClassFinder; 41 import com.sun.tools.javac.code.DeferredCompletionFailureHandler; 42 import com.sun.tools.javac.code.Symbol.Completer; 43 import com.sun.tools.javac.code.Symbol.CompletionFailure; 44 import com.sun.tools.javac.comp.Enter; 45 import com.sun.tools.javac.tree.JCTree; 46 import com.sun.tools.javac.tree.JCTree.JCClassDecl; 47 import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; 48 import com.sun.tools.javac.util.Abort; 49 import com.sun.tools.javac.util.Context; 50 import com.sun.tools.javac.util.ListBuffer; 51 import com.sun.tools.javac.util.Position; 52 import jdk.javadoc.doclet.DocletEnvironment; 53 54 import static jdk.javadoc.internal.tool.Main.Result.*; 55 56 /** 57 * This class could be the main entry point for Javadoc when Javadoc is used as a 58 * component in a larger software system. It provides operations to 59 * construct a new javadoc processor, and to run it on a set of source 60 * files. 61 * 62 * <p><b>This is NOT part of any supported API. 63 * If you write code that depends on this, you do so at your own risk. 64 * This code and its internal interfaces are subject to change or 65 * deletion without notice.</b> 66 * 67 * @author Neal Gafter 68 */ 69 public class JavadocTool extends com.sun.tools.javac.main.JavaCompiler { 70 ToolEnvironment toolEnv; 71 72 final Messager messager; 73 final ClassFinder javadocFinder; 74 final DeferredCompletionFailureHandler dcfh; 75 final Enter javadocEnter; 76 final Set<JavaFileObject> uniquefiles; 77 78 /** 79 * Construct a new JavaCompiler processor, using appropriately 80 * extended phases of the underlying compiler. 81 */ 82 protected JavadocTool(Context context) { 83 super(context); 84 messager = Messager.instance0(context); 85 javadocFinder = JavadocClassFinder.instance(context); 86 dcfh = DeferredCompletionFailureHandler.instance(context); 87 javadocEnter = JavadocEnter.instance(context); 88 uniquefiles = new HashSet<>(); 89 } 90 91 /** 92 * For javadoc, the parser needs to keep comments. Overrides method from JavaCompiler. 93 */ 94 @Override 95 protected boolean keepComments() { 96 return true; 97 } 98 99 /** 100 * Construct a new javadoc tool. 101 */ 102 public static JavadocTool make0(Context context) { 103 Messager messager = null; 104 try { 105 // force the use of Javadoc's class finder 106 JavadocClassFinder.preRegister(context); 107 108 // force the use of Javadoc's own enter phase 109 JavadocEnter.preRegister(context); 110 111 // force the use of Javadoc's own member enter phase 112 JavadocMemberEnter.preRegister(context); 113 114 // force the use of Javadoc's own todo phase 115 JavadocTodo.preRegister(context); 116 117 // force the use of Messager as a Log 118 messager = Messager.instance0(context); 119 120 return new JavadocTool(context); 121 } catch (CompletionFailure ex) { 122 messager.error(Position.NOPOS, ex.getMessage()); 123 return null; 124 } 125 } 126 127 public DocletEnvironment getEnvironment(Map<ToolOption, 128 Object> jdtoolOpts, 129 List<String> javaNames, 130 Iterable<? extends JavaFileObject> fileObjects) throws ToolException { 131 toolEnv = ToolEnvironment.instance(context); 132 toolEnv.initialize(jdtoolOpts); 133 ElementsTable etable = new ElementsTable(context, jdtoolOpts); 134 javadocFinder.sourceCompleter = etable.xclasses 135 ? Completer.NULL_COMPLETER 136 : sourceCompleter; 137 138 if (etable.xclasses) { 139 // If -Xclasses is set, the args should be a list of class names 140 for (String arg: javaNames) { 141 if (!isValidPackageName(arg)) { // checks 142 String text = messager.getText("main.illegal_class_name", arg); 143 throw new ToolException(CMDERR, text); 144 } 145 } 146 if (messager.hasErrors()) { 147 return null; 148 } 149 etable.setClassArgList(javaNames); 150 // prepare, force the data structures to be analyzed 151 etable.analyze(); 152 return new DocEnvImpl(toolEnv, etable); 153 } 154 155 ListBuffer<JCCompilationUnit> classTrees = new ListBuffer<>(); 156 157 try { 158 StandardJavaFileManager fm = toolEnv.fileManager instanceof StandardJavaFileManager 159 ? (StandardJavaFileManager) toolEnv.fileManager 160 : null; 161 Set<String> packageNames = new LinkedHashSet<>(); 162 // Normally, the args should be a series of package names or file names. 163 // Parse the files and collect the package names. 164 for (String arg: javaNames) { 165 if (fm != null && arg.endsWith(".java") && new File(arg).exists()) { 166 parse(fm.getJavaFileObjects(arg), classTrees, true); 167 } else if (isValidPackageName(arg)) { 168 packageNames.add(arg); 169 } else if (arg.endsWith(".java")) { 170 if (fm == null) { 171 String text = messager.getText("main.assertion.error", "fm == null"); 172 throw new ToolException(ABNORMAL, text); 173 } else { 174 String text = messager.getText("main.file_not_found", arg); 175 throw new ToolException(ERROR, text); 176 } 177 } else { 178 String text = messager.getText("main.illegal_package_name", arg); 179 throw new ToolException(CMDERR, text); 180 } 181 } 182 183 // Parse file objects provide via the DocumentationTool API 184 parse(fileObjects, classTrees, true); 185 186 etable.packages(packageNames) 187 .classTrees(classTrees.toList()) 188 .scanSpecifiedItems(); 189 190 // abort, if errors were encountered during modules initialization 191 if (messager.hasErrors()) { 192 return null; 193 } 194 195 // Parse the files in the packages and subpackages to be documented 196 ListBuffer<JCCompilationUnit> packageTrees = new ListBuffer<>(); 197 parse(etable.getFilesToParse(), packageTrees, false); 198 modules.enter(packageTrees.toList(), null); 199 200 if (messager.hasErrors()) { 201 return null; 202 } 203 204 // Enter symbols for all files 205 toolEnv.notice("main.Building_tree"); 206 javadocEnter.main(classTrees.toList().appendList(packageTrees)); 207 208 if (messager.hasErrors()) { 209 return null; 210 } 211 212 etable.setClassDeclList(listClasses(classTrees.toList())); 213 214 dcfh.setHandler(dcfh.userCodeHandler); 215 etable.analyze(); 216 } catch (CompletionFailure cf) { 217 throw new ToolException(ABNORMAL, cf.getMessage(), cf); 218 } catch (Abort abort) { 219 if (messager.hasErrors()) { 220 // presumably a message has been emitted, keep silent 221 throw new ToolException(ABNORMAL, "", abort); 222 } else { 223 String text = messager.getText("main.internal.error"); 224 Throwable t = abort.getCause() == null ? abort : abort.getCause(); 225 throw new ToolException(ABNORMAL, text, t); 226 } 227 } 228 229 if (messager.hasErrors()) 230 return null; 231 232 toolEnv.docEnv = new DocEnvImpl(toolEnv, etable); 233 return toolEnv.docEnv; 234 } 235 236 /** Is the given string a valid package name? */ 237 boolean isValidPackageName(String s) { 238 if (s.contains("/")) { 239 String[] a = s.split("/"); 240 if (a.length == 2) { 241 return isValidPackageName0(a[0]) && isValidPackageName0(a[1]); 242 } 243 return false; 244 } 245 return isValidPackageName0(s); 246 } 247 248 private boolean isValidPackageName0(String s) { 249 for (int index = s.indexOf('.') ; index != -1; index = s.indexOf('.')) { 250 if (!isValidClassName(s.substring(0, index))) { 251 return false; 252 } 253 s = s.substring(index + 1); 254 } 255 return isValidClassName(s); 256 } 257 258 private void parse(Iterable<? extends JavaFileObject> files, ListBuffer<JCCompilationUnit> trees, 259 boolean trace) { 260 for (JavaFileObject fo: files) { 261 if (uniquefiles.add(fo)) { // ignore duplicates 262 if (trace) 263 toolEnv.notice("main.Loading_source_file", fo.getName()); 264 trees.append(parse(fo)); 265 } 266 } 267 } 268 269 /** Are surrogates supported? */ 270 final static boolean surrogatesSupported = surrogatesSupported(); 271 private static boolean surrogatesSupported() { 272 try { 273 boolean b = Character.isHighSurrogate('a'); 274 return true; 275 } catch (NoSuchMethodError ex) { 276 return false; 277 } 278 } 279 280 /** 281 * Return true if given file name is a valid class name 282 * (including "package-info"). 283 * @param s the name of the class to check. 284 * @return true if given class name is a valid class name 285 * and false otherwise. 286 */ 287 public static boolean isValidClassName(String s) { 288 if (s.length() < 1) return false; 289 if (s.equals("package-info")) return true; 290 if (surrogatesSupported) { 291 int cp = s.codePointAt(0); 292 if (!Character.isJavaIdentifierStart(cp)) 293 return false; 294 for (int j = Character.charCount(cp); j < s.length(); j += Character.charCount(cp)) { 295 cp = s.codePointAt(j); 296 if (!Character.isJavaIdentifierPart(cp)) 297 return false; 298 } 299 } else { 300 if (!Character.isJavaIdentifierStart(s.charAt(0))) 301 return false; 302 for (int j = 1; j < s.length(); j++) 303 if (!Character.isJavaIdentifierPart(s.charAt(j))) 304 return false; 305 } 306 return true; 307 } 308 309 /** 310 * From a list of top level trees, return the list of contained class definitions 311 */ 312 List<JCClassDecl> listClasses(List<JCCompilationUnit> trees) { 313 List<JCClassDecl> result = new ArrayList<>(); 314 for (JCCompilationUnit t : trees) { 315 for (JCTree def : t.defs) { 316 if (def.hasTag(JCTree.Tag.CLASSDEF)) 317 result.add((JCClassDecl)def); 318 } 319 } 320 return result; 321 } 322 }