text:
package java.time.zone;

import static java.time.calendrical.ChronoField.HOUR_OF_DAY;
import static java.time.calendrical.ChronoField.MINUTE_OF_HOUR;
import static java.time.calendrical.ChronoField.SECOND_OF_MINUTE;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.text.ParsePosition;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.Year;
import java.time.ZoneOffset;
import java.time.calendrical.DateTimeAdjusters;
import java.time.calendrical.DateTimeBuilder;
import java.time.calendrical.DateTimeField;
import java.time.calendrical.JulianDayField;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.zone.ZoneOffsetTransitionRule.TimeDefinition;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;

/**
 * A builder that can read the TZDB time-zone files and build {@code ZoneRules} instances.
 *
 *
Implementation notes
 This class is a mutable builder. A new instance must be created for each
 * compile.
 */
final class TzdbZoneRulesCompiler {

  /**
   * Time parser.
   */
  private static final DateTimeFormatter TIME_PARSER;
  static {
    TIME_PARSER = new DateTimeFormatterBuilder().appendValue(HOUR_OF_DAY).optionalStart().appendLiteral(':')
        .appendValue(MINUTE_OF_HOUR, 2).optionalStart().appendLiteral(':').appendValue(SECOND_OF_MINUTE, 2)
        .toFormatter();
  }

  /**
   * Constant for MJD 1972-01-01.
   */
  private static final long MJD_1972_01_01 = 41317L;

  /**
   * Reads a set of TZDB files and builds a single combined data file.
   *
   * @param args the arguments
   */
  public static void main(String[] args) {

    if (args.length < 2) {
      outputHelp();
      return;
    }

    // parse args
    String version = null;
    File baseSrcDir = null;
    File dstDir = null;
    boolean verbose = false;

    // parse options
    int i;
    for (i = 0; i < args.length; i++) {
      String arg = args[i];
      if (arg.startsWith("-") == false) {
        break;
      }
      if ("-srcdir".equals(arg)) {
        if (baseSrcDir == null && ++i < args.length) {
          baseSrcDir = new File(args[i]);
          continue;
        }
      } else if ("-dstdir".equals(arg)) {
        if (dstDir == null && ++i < args.length) {
          dstDir = new File(args[i]);
          continue;
        }
      } else if ("-version".equals(arg)) {
        if (version == null && ++i < args.length) {
          version = args[i];
          continue;
        }
      } else if ("-verbose".equals(arg)) {
        if (verbose == false) {
          verbose = true;
          continue;
        }
      } else if ("-help".equals(arg) == false) {
        System.out.println("Unrecognised option: " + arg);
      }
      outputHelp();
      return;
    }

    // check source directory
    if (baseSrcDir == null) {
      System.out.println("Source directory must be specified using -srcdir: " + baseSrcDir);
      return;
    }
    if (baseSrcDir.isDirectory() == false) {
      System.out.println("Source does not exist or is not a directory: " + baseSrcDir);
      return;
    }
    dstDir = (dstDir != null ? dstDir : baseSrcDir);

    // parse source file names
    List<String> srcFileNames = Arrays.asList(Arrays.copyOfRange(args, i, args.length));
    if (srcFileNames.isEmpty()) {
      System.out.println("Source filenames not specified, using default set");
      System.out.println("(africa antarctica asia australasia backward etcetera europe northamerica southamerica)");
      srcFileNames = Arrays.asList("africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe",
          "northamerica", "southamerica");
    }

    // find source directories to process
    List<File> srcDirs = new ArrayList<File>();
    if (version != null) {
      File srcDir = new File(baseSrcDir, version);
      if (srcDir.isDirectory() == false) {
        System.out.println("Version does not represent a valid source directory : " + srcDir);
        return;
      }
      srcDirs.add(srcDir);
    } else {
      File[] dirs = baseSrcDir.listFiles();
      for (File dir : dirs) {
        if (dir.isDirectory() && dir.getName().matches("[12][0-9][0-9][0-9][A-Za-z0-9._-]+")) {
          srcDirs.add(dir);
        }
      }
    }
    if (srcDirs.isEmpty()) {
      System.out.println("Source directory contains no valid source folders: " + baseSrcDir);
      return;
    }

    // check destination directory
    if (dstDir.exists() == false && dstDir.mkdirs() == false) {
      System.out.println("Destination directory could not be created: " + dstDir);
      return;
    }
    if (dstDir.isDirectory() == false) {
      System.out.println("Destination is not a directory: " + dstDir);
      return;
    }
    process(srcDirs, srcFileNames, dstDir, verbose);
    System.exit(0);
  }

  /**
   * Output usage text for the command line.
   */
  private static void outputHelp() {

    System.out.println("Usage: TzdbZoneRulesCompiler <options> <tzdb source filenames>");
    System.out.println("where options include:");
    System.out.println("   -srcdir <directory>   Where to find source directories (required)");
    System.out.println("   -dstdir <directory>   Where to output generated files (default srcdir)");
    System.out.println("   -version <version>    Specify the version, such as 2009a (optional)");
    System.out.println("   -help                 Print this usage message");
    System.out.println("   -verbose              Output verbose information during compilation");
    System.out.println(" There must be one directory for each version in srcdir");
    System.out.println(" Each directory must have the name of the version, such as 2009a");
    System.out.println(" Each directory must contain the unpacked tzdb files, such as asia or europe");
    System.out.println(" Directories must match the regex [12][0-9][0-9][0-9][A-Za-z0-9._-]+");
    System.out.println(" There will be one jar file for each version and one combined jar in dstdir");
    System.out.println(" If the version is specified, only that version is processed");
  }

  /**
   * Process to create the jar files.
   */
  private static void process(List<File> srcDirs, List<String> srcFileNames, File dstDir, boolean verbose) {

    // build actual jar files
    Map<Object, Object> deduplicateMap = new HashMap<Object, Object>();
    Map<String, SortedMap<String, ZoneRules>> allBuiltZones = new TreeMap<String, SortedMap<String, ZoneRules>>();
    Set<String> allRegionIds = new TreeSet<String>();
    Set<ZoneRules> allRules = new HashSet<ZoneRules>();
    SortedMap<LocalDate, Byte> bestLeapSeconds = null;

    for (File srcDir : srcDirs) {
      // source files in this directory
      List<File> srcFiles = new ArrayList<File>();
      for (String srcFileName : srcFileNames) {
        File file = new File(srcDir, srcFileName);
        if (file.exists()) {
          srcFiles.add(file);
        }
      }
      if (srcFiles.isEmpty()) {
        continue; // nothing to process
      }
      File leapSecondsFile = new File(srcDir, "leapseconds");
      if (!leapSecondsFile.exists()) {
        System.out.println("Version " + srcDir.getName() + " does not include leap seconds information.");
        leapSecondsFile = null;
      }

      // compile
      String loopVersion = srcDir.getName();
      TzdbZoneRulesCompiler compiler = new TzdbZoneRulesCompiler(loopVersion, srcFiles, leapSecondsFile, verbose);
      compiler.setDeduplicateMap(deduplicateMap);
      try {
        // compile
        compiler.compile();
        SortedMap<String, ZoneRules> builtZones = compiler.getZones();
        SortedMap<LocalDate, Byte> parsedLeapSeconds = compiler.getLeapSeconds();

        // output version-specific file
        File dstFile = new File(dstDir, "jsr-310-TZDB-" + loopVersion + ".jar");
        if (verbose) {
          System.out.println("Outputting file: " + dstFile);
        }
        outputFile(dstFile, loopVersion, builtZones, parsedLeapSeconds);

        // create totals
        allBuiltZones.put(loopVersion, builtZones);
        allRegionIds.addAll(builtZones.keySet());
        allRules.addAll(builtZones.values());

        // track best possible leap seconds collection
        if (compiler.getMostRecentLeapSecond() != null) {
          // we've got a live one!
          if (bestLeapSeconds == null || compiler.getMostRecentLeapSecond().compareTo(bestLeapSeconds.lastKey()) > 0) {
            // found the first one, or found a better one
            bestLeapSeconds = parsedLeapSeconds;
          }
        }
      } catch (Exception ex) {
        System.out.println("Failed: " + ex.toString());
        ex.printStackTrace();
        System.exit(1);
      }
    }

    // output merged file
    File dstFile = new File(dstDir, "jsr-310-TZDB-all.jar");
    if (verbose) {
      System.out.println("Outputting combined file: " + dstFile);
    }
    outputFile(dstFile, allBuiltZones, allRegionIds, allRules, bestLeapSeconds);
  }

  /**
   * Outputs the file.
   */
  private static void outputFile(File dstFile, String version, SortedMap<String, ZoneRules> builtZones,
      SortedMap<LocalDate, Byte> leapSeconds) {

    Map<String, SortedMap<String, ZoneRules>> loopAllBuiltZones = new TreeMap<String, SortedMap<String, ZoneRules>>();
    loopAllBuiltZones.put(version, builtZones);
    Set<String> loopAllRegionIds = new TreeSet<String>(builtZones.keySet());
    Set<ZoneRules> loopAllRules = new HashSet<ZoneRules>(builtZones.values());
    outputFile(dstFile, loopAllBuiltZones, loopAllRegionIds, loopAllRules, leapSeconds);
  }

  /**
   * Outputs the file.
   */
  private static void outputFile(File dstFile, Map<String, SortedMap<String, ZoneRules>> allBuiltZones,
      Set<String> allRegionIds, Set<ZoneRules> allRules, SortedMap<LocalDate, Byte> leapSeconds) {

    // try (JarOutputStream jos = new JarOutputStream(new FileOutputStream(dstFile))) {
    JarOutputStream jos = null;
    try {
      jos = new JarOutputStream(new FileOutputStream(dstFile));
      outputTZEntry(jos, allBuiltZones, allRegionIds, allRules);
      outputLeapSecondEntry(jos, leapSeconds);
    } catch (Exception ex) {
      System.out.println("Failed: " + ex.toString());
      ex.printStackTrace();
      System.exit(1);
    } finally {
      if (jos != null) {
        try {
          jos.close();
        } catch (IOException e) {
          // ignore to prevent supression of potential important exception
        }
      }
    }
  }

  /**
   * Outputs the timezone entry in the JAR file.
   */
  private static void outputTZEntry(JarOutputStream jos, Map<String, SortedMap<String, ZoneRules>> allBuiltZones,
      Set<String> allRegionIds, Set<ZoneRules> allRules) {

    // this format is not publicly specified
    try {
      jos.putNextEntry(new ZipEntry("javax/time/zone/TZDB.dat"));
      DataOutputStream out = new DataOutputStream(jos);

      // file version
      out.writeByte(1);
      // group
      out.writeUTF("TZDB");
      // versions
      String[] versionArray = allBuiltZones.keySet().toArray(new String[allBuiltZones.size()]);
      out.writeShort(versionArray.length);
      for (String version : versionArray) {
        out.writeUTF(version);
      }
      // regions
      String[] regionArray = allRegionIds.toArray(new String[allRegionIds.size()]);
      out.writeShort(regionArray.length);
      for (String regionId : regionArray) {
        out.writeUTF(regionId);
      }
      // rules
      List<ZoneRules> rulesList = new ArrayList<ZoneRules>(allRules);
      out.writeShort(rulesList.size());
      ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
      for (ZoneRules rules : rulesList) {
        baos.reset();
        DataOutputStream dataos = new DataOutputStream(baos);
        Ser.write(rules, dataos);
        dataos.close();
        byte[] bytes = baos.toByteArray();
        out.writeShort(bytes.length);
        out.write(bytes);
      }
      // link version-region-rules
      for (String version : allBuiltZones.keySet()) {
        out.writeShort(allBuiltZones.get(version).size());
        for (Map.Entry<String, ZoneRules> entry : allBuiltZones.get(version).entrySet()) {
          int regionIndex = Arrays.binarySearch(regionArray, entry.getKey());
          int rulesIndex = rulesList.indexOf(entry.getValue());
          out.writeShort(regionIndex);
          out.writeShort(rulesIndex);
        }
      }

      out.flush();
      jos.closeEntry();
    } catch (Exception ex) {
      System.out.println("Failed: " + ex.toString());
      ex.printStackTrace();
      System.exit(1);
    }
  }

  /**
   * Outputs the leap second entries in the JAR file.
   */
  private static void outputLeapSecondEntry(JarOutputStream jos, SortedMap<LocalDate, Byte> leapSeconds) {

    // this format is not publicly specified
    try {
      jos.putNextEntry(new ZipEntry("javax/time/LeapSecondRules.dat"));
      DataOutputStream out = new DataOutputStream(jos);

      // file version
      out.writeByte(1);
      // count
      out.writeInt(leapSeconds.size() + 1);

      // first line is fixed in UTC-TAI leap second system, always 10 seconds at 1972-01-01
      int offset = 10;
      out.writeLong(MJD_1972_01_01);
      out.writeInt(offset);

      // now treat all the transitions
      for (Map.Entry<LocalDate, Byte> rule : leapSeconds.entrySet()) {
        out.writeLong(JulianDayField.MODIFIED_JULIAN_DAY.doGet(rule.getKey()));
        offset += rule.getValue();
        out.writeInt(offset);
      }
      out.flush();
      jos.closeEntry();
    } catch (Exception ex) {
      System.out.println("Failed: " + ex.toString());
      ex.printStackTrace();
      System.exit(1);
    }
  }

  // -----------------------------------------------------------------------
  /** The TZDB rules. */
  private final Map<String, List<TZDBRule>> rules = new HashMap<String, List<TZDBRule>>();

  /** The TZDB zones. */
  private final Map<String, List<TZDBZone>> zones = new HashMap<String, List<TZDBZone>>();

  /** The TZDB links. */
  private final Map<String, String> links = new HashMap<String, String>();

  /** The built zones. */
  private final SortedMap<String, ZoneRules> builtZones = new TreeMap<String, ZoneRules>();

  /** A map to deduplicate object instances. */
  private Map<Object, Object> deduplicateMap = new HashMap<Object, Object>();

  /** Sorted collection of LeapSecondRules. */
  private final SortedMap<LocalDate, Byte> leapSeconds = new TreeMap<LocalDate, Byte>();

  /** The version to produce. */
  private final String version;

  /** The source files. */
  private final List<File> sourceFiles;

  /** The leap seconds file. */
  private final File leapSecondsFile;

  /** The version to produce. */
  private final boolean verbose;

  /**
   * Creates an instance if you want to invoke the compiler manually.
   *
   * @param version the version, such as 2009a, not null
   * @param sourceFiles the list of source files, not empty, not null
   * @param verbose whether to output verbose messages
   */
  public TzdbZoneRulesCompiler(String version, List<File> sourceFiles, File leapSecondsFile, boolean verbose) {

    this.version = version;
    this.sourceFiles = sourceFiles;
    this.leapSecondsFile = leapSecondsFile;
    this.verbose = verbose;
  }

  /**
   * Compile the rules file.
   *

   * Use {@link #getZones()} and {@link #getLeapSeconds()} to retrieve the parsed data.
   *
   * @throws Exception if an error occurs
   */
  public void compile() throws Exception {

    printVerbose("Compiling TZDB version " + this.version);
    parseFiles();
    parseLeapSecondsFile();
    buildZoneRules();
    printVerbose("Compiled TZDB version " + this.version);
  }

  /**
   * Gets the parsed zone rules.
   *
   * @return the parsed zone rules, not null
   */
  public SortedMap<String, ZoneRules> getZones() {

    return this.builtZones;
  }

  /**
   * Gets the parsed leap seconds.
   *
   * @return the parsed and sorted leap seconds, not null
   */
  public SortedMap<LocalDate, Byte> getLeapSeconds() {

    return this.leapSeconds;
  }

  /**
   * Gets the most recent leap second.
   *
   * @return the most recent leap second, null if none
   */
  private LocalDate getMostRecentLeapSecond() {

    return this.leapSeconds.isEmpty() ? null : this.leapSeconds.lastKey();
  }

  /**
   * Sets the deduplication map.
   *
   * @param deduplicateMap the map to deduplicate items
   */
  void setDeduplicateMap(Map<Object, Object> deduplicateMap) {

    this.deduplicateMap = deduplicateMap;
  }

  // -----------------------------------------------------------------------
  /**
   * Parses the source files.
   *
   * @throws Exception if an error occurs
   */
  private void parseFiles() throws Exception {

    for (File file : this.sourceFiles) {
      printVerbose("Parsing file: " + file);
      parseFile(file);
    }
  }

  /**
   * Parses the leap seconds file.
   *
   * @throws Exception if an error occurs
   */
  private void parseLeapSecondsFile() throws Exception {

    printVerbose("Parsing leap second file: " + this.leapSecondsFile);
    int lineNumber = 1;
    String line = null;
    BufferedReader in = null;

    try {
      in = new BufferedReader(new FileReader(this.leapSecondsFile));
      for (; (line = in.readLine()) != null; lineNumber++) {
        int index = line.indexOf('#'); // remove comments (doesn't handle # in quotes)
        if (index >= 0) {
          line = line.substring(0, index);
        }
        if (line.trim().length() == 0) { // ignore blank lines
          continue;
        }
        LeapSecondRule secondRule = parseLeapSecondRule(line);
        this.leapSeconds.put(secondRule.leapDate, secondRule.secondAdjustment);
      }
    } catch (Exception ex) {
      throw new Exception("Failed while processing file '" + this.leapSecondsFile + "' on line " + lineNumber + " '"
          + line + "'", ex);
    } finally {
      try {
        if (in != null) {
          in.close();
        }
      } catch (Exception ex) {
        // ignore NPE and IOE
      }
    }
  }

  private LeapSecondRule parseLeapSecondRule(String line) {

    // # Leap YEAR MONTH DAY HH:MM:SS CORR R/S
    // Leap 1972 Jun 30 23:59:60 + S
    // Leap 1972 Dec 31 23:59:60 + S
    // Leap 1973 Dec 31 23:59:60 + S
    // Leap 1974 Dec 31 23:59:60 + S
    // Leap 1975 Dec 31 23:59:60 + S
    // Leap 1976 Dec 31 23:59:60 + S
    // Leap 1977 Dec 31 23:59:60 + S
    // Leap 1978 Dec 31 23:59:60 + S
    // Leap 1979 Dec 31 23:59:60 + S
    // Leap 1981 Jun 30 23:59:60 + S
    // Leap 1982 Jun 30 23:59:60 + S
    // Leap 1983 Jun 30 23:59:60 + S

    StringTokenizer st = new StringTokenizer(line, " \t");
    String first = st.nextToken();
    if (first.equals("Leap")) {
      if (st.countTokens() < 6) {
        printVerbose("Invalid leap second line in file: " + this.leapSecondsFile + ", line: " + line);
        throw new IllegalArgumentException("Invalid leap second line");
      }
    } else {
      throw new IllegalArgumentException("Unknown line");
    }

    int year = Integer.parseInt(st.nextToken());
    Month month = parseMonth(st.nextToken());
    int dayOfMonth = Integer.parseInt(st.nextToken());
    LocalDate leapDate = LocalDate.of(year, month, dayOfMonth);
    String timeOfLeapSecond = st.nextToken();

    byte adjustmentByte = 0;
    String adjustment = st.nextToken();
    if (adjustment.equals("+")) {
      if (!("23:59:60".equals(timeOfLeapSecond))) {
        throw new IllegalArgumentException("Leap seconds can only be inserted at 23:59:60 - Date:" + leapDate);
      }
      adjustmentByte = +1;
    } else if (adjustment.equals("-")) {
      if (!("23:59:59".equals(timeOfLeapSecond))) {
        throw new IllegalArgumentException("Leap seconds can only be removed at 23:59:59 - Date:" + leapDate);
      }
      adjustmentByte = -1;
    } else {
      throw new IllegalArgumentException("Invalid adjustment '" + adjustment + "' in leap second rule for " + leapDate);
    }

    String rollingOrStationary = st.nextToken();
    if (!"S".equalsIgnoreCase(rollingOrStationary)) {
      throw new IllegalArgumentException("Only stationary ('S') leap seconds are supported, not '"
          + rollingOrStationary + "'");
    }
    return new LeapSecondRule(leapDate, adjustmentByte);
  }

  /**
   * Parses a source file.
   *
   * @param file the file being read, not null
   * @throws Exception if an error occurs
   */
  private void parseFile(File file) throws Exception {

    int lineNumber = 1;
    String line = null;
    BufferedReader in = null;
    try {
      in = new BufferedReader(new FileReader(file));
      List<TZDBZone> openZone = null;
      for (; (line = in.readLine()) != null; lineNumber++) {
        int index = line.indexOf('#'); // remove comments (doesn't handle # in quotes)
        if (index >= 0) {
          line = line.substring(0, index);
        }
        if (line.trim().length() == 0) { // ignore blank lines
          continue;
        }
        StringTokenizer st = new StringTokenizer(line, " \t");
        if (openZone != null && Character.isWhitespace(line.charAt(0)) && st.hasMoreTokens()) {
          if (parseZoneLine(st, openZone)) {
            openZone = null;
          }
        } else {
          if (st.hasMoreTokens()) {
            String first = st.nextToken();
            if (first.equals("Zone")) {
              if (st.countTokens() < 3) {
                printVerbose("Invalid Zone line in file: " + file + ", line: " + line);
                throw new IllegalArgumentException("Invalid Zone line");
              }
              openZone = new ArrayList<TZDBZone>();
              this.zones.put(st.nextToken(), openZone);
              if (parseZoneLine(st, openZone)) {
                openZone = null;
              }
            } else {
              openZone = null;
              if (first.equals("Rule")) {
                if (st.countTokens() < 9) {
                  printVerbose("Invalid Rule line in file: " + file + ", line: " + line);
                  throw new IllegalArgumentException("Invalid Rule line");
                }
                parseRuleLine(st);

              } else if (first.equals("Link")) {
                if (st.countTokens() < 2) {
                  printVerbose("Invalid Link line in file: " + file + ", line: " + line);
                  throw new IllegalArgumentException("Invalid Link line");
                }
                String realId = st.nextToken();
                String aliasId = st.nextToken();
                this.links.put(aliasId, realId);

              } else {
                throw new IllegalArgumentException("Unknown line");
              }
            }
          }
        }
      }
    } catch (Exception ex) {
      throw new Exception("Failed while processing file '" + file + "' on line " + lineNumber + " '" + line + "'", ex);
    } finally {
      try {
        if (in != null) {
          in.close();
        }
      } catch (Exception ex) {
        // ignore NPE and IOE
      }
    }
  }

  /**
   * Parses a Rule line.
   *
   * @param st the tokenizer, not null
   */
  private void parseRuleLine(StringTokenizer st) {

    TZDBRule rule = new TZDBRule();
    String name = st.nextToken();
    if (this.rules.containsKey(name) == false) {
      this.rules.put(name, new ArrayList<TZDBRule>());
    }
    this.rules.get(name).add(rule);
    rule.startYear = parseYear(st.nextToken(), 0);
    rule.endYear = parseYear(st.nextToken(), rule.startYear);
    if (rule.startYear > rule.endYear) {
      throw new IllegalArgumentException("Year order invalid: " + rule.startYear + " > " + rule.endYear);
    }
    parseOptional(st.nextToken()); // type is unused
    parseMonthDayTime(st, rule);
    rule.savingsAmount = parsePeriod(st.nextToken());
    rule.text = parseOptional(st.nextToken());
  }

  /**
   * Parses a Zone line.
   *
   * @param st the tokenizer, not null
   * @return true if the zone is complete
   */
  private boolean parseZoneLine(StringTokenizer st, List<TZDBZone> zoneList) {

    TZDBZone zone = new TZDBZone();
    zoneList.add(zone);
    zone.standardOffset = parseOffset(st.nextToken());
    String savingsRule = parseOptional(st.nextToken());
    if (savingsRule == null) {
      zone.fixedSavingsSecs = 0;
      zone.savingsRule = null;
    } else {
      try {
        zone.fixedSavingsSecs = parsePeriod(savingsRule);
        zone.savingsRule = null;
      } catch (Exception ex) {
        zone.fixedSavingsSecs = null;
        zone.savingsRule = savingsRule;
      }
    }
    zone.text = st.nextToken();
    if (st.hasMoreTokens()) {
      zone.year = Year.of(Integer.parseInt(st.nextToken()));
      if (st.hasMoreTokens()) {
        parseMonthDayTime(st, zone);
      }
      return false;
    } else {
      return true;
    }
  }

  /**
   * Parses a Rule line.
   *
   * @param st the tokenizer, not null
   * @param mdt the object to parse into, not null
   */
  private void parseMonthDayTime(StringTokenizer st, TZDBMonthDayTime mdt) {

    mdt.month = parseMonth(st.nextToken());
    if (st.hasMoreTokens()) {
      String dayRule = st.nextToken();
      if (dayRule.startsWith("last")) {
        mdt.dayOfMonth = -1;
        mdt.dayOfWeek = parseDayOfWeek(dayRule.substring(4));
        mdt.adjustForwards = false;
      } else {
        int index = dayRule.indexOf(">=");
        if (index > 0) {
          mdt.dayOfWeek = parseDayOfWeek(dayRule.substring(0, index));
          dayRule = dayRule.substring(index + 2);
        } else {
          index = dayRule.indexOf("<=");
          if (index > 0) {
            mdt.dayOfWeek = parseDayOfWeek(dayRule.substring(0, index));
            mdt.adjustForwards = false;
            dayRule = dayRule.substring(index + 2);
          }
        }
        mdt.dayOfMonth = Integer.parseInt(dayRule);
      }
      if (st.hasMoreTokens()) {
        String timeStr = st.nextToken();
        int secsOfDay = parseSecs(timeStr);
        if (secsOfDay == 86400) {
          mdt.endOfDay = true;
          secsOfDay = 0;
        }
        LocalTime time = deduplicate(LocalTime.ofSecondOfDay(secsOfDay));
        mdt.time = time;
        mdt.timeDefinition = parseTimeDefinition(timeStr.charAt(timeStr.length() - 1));
      }
    }
  }

  private int parseYear(String str, int defaultYear) {

    str = str.toLowerCase();
    if (matches(str, "minimum")) {
      return Year.MIN_YEAR;
    } else if (matches(str, "maximum")) {
      return Year.MAX_YEAR;
    } else if (str.equals("only")) {
      return defaultYear;
    }
    return Integer.parseInt(str);
  }

  private Month parseMonth(String str) {

    str = str.toLowerCase();
    for (Month moy : Month.values()) {
      if (matches(str, moy.name().toLowerCase())) {
        return moy;
      }
    }
    throw new IllegalArgumentException("Unknown month: " + str);
  }

  private DayOfWeek parseDayOfWeek(String str) {

    str = str.toLowerCase();
    for (DayOfWeek dow : DayOfWeek.values()) {
      if (matches(str, dow.name().toLowerCase())) {
        return dow;
      }
    }
    throw new IllegalArgumentException("Unknown day-of-week: " + str);
  }

  private boolean matches(String str, String search) {

    return str.startsWith(search.substring(0, 3)) && search.startsWith(str) && str.length() <= search.length();
  }

  private String parseOptional(String str) {

    return str.equals("-") ? null : str;
  }

  private int parseSecs(String str) {

    if (str.equals("-")) {
      return 0;
    }
    int pos = 0;
    if (str.startsWith("-")) {
      pos = 1;
    }
    ParsePosition pp = new ParsePosition(pos);
    DateTimeBuilder bld = TIME_PARSER.parseToBuilder(str, pp);
    if (bld == null || pp.getErrorIndex() >= 0) {
      throw new IllegalArgumentException(str);
    }
    Map<DateTimeField, Long> parsed = bld.getFieldValueMap();
    long hour = parsed.get(HOUR_OF_DAY);
    Long min = parsed.get(MINUTE_OF_HOUR);
    Long sec = parsed.get(SECOND_OF_MINUTE);
    int secs = (int) (hour * 60 * 60 + (min != null ? min : 0) * 60 + (sec != null ? sec : 0));
    if (pos == 1) {
      secs = -secs;
    }
    return secs;
  }

  private ZoneOffset parseOffset(String str) {

    int secs = parseSecs(str);
    return ZoneOffset.ofTotalSeconds(secs);
  }

  private int parsePeriod(String str) {

    return parseSecs(str);
  }

  private TimeDefinition parseTimeDefinition(char c) {

    switch (c) {
      case 's':
      case 'S':
        // standard time
        return TimeDefinition.STANDARD;
      case 'u':
      case 'U':
      case 'g':
      case 'G':
      case 'z':
      case 'Z':
        // UTC
        return TimeDefinition.UTC;
      case 'w':
      case 'W':
      default :
        // wall time
        return TimeDefinition.WALL;
    }
  }

  // -----------------------------------------------------------------------
  /**
   * Build the rules, zones and links into real zones.
   *
   * @throws Exception if an error occurs
   */
  private void buildZoneRules() throws Exception {

    // build zones
    for (String zoneId : this.zones.keySet()) {
      printVerbose("Building zone " + zoneId);
      zoneId = deduplicate(zoneId);
      List<TZDBZone> tzdbZones = this.zones.get(zoneId);
      ZoneRulesBuilder bld = new ZoneRulesBuilder();
      for (TZDBZone tzdbZone : tzdbZones) {
        bld = tzdbZone.addToBuilder(bld, this.rules);
      }
      ZoneRules buildRules = bld.toRules(zoneId, this.deduplicateMap);
      this.builtZones.put(zoneId, deduplicate(buildRules));
    }

    // build aliases
    for (String aliasId : this.links.keySet()) {
      aliasId = deduplicate(aliasId);
      String realId = this.links.get(aliasId);
      printVerbose("Linking alias " + aliasId + " to " + realId);
      ZoneRules realRules = this.builtZones.get(realId);
      if (realRules == null) {
        realId = this.links.get(realId); // try again (handle alias liked to alias)
        printVerbose("Relinking alias " + aliasId + " to " + realId);
        realRules = this.builtZones.get(realId);
        if (realRules == null) {
          throw new IllegalArgumentException("Alias '" + aliasId + "' links to invalid zone '" + realId + "' for '"
              + this.version + "'");
        }
      }
      this.builtZones.put(aliasId, realRules);
    }

    // remove UTC and GMT
    this.builtZones.remove("UTC");
    this.builtZones.remove("GMT");
    this.builtZones.remove("GMT0");
    this.builtZones.remove("GMT+0");
    this.builtZones.remove("GMT-0");
  }

  // -----------------------------------------------------------------------
  /**
   * Deduplicates an object instance.
   *
   * @param <T> the generic type
   * @param object the object to deduplicate
   * @return the deduplicated object
   */
  @SuppressWarnings("unchecked")
  <T> T deduplicate(T object) {

    if (this.deduplicateMap.containsKey(object) == false) {
      this.deduplicateMap.put(object, object);
    }
    return (T) this.deduplicateMap.get(object);
  }

  // -----------------------------------------------------------------------
  /**
   * Prints a verbose message.
   *
   * @param message the message, not null
   */
  private void printVerbose(String message) {

    if (this.verbose) {
      System.out.println(message);
    }
  }

  // -----------------------------------------------------------------------
  /**
   * Class representing a month-day-time in the TZDB file.
   */
  abstract class TZDBMonthDayTime {

    /** The month of the cutover. */
    Month month = Month.JANUARY;

    /** The day-of-month of the cutover. */
    int dayOfMonth = 1;

    /** Whether to adjust forwards. */
    boolean adjustForwards = true;

    /** The day-of-week of the cutover. */
    DayOfWeek dayOfWeek;

    /** The time of the cutover. */
    LocalTime time = LocalTime.MIDNIGHT;

    /** Whether this is midnight end of day. */
    boolean endOfDay;

    /** The time of the cutover. */
    TimeDefinition timeDefinition = TimeDefinition.WALL;

    void adjustToFowards(int year) {

      if (this.adjustForwards == false && this.dayOfMonth > 0) {
        LocalDate adjustedDate = LocalDate.of(year, this.month, this.dayOfMonth).minusDays(6);
        this.dayOfMonth = adjustedDate.getDayOfMonth();
        this.month = adjustedDate.getMonth();
        this.adjustForwards = true;
      }
    }
  }

  // -----------------------------------------------------------------------
  /**
   * Class representing a rule line in the TZDB file.
   */
  final class TZDBRule extends TZDBMonthDayTime {

    /** The start year. */
    int startYear;

    /** The end year. */
    int endYear;

    /** The amount of savings. */
    int savingsAmount;

    /** The text name of the zone. */
    String text;

    void addToBuilder(ZoneRulesBuilder bld) {

      adjustToFowards(2004); // irrelevant, treat as leap year
      bld.addRuleToWindow(this.startYear, this.endYear, this.month, this.dayOfMonth, this.dayOfWeek, this.time,
          this.endOfDay, this.timeDefinition, this.savingsAmount);
    }
  }

  // -----------------------------------------------------------------------
  /**
   * Class representing a linked set of zone lines in the TZDB file.
   */
  final class TZDBZone extends TZDBMonthDayTime {

    /** The standard offset. */
    ZoneOffset standardOffset;

    /** The fixed savings amount. */
    Integer fixedSavingsSecs;

    /** The savings rule. */
    String savingsRule;

    /** The text name of the zone. */
    String text;

    /** The year of the cutover. */
    Year year;

    ZoneRulesBuilder addToBuilder(ZoneRulesBuilder bld, Map<String, List<TZDBRule>> rules) {

      if (this.year != null) {
        bld.addWindow(this.standardOffset, toDateTime(this.year.getValue()), this.timeDefinition);
      } else {
        bld.addWindowForever(this.standardOffset);
      }

      if (this.fixedSavingsSecs != null) {
        bld.setFixedSavingsToWindow(this.fixedSavingsSecs);
      } else {
        List<TZDBRule> tzdbRules = rules.get(this.savingsRule);
        if (tzdbRules == null) {
          throw new IllegalArgumentException("Rule not found: " + this.savingsRule);
        }
        for (TZDBRule tzdbRule : tzdbRules) {
          tzdbRule.addToBuilder(bld);
        }
      }

      return bld;
    }

    private LocalDateTime toDateTime(int year) {

      adjustToFowards(year);
      LocalDate date;
      if (this.dayOfMonth == -1) {
        this.dayOfMonth = this.month.length(Year.isLeap(year));
        date = LocalDate.of(year, this.month, this.dayOfMonth);
        if (this.dayOfWeek != null) {
          date = date.with(DateTimeAdjusters.previousOrSame(this.dayOfWeek));
        }
      } else {
        date = LocalDate.of(year, this.month, this.dayOfMonth);
        if (this.dayOfWeek != null) {
          date = date.with(DateTimeAdjusters.nextOrSame(this.dayOfWeek));
        }
      }
      date = deduplicate(date);
      LocalDateTime ldt = LocalDateTime.of(date, this.time);
      if (this.endOfDay) {
        ldt = ldt.plusDays(1);
      }
      return ldt;
    }
  }

  // -----------------------------------------------------------------------
  /**
   * Class representing a rule line in the TZDB file.
   */
  public static final class LeapSecondRule {

    /**
     * Constructs a rule using fields.
     *
     * @param leapDate Date which has gets leap second adjustment (at the end)
     * @param secondAdjustment +1 or -1 for inserting or dropping a second
     */
    public LeapSecondRule(LocalDate leapDate, byte secondAdjustment) {

      this.leapDate = leapDate;
      this.secondAdjustment = secondAdjustment;
    }

    /** The date of the leap second. */
    final LocalDate leapDate;

    /**
     * The adjustment (in seconds), +1 means a second is inserted, -1 means a second is dropped.
     */
    byte secondAdjustment;
  }

}


text:
namespace net {
class HttpResponseInfo;
}

namespace content {

class EmbeddedWorkerRegistry;
class ServiceWorkerContextCore;
class ServiceWorkerProviderHost;
class ServiceWorkerRegistration;
class ServiceWorkerURLRequestJob;
struct NavigatorConnectClient;
struct ServiceWorkerClientInfo;
struct ServiceWorkerVersionInfo;
struct TransferredMessagePort;

// This class corresponds to a specific version of a ServiceWorker
// script for a given pattern. When a script is upgraded, there may be
// more than one ServiceWorkerVersion "running" at a time, but only
// one of them is activated. This class connects the actual script with a
// running worker.
class CONTENT_EXPORT ServiceWorkerVersion
    : NON_EXPORTED_BASE(public base::RefCounted<ServiceWorkerVersion>),
      public EmbeddedWorkerInstance::Listener {
 public:
  typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback;

  enum RunningStatus {
    STOPPED = EmbeddedWorkerInstance::STOPPED,
    STARTING = EmbeddedWorkerInstance::STARTING,
    RUNNING = EmbeddedWorkerInstance::RUNNING,
    STOPPING = EmbeddedWorkerInstance::STOPPING,
  };

  // Current version status; some of the status (e.g. INSTALLED and ACTIVATED)
  // should be persisted unlike running status.
  enum Status {
    NEW,         // The version is just created.
    INSTALLING,  // Install event is dispatched and being handled.
    INSTALLED,   // Install event is finished and is ready to be activated.
    ACTIVATING,  // Activate event is dispatched and being handled.
    ACTIVATED,   // Activation is finished and can run as activated.
    REDUNDANT,   // The version is no longer running as activated, due to
                 // unregistration or replace.
  };

  // Behavior when a request times out.
  enum TimeoutBehavior {
    KILL_ON_TIMEOUT,     // Kill the worker if this request times out.
    CONTINUE_ON_TIMEOUT  // Keep the worker alive, only abandon the request that
                         // timed out.
  };

  class Listener {
   public:
    virtual void OnRunningStateChanged(ServiceWorkerVersion* version) {}
    virtual void OnVersionStateChanged(ServiceWorkerVersion* version) {}
    virtual void OnMainScriptHttpResponseInfoSet(
        ServiceWorkerVersion* version) {}
    virtual void OnErrorReported(ServiceWorkerVersion* version,
                                 const base::string16& error_message,
                                 int line_number,
                                 int column_number,
                                 const GURL& source_url) {}
    virtual void OnReportConsoleMessage(ServiceWorkerVersion* version,
                                        int source_identifier,
                                        int message_level,
                                        const base::string16& message,
                                        int line_number,
                                        const GURL& source_url) {}
    virtual void OnControlleeAdded(ServiceWorkerVersion* version,
                                   ServiceWorkerProviderHost* provider_host) {}
    virtual void OnControlleeRemoved(ServiceWorkerVersion* version,
                                     ServiceWorkerProviderHost* provider_host) {
    }
    // Fires when a version transitions from having a controllee to not.
    virtual void OnNoControllees(ServiceWorkerVersion* version) {}
    virtual void OnCachedMetadataUpdated(ServiceWorkerVersion* version) {}

   protected:
    virtual ~Listener() {}
  };

  ServiceWorkerVersion(ServiceWorkerRegistration* registration,
                       const GURL& script_url,
                       int64_t version_id,
                       base::WeakPtr<ServiceWorkerContextCore> context);

  int64_t version_id() const { return version_id_; }
  int64_t registration_id() const { return registration_id_; }
  const GURL& script_url() const { return script_url_; }
  const GURL& scope() const { return scope_; }
  RunningStatus running_status() const {
    return static_cast<RunningStatus>(embedded_worker_->status());
  }
  ServiceWorkerVersionInfo GetInfo();
  Status status() const { return status_; }

  const std::vector<GURL>& foreign_fetch_scopes() const {
    return foreign_fetch_scopes_;
  }
  void set_foreign_fetch_scopes(const std::vector<GURL>& scopes) {
    foreign_fetch_scopes_ = scopes;
  }

  const std::vector<url::Origin>& foreign_fetch_origins() const {
    return foreign_fetch_origins_;
  }
  void set_foreign_fetch_origins(const std::vector<url::Origin>& origins) {
    foreign_fetch_origins_ = origins;
  }

  // This sets the new status and also run status change callbacks
  // if there're any (see RegisterStatusChangeCallback).
  void SetStatus(Status status);

  // Registers status change callback. (This is for one-off observation,
  // the consumer needs to re-register if it wants to continue observing
  // status changes)
  void RegisterStatusChangeCallback(const base::Closure& callback);

  // Starts an embedded worker for this version.
  // This returns OK (success) if the worker is already running.
  void StartWorker(const StatusCallback& callback);

  // Stops an embedded worker for this version.
  // This returns OK (success) if the worker is already stopped.
  void StopWorker(const StatusCallback& callback);

  // Schedules an update to be run 'soon'.
  void ScheduleUpdate();

  // If an update is scheduled but not yet started, this resets the timer
  // delaying the start time by a 'small' amount.
  void DeferScheduledUpdate();

  // Starts an update now.
  void StartUpdate();

  // Starts the worker if it isn't already running, and calls |task| when the
  // worker is running, or |error_callback| if starting the worker failed.
  // If the worker is already running, |task| is executed synchronously (before
  // this method returns).
  void RunAfterStartWorker(const base::Closure& task,
                           const StatusCallback& error_callback);

  // Call this while the worker is running before dispatching an event to the
  // worker. This informs ServiceWorkerVersion about the event in progress.
  // Returns a request id, which should later be passed to FinishRequest when
  // the event finished.
  // The |error_callback| is called if either ServiceWorkerVersion decides the
  // event is taking too long, or if for some reason the worker stops or is
  // killed before the request finishes.
  int StartRequest(ServiceWorkerMetrics::EventType event_type,
                   const StatusCallback& error_callback);

  // Same as StartRequest, but allows the caller to specify a custom timeout for
  // the event, as well as the behavior for when the request times out.
  int StartRequestWithCustomTimeout(ServiceWorkerMetrics::EventType event_type,
                                    const StatusCallback& error_callback,
                                    const base::TimeDelta& timeout,
                                    TimeoutBehavior timeout_behavior);

  // Informs ServiceWorkerVersion that an event has finished being dispatched.
  // Returns false if no pending requests with the provided id exist, for
  // example if the request has already timed out.
  // Pass the result of the event to |was_handled|, which is used to record
  // statistics based on the event status.
  // TODO(mek): Use something other than a bool for event status.
  bool FinishRequest(int request_id, bool was_handled);

  // Connects to a specific mojo service exposed by the (running) service
  // worker. If a connection to a service for the same Interface already exists
  // this will return that existing connection. The |request_id| must be a value
  // previously returned by StartRequest. If the connection to the service
  // fails or closes before the request finished, the error callback associated
  // with |request_id| is called.
  // Only call GetMojoServiceForRequest once for a specific |request_id|.
  template <typename Interface>
  base::WeakPtr<Interface> GetMojoServiceForRequest(int request_id);

  // Dispatches an event. If dispatching the event fails, the error callback
  // associated with the |request_id| is called. Any messages sent back in
  // response to this event are passed on to the response |callback|.
  // ResponseMessage is the type of the IPC message that is used for the
  // response, and its first argument MUST be the request_id.
  // This must be called when the worker is running.
  template <typename ResponseMessage, typename ResponseCallbackType>
  void DispatchEvent(int request_id,
                     const IPC::Message& message,
                     const ResponseCallbackType& callback);

  // For simple events where the full functionality of DispatchEvent is not
  // needed, this method can be used instead. The ResponseMessage must consist
  // of just a request_id and a blink::WebServiceWorkerEventResult field. The
  // result is converted to a ServiceWorkerStatusCode and passed to the error
  // handler associated with the request. Additionally this methods calls
  // FinishRequest before passing the reply to the callback.
  template <typename ResponseMessage>
  void DispatchSimpleEvent(int request_id, const IPC::Message& message);

  // Sends a message event to the associated embedded worker.
  // TODO(nhiroki): Remove this after ExtendableMessageEvent is enabled by
  // default (crbug.com/543198).
  void DispatchMessageEvent(
      const base::string16& message,
      const std::vector<TransferredMessagePort>& sent_message_ports,
      const StatusCallback& callback);

  // Sends an extendable message event to the associated embedded worker.
  // TODO(nhiroki): This should be moved to ServiceWorkerDispatcherHost in favor
  // of crbug.com/570820 after ExtendableMessageEvent is implemented
  // (crbug.com/543198).
  void DispatchExtendableMessageEvent(
      const base::string16& message,
      const std::vector<TransferredMessagePort>& sent_message_ports,
      const StatusCallback& callback);

  // Sends a cross origin message event to the associated embedded worker and
  // asynchronously calls |callback| when the message was sent (or failed to
  // sent).
  // It is the responsibility of the code calling this method to make sure that
  // any transferred message ports are put on hold while potentially a process
  // for the service worker is spun up.
  //
  // This must be called when the status() is ACTIVATED.
  void DispatchCrossOriginMessageEvent(
      const NavigatorConnectClient& client,
      const base::string16& message,
      const std::vector<TransferredMessagePort>& sent_message_ports,
      const StatusCallback& callback);

  // Adds and removes |provider_host| as a controllee of this ServiceWorker.
  // A potential controllee is a host having the version as its .installing
  // or .waiting version.
  void AddControllee(ServiceWorkerProviderHost* provider_host);
  void RemoveControllee(ServiceWorkerProviderHost* provider_host);

  // Returns if it has controllee.
  bool HasControllee() const { return !controllee_map_.empty(); }
  std::map<std::string, ServiceWorkerProviderHost*> controllee_map() {
    return controllee_map_;
  }

  base::WeakPtr<ServiceWorkerContextCore> context() const { return context_; }

  // Adds and removes |request_job| as a dependent job not to stop the
  // ServiceWorker while |request_job| is reading the stream of the fetch event
  // response from the ServiceWorker.
  void AddStreamingURLRequestJob(const ServiceWorkerURLRequestJob* request_job);
  void RemoveStreamingURLRequestJob(
      const ServiceWorkerURLRequestJob* request_job);

  // Adds and removes Listeners.
  void AddListener(Listener* listener);
  void RemoveListener(Listener* listener);

  ServiceWorkerScriptCacheMap* script_cache_map() { return &script_cache_map_; }
  EmbeddedWorkerInstance* embedded_worker() { return embedded_worker_.get(); }

  // Reports the error message to |listeners_|.
  void ReportError(ServiceWorkerStatusCode status,
                   const std::string& status_message);

  // Sets the status code to pass to StartWorker callbacks if start fails.
  void SetStartWorkerStatusCode(ServiceWorkerStatusCode status);

  // Sets this version's status to REDUNDANT and deletes its resources.
  // The version must not have controllees.
  void Doom();
  bool is_redundant() const { return status_ == REDUNDANT; }

  bool skip_waiting() const { return skip_waiting_; }
  void set_skip_waiting(bool skip_waiting) { skip_waiting_ = skip_waiting; }

  bool force_bypass_cache_for_scripts() const {
    return force_bypass_cache_for_scripts_;
  }
  void set_force_bypass_cache_for_scripts(bool force_bypass_cache_for_scripts) {
    force_bypass_cache_for_scripts_ = force_bypass_cache_for_scripts;
  }

  bool pause_after_download() const { return pause_after_download_; }
  void set_pause_after_download(bool pause_after_download) {
    pause_after_download_ = pause_after_download;
  }

  void SetDevToolsAttached(bool attached);

  // Sets the HttpResponseInfo used to load the main script.
  // This HttpResponseInfo will be used for all responses sent back from the
  // service worker, as the effective security of these responses is equivalent
  // to that of the ServiceWorker.
  void SetMainScriptHttpResponseInfo(const net::HttpResponseInfo& http_info);
  const net::HttpResponseInfo* GetMainScriptHttpResponseInfo();

  // Simulate ping timeout. Should be used for tests-only.
  void SimulatePingTimeoutForTesting();

 private:
  friend class base::RefCounted<ServiceWorkerVersion>;
  friend class ServiceWorkerMetrics;
  friend class ServiceWorkerReadFromCacheJobTest;
  friend class ServiceWorkerStallInStoppingTest;
  friend class ServiceWorkerURLRequestJobTest;
  friend class ServiceWorkerVersionBrowserTest;
  friend class ServiceWorkerVersionTest;

  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerControlleeRequestHandlerTest,
                           ActivateWaitingVersion);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, IdleTimeout);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, SetDevToolsAttached);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, StaleUpdate_FreshWorker);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest,
                           StaleUpdate_NonActiveWorker);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, StaleUpdate_StartWorker);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, StaleUpdate_RunningWorker);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest,
                           StaleUpdate_DoNotDeferTimer);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, RequestTimeout);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerFailToStartTest, Timeout);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionBrowserTest,
                           TimeoutStartingWorker);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionBrowserTest,
                           TimeoutWorkerInEvent);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerStallInStoppingTest, DetachThenStart);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerStallInStoppingTest, DetachThenRestart);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest,
                           RegisterForeignFetchScopes);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, RequestCustomizedTimeout);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest,
                           RequestCustomizedTimeoutKill);
  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerVersionTest, MixedRequestTimeouts);

  class Metrics;
  class PingController;

  enum RequestType {
    REQUEST_CUSTOM,
    NUM_REQUEST_TYPES
  };

  struct RequestInfo {
    RequestInfo(int id,
                RequestType type,
                ServiceWorkerMetrics::EventType event_type,
                const base::TimeTicks& expiration,
                TimeoutBehavior timeout_behavior);
    ~RequestInfo();
    bool operator>(const RequestInfo& other) const;
    int id;
    RequestType type;
    ServiceWorkerMetrics::EventType event_type;
    base::TimeTicks expiration;
    TimeoutBehavior timeout_behavior;
  };

  template <typename CallbackType>
  struct PendingRequest {
    PendingRequest(const CallbackType& callback,
                   const base::TimeTicks& time,
                   ServiceWorkerMetrics::EventType event_type);
    ~PendingRequest() {}

    CallbackType callback;
    base::TimeTicks start_time;
    ServiceWorkerMetrics::EventType event_type;
    // Name of the mojo service this request is associated with. Used to call
    // the callback when a connection closes with outstanding requests.
    // Compared as pointer, so should only contain static strings. Typically
    // this would be Interface::Name_ for some mojo interface.
    const char* mojo_service = nullptr;
    scoped_ptr<EmbeddedWorkerInstance::Listener> listener;
  };

  // Base class to enable storing a list of mojo interface pointers for
  // arbitrary interfaces. The destructor is also responsible for calling the
  // error callbacks for any outstanding requests using this service.
  class CONTENT_EXPORT BaseMojoServiceWrapper {
   public:
    BaseMojoServiceWrapper(ServiceWorkerVersion* worker,
                           const char* service_name);
    virtual ~BaseMojoServiceWrapper();

   private:
    ServiceWorkerVersion* worker_;
    const char* service_name_;

    DISALLOW_COPY_AND_ASSIGN(BaseMojoServiceWrapper);
  };

  // Wrapper around a mojo::InterfacePtr, which passes out WeakPtr's to the
  // interface.
  template <typename Interface>
  class MojoServiceWrapper : public BaseMojoServiceWrapper {
   public:
    MojoServiceWrapper(ServiceWorkerVersion* worker,
                       mojo::InterfacePtr<Interface> interface)
        : BaseMojoServiceWrapper(worker, Interface::Name_),
          interface_(std::move(interface)),
          weak_ptr_factory_(interface_.get()) {}

    base::WeakPtr<Interface> GetWeakPtr() {
      return weak_ptr_factory_.GetWeakPtr();
    }

   private:
    mojo::InterfacePtr<Interface> interface_;
    base::WeakPtrFactory<Interface> weak_ptr_factory_;
  };

  typedef ServiceWorkerVersion self;
  using ServiceWorkerClients = std::vector<ServiceWorkerClientInfo>;
  using RequestInfoPriorityQueue =
      std::priority_queue<RequestInfo,
                          std::vector<RequestInfo>,
                          std::greater<RequestInfo>>;

  // EmbeddedWorkerInstance Listener implementation which calls a callback
  // on receiving a particular IPC message. ResponseMessage is the type of
  // the IPC message to listen for, while CallbackType should be a callback
  // with same arguments as the IPC message.
  // Additionally only calls the callback for messages with a specific request
  // id, which must be the first argument of the IPC message.
  template <typename ResponseMessage, typename CallbackType>
  class EventResponseHandler : public EmbeddedWorkerInstance::Listener {
   public:
    EventResponseHandler(const base::WeakPtr<EmbeddedWorkerInstance>& worker,
                         int request_id,
                         const CallbackType& callback)
        : worker_(worker), request_id_(request_id), callback_(callback) {
      worker_->AddListener(this);
    }
    ~EventResponseHandler() override {
      if (worker_)
        worker_->RemoveListener(this);
    }
    bool OnMessageReceived(const IPC::Message& message) override;

   private:
    base::WeakPtr<EmbeddedWorkerInstance> const worker_;
    const int request_id_;
    const CallbackType callback_;
  };

  // The timeout timer interval.
  static const int kTimeoutTimerDelaySeconds;
  // Timeout for an installed worker to start.
  static const int kStartInstalledWorkerTimeoutSeconds;
  // Timeout for a new worker to start.
  static const int kStartNewWorkerTimeoutMinutes;
  // Timeout for a request to be handled.
  static const int kRequestTimeoutMinutes;
  // Timeout for the worker to stop.
  static const int kStopWorkerTimeoutSeconds;

  ~ServiceWorkerVersion() override;

  // EmbeddedWorkerInstance::Listener overrides:
  void OnThreadStarted() override;
  void OnStarting() override;
  void OnStarted() override;
  void OnStopping() override;
  void OnStopped(EmbeddedWorkerInstance::Status old_status) override;
  void OnDetached(EmbeddedWorkerInstance::Status old_status) override;
  void OnScriptLoaded() override;
  void OnScriptLoadFailed() override;
  void OnReportException(const base::string16& error_message,
                         int line_number,
                         int column_number,
                         const GURL& source_url) override;
  void OnReportConsoleMessage(int source_identifier,
                              int message_level,
                              const base::string16& message,
                              int line_number,
                              const GURL& source_url) override;
  bool OnMessageReceived(const IPC::Message& message) override;

  void OnStartSentAndScriptEvaluated(ServiceWorkerStatusCode status);

  void DispatchExtendableMessageEventAfterStartWorker(
      const base::string16& message,
      const std::vector<TransferredMessagePort>& sent_message_ports,
      const StatusCallback& callback);

  void DispatchMessageEventInternal(
      const base::string16& message,
      const std::vector<TransferredMessagePort>& sent_message_ports,
      const StatusCallback& callback);

  // Message handlers.

  // This corresponds to the spec's get(id) steps.
  void OnGetClient(int request_id, const std::string& client_uuid);

  // This corresponds to the spec's matchAll(options) steps.
  void OnGetClients(int request_id,
                    const ServiceWorkerClientQueryOptions& options);

  void OnSimpleEventResponse(int request_id,
                             blink::WebServiceWorkerEventResult result);
  void OnOpenWindow(int request_id, GURL url);
  void OnOpenWindowFinished(int request_id,
                            ServiceWorkerStatusCode status,
                            const ServiceWorkerClientInfo& client_info);

  void OnSetCachedMetadata(const GURL& url, const std::vector<char>& data);
  void OnSetCachedMetadataFinished(int64_t callback_id, int result);
  void OnClearCachedMetadata(const GURL& url);
  void OnClearCachedMetadataFinished(int64_t callback_id, int result);

  void OnPostMessageToClient(
      const std::string& client_uuid,
      const base::string16& message,
      const std::vector<TransferredMessagePort>& sent_message_ports);
  void OnFocusClient(int request_id, const std::string& client_uuid);
  void OnNavigateClient(int request_id,
                        const std::string& client_uuid,
                        const GURL& url);
  void OnNavigateClientFinished(int request_id,
                                ServiceWorkerStatusCode status,
                                const ServiceWorkerClientInfo& client_info);
  void OnSkipWaiting(int request_id);
  void OnClaimClients(int request_id);
  void OnPongFromWorker();

  void OnFocusClientFinished(int request_id,
                             const ServiceWorkerClientInfo& client_info);

  void OnRegisterForeignFetchScopes(const std::vector<GURL>& sub_scopes,
                                    const std::vector<url::Origin>& origins);

  void DidEnsureLiveRegistrationForStartWorker(
      const StatusCallback& callback,
      ServiceWorkerStatusCode status,
      const scoped_refptr<ServiceWorkerRegistration>& registration);
  void StartWorkerInternal();

  void DidSkipWaiting(int request_id);

  void OnGetClientFinished(int request_id,
                           const ServiceWorkerClientInfo& client_info);

  void OnGetClientsFinished(int request_id, ServiceWorkerClients* clients);

  // The timeout timer periodically calls OnTimeoutTimer, which stops the worker
  // if it is excessively idle or unresponsive to ping.
  void StartTimeoutTimer();
  void StopTimeoutTimer();
  void OnTimeoutTimer();
  void SetTimeoutTimerInterval(base::TimeDelta interval);

  // Called by PingController for ping protocol.
  ServiceWorkerStatusCode PingWorker();
  void OnPingTimeout();

  // Stops the worker if it is idle (has no in-flight requests) or timed out
  // ping.
  void StopWorkerIfIdle();
  bool HasInflightRequests() const;

  // RecordStartWorkerResult is added as a start callback by StartTimeoutTimer
  // and records metrics about startup.
  void RecordStartWorkerResult(ServiceWorkerStatusCode status);

  template <typename IDMAP>
  void RemoveCallbackAndStopIfRedundant(IDMAP* callbacks, int request_id);

  template <typename CallbackType>
  int AddRequest(
      const CallbackType& callback,
      IDMap<PendingRequest<CallbackType>, IDMapOwnPointer>* callback_map,
      RequestType request_type,
      ServiceWorkerMetrics::EventType event_type);

  template <typename CallbackType>
  int AddRequestWithExpiration(
      const CallbackType& callback,
      IDMap<PendingRequest<CallbackType>, IDMapOwnPointer>* callback_map,
      RequestType request_type,
      ServiceWorkerMetrics::EventType event_type,
      base::TimeTicks expiration,
      TimeoutBehavior timeout_behavior);

  bool MaybeTimeOutRequest(const RequestInfo& info);
  void SetAllRequestExpirations(const base::TimeTicks& expiration);

  // Returns the reason the embedded worker failed to start, using information
  // inaccessible to EmbeddedWorkerInstance. Returns |default_code| if it can't
  // deduce a reason.
  ServiceWorkerStatusCode DeduceStartWorkerFailureReason(
      ServiceWorkerStatusCode default_code);

  // Sets |stale_time_| if this worker is stale, causing an update to eventually
  // occur once the worker stops or is running too long.
  void MarkIfStale();

  void FoundRegistrationForUpdate(
      ServiceWorkerStatusCode status,
      const scoped_refptr<ServiceWorkerRegistration>& registration);

  void OnStoppedInternal(EmbeddedWorkerInstance::Status old_status);

  // Called when the remote side of a connection to a mojo service is lost.
  void OnMojoConnectionError(const char* service_name);

  // Called at the beginning of each Dispatch*Event function: records
  // the time elapsed since idle (generally the time since the previous
  // event ended).
  void OnBeginEvent();

  const int64_t version_id_;
  const int64_t registration_id_;
  const GURL script_url_;
  const GURL scope_;
  std::vector<GURL> foreign_fetch_scopes_;
  std::vector<url::Origin> foreign_fetch_origins_;

  Status status_ = NEW;
  scoped_ptr<EmbeddedWorkerInstance> embedded_worker_;
  std::vector<StatusCallback> start_callbacks_;
  std::vector<StatusCallback> stop_callbacks_;
  std::vector<base::Closure> status_change_callbacks_;

  // Message callbacks. (Update HasInflightRequests() too when you update this
  // list.)
  IDMap<PendingRequest<StatusCallback>, IDMapOwnPointer> custom_requests_;

  // Stores all open connections to mojo services. Maps the service name to
  // the actual interface pointer. When a connection is closed it is removed
  // from this map.
  // mojo_services_[Interface::Name_] is assumed to always contain a
  // MojoServiceWrapper<Interface> instance.
  base::ScopedPtrHashMap<const char*, scoped_ptr<BaseMojoServiceWrapper>>
      mojo_services_;

  std::set<const ServiceWorkerURLRequestJob*> streaming_url_request_jobs_;

  std::map<std::string, ServiceWorkerProviderHost*> controllee_map_;
  // Will be null while shutting down.
  base::WeakPtr<ServiceWorkerContextCore> context_;
  base::ObserverList<Listener> listeners_;
  ServiceWorkerScriptCacheMap script_cache_map_;
  base::OneShotTimer update_timer_;

  // Starts running in StartWorker and continues until the worker is stopped.
  base::RepeatingTimer timeout_timer_;
  // Holds the time the worker last started being considered idle.
  base::TimeTicks idle_time_;
  // Holds the time that the outstanding StartWorker() request started.
  base::TimeTicks start_time_;
  // Holds the time the worker entered STOPPING status.
  base::TimeTicks stop_time_;
  // Holds the time the worker was detected as stale and needs updating. We try
  // to update once the worker stops, but will also update if it stays alive too
  // long.
  base::TimeTicks stale_time_;

  // New requests are added to |requests_| along with their entry in a callback
  // map. Requests are sorted by their expiration time (soonest to expire on top
  // of the priority queue). The timeout timer periodically checks |requests_|
  // for entries that should time out or have already been fulfilled (i.e.,
  // removed from the callback map).
  RequestInfoPriorityQueue requests_;

  bool skip_waiting_ = false;
  bool skip_recording_startup_time_ = false;
  bool force_bypass_cache_for_scripts_ = false;
  bool pause_after_download_ = false;
  bool is_update_scheduled_ = false;
  bool in_dtor_ = false;

  std::vector<int> pending_skip_waiting_requests_;
  scoped_ptr<net::HttpResponseInfo> main_script_http_info_;

  // The status when StartWorker was invoked. Used for UMA.
  Status prestart_status_ = NEW;
  // If not OK, the reason that StartWorker failed. Used for
  // running |start_callbacks_|.
  ServiceWorkerStatusCode start_worker_status_ = SERVICE_WORKER_OK;

  scoped_ptr<PingController> ping_controller_;
  scoped_ptr<Metrics> metrics_;
  const bool should_exclude_from_uma_ = false;

  base::WeakPtrFactory<ServiceWorkerVersion> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerVersion);
};

template <typename Interface>
base::WeakPtr<Interface> ServiceWorkerVersion::GetMojoServiceForRequest(
    int request_id) {
  DCHECK_EQ(RUNNING, running_status());
  PendingRequest<StatusCallback>* request = custom_requests_.Lookup(request_id);
  DCHECK(request) << "Invalid request id";
  DCHECK(!request->mojo_service)
      << "Request is already associated with a mojo service";

  MojoServiceWrapper<Interface>* service =
      static_cast<MojoServiceWrapper<Interface>*>(
          mojo_services_.get(Interface::Name_));
  if (!service) {
    mojo::InterfacePtr<Interface> interface;
    embedded_worker_->GetServiceRegistry()->ConnectToRemoteService(
        mojo::GetProxy(&interface));
    interface.set_connection_error_handler(
        base::Bind(&ServiceWorkerVersion::OnMojoConnectionError,
                   weak_factory_.GetWeakPtr(), Interface::Name_));
    service = new MojoServiceWrapper<Interface>(this, std::move(interface));
    mojo_services_.add(Interface::Name_, make_scoped_ptr(service));
  }
  request->mojo_service = Interface::Name_;
  return service->GetWeakPtr();
}

template <typename ResponseMessage, typename ResponseCallbackType>
void ServiceWorkerVersion::DispatchEvent(int request_id,
                                         const IPC::Message& message,
                                         const ResponseCallbackType& callback) {
  DCHECK_EQ(RUNNING, running_status());
  PendingRequest<StatusCallback>* request = custom_requests_.Lookup(request_id);
  DCHECK(request) << "Invalid request id";
  DCHECK(!request->listener) << "Request already dispatched an IPC event";

  ServiceWorkerStatusCode status = embedded_worker_->SendMessage(message);
  if (status != SERVICE_WORKER_OK) {
    base::ThreadTaskRunnerHandle::Get()->PostTask(
        FROM_HERE, base::Bind(request->callback, status));
    custom_requests_.Remove(request_id);
  } else {
    request->listener.reset(
        new EventResponseHandler<ResponseMessage, ResponseCallbackType>(
            embedded_worker()->AsWeakPtr(), request_id, callback));
  }
}

template <typename ResponseMessage>
void ServiceWorkerVersion::DispatchSimpleEvent(int request_id,
                                               const IPC::Message& message) {
  DispatchEvent<ResponseMessage>(
      request_id, message,
      base::Bind(&ServiceWorkerVersion::OnSimpleEventResponse, this));
}

template <typename ResponseMessage, typename CallbackType>
bool ServiceWorkerVersion::EventResponseHandler<ResponseMessage, CallbackType>::
    OnMessageReceived(const IPC::Message& message) {
  if (message.type() != ResponseMessage::ID)
    return false;
  int received_request_id;
  bool result = base::PickleIterator(message).ReadInt(&received_request_id);
  if (!result || received_request_id != request_id_)
    return false;

  CallbackType protect(callback_);
  // Essentially same code as what IPC_MESSAGE_FORWARD expands to.
  void* param = nullptr;
  if (!ResponseMessage::Dispatch(&message, &callback_, this, param,
                                 &CallbackType::Run))
    message.set_dispatch_error();

  // At this point |this| can have been deleted, so don't do anything other
  // than returning.

  return true;
}

}  // namespace content

#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_VERSION_H_

redpajama_set_name: RedPajamaGithub


text:
{% extends "base.html" %}
{% load staticfiles %}

{% block side_bar %}

{% endblock side_bar %}

{% block content %}

{% endblock content %}


text:
using System.Threading.Tasks;

namespace SuperGlue.CommandSender
{
    public interface IHandleCommand<in TCommand>
    {
        Task Handle(TCommand command);
    }
}


text:
'''
OpenGL
======

Select and use the best OpenGL library available. Depending on your system, the
core provider can select an OpenGL ES or a 'classic' desktop OpenGL library.
'''

from os import environ
from sys import platform, exit

MIN_REQUIRED_GL_VERSION = (2, 0)

def msgbox(message):
    if platform == 'win32':
        import win32ui
        win32ui.MessageBox(message, 'Kivy Fatal Error')
        exit(1)

if 'KIVY_DOC' not in environ:

    from kivy.logger import Logger
    from kivy.graphics import gl_init_resources
    from kivy.graphics.opengl_utils import gl_get_version
    from kivy.graphics.opengl import *
    from kivy.utils import platform

    def init_gl():
        gl_init_symbols()
        print_gl_version()
        gl_init_resources()

    def print_gl_version():
        version = glGetString(GL_VERSION)
        vendor = glGetString(GL_VENDOR)
        renderer = glGetString(GL_RENDERER)
        Logger.info('GL: OpenGL version <{0}>'.format(version))
        Logger.info('GL: OpenGL vendor <{0}>'.format(vendor))
        Logger.info('GL: OpenGL renderer <{0}>'.format(renderer))

        # Let the user know if his graphics hardware/drivers are too old
        major, minor = gl_get_version()
        Logger.info('GL: OpenGL parsed version: %d, %d' % (major, minor))
        if (major, minor) < MIN_REQUIRED_GL_VERSION:
            msg = (
                'GL: Minimum required OpenGL version (2.0) NOT found!\n\n'
                'OpenGL version detected: {0}.{1}\n\n'
                'Version: {2}\nVendor: {3}\nRenderer: {4}\n\n'
                'Try upgrading your graphics drivers and/or your '
                'graphics hardware in case of problems.\n\n'
                'The application will leave now.').format(
                        major, minor, version, vendor, renderer)
            Logger.critical(msg)
            msgbox(msg)

        if platform != 'android':
            # XXX in the android emulator (latest version at 22 march 2013),
            # this call was segfaulting the gl stack.
            Logger.info('GL: Shading version <{0}>'.format(
                glGetString(GL_SHADING_LANGUAGE_VERSION)))
        Logger.info('GL: Texture max size <{0}>'.format(
            glGetIntegerv(GL_MAX_TEXTURE_SIZE)[0]))
        Logger.info('GL: Texture max units <{0}>'.format(
            glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS)[0]))

    # To be able to use our GL provider, we must have a window
    # Automaticly import window auto to ensure the default window creation
    import kivy.core.window


text:
#!/usr/bin/env node

/**
 * Tmplify prototype.
 */

'use strict'

process.chdir(`${__dirname}/..`)

const path = require('path')
const fs = require('fs')
const { runTasks } = require('ape-tasking')
const tmplconv = require('tmplconv')

const scaffold = require('../lib')

let demo = path.dirname(require.resolve('apeman-demo-web/package.json'))
let demoPkg = require('apeman-demo-web/package.json')

runTasks('tmplify', [
  () => tmplconv.tmplify(demo, 'asset/tmpl', {
    clean: true,
    mode: '444',
    pattern: [
      '**/*.*',
      '.*',
      'src/.*.json',
      '*/.*.bud',
      '+(ui|test)/**/.*.bud',
      '*/.*.hbs'
    ],
    ignore: [
      'node_modules/**/*.*',
      'dist/**/*.*',
      'public/**/*.*',
      'shim/**/*.*',
      '.DS_Store',
      '.svg'
    ],
    data: {
      'github_repository': 'apeman-demo-labo/apeman-demo-web',
      'package_name': demoPkg.name,
      'package_description': demoPkg.description,
      'domain_name': 'demo-web.apeman.info',
      'app_name': 'Apeman Demo Web',
      'app_description': 'Demo web site with apeman framework.',
      'author_name': demoPkg.author.name,
      'author_email': demoPkg.author.email,
      'author_url': demoPkg.author.url,
      'ga_tracking_id': 'UA-88384782-1'
    },
    prefix: scaffold.prefix,
    suffix: scaffold.suffix
  }),

  () => tmplconv.tmplify(__dirname + '/..', 'asset/tmpl', {
    pattern: [
      '.gitignore'
    ],
    prefix: scaffold.prefix,
    suffix: scaffold.suffix
  }),
  (callback) => {
    let filename = require.resolve('../asset/tmpl/package.json.tmpl')
    let str = fs.readFileSync(filename).toString()
    let pkg = JSON.parse(str)
    fs.chmodSync(filename, '644')
    fs.writeFileSync(filename, JSON.stringify({
      name: pkg.name,
      version: '1.0.0',
      description: pkg.description,
      main: pkg.main,
      scripts: pkg.scripts,
      repository: pkg.repository.url.split(/\//g).slice(-1).join('/').replace(/\.git$/, ''),
      keywords: pkg.keywords,
      author: pkg.author,
      license: pkg.license,
      bugs: pkg.bugs,
      homepage: pkg.homepage,
      dependencies: pkg.dependencies,
      devDependencies: pkg.devDependencies,
      engines: pkg.engines
    }, null, 2))
    fs.chmodSync(filename, '444')
    callback(null)
  }
], true)


text:
short-description: Build options to configure project properties
...

# Build options

Most non-trivial builds require user-settable options. As an example a
program may have two different data backends that are selectable at
build time. Meson provides for this by having a option definition
file. Its name is `meson_options.txt` and it is placed at the root of
your source tree.

Here is a simple option file.

```meson
option('someoption', type : 'string', value : 'optval', description : 'An option')
option('other_one', type : 'boolean', value : false)
option('combo_opt', type : 'combo', choices : ['one', 'two', 'three'], value : 'three')
option('integer_opt', type : 'integer', min : 0, max : 5, value : 3) # Since 0.45.0
option('free_array_opt', type : 'array', value : ['one', 'two'])  # Since 0.44.0
option('array_opt', type : 'array', choices : ['one', 'two', 'three'], value : ['one', 'two'])
option('some_feature', type : 'feature', value : 'enabled')  # Since 0.47.0
option('long_desc', type : 'string', value : 'optval',
       description : 'An option with a very long description' +
                     'that does something in a specific context') # Since 0.55.0
```

For built-in options, see [Built-in options][builtin_opts].

## Build option types

All types allow a `description` value to be set describing the option,
if no description is set then the name of the option will be used instead.

### Strings

The string type is a free form string. If the default value is not set
then an empty string will be used as the default.

### Booleans

Booleans may have values of either `true` or `false`. If no default
value is supplied then `true` will be used as the default.

### Combos

A combo allows any one of the values in the `choices` parameter to be
selected.  If no default value is set then the first value will be the
default.

### Integers

An integer option contains a single integer with optional upper and
lower values that are specified with the `min` and `max` keyword
arguments.

This type is available since Meson version 0.45.0.

### Arrays

Arrays represent an array of strings. By default the array can contain
arbitrary strings. To limit the possible values that can used set the
`choices` parameter. Meson will then only allow the value array to
contain strings that are in the given list. The array may be
empty. The `value` parameter specifies the default value of the option
and if it is unset then the values of `choices` will be used as the
default.

As of 0.47.0 -Dopt= and -Dopt=[] both pass an empty list, before this
-Dopt= would pass a list with an empty string.

This type is available since version 0.44.0

### Features

A `feature` option has three states: `enabled`, `disabled` or `auto`.
It is intended to be passed as value for the `required` keyword
argument of most functions. Currently supported in
[`dependency()`](Reference-manual.md#dependency),
[`find_library()`](Reference-manual.md#compiler-object),
[`find_program()`](Reference-manual.md#find_program) and
[`add_languages()`](Reference-manual.md#add_languages) functions.

- `enabled` is the same as passing `required : true`.
- `auto` is the same as passing `required : false`.
- `disabled` do not look for the dependency and always return 'not-found'.

When getting the value of this type of option using `get_option()`, a
special [feature option
object](Reference-manual.md#feature-option-object) is returned instead
of the string representation of the option's value. This object can be
passed to `required`:

```meson
d = dependency('foo', required : get_option('myfeature'))
if d.found()
  app = executable('myapp', 'main.c', dependencies : [d])
endif
```

To check the value of the feature, the object has three methods
returning a boolean and taking no argument:

- `.enabled()`
- `.disabled()`
- `.auto()`

This is useful for custom code depending on the feature:

```meson
if get_option('myfeature').enabled()
  # ...
endif
```

If the value of a `feature` option is set to `auto`, that value is
overridden by the global `auto_features` option (which defaults to
`auto`). This is intended to be used by packagers who want to have
full control on which dependencies are required and which are
disabled, and not rely on build-deps being installed (at the right
version) to get a feature enabled. They could set
`auto_features=enabled` to enable all features and disable explicitly
only the few they don't want, if any.

This type is available since version 0.47.0

## Using build options

```meson
optval = get_option('opt_name')
```

This function also allows you to query the value of Meson's built-in
project options. For example, to get the installation prefix you would
issue the following command:

```meson
prefix = get_option('prefix')
```

It should be noted that you can not set option values in your Meson
scripts. They have to be set externally with the `meson configure`
command line tool. Running `meson configure` without arguments in a
build dir shows you all options you can set.

To change their values use the `-D`
option:

```console
$ meson configure -Doption=newvalue
```

Setting the value of arrays is a bit special. If you only pass a
single string, then it is considered to have all values separated by
commas. Thus invoking the following command:

```console
$ meson configure -Darray_opt=foo,bar
```

would set the value to an array of two elements, `foo` and `bar`.

If you need to have commas in your string values, then you need to
pass the value with proper shell quoting like this:

```console
$ meson configure "-Doption=['a,b', 'c,d']"
```

The inner values must always be single quotes and the outer ones
double quotes.

To change values in subprojects prepend the name of the subproject and
a colon:

```console
$ meson configure -Dsubproject:option=newvalue
```

**NOTE:** If you cannot call `meson configure` you likely have a old
  version of Meson. In that case you can call `mesonconf` instead, but
  that is deprecated in newer versions

## Yielding to superproject option

Suppose you have a master project and a subproject. In some cases it
might be useful to have an option that has the same value in both of
them. This can be achieved with the `yield` keyword. Suppose you have
an option definition like this:

```meson
option('some_option', type : 'string', value : 'value', yield : true)
```

If you build this project on its own, this option behaves like
usual. However if you build this project as a subproject of another
project which also has an option called `some_option`, then calling
`get_option` returns the value of the superproject. If the value of
`yield` is `false`, `get_option` returns the value of the subproject's
option.

## Built-in build options

There are a number of [built-in options][builtin_opts]. To get the
current list execute `meson configure` in the build directory.

[builtin_opts]: https://mesonbuild.com/Builtin-options.html

### Visual Studio

#### Startup project

The `backend_startup_project` option can be set to define the default
project that will be executed with the "Start debugging F5" action in
visual studio. It should be the same name as an executable target
name.

```meson
project('my_project', 'c', default_options: ['backend_startup_project=my_exe'])
executable('my_exe', ...)
```

### Ninja

#### Max links

The `backend_max_links` can be set to limit the number of processes
that ninja will use to link.


text:
using System.Collections;
using System.Collections.Generic;

using UnityEngine;
using UnityEngine.InputSystem;

namespace Stratus.Gameplay
{
	/// <summary>
	/// How a character is controlled
	/// </summary>
	public enum StratusCharacterControlMode
	{
		Manual,
		Automatic
	}

	/// <summary>
	/// Base class for a stratus character
	/// </summary>
	public abstract class StratusCharacter
	{
		public abstract string name { get; }
	}

	/// <summary>
	/// Models a character used in combat
	/// </summary>
	/// <typeparam name="DescriptionModel"></typeparam>
	/// <typeparam name="ProgressionModel"></typeparam>
	/// <typeparam name="AttributeModel"></typeparam>
	/// <typeparam name="SkillModel"></typeparam>
	public abstract class StratusCharacter<DescriptionModel,
								  ProgressionModel,
								  AttributeModel>
								  : StratusCharacter

	  where DescriptionModel : StratusDescriptionModel
	  where ProgressionModel : StratusProgressionModel
	  where AttributeModel : IStratusAttributeModel
	{
		//------------------------------------------------------------------------/
		// Fields
		//------------------------------------------------------------------------/
		/// <summary>
		/// How the character is described
		/// </summary>
		[Tooltip("How the character is described")]
		public DescriptionModel description;
		/// <summary>
		/// Progression attribute for this character
		/// </summary>
		public ProgressionModel progression;
		/// <summary>
		/// Attributes for a character
		/// </summary>
		public AttributeModel attributes;

		//------------------------------------------------------------------------/
		// Properties
		//------------------------------------------------------------------------/
		/// <summary>
		/// The name of this character
		/// </summary>
		public override string name => description.name;

		//------------------------------------------------------------------------/
		// Method
		//------------------------------------------------------------------------/
		public override string ToString()
		{
			return description.name;
		}
	}

	/// <summary>
	/// Data about a given character, ranging from attributes to equipped skills, etc
	/// </summary>
	public abstract class StratusCharacter<DescriptionModel,
							  ProgressionModel,
							  AttributeModel,
							  SkillModel>
							  : StratusCharacter<DescriptionModel, ProgressionModel, AttributeModel>
		where DescriptionModel : StratusDescriptionModel
		where ProgressionModel : StratusProgressionModel
		where AttributeModel : IStratusAttributeModel
		where SkillModel : IStratusCombatSkillModel, new()
	{
		//------------------------------------------------------------------------/
		// Fields
		//------------------------------------------------------------------------/
		/// <summary>
		/// The character's equipped skills
		/// </summary>
		public List<SkillModel> skills;
	}

}


text:
var pg = require('pg');
var Database = require("../Database");
var fs = require("fs");

var sourceUrl = process.env.SOURCE;
var targetUrl = process.env.MONGO_URL;

process.on('uncaughtException', function (err) {
    console.log(err.stack, "\n", err.message);
});

var parseString = require('xml2js').parseString;

var ideaParser = {
    parse: function(file, callback) {
        parseString(file, function(err, theme) {
            if (err || !theme) {
                return callback();
            }

            var styles = {};

            var scheme = theme.scheme;
            if (!scheme) {
                return callback();
            }

            var colors = scheme.colors && scheme.colors[0];
            if (colors && colors.option) {
                colors.option.forEach(function(option) {
                    styles[option.$.name.replace(/\./g, "$")] = { color: option.$.value, simple: true };
                });
            }

            var attributes = scheme.attributes && scheme.attributes[0];
            if (attributes && attributes.option) {
                attributes.option.forEach(function(option) {
                    var style = {};
                    var value = option.value && option.value[0];
                    if (value.option) {
                        value.option.forEach(function(vo) {
                            var val = vo.$.value;
                            switch(vo.$.name) {
                                case "FONT_TYPE":
                                    style.bold = (val & 1) !== 0;
                                    style.italic = (val & 2) !== 0;
                                    break;
                                case "FOREGROUND":
                                    style.color = val;
                                    break;
                                case "BACKGROUND":
                                    style.backgroundColor = val;
                                    break;
                                case "ERROR_STRIPE_COLOR":
                                    style.markerColor = val;
                                    break;
                                case "EFFECT_TYPE":
                                    style.effectType = parseInt(val);
                                    break;
                                case "EFFECT_COLOR":
                                    style.effectColor = val;
                                    break;
                            }
                        });
                    }
                    styles[option.$.name.replace(/\./g, "$")] = style;
                });
            }

            callback(null, styles);
        });
    }
};

var parserList = [ideaParser];

var parsers = {
    parse: function(file, callback) {
        var results = [];
        parserList.forEach(function(parser) {
            parser.parse(file, function(err, styles) {
                if (err) {
                    throw err;
                }
                results.push(styles);

                if (results.length == parserList.length) {
                    callback(null, results.filter(function(styles) { return !!styles; })[0]);
                }
            });
        });
    }
};

Database.connect(targetUrl,  function(err, models) {
    pg.connect(sourceUrl, function(err, client, done) {
        if(err) {
            throw err;
        }
        Database.models.Theme.find({}).remove(function(err) {
            if(err) {
                throw err;
            }
            client.query("SELECT * FROM backend_theme WHERE elements <> '' ORDER BY id", null, function(err, result) {
                done();

                if(err) {
                    throw err;
                }
                var index = 0;

                var processedThemes = [];

                processRow(result.rows[index++]);

                function genCss(styles) {
                    Object.keys(styles).forEach(function(key) {
                        var item = styles[key];
                        var css = "";
                        if (item.bold) {
                            css += "font-weight: bold; ";
                        }
                        if (item.italic) {
                            css += "font-style: italic; ";
                        }
                        if (item.color) {
                            css += "color: #" + item.color + "; ";
                        }
                        if (item.backgroundColor) {
                            css += "background-color: #" + item.backgroundColor + "; ";
                        }
                        if (item.effectColor !== undefined) {
                            if (item.effectType === 1) {
                                css += "border-bottom: 1px solid #" + item.effectColor + "; ";
                            }

                            if (item.effectType === 2) {
                                css += "border-bottom: 2px solid #" + item.effectColor + "; ";
                            }
                            if (item.effectType === 3) {
                                css += "border-bottom: 1px solid #" + item.effectColor + "; ";   //TODO: this is should be wavy line
                            }
                            if (item.effectType === 4) {
                                css += "border: 1px solid #" + item.effectColor + "; ";
                            }
                            if (item.effectType === 5) {
                                css += "text-decoration: line-through; text-decoration-color: " + item.effectColor + "; ";
                            }
                            if (item.effectType === 6) {
                                css += "border-bottom: 1px dotted #" + item.effectColor + "; ";
                            }
                        }
                        item.css = css;
                    });
                }

                function processRow(row) {
                    if (!row) {
                        fs.writeFile("themes.json", JSON.stringify(processedThemes), function() {
                            process.exit();
                        });
                        return;
                    }
                    console.log("<" + index + " " + row.name);
                    function processTheme(styles) {
                        genCss(styles);

                        var themeData = {
                            title: row.name,
                            styles: styles,
                            downloads: row.downloads,
                            date: row.date,
                            author: row.author,
                            website: row.website,
                            comment: row.comment
                        };
                        processedThemes.push(themeData);

                        var theme = new Database.models.Theme(themeData);
                        theme.save(function(err) {
                            if (err) {
                                throw err;
                            }
                            console.log(">" + index + " " + theme.title);
                            processRow(result.rows[index++]);
                        });
                    }

                    if (row.archive) {
                        fs.readFile(row.archive, function(err, file) {
                            if (err) {
                                throw  err;
                            }
                            parsers.parse(file, function(err, styles) {
                                if (err) {
                                    throw err;
                                }
                                processTheme(styles);
                            });
                        });
                    }
                    else {
                        processTheme(row.elements.split('\n').
                            map(function(line) { return line.split('=');}).
                            map(function(item) {
                                var value = item[1];
                                if (value[0] !== '(') {
                                    value = '(None,None,' + value +',None,None,None,None)'
                                    var simple = true;
                                }
                                value = value.substring(1, value.length - 1);
                                value = value.split(',').map(function(s) { return s.trim(); });
                                item[1] = {};
                                if (value[0] === 'True') { item[1].bold = true; }
                                if (value[1] === 'True') { item[1].italic = true; }
                                if (value[2] !== 'None') { item[1].color = value[2]; }
                                if (value[3] !== 'None') { item[1].backgroundColor = value[3]; }
                                if (value[4] !== 'None') { item[1].markerColor = value[4]; }
                                if (value[5] !== 'None') { item[1].effectType = parseInt(value[5], 10); }
                                if (value[6] !== 'None') { item[1].effectColor = value[6]; }
                                if (simple) { item[1].simple = true; }
                                return item;
                            }).
                            reduce(function(styles, item) {
                                styles[item[0]] = item[1];
                                return styles;
                            }, {}));
                    }
                }
            });
        });
    });

});


text:
package aot.view;

/**
 * @author Dmitry Kotlyarov
 * @since 1.0
 */
public interface EventSource {
    public Iterable<Event> getEvents(EventFilter filter);
}


text:
Fixes #[replace brackets with the issue number that your pull request addresses].

**Changes proposed in this pull request:**

- [list out summary of changes here]
- [list out summary of changes here]
- [list out summary of changes here]
- [etc]

**Newly added dependencies with [Bundlephobia](https://bundlephobia.com/) links:**

- [Link of the new dependency]
- [Link of the new dependency]
- [etc]

_Note: pull requests without proper descriptions may simply be closed without further discussion. We appreciate your contributions, but need to know what you are offering in clearly described format. Provide tests for all code that you add/modify. If you add/modify any components update the storybook. Thanks! (you can delete this text)_