// Path: src/main/java/ys/gclog/parser/extract/ExtractContext.java
// public class ExtractContext {
// 
//   private final List<LogExtractor> extractors = new ArrayList<>();
//   private final List<LogExtractor> availableExtractors;
// 
//   public ExtractContext(List<LogExtractor> availableExtractors) {
//     this.availableExtractors = availableExtractors;
//   }
// 
//   public List<LogExtractor> getExtractors() {
//     return extractors;
//   }
// 
//   public List<LogExtractor> getAvailableExtractors() {
//     return availableExtractors;
//   }
// }
// 
// Path: src/main/java/ys/gclog/parser/extract/Extractors.java
// public final class Extractors {
// 
//   private static final List<LogExtractor> PRIMARY_EXTRACTORS =
//       Collections.unmodifiableList(Arrays.asList(
//           new SimpleMinorCollectionExtractor(),
//           new SimpleFullCollectionExtractor(),
//           new DetailedMinorCollectionExtractor(),
//           new DetailedFullCollectionExtractor(),
//           new CmsCollectorExtractor(),
//           new G1CollectorExtractor()));
// 
//   private static final List<LogExtractor> SECONDARY_EXTRACTORS =
//       Collections.unmodifiableList(Arrays.asList(
//           new SerialYoungCollectorExtractor(),
//           new ParallelYoungCollectorExtractor(),
//           new ParallelNewYoungCollectorExtractor(),
//           new SerialOldCollectorExtractor(),
//           new ParallelOldCollectorExtractor(),
//           new PermGenCollectorExtractor(),
//           new MetaspaceCollectorExtractor()));
// 
//   private Extractors() {}
// 
//   public static List<LogExtractor> primaryExtractors() {
//     return PRIMARY_EXTRACTORS;
//   }
// 
//   public static List<LogExtractor> secondaryExtractors() {
//     return SECONDARY_EXTRACTORS;
//   }
// }
// 
// Path: src/main/java/ys/gclog/parser/extract/LogExtractor.java
// public interface LogExtractor {
// 
//   String REGEX_DECIMAL = "(\\d+\\.\\d+)";
//   String REGEX_TIME = REGEX_DECIMAL;
//   /**
//    * Regular expression for the date field, e.g. {@code 2015-01-15T12:30:38.059+0300}.
//    */
//   String REGEX_DATE = "(\\d+-\\d+-\\d+T\\d+:\\d+:\\d+\\.\\d+\\+\\d+)";
//   String REGEX_MEMORY_AT = "(\\d+)K\\((\\d+)K\\)";
//   String REGEX_MEMORY_FROM_TO_KB = "(\\d+)K->(\\d+)K\\((\\d+)K\\)";
//   String REGEX_MEMORY_FROM_TO_MB = "(\\d+)M->(\\d+)M\\((\\d+)M\\)";
//   String REGEX_MEMORY_WITH_TOTAL_FROM_TO = String.format(
//       "%s[BKM]\\(%s[BKM]\\)->%s[BKM]\\(%s[BKM]\\)",
//       REGEX_DECIMAL, REGEX_DECIMAL, REGEX_DECIMAL, REGEX_DECIMAL);
//   String REGEX_MEMORY_WITHOUT_TOTAL_FROM_TO =
//       String.format("%s[BKM]->%s[BKM]", REGEX_DECIMAL, REGEX_DECIMAL);
//   String REGEX_TIMES = String.format("\\[Times: user=%s sys=%s, real=%s secs]",
//       REGEX_DECIMAL, REGEX_DECIMAL, REGEX_DECIMAL);
// 
//   boolean accept(String log);
// 
//   Optional<Map<String, String>> extract(String log, ExtractContext context);
// 
//   static void putIfNotNull(Map<String, String> map, String key, String value) {
//     if (value != null) {
//       map.put(key, value);
//     }
//   }
// }


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ys.gclog.parser.extract.ExtractContext;
import ys.gclog.parser.extract.Extractors;
import ys.gclog.parser.extract.LogExtractor;
      String startLine = reader.readLine();
      if (startLine == null) {
        return null;
      }
      buffer.add(startLine);
    }

    String nextLine = reader.readLine();
    while (nextLine != null && PATTERN_IDENT.matcher(nextLine).find()) {
      buffer.add(nextLine);
      nextLine = reader.readLine();
    }

    String result = buffer.stream().collect(Collectors.joining("\n"));
    buffer.clear();
    if (nextLine != null) {
      buffer.add(nextLine);
    }
    return result;
  }

  private static Optional<Map<String, String>> extract(
      String line, List<LogExtractor> extractors, Map<LogExtractor, ExtractContext> contexts) {
    for (LogExtractor extractor : extractors) {
      Optional<Map<String, String>> option = extractor.extract(line, contexts.get(extractor));
      if (option.isPresent()) {
        return option;
      }
    }

// Path: src/main/java/ys/gclog/parser/extract/ExtractContext.java
// public class ExtractContext {
// 
//   private final List<LogExtractor> extractors = new ArrayList<>();
//   private final List<LogExtractor> availableExtractors;
// 
//   public ExtractContext(List<LogExtractor> availableExtractors) {
//     this.availableExtractors = availableExtractors;
//   }
// 
//   public List<LogExtractor> getExtractors() {
//     return extractors;
//   }
// 
//   public List<LogExtractor> getAvailableExtractors() {
//     return availableExtractors;
//   }
// }
// 
// Path: src/main/java/ys/gclog/parser/extract/Extractors.java
// public final class Extractors {
// 
//   private static final List<LogExtractor> PRIMARY_EXTRACTORS =
//       Collections.unmodifiableList(Arrays.asList(
//           new SimpleMinorCollectionExtractor(),
//           new SimpleFullCollectionExtractor(),
//           new DetailedMinorCollectionExtractor(),
//           new DetailedFullCollectionExtractor(),
//           new CmsCollectorExtractor(),
//           new G1CollectorExtractor()));
// 
//   private static final List<LogExtractor> SECONDARY_EXTRACTORS =
//       Collections.unmodifiableList(Arrays.asList(
//           new SerialYoungCollectorExtractor(),
//           new ParallelYoungCollectorExtractor(),
//           new ParallelNewYoungCollectorExtractor(),
//           new SerialOldCollectorExtractor(),
//           new ParallelOldCollectorExtractor(),
//           new PermGenCollectorExtractor(),
//           new MetaspaceCollectorExtractor()));
// 
//   private Extractors() {}
// 
//   public static List<LogExtractor> primaryExtractors() {
//     return PRIMARY_EXTRACTORS;
//   }
// 
//   public static List<LogExtractor> secondaryExtractors() {
//     return SECONDARY_EXTRACTORS;
//   }
// }
// 
// Path: src/main/java/ys/gclog/parser/extract/LogExtractor.java
// public interface LogExtractor {
// 
//   String REGEX_DECIMAL = "(\\d+\\.\\d+)";
//   String REGEX_TIME = REGEX_DECIMAL;
//   /**
//    * Regular expression for the date field, e.g. {@code 2015-01-15T12:30:38.059+0300}.
//    */
//   String REGEX_DATE = "(\\d+-\\d+-\\d+T\\d+:\\d+:\\d+\\.\\d+\\+\\d+)";
//   String REGEX_MEMORY_AT = "(\\d+)K\\((\\d+)K\\)";
//   String REGEX_MEMORY_FROM_TO_KB = "(\\d+)K->(\\d+)K\\((\\d+)K\\)";
//   String REGEX_MEMORY_FROM_TO_MB = "(\\d+)M->(\\d+)M\\((\\d+)M\\)";
//   String REGEX_MEMORY_WITH_TOTAL_FROM_TO = String.format(
//       "%s[BKM]\\(%s[BKM]\\)->%s[BKM]\\(%s[BKM]\\)",
//       REGEX_DECIMAL, REGEX_DECIMAL, REGEX_DECIMAL, REGEX_DECIMAL);
//   String REGEX_MEMORY_WITHOUT_TOTAL_FROM_TO =
//       String.format("%s[BKM]->%s[BKM]", REGEX_DECIMAL, REGEX_DECIMAL);
//   String REGEX_TIMES = String.format("\\[Times: user=%s sys=%s, real=%s secs]",
//       REGEX_DECIMAL, REGEX_DECIMAL, REGEX_DECIMAL);
// 
//   boolean accept(String log);
// 
//   Optional<Map<String, String>> extract(String log, ExtractContext context);
// 
//   static void putIfNotNull(Map<String, String> map, String key, String value) {
//     if (value != null) {
//       map.put(key, value);
//     }
//   }
// }


// Path: src/main/java/ys/gclog/parser/GCLogParser.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import ys.gclog.parser.extract.ExtractContext;
import ys.gclog.parser.extract.Extractors;
import ys.gclog.parser.extract.LogExtractor;

      String startLine = reader.readLine();
      if (startLine == null) {
        return null;
      }
      buffer.add(startLine);
    }

    String nextLine = reader.readLine();
    while (nextLine != null && PATTERN_IDENT.matcher(nextLine).find()) {
      buffer.add(nextLine);
      nextLine = reader.readLine();
    }

    String result = buffer.stream().collect(Collectors.joining("\n"));
    buffer.clear();
    if (nextLine != null) {
      buffer.add(nextLine);
    }
    return result;
  }

  private static Optional<Map<String, String>> extract(
      String line, List<LogExtractor> extractors, Map<LogExtractor, ExtractContext> contexts) {
    for (LogExtractor extractor : extractors) {
      Optional<Map<String, String>> option = extractor.extract(line, contexts.get(extractor));
      if (option.isPresent()) {
        return option;
      }
    }

    ExtractContext context = new ExtractContext(Extractors.secondaryExtractors());