2 * Copyright (c) 2009, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 package com.sun.classanalyzer; 25 26 import java.io.BufferedInputStream; 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.ArrayList; 32 import java.util.Enumeration; 33 import java.util.HashSet; 34 import java.util.List; 35 import java.util.Set; 36 import java.util.jar.JarEntry; 37 import java.util.jar.JarFile; 38 39 /** 40 * 41 * @author mchung 42 */ 43 public class ClassPath { 44 45 public class FileInfo { 46 47 File file; 48 JarFile jarfile; 49 int classCount; 50 long filesize; 51 52 FileInfo(File f) throws IOException { 53 this.file = f; 54 this.classCount = 0; 55 if (file.getName().endsWith(".jar")) { 56 this.filesize = file.length(); 57 jarfile = new JarFile(f); 58 } 59 } 60 61 File getFile() { 62 return file; 63 } 64 65 JarFile getJarFile() { 66 return jarfile; 67 } 68 69 String getName() throws IOException { 70 return file.getCanonicalPath(); 71 } 72 } 73 private List<FileInfo> fileList = new ArrayList<FileInfo>(); 74 private static ClassPath instance = new ClassPath(); 75 76 static List<FileInfo> getFileInfos() { 77 return instance.fileList; 78 } 79 80 static ClassPath setJDKHome(String jdkhome) throws IOException { 81 List<File> files = new ArrayList<File>(); 82 File jre = new File(jdkhome, "jre"); 83 File lib = new File(jdkhome, "lib"); 84 if (jre.exists() && jre.isDirectory()) { 85 listFiles(new File(jre, "lib"), ".jar", files); 86 listFiles(lib, ".jar", files); 87 } else if (lib.exists() && lib.isDirectory()) { 88 // either a JRE or a jdk build image 89 listFiles(lib, ".jar", files); 90 91 File classes = new File(jdkhome, "classes"); 92 if (classes.exists() && classes.isDirectory()) { 93 // jdk build outputdir 94 instance.add(classes); 95 } 96 } else { 97 throw new RuntimeException("\"" + jdkhome + "\" not a JDK home"); 98 } 99 100 for (File f : files) { 101 instance.add(f); 102 } 103 return instance; 104 } 105 106 static ClassPath setClassPath(String path) throws IOException { 107 if (path.endsWith(".class")) { 108 // one class file 109 File f = new File(path); 110 if (!f.exists()) { 111 throw new RuntimeException("Classfile \"" + f + "\" doesn't exist"); 112 } 113 114 instance.add(f); 115 } else { 116 List<File> jarFiles = new ArrayList<File>(); 117 String[] locs = path.split(File.pathSeparator); 118 for (String p : locs) { 119 File f = new File(p); 120 if (!f.exists()) { 121 throw new RuntimeException("\"" + f + "\" doesn't exist"); 122 } 123 124 if (f.isDirectory()) { 125 instance.add(f); // add the directory to look up .class files 126 listFiles(f, ".jar", jarFiles); 127 } else if (p.endsWith(".jar")) { 128 // jar files 129 jarFiles.add(f); 130 } else { 131 throw new RuntimeException("Invalid file \"" + f); 132 } 133 } 134 // add jarFiles if any 135 for (File f : jarFiles) { 136 instance.add(f); 137 } 138 } 139 140 return instance; 141 } 142 143 private void add(File f) throws IOException { 144 fileList.add(new FileInfo(f)); 145 } 146 147 public static InputStream open(String pathname) throws IOException { 148 for (FileInfo fi : instance.fileList) { 149 if (fi.getName().endsWith(".jar")) { 150 String path = pathname.replace(File.separatorChar, '/'); 151 JarEntry e = fi.jarfile.getJarEntry(path); 152 if (e != null) { 153 return fi.jarfile.getInputStream(e); 154 } 155 } else if (fi.getFile().isDirectory()) { 156 File f = new File(fi.getFile(), pathname); 157 if (f.exists()) { 158 return new FileInputStream(f); 159 } 160 } else if (fi.file.isFile()) { 161 if (fi.getName().endsWith(File.separator + pathname)) { 162 return new FileInputStream(fi.file); 163 } 164 } 165 } 166 return null; 167 } 168 169 static ClassFileParser parserForClass(String classname) throws IOException { 170 String pathname = classname.replace('.', File.separatorChar) + ".class"; 171 172 ClassFileParser cfparser = null; 173 for (FileInfo fi : instance.fileList) { 174 if (fi.getName().endsWith(".class")) { 175 if (fi.getName().endsWith(File.separator + pathname)) { 176 cfparser = ClassFileParser.newParser(fi.getFile(), true); 177 break; 178 } 179 } else if (fi.getName().endsWith(".jar")) { 180 JarEntry e = fi.jarfile.getJarEntry(classname.replace('.', '/') + ".class"); 181 if (e != null) { 182 cfparser = ClassFileParser.newParser(fi.jarfile.getInputStream(e), e.getSize(), true); 183 break; 184 } 185 } else if (fi.getFile().isDirectory()) { 186 File f = new File(fi.getFile(), pathname); 187 if (f.exists()) { 188 cfparser = ClassFileParser.newParser(f, true); 189 break; 190 } 191 } 192 } 193 return cfparser; 194 } 195 196 public static void parseAllClassFiles() throws IOException { 197 instance.parseFiles(); 198 } 199 200 private void parseFiles() throws IOException { 201 Set<Klass> classes = new HashSet<Klass>(); 202 203 int count = 0; 204 for (FileInfo fi : fileList) { 205 // filter out public generated classes (i.e. not public API) 206 // javax.management.remote.rmi._RMIConnectionImpl_Tie 207 // javax.management.remote.rmi._RMIServerImpl_Tie 208 if (fi.getName().endsWith(".class")) { 209 parseClass(fi); 210 } else if (fi.getName().endsWith(".jar")) { 211 Enumeration<JarEntry> entries = fi.jarfile.entries(); 212 while (entries.hasMoreElements()) { 213 JarEntry e = entries.nextElement(); 214 if (e.getName().endsWith(".class")) { 215 ClassFileParser cfparser = ClassFileParser.newParser(fi.jarfile.getInputStream(e), e.getSize(), true); 216 cfparser.parseDependency(false); 217 fi.classCount++; 218 } else if (!e.isDirectory() && ResourceFile.isResource(e.getName())) { 219 ResourceFile.addResource(e.getName(), fi.jarfile.getInputStream(e)); 220 } 221 } 222 } else if (fi.getFile().isDirectory()) { 223 List<File> files = new ArrayList<File>(); 224 listFiles(fi.getFile(), "", files); 225 for (File f : files) { 226 if (f.getName().endsWith(".class")) { 227 parseClass(fi, f); 228 } else if (!f.isDirectory() && ResourceFile.isResource(f.getCanonicalPath())) { 229 String pathname = f.getCanonicalPath(); 230 String dir = fi.getName(); 231 if (!pathname.startsWith(dir)) { 232 throw new RuntimeException("Incorrect pathname " + pathname); 233 } 234 String name = pathname.substring(dir.length() + 1, pathname.length()); 235 BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); 236 try { 237 ResourceFile.addResource(name, in); 238 } finally { 239 in.close(); 240 } 241 } 242 } 243 } else { 244 // should not reach here 245 throw new RuntimeException("Unexpected class path: " + fi.getFile()); 246 } 247 } 248 } 249 250 private void parseClass(FileInfo fi) throws IOException { 251 parseClass(fi, fi.getFile()); 252 } 253 254 private void parseClass(FileInfo fi, File f) throws IOException { 255 ClassFileParser cfparser = ClassFileParser.newParser(f, true); 256 cfparser.parseDependency(false); 257 fi.classCount++; 258 // need to update the filesize for this directory 259 fi.filesize += fi.getFile().length(); 260 261 } 262 263 public static void listFiles(File path, String suffix, List<File> result) { 264 if (path.isDirectory()) { 265 File[] children = path.listFiles(); 266 for (File c : children) { 267 listFiles(c, suffix, result); 268 } 269 270 } else { 271 if (suffix.isEmpty() || path.getName().endsWith(suffix)) { 272 result.add(path); 273 } 274 } 275 } 276 } | 2 * Copyright (c) 2009, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package com.sun.classanalyzer; 24 25 import com.sun.classanalyzer.ClassPaths.*; 26 import java.io.BufferedInputStream; 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.*; 32 import java.util.jar.JarEntry; 33 import java.util.jar.JarFile; 34 35 /** 36 * Legacy class path. Each entry can be a directory containing 37 * classes and resources, or a jar file. It supports classpath 38 * wildcard "*" that lists all jar files in the given directory 39 * and also recursive wildcard "**" that lists all jar files 40 * recursively in the given directory and its subdirectories. 41 * 42 * @author Mandy Chung 43 */ 44 public class ClassPaths { 45 46 protected final List<ClassPath> entries = new LinkedList<ClassPath>(); 47 private final Set<Klass> classes = new LinkedHashSet<Klass>(); 48 private final Set<ResourceFile> resources = new LinkedHashSet<ResourceFile>(); 49 50 private ClassPaths() { 51 } 52 53 public ClassPaths(String... paths) throws IOException { 54 for (String p : paths) { 55 String cp = p; 56 int index = p.indexOf("*"); 57 String wildcard = ""; 58 if (index >= 0) { 59 cp = p.substring(0, index); 60 wildcard = p.substring(index, p.length()); 61 } 62 63 File f = new File(cp); 64 if (!f.exists()) { 65 throw new RuntimeException("\"" + f + "\" doesn't exist"); 66 } 67 if (wildcard.isEmpty()) { 68 if (f.isDirectory()) { 69 entries.add(new DirClassPath(f)); 70 } else if (cp.endsWith(".jar")) { 71 entries.add(new JarFileClassPath(f)); 72 } else { 73 entries.add(new ClassPath(f)); 74 } 75 } else { 76 if (wildcard.equals("*")) { 77 // add jar files in the specified directory 78 String[] ls = Files.list(f); 79 for (String s : ls) { 80 File sf = new File(f, s); 81 if (s.endsWith(".jar")) { 82 entries.add(new JarFileClassPath(f)); 83 } 84 } 85 } else if (wildcard.equals("**")) { 86 // add all jar files in all directories under f 87 addJarFileEntries(f); 88 } 89 } 90 } 91 } 92 93 public List<ClassPath> entries() { 94 return Collections.unmodifiableList(entries); 95 } 96 97 /** 98 * Returns the modules containing the classes and resources being 99 * processed. 100 */ 101 public Set<Module> getModules() { 102 Set<Module> modules = new LinkedHashSet<Module>(); 103 for (Klass k : classes) { 104 modules.add(k.getModule().group()); 105 } 106 for (ResourceFile r : resources) { 107 modules.add(r.getModule().group()); 108 } 109 return modules; 110 } 111 112 public void parse() throws IOException { 113 parse(null, true, false); 114 } 115 116 public void parse(boolean deps, boolean apiOnly) throws IOException { 117 parse(null, deps, apiOnly); 118 } 119 120 public void parse(Filter filter, boolean deps, boolean apiOnly) throws IOException { 121 ClassResourceVisitor crv = new ClassResourceVisitor(classes, resources, deps, apiOnly); 122 ClassPathVisitor cpvisitor = new ClassPathVisitor(crv, filter); 123 visit(cpvisitor, filter, null); 124 } 125 126 public void printStats() { 127 System.out.format("%d classes %d resource files processed%n", 128 classes.size(), resources.size()); 129 } 130 131 protected void addJarFileEntries(File f) throws IOException { 132 List<File> ls; 133 if (f.isDirectory()) { 134 ls = Files.walkTree(f, new Files.Filter<File>() { 135 @Override 136 public boolean accept(File f) throws IOException { 137 return f.isDirectory() || f.getName().endsWith(".jar"); 138 } 139 }); 140 } else { 141 ls = Collections.singletonList(f); 142 } 143 for (File jf : ls) { 144 entries.add(new JarFileClassPath(jf)); 145 } 146 } 147 // FIXME - used by open() method 148 static ClassPaths instance = null; 149 150 static ClassPaths newInstance(String cpaths) throws IOException { 151 String[] paths = cpaths.split(File.pathSeparator); 152 instance = new ClassPaths(paths); 153 return instance; 154 } 155 156 static ClassPaths newJDKClassPaths(String jdkhome) throws IOException { 157 instance = new JDKClassPaths(jdkhome); 158 return instance; 159 } 160 161 static class JDKClassPaths extends ClassPaths { 162 JDKClassPaths(String jdkhome) throws IOException { 163 super(); 164 List<File> files = new ArrayList<File>(); 165 File jre = new File(jdkhome, "jre"); 166 File lib = new File(jdkhome, "lib"); 167 168 if (jre.exists() && jre.isDirectory()) { 169 addJarFiles(new File(jre, "lib")); 170 addJarFiles(lib); 171 } else if (lib.exists() && lib.isDirectory()) { 172 // either a JRE or a jdk build image 173 File classes = new File(jdkhome, "classes"); 174 if (classes.exists() && classes.isDirectory()) { 175 // jdk build outputdir 176 this.entries.add(new DirClassPath(classes)); 177 } 178 addJarFiles(lib); 179 } else { 180 throw new RuntimeException("\"" + jdkhome + "\" not a JDK home"); 181 } 182 } 183 184 // Filter the jigsaw module library, if any 185 final void addJarFiles(File dir) throws IOException { 186 String[] ls = dir.list(); 187 for (String fn : ls) { 188 File f = new File(dir, fn); 189 if (f.isDirectory() && !fn.equals("modules")) { 190 addJarFileEntries(f); 191 } else if (f.isFile() && fn.endsWith(".jar")) { 192 addJarFileEntries(f); 193 } 194 } 195 } 196 } 197 198 /** 199 * Visits all entries in the class path. 200 */ 201 public <R, P> List<R> visit(final ClassPath.Visitor<R, P> visitor, 202 final Filter filter, P p) 203 throws IOException { 204 List<R> result = new ArrayList<R>(); 205 for (ClassPath cp : entries) { 206 if (filter != null && !filter.accept(cp.file)) { 207 continue; 208 } 209 R r = cp.accept(visitor, p); 210 result.add(r); 211 } 212 return result; 213 } 214 215 public static interface FileVisitor { 216 public void visitClass(File f, String cn) throws IOException; 217 public void visitClass(JarFile jf, JarEntry e) throws IOException; 218 public void visitResource(File f, String rn) throws IOException; 219 public void visitResource(JarFile jf, JarEntry e) throws IOException; 220 } 221 222 public static interface Filter { 223 // any file, jar file, directory 224 public boolean accept(File f) throws IOException; 225 public boolean accept(JarFile jf, JarEntry e) throws IOException; 226 } 227 228 public static class ClassPath { 229 230 private final File file; 231 private final String name; 232 233 ClassPath(File f) throws IOException { 234 this.file = f; 235 this.name = file.getCanonicalPath(); 236 } 237 238 File getFile() { 239 return file; 240 } 241 242 String getName() { 243 return name; 244 } 245 246 <R, P> R accept(Visitor<R, P> visitor, P p) throws IOException { 247 return visitor.visitFile(file, this, p); 248 } 249 250 public interface Visitor<R, P> { 251 public R visitFile(File f, ClassPath cp, P p) throws IOException; 252 public R visitDir(File dir, ClassPath cp, P p) throws IOException; 253 public R visitJarFile(JarFile jf, ClassPath cp, P p) throws IOException; 254 } 255 } 256 257 static class JarFileClassPath extends ClassPath { 258 JarFile jarfile; 259 JarFileClassPath(File f) throws IOException { 260 super(f); 261 this.jarfile = new JarFile(f); 262 } 263 264 @Override 265 <R, P> R accept(Visitor<R, P> visitor, P p) throws IOException { 266 return visitor.visitJarFile(jarfile, this, p); 267 } 268 } 269 270 class DirClassPath extends ClassPath { 271 272 DirClassPath(File f) throws IOException { 273 super(f); 274 } 275 276 @Override 277 <R, P> R accept(final Visitor<R, P> visitor, P p) throws IOException { 278 return visitor.visitDir(getFile(), this, p); 279 } 280 } 281 282 static class ClassResourceVisitor implements FileVisitor { 283 284 private final Set<Klass> classes; 285 private final Set<ResourceFile> resources; 286 private final boolean parseDeps; 287 private final boolean apiOnly; 288 289 ClassResourceVisitor(Set<Klass> classes, 290 Set<ResourceFile> resources, 291 boolean parseDeps, 292 boolean apiOnly) { 293 this.classes = classes; 294 this.resources = resources; 295 this.apiOnly = apiOnly; 296 this.parseDeps = parseDeps; 297 } 298 299 @Override 300 public void visitClass(File f, String cn) throws IOException { 301 ClassFileParser cfparser = ClassFileParser.newParser(f, true); 302 classes.add(cfparser.this_klass); 303 if (parseDeps) { 304 cfparser.parseDependency(apiOnly); 305 } 306 } 307 308 @Override 309 public void visitClass(JarFile jf, JarEntry e) throws IOException { 310 ClassFileParser cfparser = ClassFileParser.newParser(jf.getInputStream(e), e.getSize(), true); 311 classes.add(cfparser.this_klass); 312 if (parseDeps) { 313 cfparser.parseDependency(apiOnly); 314 } 315 } 316 317 @Override 318 public void visitResource(File f, String rn) throws IOException { 319 BufferedInputStream in = new BufferedInputStream(new FileInputStream(f)); 320 try { 321 ResourceFile res = ResourceFile.addResource(rn, in); 322 resources.add(res); 323 } finally { 324 in.close(); 325 } 326 } 327 328 @Override 329 public void visitResource(JarFile jf, JarEntry e) throws IOException { 330 ResourceFile res = ResourceFile.addResource(e.getName(), jf.getInputStream(e)); 331 resources.add(res); 332 } 333 } 334 335 static class ClassPathVisitor implements ClassPath.Visitor<Void, Void> { 336 337 private final FileVisitor visitor; 338 private final Filter filter; 339 340 ClassPathVisitor(FileVisitor fv, Filter filter) { 341 this.visitor = fv; 342 this.filter = filter; 343 } 344 345 @Override 346 public Void visitFile(File f, ClassPath cp, Void v) throws IOException { 347 if (filter != null && !filter.accept(f)) { 348 return null; 349 } 350 351 String name = f.getName(); 352 String pathname = f.getCanonicalPath(); 353 String root = cp.getName(); 354 if (!name.equals(root) && !pathname.equals(root)) { 355 if (!pathname.startsWith(root)) { 356 throw new RuntimeException("Incorrect pathname " + pathname); 357 } 358 359 name = pathname.substring(root.length() + 1, pathname.length()); 360 } 361 362 if (name.endsWith(".class")) { 363 visitor.visitClass(f, name); 364 } else if (!f.isDirectory() && ResourceFile.isResource(f.getCanonicalPath())) { 365 visitor.visitResource(f, name); 366 } 367 return null; 368 } 369 370 @Override 371 public Void visitDir(final File dir, final ClassPath cp, Void v) throws IOException { 372 List<File> ls = Files.walkTree(dir, null); 373 for (File f : ls) { 374 visitFile(f, (ClassPath) cp, null); 375 } 376 return null; 377 } 378 379 @Override 380 public Void visitJarFile(JarFile jf, ClassPath cp, Void v) throws IOException { 381 if (filter != null && !filter.accept(cp.getFile())) { 382 return null; 383 } 384 385 Enumeration<JarEntry> entries = jf.entries(); 386 while (entries.hasMoreElements()) { 387 JarEntry e = entries.nextElement(); 388 String name = e.getName(); 389 if (filter != null && !filter.accept(jf, e)) { 390 continue; 391 } 392 if (name.endsWith(".class")) { 393 visitor.visitClass(jf, e); 394 } else if (!e.isDirectory() && ResourceFile.isResource(name)) { 395 visitor.visitResource(jf, e); 396 } 397 } 398 return null; 399 } 400 }; 401 402 public static InputStream open(String pathname) throws IOException { 403 ClassPath.Visitor<InputStream, String> fv = new ClassPath.Visitor<InputStream, String>() { 404 405 @Override 406 public InputStream visitFile(File f, ClassPath cp, String pathname) throws IOException { 407 if (cp.getName().endsWith(File.separator + pathname)) { 408 return new FileInputStream(f); 409 } else { 410 return null; 411 } 412 } 413 414 @Override 415 public InputStream visitDir(File dir, ClassPath cp, String pathname) throws IOException { 416 File f = new File(cp.getFile(), pathname); 417 if (f.exists()) { 418 return new FileInputStream(f); 419 } else { 420 return null; 421 } 422 } 423 424 @Override 425 public InputStream visitJarFile(JarFile jf, ClassPath cp, String pathname) throws IOException { 426 String p = pathname.replace(File.separatorChar, '/'); 427 JarEntry e = jf.getJarEntry(p); 428 if (e != null) { 429 return jf.getInputStream(e); 430 } else { 431 return null; 432 } 433 } 434 }; 435 436 for (ClassPath cp : instance.entries) { 437 InputStream in = cp.accept(fv, pathname); 438 if (in != null) { 439 return in; 440 } 441 } 442 return null; 443 } 444 445 public ClassFileParser parserForClass(String classname) throws IOException { 446 String pathname = classname.replace('.', File.separatorChar) + ".class"; 447 ClassPath.Visitor<ClassFileParser, String> fv = new ClassPath.Visitor<ClassFileParser, String>() { 448 449 @Override 450 public ClassFileParser visitFile(File f, ClassPath cp, String pathname) throws IOException { 451 if (cp.getName().endsWith(File.separator + pathname)) { 452 return ClassFileParser.newParser(f, true); 453 } else { 454 return null; 455 } 456 } 457 458 @Override 459 public ClassFileParser visitDir(File dir, ClassPath cp, String pathname) throws IOException { 460 File f = new File(cp.getFile(), pathname); 461 if (f.exists()) { 462 return ClassFileParser.newParser(f, true); 463 } else { 464 return null; 465 } 466 } 467 468 @Override 469 public ClassFileParser visitJarFile(JarFile jf, ClassPath cp, String pathname) throws IOException { 470 String p = pathname.replace(File.separatorChar, '/'); 471 JarEntry e = jf.getJarEntry(p); 472 if (e != null) { 473 return ClassFileParser.newParser(jf.getInputStream(e), e.getSize(), true); 474 } else { 475 return null; 476 } 477 } 478 }; 479 480 for (ClassPath cp : entries) { 481 ClassFileParser cfparser = cp.accept(fv, pathname); 482 if (cfparser != null) { 483 return cfparser; 484 } 485 } 486 return null; 487 } 488 } |