protected Listener getListener(Container.Entry entry) {
    URI key = entry.getUri();

    if (cache.containsKey(key)) {
      return cache.get(key);
    } else {
      Listener listener;

      try (InputStream inputStream = entry.getInputStream()) {
        ANTLRJavaParser.parse(new ANTLRInputStream(inputStream), listener = new Listener(entry));
      } catch (IOException ignore) {
        listener = null;
      }

      cache.put(key, listener);
      return listener;
    }
  }
  /** Index format : @see jd.gui.spi.Indexer */
  @SuppressWarnings("unchecked")
  public void index(API api, Container.Entry entry, Indexes indexes) {
    // Cleaning sets...
    typeDeclarationSet.clear();
    constructorDeclarationSet.clear();
    methodDeclarationSet.clear();
    fieldDeclarationSet.clear();
    typeReferenceSet.clear();
    constructorReferenceSet.clear();
    methodReferenceSet.clear();
    fieldReferenceSet.clear();
    stringSet.clear();
    superTypeNameSet.clear();
    descriptorSet.clear();

    try (InputStream inputStream = entry.getInputStream()) {
      // Index field, method, interfaces & super type
      ClassReader classReader = new ClassReader(inputStream);
      classReader.accept(
          classIndexer, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);

      // Index descriptors
      for (String descriptor : descriptorSet) {
        new SignatureReader(descriptor).accept(signatureIndexer);
      }

      // Index references
      char[] buffer = new char[classReader.getMaxStringLength()];

      for (int i = classReader.getItemCount() - 1; i > 0; i--) {
        int startIndex = classReader.getItem(i);

        if (startIndex != 0) {
          int tag = classReader.readByte(startIndex - 1);

          switch (tag) {
            case 7: // CONSTANT_Class
              String className = classReader.readUTF8(startIndex, buffer);
              if (className.startsWith("[")) {
                new SignatureReader(className).acceptType(signatureIndexer);
              } else {
                typeReferenceSet.add(className);
              }
              break;
            case 8: // CONSTANT_String
              String str = classReader.readUTF8(startIndex, buffer);
              stringSet.add(str);
              break;
            case 9: // CONSTANT_Fieldref
              int nameAndTypeItem = classReader.readUnsignedShort(startIndex + 2);
              int nameAndTypeIndex = classReader.getItem(nameAndTypeItem);
              tag = classReader.readByte(nameAndTypeIndex - 1);
              if (tag == 12) { // CONSTANT_NameAndType
                String fieldName = classReader.readUTF8(nameAndTypeIndex, buffer);
                fieldReferenceSet.add(fieldName);
              }
              break;
            case 10: // CONSTANT_Methodref:
            case 11: // CONSTANT_InterfaceMethodref:
              nameAndTypeItem = classReader.readUnsignedShort(startIndex + 2);
              nameAndTypeIndex = classReader.getItem(nameAndTypeItem);
              tag = classReader.readByte(nameAndTypeIndex - 1);
              if (tag == 12) { // CONSTANT_NameAndType
                String methodName = classReader.readUTF8(nameAndTypeIndex, buffer);
                if ("<init>".equals(methodName)) {
                  int classItem = classReader.readUnsignedShort(startIndex);
                  int classIndex = classReader.getItem(classItem);
                  className = classReader.readUTF8(classIndex, buffer);
                  constructorReferenceSet.add(className);
                } else {
                  methodReferenceSet.add(methodName);
                }
              }
              break;
          }
        }
      }

      String typeName = classIndexer.name;

      // Append sets to indexes
      addToIndex(indexes, "typeDeclarations", typeDeclarationSet, entry);
      addToIndex(indexes, "constructorDeclarations", constructorDeclarationSet, entry);
      addToIndex(indexes, "methodDeclarations", methodDeclarationSet, entry);
      addToIndex(indexes, "fieldDeclarations", fieldDeclarationSet, entry);
      addToIndex(indexes, "typeReferences", typeReferenceSet, entry);
      addToIndex(indexes, "constructorReferences", constructorReferenceSet, entry);
      addToIndex(indexes, "methodReferences", methodReferenceSet, entry);
      addToIndex(indexes, "fieldReferences", fieldReferenceSet, entry);
      addToIndex(indexes, "strings", stringSet, entry);

      // Populate map [super type name : [sub type name]]
      if (superTypeNameSet.size() > 0) {
        Map<String, Collection> index = indexes.getIndex("subTypeNames");

        for (String superTypeName : superTypeNameSet) {
          index.get(superTypeName).add(typeName);
        }
      }

    } catch (Exception ignore) {
    }
  }