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