content: package org.netbeans.modules.python.debugger.attach;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.util.Collections;
  import java.util.List;
  import java.util.Optional;
  import java.util.function.Function;
  import java.util.stream.Collectors;
  import java.util.stream.Stream;
  import org.openide.util.Exceptions;
  import org.openide.util.Utilities;
  
  /**
   * Retrieve a list of native processes. Duplicates
   * org.netbeans.modules.java.lsp.server.debugging.attach.Processes Replace with
   * the direct use of ProcessHandle, after JDK 8 is abandoned.
   *
   * @author albilu
   */
  final class Processes {
  
      private static final String CMD_UNIX = "ps -x -o pid,command";
      private static final String CMD_WIN = "tasklist /FO CSV";
  
      static List<ProcessInfo> getAllProcesses() {
          List<ProcessInfo> processInfos;
          Function<Stream<ProcessInfo>, List<ProcessInfo>> collector
                  = infoStream -> infoStream
                          .filter(info -> info != null)
                          .sorted((info1, info2) -> (info1.getPid() == info2.getPid() ? 0
                          : info1.getPid() > info2.getPid() ? -1 : 1)) // descending
                          .collect(Collectors.toList());
          try {
              Class<?> processHandleClass = Class.forName("java.lang.ProcessHandle");
              processInfos = getProcesses(processHandleClass, collector);
          } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException ex) {
              if (Utilities.isWindows()) {
                  processInfos = getProcessesWinCmd(collector);
              } else {
                  processInfos = getProcessesUNIXCmd(collector);
              }
          }
          return processInfos;
      }
  
      private static List<ProcessInfo> getProcesses(Class<?> processHandleClass,
              Function<Stream<ProcessInfo>, List<ProcessInfo>> collector) throws NoSuchMethodException,
              IllegalAccessException {
          Method allProcessesMethod = processHandleClass.getMethod("allProcesses");
          try {
              @SuppressWarnings("unchecked")
              Stream<Object> allProcesses = (Stream<Object>) allProcessesMethod.invoke(null);
              return collector.apply(allProcesses
                      .map(new ProcessHandle2Info(processHandleClass)));
          } catch (IllegalArgumentException | InvocationTargetException ex) {
              Exceptions.printStackTrace(ex);
              return Collections.emptyList();
          }
      }
  
      private static class ProcessHandle2Info implements Function<Object, ProcessInfo> {
  
          private final Method pidMethod;
          private final Method infoMethod;
          private final Method commandMethod;
          private final Method commandLineMethod;
  
          ProcessHandle2Info(Class<?> processHandleClass) throws NoSuchMethodException {
              pidMethod = processHandleClass.getMethod("pid");
              infoMethod = processHandleClass.getMethod("info");
              commandMethod = infoMethod.getReturnType().getMethod("command");
              commandLineMethod = infoMethod.getReturnType().getMethod("commandLine");
          }
  
          @Override
          @SuppressWarnings("unchecked")
          public ProcessInfo apply(Object processHandle) {
              try {
                  long pid = (Long) pidMethod.invoke(processHandle);
                  Object info = infoMethod.invoke(processHandle);
                  Optional<String> command = (Optional<String>) commandMethod.invoke(info);
                  Optional<String> commandLine = (Optional<String>) commandLineMethod.invoke(info);
                  if (command.isPresent() && commandLine.isPresent()) {
                      return new ProcessInfo(pid, command.get(), commandLine.get());
                  }
              } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
                  Exceptions.printStackTrace(ex);
              }
              return null;
          }
      }
  
      private static List<ProcessInfo> getProcessesUNIXCmd(Function<Stream<ProcessInfo>, List<ProcessInfo>> collector) {
          return getProcesses(CMD_UNIX, line -> {
              int i2 = line.indexOf(' ');
              if (i2 < 0) {
                  return null;
              }
              String pidStr = line.substring(0, i2);
              long pid;
              try {
                  pid = Long.parseLong(pidStr);
              } catch (NumberFormatException ex) {
                  return null;
              }
              int i1 = i2 + 1;
              String commandLine = line.substring(i1).trim();
              if (CMD_UNIX.equals(commandLine)) {
                  return null;
              }
              return new ProcessInfo(pid, null, commandLine);
          }, collector);
      }
  
      private static List<ProcessInfo> getProcessesWinCmd(Function<Stream<ProcessInfo>, List<ProcessInfo>> collector) {
          return getProcesses(CMD_WIN, line -> {
              if (!line.startsWith("")) {
                  return null;
              }
              int i1 = 1;
              int i2 = line.indexOf('"', i1);
              if (i2 < 0) {
                  return null;
              }
              String execName = line.substring(i1, i2);
              i1 = line.indexOf('"', i2 + 1);
              if (i1 < 0) {
                  return null;
              }
              i1++;
              i2 = line.indexOf('"', i1);
              if (i2 < 0) {
                  return null;
              }
              String pidStr = line.substring(i1, i2);
              long pid;
              try {
                  pid = Long.parseLong(pidStr);
              } catch (NumberFormatException ex) {
                  return null;
              }
              return new ProcessInfo(pid, execName, execName);
          }, collector);
      }
  
      private static List<ProcessInfo> getProcesses(String cmd,
              Function<? super String, ? extends ProcessInfo> pmap,
              Function<Stream<ProcessInfo>, List<ProcessInfo>> collector) {
          try {
              Process p = Runtime.getRuntime().exec(cmd);
              try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
                  return collector.apply(reader
                          .lines()
                          .map(line -> line.trim())
                          .map(pmap));
              }
          } catch (IOException ex) {
              return Collections.emptyList();
          }
      }
  
      static final class ProcessInfo {
  
          private final long pid;
          private final String executable;
          private final String command;
  
          ProcessInfo(long id, String executable, String command) {
              assert command != null;
              this.pid = id;
              this.executable = executable;
              this.command = command;
          }
  
          /**
           * Get the ID of the process.
           */
          long getPid() {
              return pid;
          }
  
          /**
           * Get the executable of the process.
           */
          String getExecutable() {
              if (executable != null) {
                  return executable;
              }
              File exeFile = new File("/proc/" + pid + "/exe");
              if (exeFile.exists()) {
                  try {
                      return exeFile.getCanonicalPath();
                  } catch (IOException ex) {
                  }
              }
              int i = command.indexOf(' ');
              return i > 0 ? command.substring(0, i) : command;
          }
  
          /**
           * Get the command line of the process.
           */
          String getCommand() {
              return command;
          }
  
      }
  }
  