| /* |
| * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package com.sun.tools.sjavac.comp; |
| |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import javax.tools.JavaFileObject; |
| |
| import com.sun.source.tree.CompilationUnitTree; |
| import com.sun.source.util.TaskEvent; |
| import com.sun.source.util.TaskListener; |
| import com.sun.tools.javac.tree.JCTree; |
| import com.sun.tools.javac.tree.JCTree.JCFieldAccess; |
| import com.sun.tools.javac.tree.JCTree.JCIdent; |
| import com.sun.tools.javac.util.DefinedBy; |
| import com.sun.tools.javac.util.DefinedBy.Api; |
| import com.sun.tools.javac.util.Name; |
| import com.sun.tools.sjavac.Log; |
| |
| public class PathAndPackageVerifier implements TaskListener { |
| |
| // Stores the set of compilation units whose source file path does not |
| // match the package declaration. |
| Set<CompilationUnitTree> misplacedCompilationUnits = new HashSet<>(); |
| |
| @Override |
| @DefinedBy(Api.COMPILER_TREE) |
| public void finished(TaskEvent e) { |
| if (e.getKind() == TaskEvent.Kind.ANALYZE) { |
| |
| CompilationUnitTree cu = e.getCompilationUnit(); |
| if (cu == null) |
| return; |
| |
| JavaFileObject jfo = cu.getSourceFile(); |
| if (jfo == null) |
| return; // No source file -> package doesn't matter |
| |
| JCTree pkg = (JCTree) cu.getPackageName(); |
| if (pkg == null) |
| return; // Default package. See JDK-8048144. |
| |
| Path dir = Paths.get(jfo.toUri()).normalize().getParent(); |
| if (!checkPathAndPackage(dir, pkg)) |
| misplacedCompilationUnits.add(cu); |
| } |
| |
| if (e.getKind() == TaskEvent.Kind.COMPILATION) { |
| for (CompilationUnitTree cu : misplacedCompilationUnits) { |
| Log.error("Misplaced compilation unit."); |
| Log.error(" Directory: " + Paths.get(cu.getSourceFile().toUri()).getParent()); |
| Log.error(" Package: " + cu.getPackageName()); |
| } |
| } |
| } |
| |
| public boolean errorsDiscovered() { |
| return misplacedCompilationUnits.size() > 0; |
| } |
| |
| /* Returns true if dir matches pkgName. |
| * |
| * Examples: |
| * (a/b/c, a.b.c) gives true |
| * (i/j/k, i.x.k) gives false |
| * |
| * Currently (x/a/b/c, a.b.c) also gives true. See JDK-8059598. |
| */ |
| private boolean checkPathAndPackage(Path dir, JCTree pkgName) { |
| Iterator<String> pathIter = new ParentIterator(dir); |
| Iterator<String> pkgIter = new EnclosingPkgIterator(pkgName); |
| while (pathIter.hasNext() && pkgIter.hasNext()) { |
| if (!pathIter.next().equals(pkgIter.next())) |
| return false; |
| } |
| return !pkgIter.hasNext(); /*&& !pathIter.hasNext() See JDK-8059598 */ |
| } |
| |
| /* Iterates over the names of the parents of the given path: |
| * Example: dir1/dir2/dir3 results in dir3 -> dir2 -> dir1 |
| */ |
| private static class ParentIterator implements Iterator<String> { |
| Path next; |
| ParentIterator(Path initial) { |
| next = initial; |
| } |
| @Override |
| public boolean hasNext() { |
| return next != null; |
| } |
| @Override |
| public String next() { |
| String tmp = next.getFileName().toString(); |
| next = next.getParent(); |
| return tmp; |
| } |
| } |
| |
| /* Iterates over the names of the enclosing packages: |
| * Example: pkg1.pkg2.pkg3 results in pkg3 -> pkg2 -> pkg1 |
| */ |
| private static class EnclosingPkgIterator implements Iterator<String> { |
| JCTree next; |
| EnclosingPkgIterator(JCTree initial) { |
| next = initial; |
| } |
| @Override |
| public boolean hasNext() { |
| return next != null; |
| } |
| @Override |
| public String next() { |
| Name name; |
| if (next instanceof JCIdent) { |
| name = ((JCIdent) next).name; |
| next = null; |
| } else { |
| JCFieldAccess fa = (JCFieldAccess) next; |
| name = fa.name; |
| next = fa.selected; |
| } |
| return name.toString(); |
| } |
| } |
| } |