public Set<String> getOperatorClasses(String parent, String searchTerm)
      throws ClassNotFoundException {
    if (CollectionUtils.isEmpty(operatorClassNames)) {
      loadOperatorClass();
    }
    if (parent == null) {
      parent = Operator.class.getName();
    } else {
      if (!typeGraph.isAncestor(Operator.class.getName(), parent)) {
        throw new IllegalArgumentException("Argument must be a subclass of Operator class");
      }
    }

    Set<String> filteredClass =
        Sets.filter(
            operatorClassNames,
            new Predicate<String>() {
              @Override
              public boolean apply(String className) {
                OperatorClassInfo oci = classInfo.get(className);
                return oci == null || !oci.tags.containsKey("@omitFromUI");
              }
            });

    if (searchTerm == null && parent.equals(Operator.class.getName())) {
      return filteredClass;
    }

    if (searchTerm != null) {
      searchTerm = searchTerm.toLowerCase();
    }

    Set<String> result = new HashSet<String>();
    for (String clazz : filteredClass) {
      if (parent.equals(Operator.class.getName()) || typeGraph.isAncestor(parent, clazz)) {
        if (searchTerm == null) {
          result.add(clazz);
        } else {
          if (clazz.toLowerCase().contains(searchTerm)) {
            result.add(clazz);
          } else {
            OperatorClassInfo oci = classInfo.get(clazz);
            if (oci != null) {
              if (oci.comment != null && oci.comment.toLowerCase().contains(searchTerm)) {
                result.add(clazz);
              } else {
                for (Map.Entry<String, String> entry : oci.tags.entrySet()) {
                  if (entry.getValue().toLowerCase().contains(searchTerm)) {
                    result.add(clazz);
                    break;
                  }
                }
              }
            }
          }
        }
      }
    }
    return result;
  }