/**
   * Method used to recursively scan our package map for classes whose names start with a given
   * prefix, ignoring case.
   *
   * @param prefix The prefix that the unqualified class names must match (ignoring case).
   * @param currentPkg The package that <code>map</code> belongs to (i.e. all levels of packages
   *     scanned before this one), separated by '<code>/</code>'.
   * @param addTo The list to add any matching <code>ClassFile</code>s to.
   */
  void getClassesWithNamesStartingWith(
      LibraryInfo info, String prefix, String currentPkg, List<ClassFile> addTo) {

    final int prefixLen = prefix.length();

    for (Map.Entry<String, PackageMapNode> children : subpackages.entrySet()) {
      String key = children.getKey();
      PackageMapNode child = children.getValue();
      child.getClassesWithNamesStartingWith(info, prefix, currentPkg + key + "/", addTo);
    }

    for (Map.Entry<String, ClassFile> cfEntry : classFiles.entrySet()) {
      // If value is null, we only lazily create the ClassFile if
      // necessary (i.e. if the class name does match what they've
      // typed).
      String className = cfEntry.getKey();
      if (className.regionMatches(true, 0, prefix, 0, prefixLen)) {
        ClassFile cf = cfEntry.getValue();
        if (cf == null) {
          String fqClassName = currentPkg + className + ".class";
          try {
            cf = info.createClassFile(fqClassName);
            cfEntry.setValue(cf); // Update the map
          } catch (IOException ioe) {
            ioe.printStackTrace();
          }
        }
        if (cf != null) { // possibly null if IOException above
          addTo.add(cf);
        }
      }
    }
  }
  public ClassFile getClassEntry(LibraryInfo info, String[] items) {

    PackageMapNode pmn = this;
    for (int i = 0; i < items.length - 1; i++) {
      pmn = pmn.subpackages.get(items[i]);
      if (pmn == null) {
        return null;
      }
    }

    String className = items[items.length - 1];
    if (pmn.classFiles.containsKey(className)) {
      ClassFile value = pmn.classFiles.get(className);
      if (value != null) { // Already created
        return value;
      } else { // A class, just no ClassFile cached yet
        try {
          StringBuilder name = new StringBuilder(items[0]);
          for (int i = 1; i < items.length; i++) {
            name.append('/').append(items[i]);
          }
          name.append(".class");
          ClassFile cf = info.createClassFile(name.toString());
          pmn.classFiles.put(className, cf);
          return cf;
        } catch (IOException ioe) {
          ioe.printStackTrace();
        }
      }
    }

    return null;
  }
  public void getClassesInPackage(
      LibraryInfo info, List<ClassFile> addTo, String[] pkgs, boolean inPkg) {

    PackageMapNode map = this;

    for (int i = 0; i < pkgs.length; i++) {
      map = map.subpackages.get(pkgs[i]);
      if (map == null) {
        return;
      }
    }

    try {

      info.bulkClassFileCreationStart();
      try {

        for (Map.Entry<String, ClassFile> entry : map.classFiles.entrySet()) {

          ClassFile cf = entry.getValue();
          if (cf == null) {
            StringBuilder name = new StringBuilder(pkgs[0]);
            for (int j = 1; j < pkgs.length; j++) {
              name.append('/').append(pkgs[j]);
            }
            name.append('/');
            name.append(entry.getKey()).append(".class");
            cf = info.createClassFileBulk(name.toString());
            map.classFiles.put(entry.getKey(), cf);
            possiblyAddTo(addTo, cf, inPkg);
          } else {
            possiblyAddTo(addTo, cf, inPkg);
          }
        }

      } finally {
        info.bulkClassFileCreationEnd();
      }

    } catch (IOException ioe) {
      ioe.printStackTrace();
    }
  }