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