1 /* 2 * Copyright (c) 2016, 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.jdeps; 26 27 import com.sun.tools.classfile.Dependencies; 28 import com.sun.tools.classfile.Dependency; 29 import com.sun.tools.classfile.Dependency.Location; 30 31 import java.util.HashSet; 32 import java.util.Set; 33 import java.util.regex.Pattern; 34 35 /* 36 * Filter configured based on the input jdeps option 37 * 1. -p and -regex to match target dependencies 38 * 2. -filter:package to filter out same-package dependencies 39 * This filter is applied when jdeps parses the class files 40 * and filtered dependencies are not stored in the Analyzer. 41 * 3. --require specifies to match target dependence from the given module 42 * This gets expanded into package lists to be filtered. 43 * 4. -filter:archive to filter out same-archive dependencies 44 * This filter is applied later in the Analyzer as the 45 * containing archive of a target class may not be known until 46 * the entire archive 47 */ 48 public class JdepsFilter implements Dependency.Filter, Analyzer.Filter { 49 50 public static final JdepsFilter DEFAULT_FILTER = 51 new JdepsFilter.Builder().filter(true, true).build(); 52 53 private final Dependency.Filter filter; 54 private final Pattern filterPattern; 55 private final boolean filterSamePackage; 56 private final boolean filterSameArchive; 57 private final boolean findJDKInternals; 58 private final boolean findMissingDeps; 59 private final Pattern includePattern; 60 61 private final Set<String> requires; 62 63 private JdepsFilter(Dependency.Filter filter, 64 Pattern filterPattern, 65 boolean filterSamePackage, 66 boolean filterSameArchive, 67 boolean findJDKInternals, 68 boolean findMissingDeps, 69 Pattern includePattern, 70 Set<String> requires) { 71 this.filter = filter; 72 this.filterPattern = filterPattern; 73 this.filterSamePackage = filterSamePackage; 74 this.filterSameArchive = filterSameArchive; 75 this.findJDKInternals = findJDKInternals; 76 this.findMissingDeps = findMissingDeps; 77 this.includePattern = includePattern; 78 this.requires = requires; 79 } 80 81 /** 82 * Tests if the given class matches the pattern given in the -include option 83 * 84 * @param cn fully-qualified name 85 */ 86 public boolean matches(String cn) { 87 if (includePattern == null) 88 return true; 89 90 if (includePattern != null) 91 return includePattern.matcher(cn).matches(); 92 93 return false; 94 } 95 96 /** 97 * Tests if the given source includes classes specified in -include option 98 * 99 * This method can be used to determine if the given source should eagerly 100 * be processed. 101 */ 102 public boolean matches(Archive source) { 103 if (includePattern != null) { 104 return source.reader().entries().stream() 105 .map(name -> name.replace('/', '.')) 106 .filter(name -> !name.equals("module-info.class")) 107 .anyMatch(this::matches); 108 } 109 return hasTargetFilter(); 110 } 111 112 public boolean hasIncludePattern() { 113 return includePattern != null; 114 } 115 116 public boolean hasTargetFilter() { 117 return filter != null; 118 } 119 120 public Set<String> requiresFilter() { 121 return requires; 122 } 123 124 // ----- Dependency.Filter ----- 125 126 @Override 127 public boolean accepts(Dependency d) { 128 if (d.getOrigin().equals(d.getTarget())) 129 return false; 130 131 // filter same package dependency 132 String pn = d.getTarget().getPackageName(); 133 if (filterSamePackage && d.getOrigin().getPackageName().equals(pn)) { 134 return false; 135 } 136 137 // filter if the target package matches the given filter 138 if (filterPattern != null && filterPattern.matcher(pn).matches()) { 139 return false; 140 } 141 142 // filter if the target matches the given filtered package name or regex 143 return filter != null ? filter.accepts(d) : true; 144 } 145 146 // ----- Analyzer.Filter ------ 147 148 /** 149 * Filter depending on the containing archive or module 150 */ 151 @Override 152 public boolean accepts(Location origin, Archive originArchive, 153 Location target, Archive targetArchive) { 154 if (findJDKInternals) { 155 // accepts target that is JDK class but not exported 156 Module module = targetArchive.getModule(); 157 return originArchive != targetArchive && 158 isJDKInternalPackage(module, target.getPackageName()); 159 } else if (findMissingDeps) { 160 return Analyzer.notFound(targetArchive); 161 } else if (filterSameArchive) { 162 // accepts origin and target that from different archive 163 return originArchive != targetArchive; 164 } 165 return true; 166 } 167 168 /** 169 * Tests if the package is an internal package of the given module. 170 */ 171 public boolean isJDKInternalPackage(Module module, String pn) { 172 if (module.isJDKUnsupported()) { 173 // its exported APIs are unsupported 174 return true; 175 } 176 177 return module.isJDK() && !module.isExported(pn); 178 } 179 180 @Override 181 public String toString() { 182 StringBuilder sb = new StringBuilder(); 183 sb.append("include pattern: ").append(includePattern).append("\n"); 184 sb.append("filter same archive: ").append(filterSameArchive).append("\n"); 185 sb.append("filter same package: ").append(filterSamePackage).append("\n"); 186 sb.append("requires: ").append(requires).append("\n"); 187 return sb.toString(); 188 } 189 190 public static class Builder { 191 Pattern filterPattern; 192 Pattern regex; 193 boolean filterSamePackage; 194 boolean filterSameArchive; 195 boolean findJDKInterals; 196 boolean findMissingDeps; 197 // source filters 198 Pattern includePattern; 199 Set<String> requires = new HashSet<>(); 200 Set<String> targetPackages = new HashSet<>(); 201 202 public Builder() {}; 203 204 public Builder packages(Set<String> packageNames) { 205 this.targetPackages.addAll(packageNames); 206 return this; 207 } 208 public Builder regex(Pattern regex) { 209 this.regex = regex; 210 return this; 211 } 212 public Builder filter(Pattern regex) { 213 this.filterPattern = regex; 214 return this; 215 } 216 public Builder filter(boolean samePackage, boolean sameArchive) { 217 this.filterSamePackage = samePackage; 218 this.filterSameArchive = sameArchive; 219 return this; 220 } 221 public Builder requires(String name, Set<String> packageNames) { 222 this.requires.add(name); 223 this.targetPackages.addAll(packageNames); 224 return this; 225 } 226 public Builder findJDKInternals(boolean value) { 227 this.findJDKInterals = value; 228 return this; 229 } 230 public Builder findMissingDeps(boolean value) { 231 this.findMissingDeps = value; 232 return this; 233 } 234 public Builder includePattern(Pattern regex) { 235 this.includePattern = regex; 236 return this; 237 } 238 239 public JdepsFilter build() { 240 Dependency.Filter filter = null; 241 if (regex != null) 242 filter = Dependencies.getRegexFilter(regex); 243 else if (!targetPackages.isEmpty()) { 244 filter = Dependencies.getPackageFilter(targetPackages, false); 245 } 246 return new JdepsFilter(filter, 247 filterPattern, 248 filterSamePackage, 249 filterSameArchive, 250 findJDKInterals, 251 findMissingDeps, 252 includePattern, 253 requires); 254 } 255 256 } 257 }