docs: protected Set listMissingCoreFeatures(File outputDir) throws SelfExtractorFileException {
  
          Set missingFeatures = new HashSet();
  
          // If we have a Require Feature manifest header, we need to check that the runtime we're extracting into contains the
          // required features. If the customer has minified their runtime to a smaller set of features, then we may
          // not be able to install this extract into the runtime.
          // If we don't have a list of features then this is probably because we're not an extended install, and we don't need to check anymore.
          if (requiredFeatures != null && !"".equals(requiredFeatures)) {
  
              // Break the required feature headers value into indiviual strings and load them into a set.
              StringTokenizer tokenizer = new StringTokenizer(requiredFeatures, ",");
              while (tokenizer.hasMoreElements()) {
                  String nextFeature = tokenizer.nextToken();
                  if (nextFeature.indexOf(";") >= 0)
                      nextFeature = nextFeature.substring(0, nextFeature.indexOf(";"));
                  missingFeatures.add(nextFeature.trim());
              }
  
              // Create fileFilter to get just the manifest files.
              FilenameFilter manifestFilter = createManifestFilter();
  
              File featuresDir = new File(outputDir + "/lib/features");
              File[] manifestFiles = featuresDir.listFiles(manifestFilter);
              // Iterate over each manifest in the runtime we're extracting to, until we've read all manifests or until we've found all of the
              // required features.
              if (manifestFiles != null) {
                  for (int i = 0; i < manifestFiles.length && !missingFeatures.isEmpty(); i++) {
  
                      FileInputStream fis = null;
                      File currentManifestFile = null;
                      try {
                          currentManifestFile = manifestFiles[i];
                          fis = new FileInputStream(currentManifestFile);
                          Manifest currentManifest = new Manifest(fis);
  
                          Attributes attrs = currentManifest.getMainAttributes();
                          String manifestSymbolicName = attrs.getValue("Subsystem-SymbolicName");
                          if (manifestSymbolicName.indexOf(";") >= 0)
                              manifestSymbolicName = manifestSymbolicName.substring(0, manifestSymbolicName.indexOf(";"));
  
                          // Remove the current manifest from the list of required features. We may need to remove the short name depending on what has
                          // been stored.
                          missingFeatures.remove(manifestSymbolicName.trim());
                      } catch (FileNotFoundException fnfe) {
                          throw new SelfExtractorFileException(currentManifestFile.getAbsolutePath(), fnfe);
                      } catch (IOException ioe) {
                          throw new SelfExtractorFileException(currentManifestFile.getAbsolutePath(), ioe);
                      } finally {
                          SelfExtractUtils.tryToClose(fis);
                      }
                  }
              }
          }
          return missingFeatures;
      }