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