PathAndPackageVerifier.java revision 2958:27da0c3ac83a
1/* 2 * Copyright (c) 2014, 2015, 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 */ 25package com.sun.tools.sjavac.comp; 26 27import java.nio.file.Path; 28import java.nio.file.Paths; 29import java.util.HashSet; 30import java.util.Iterator; 31import java.util.Set; 32 33import javax.tools.JavaFileObject; 34 35import com.sun.source.tree.CompilationUnitTree; 36import com.sun.source.util.TaskEvent; 37import com.sun.source.util.TaskListener; 38import com.sun.tools.javac.tree.JCTree; 39import com.sun.tools.javac.tree.JCTree.JCFieldAccess; 40import com.sun.tools.javac.tree.JCTree.JCIdent; 41import com.sun.tools.javac.util.DefinedBy; 42import com.sun.tools.javac.util.DefinedBy.Api; 43import com.sun.tools.javac.util.Name; 44import com.sun.tools.sjavac.Log; 45 46public class PathAndPackageVerifier implements TaskListener { 47 48 // Stores the set of compilation units whose source file path does not 49 // match the package declaration. 50 Set<CompilationUnitTree> misplacedCompilationUnits = new HashSet<>(); 51 52 @Override 53 @DefinedBy(Api.COMPILER_TREE) 54 public void finished(TaskEvent e) { 55 if (e.getKind() == TaskEvent.Kind.ANALYZE) { 56 57 CompilationUnitTree cu = e.getCompilationUnit(); 58 if (cu == null) 59 return; 60 61 JavaFileObject jfo = cu.getSourceFile(); 62 if (jfo == null) 63 return; // No source file -> package doesn't matter 64 65 JCTree pkg = (JCTree) cu.getPackageName(); 66 if (pkg == null) 67 return; // Default package. See JDK-8048144. 68 69 Path dir = Paths.get(jfo.toUri()).normalize().getParent(); 70 if (!checkPathAndPackage(dir, pkg)) 71 misplacedCompilationUnits.add(cu); 72 } 73 74 if (e.getKind() == TaskEvent.Kind.COMPILATION) { 75 for (CompilationUnitTree cu : misplacedCompilationUnits) { 76 Log.error("Misplaced compilation unit."); 77 Log.error(" Directory: " + Paths.get(cu.getSourceFile().toUri()).getParent()); 78 Log.error(" Package: " + cu.getPackageName()); 79 } 80 } 81 } 82 83 public boolean errorsDiscovered() { 84 return misplacedCompilationUnits.size() > 0; 85 } 86 87 /* Returns true if dir matches pkgName. 88 * 89 * Examples: 90 * (a/b/c, a.b.c) gives true 91 * (i/j/k, i.x.k) gives false 92 * 93 * Currently (x/a/b/c, a.b.c) also gives true. See JDK-8059598. 94 */ 95 private boolean checkPathAndPackage(Path dir, JCTree pkgName) { 96 Iterator<String> pathIter = new ParentIterator(dir); 97 Iterator<String> pkgIter = new EnclosingPkgIterator(pkgName); 98 while (pathIter.hasNext() && pkgIter.hasNext()) { 99 if (!pathIter.next().equals(pkgIter.next())) 100 return false; 101 } 102 return !pkgIter.hasNext(); /*&& !pathIter.hasNext() See JDK-8059598 */ 103 } 104 105 /* Iterates over the names of the parents of the given path: 106 * Example: dir1/dir2/dir3 results in dir3 -> dir2 -> dir1 107 */ 108 private static class ParentIterator implements Iterator<String> { 109 Path next; 110 ParentIterator(Path initial) { 111 next = initial; 112 } 113 @Override 114 public boolean hasNext() { 115 return next != null; 116 } 117 @Override 118 public String next() { 119 String tmp = next.getFileName().toString(); 120 next = next.getParent(); 121 return tmp; 122 } 123 } 124 125 /* Iterates over the names of the enclosing packages: 126 * Example: pkg1.pkg2.pkg3 results in pkg3 -> pkg2 -> pkg1 127 */ 128 private static class EnclosingPkgIterator implements Iterator<String> { 129 JCTree next; 130 EnclosingPkgIterator(JCTree initial) { 131 next = initial; 132 } 133 @Override 134 public boolean hasNext() { 135 return next != null; 136 } 137 @Override 138 public String next() { 139 Name name; 140 if (next instanceof JCIdent) { 141 name = ((JCIdent) next).name; 142 next = null; 143 } else { 144 JCFieldAccess fa = (JCFieldAccess) next; 145 name = fa.name; 146 next = fa.selected; 147 } 148 return name.toString(); 149 } 150 } 151} 152