text:
package com.facebook.buck.cli;

import static com.facebook.buck.testutil.integration.ProjectWorkspace.ProcessResult;
import static java.util.concurrent.Executors.callable;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;

import com.facebook.buck.android.AssumeAndroidPlatform;
import com.facebook.buck.android.FakeAndroidDirectoryResolver;
import com.facebook.buck.event.BuckEventListener;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.io.Watchman;
import com.facebook.buck.model.BuildId;
import com.facebook.buck.parser.ParserConfig;
import com.facebook.buck.rules.TestRepositoryBuilder;
import com.facebook.buck.rules.TestRunEvent;
import com.facebook.buck.testutil.TestConsole;
import com.facebook.buck.testutil.integration.DebuggableTemporaryFolder;
import com.facebook.buck.testutil.integration.DelegatingInputStream;
import com.facebook.buck.testutil.integration.ProjectWorkspace;
import com.facebook.buck.testutil.integration.TestContext;
import com.facebook.buck.testutil.integration.TestDataHelper;
import com.facebook.buck.util.CapturingPrintStream;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.environment.Platform;
import com.google.common.base.Charsets;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.eventbus.Subscribe;
import com.martiansoftware.nailgun.NGClientListener;
import com.martiansoftware.nailgun.NGContext;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class DaemonIntegrationTest {

  private static final int SUCCESS_EXIT_CODE = 0;
  private static final ImmutableList<String> TMP_ENV_VARS = ImmutableList.of(
      "BUCK_ORIG_TEMPDIR", "BUCK_ORIG_TEMP", "BUCK_ORIG_TMPDIR", "BUCK_ORIG_TMP");
  private ScheduledExecutorService executorService;

  private static String getTempDir() {
    // We can't use the default temporary directory or $TMP in this test, since they're overridden
    // by JUnitStep to 'buck-out/bin/...' which is not watched by watchman, causing this test to
    // fail.
    for (String envVar : TMP_ENV_VARS) {
      String val = System.getenv(envVar);
      if (val != null) {
        return val;
      }
    }
    return "/tmp";
  }

  @Rule
  public DebuggableTemporaryFolder tmp = new DebuggableTemporaryFolder(new File(getTempDir()));

  @Rule
  public ExpectedException thrown = ExpectedException.none();

  private static ImmutableMap<String, String> getWatchmanEnv() {
    ImmutableMap.Builder<String, String> envBuilder = ImmutableMap.builder();
    String systemPath = System.getenv("PATH");
    if (systemPath != null) {
      envBuilder.put("PATH", systemPath);
    }
    return envBuilder.build();
  }

  @Before
  public void setUp() throws IOException, InterruptedException{
    executorService = Executors.newScheduledThreadPool(5);
    // In case root_restrict_files is enabled in /etc/watchmanconfig, assume
    // this is one of the entries so it doesn't give up.
    tmp.newFolder(".git");
    Watchman watchman = Watchman.build(tmp.getRootPath(), getWatchmanEnv(), new TestConsole());

    // We assume watchman has been installed and configured properly on the system, and that setting
    // up the watch is successful.
    assumeFalse(watchman == Watchman.NULL_WATCHMAN);
  }

  @After
  public void tearDown() {
    Thread.interrupted(); // Clear interrupted flag, if set.
    executorService.shutdown();
    Main.resetDaemon();
  }

  /**
   * This verifies that when the user tries to run a read/write command, while another is already
   * running, the second call will fail. Serializing command execution in this way avoids
   * multiple threads accessing and corrupting the static state used by the Buck daemon and
   * trampling over each others output.
   */
  @Test
  public void whenConcurrentCommandExecutedThenSecondCommandFails()
      throws IOException, InterruptedException, ExecutionException {

    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();
    Future<?> firstThread = executorService.schedule(
        createRunnableCommand(SUCCESS_EXIT_CODE, "build", "//:sleep"),
        0,
        TimeUnit.MILLISECONDS);
    Future<?> secondThread = executorService.schedule(
        createRunnableCommand(Main.BUSY_EXIT_CODE, "build", "//:sleep"),
        500L,
        TimeUnit.MILLISECONDS);
    firstThread.get();
    secondThread.get();
  }

  /**
   * Verifies that a client timeout will be detected by a Nailgun
   * NGInputStream reading from a blocking heartbeat stream.
   */
  @Test(expected = InterruptedException.class)
  public void whenClientTimeoutDetectedThenMainThreadIsInterrupted()
      throws InterruptedException, IOException {
    final long timeoutMillis = 100;
    final long intervalMillis = timeoutMillis * 2; // Interval > timeout to trigger disconnection.
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();

    // Build an NGContext connected to an NGInputStream reading from a stream that will timeout.
    Thread.currentThread().setName("Test");
    try (TestContext context = new TestContext(
        ImmutableMap.copyOf(System.getenv()),
        TestContext.createHeartBeatStream(intervalMillis),
        timeoutMillis)) {
      final Thread commandThread = Thread.currentThread();
      context.addClientListener(
          new NGClientListener() {
            @Override
            public void clientDisconnected() throws InterruptedException {
              commandThread.interrupt();
            }
          });
      Thread.sleep(1000);
      fail("Should have been interrupted.");
    }
  }

  /**
   * This verifies that a client timeout will be detected by a Nailgun
   * NGInputStream reading from an empty heartbeat stream and that the generated
   * InterruptedException will cause command execution to fail after timeout.
   */
  @Test
  public void whenClientTimeoutDetectedThenBuildIsInterrupted()
      throws InterruptedException, IOException {

    // Sub process interruption not supported on Windows.
    assumeTrue(Platform.detect() != Platform.WINDOWS);

    final long timeoutMillis = 100;
    final long intervalMillis = timeoutMillis * 2; // Interval > timeout to trigger disconnection.
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();

    // Build an NGContext connected to an NGInputStream reading from stream that will timeout.
    try (TestContext context = new TestContext(
        ImmutableMap.copyOf(System.getenv()),
        TestContext.createHeartBeatStream(intervalMillis),
        timeoutMillis)) {
      ProcessResult result = workspace.runBuckdCommand(context, "build", "//:sleep");
      result.assertFailure();
      assertThat(result.getStderr(), containsString("InterruptedException"));
    }
  }

  @Test
  public void whenConcurrentReadOnlyCommandExecutedThenReadOnlyCommandSucceeds()
      throws IOException, InterruptedException, ExecutionException {
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();
    Future<?> firstThread = executorService.schedule(
        createRunnableCommand(SUCCESS_EXIT_CODE, "build", "//:sleep"), 0, TimeUnit.MILLISECONDS);
    Future<?> secondThread = executorService.schedule(
        createRunnableCommand(SUCCESS_EXIT_CODE, "targets"), 500L, TimeUnit.MILLISECONDS);
    firstThread.get();
    secondThread.get();
  }

  /**
   * This verifies that multiple read only commands can be executed concurrently successfully.
   */
  @Test
  public void whenReadOnlyCommandsExecutedConcurrentlyThenAllSucceed()
      throws IOException, InterruptedException, ExecutionException {

    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();
    executorService.invokeAll(
        ImmutableList.of(
            createCallableCommand(SUCCESS_EXIT_CODE, "audit", "input", "//:sleep"),
            createCallableCommand(SUCCESS_EXIT_CODE, "audit", "input", "//:sleep"),
            createCallableCommand(SUCCESS_EXIT_CODE, "audit", "input", "//:sleep"),
            createCallableCommand(SUCCESS_EXIT_CODE, "audit", "input", "//:sleep"),
            createCallableCommand(SUCCESS_EXIT_CODE, "audit", "input", "//:sleep"))
    );
  }

  private Runnable createRunnableCommand(final int expectedExitCode, final String ... args) {
    return new Runnable() {
      @Override
      public void run() {
        try {
          Main main = new Main(new CapturingPrintStream(), new CapturingPrintStream());
          int exitCode = main.tryRunMainWithExitCode(
              new BuildId(),
              tmp.getRootPath(),
              Optional.<NGContext>of(new TestContext()),
              ImmutableMap.copyOf(System.getenv()),
              args);
          assertEquals("Unexpected exit code.", expectedExitCode, exitCode);
        } catch (IOException e) {
          fail("Should not throw exception.");
          throw Throwables.propagate(e);
        } catch (InterruptedException e) {
          fail("Should not throw exception.");
          Thread.currentThread().interrupt();
        }
      }
    };
  }

  private Callable<Object> createCallableCommand(int expectedExitCode, String ... args) {
    return callable(createRunnableCommand(expectedExitCode, args));
  }

  /**
   * This verifies that a client disconnection will be detected by a Nailgun
   * NGInputStream reading from an empty heartbeat stream and that the generated
   * InterruptedException will interrupt command execution causing it to fail.
   */
  @Test
  public void whenClientTimeoutDetectedThenTestIsInterrupted()
      throws InterruptedException, IOException {

    // Sub process interruption not supported on Windows.
    assumeTrue(Platform.detect() != Platform.WINDOWS);

    final long timeoutMillis = 100;
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();

    // Start with an input stream that sends heartbeats at a regular rate.
    final DelegatingInputStream inputStream = new DelegatingInputStream(
        TestContext.createHeartBeatStream(timeoutMillis / 10));

    // Build an NGContext connected to an NGInputStream reading from stream that will timeout.
    try (TestContext context = new TestContext(
        ImmutableMap.copyOf(System.getenv()),
        inputStream,
        timeoutMillis)) {
      BuckEventListener listener = new BuckEventListener() {
        @Subscribe
        @SuppressWarnings("unused")
        public void onEvent(TestRunEvent.Started event) {
          // When tests start running, make the heartbeat stream time out.
          inputStream.setDelegate(TestContext.createHeartBeatStream(2 * timeoutMillis));
        }

        @Override
        public void outputTrace(BuildId buildId) throws InterruptedException {
          // do nothing
        }
      };
      ProcessResult result = workspace.runBuckdCommand(context, listener, "test", "//:test");
      result.assertFailure();
      assertThat(result.getStderr(), containsString("InterruptedException"));
    }
  }

  /**
   * This verifies that a client timeout will be detected by a Nailgun
   * NGInputStream reading from an empty heartbeat stream and that the generated
   * InterruptedException will cause command execution to fail after timeout.
   */
  @Test
  public void whenClientDisconnectionDetectedThenBuildIsInterrupted()
      throws InterruptedException, IOException {

    // Sub process interruption not supported on Windows.
    assumeTrue(Platform.detect() != Platform.WINDOWS);

    final long timeoutMillis = 2000; // Stream timeout > test timeout.
    final long disconnectMillis = 100; // Disconnect before test timeout.
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();

    // Build an NGContext connected to an NGInputStream reading from stream that will timeout.
    try (TestContext context = new TestContext(
        ImmutableMap.copyOf(System.getenv()),
        TestContext.createDisconnectionStream(disconnectMillis),
        timeoutMillis)) {
      ProcessResult result = workspace.runBuckdCommand(context, "build", "//:sleep");
      result.assertFailure();
      assertThat(result.getStderr(), containsString("InterruptedException"));
    }
  }

  /**
   * This verifies that a client disconnection will be detected by a Nailgun
   * NGInputStream reading from an empty heartbeat stream and that the generated
   * InterruptedException will interrupt command execution causing it to fail.
   */
  @Test
  public void whenClientDisconnectionDetectedThenTestIsInterrupted()
      throws InterruptedException, IOException {

    // Sub process interruption not supported on Windows.
    assumeTrue(Platform.detect() != Platform.WINDOWS);

    final long timeoutMillis = 2000; // Stream timeout > test timeout.
    final long disconnectMillis = 100; // Disconnect before test timeout.
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "exclusive_execution", tmp);
    workspace.setUp();

    // Start with an input stream that sends heartbeats at a regular rate.
    final DelegatingInputStream inputStream = new DelegatingInputStream(
        TestContext.createHeartBeatStream(timeoutMillis / 10));

    // Build an NGContext connected to an NGInputStream reading from stream that will timeout.
    try (TestContext context = new TestContext(
        ImmutableMap.copyOf(System.getenv()),
        inputStream,
        timeoutMillis)) {
      BuckEventListener listener = new BuckEventListener() {
        @Subscribe
        @SuppressWarnings("unused")
        public void onEvent(TestRunEvent.Started event) {
          // When tests start running, make the heartbeat stream simulate a disconnection.
          inputStream.setDelegate(TestContext.createDisconnectionStream(disconnectMillis));
        }

        @Override
        public void outputTrace(BuildId buildId) throws InterruptedException {
          // do nothing
        }
      };
      ProcessResult result = workspace.runBuckdCommand(context, listener, "test", "//:test");
      result.assertFailure();
      assertThat(result.getStderr(), containsString("InterruptedException"));
    }
  }

  @Test
  public void whenAppBuckFileRemovedThenRebuildFails()
      throws IOException, InterruptedException {
    AssumeAndroidPlatform.assumeSdkIsAvailable();

    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "file_watching", tmp);
    workspace.setUp();

    ProcessResult result = workspace.runBuckdCommand("build", "app");
    result.assertSuccess();

    String fileName = "apps/myapp/BUCK";
    Files.delete(workspace.getPath(fileName));

    workspace.runBuckdCommand("build", "app").assertFailure();
  }

  @Test
  public void whenActivityBuckFileRemovedThenRebuildFails()
      throws IOException, InterruptedException {
    AssumeAndroidPlatform.assumeSdkIsAvailable();
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "file_watching", tmp);
    workspace.setUp();

    workspace.runBuckdCommand("build", "//java/com/example/activity:activity").assertSuccess();

    String fileName = "java/com/example/activity/BUCK";
    Files.delete(workspace.getPath(fileName));

    workspace.runBuckdCommand("build", "//java/com/example/activity:activity").assertFailure();
  }

  @Test
  public void whenSourceInputRemovedThenRebuildFails()
      throws IOException, InterruptedException {
    AssumeAndroidPlatform.assumeNdkIsAvailable();
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "file_watching", tmp);
    workspace.setUp();

    workspace.runBuckdCommand("build", "//java/com/example/activity:activity").assertSuccess();

    String fileName = "java/com/example/activity/MyFirstActivity.java";
    Files.delete(workspace.getPath(fileName));

    try {
      workspace.runBuckdCommand("build", "//java/com/example/activity:activity");
      fail("Should have thrown HumanReadableException.");
    } catch (java.lang.RuntimeException e) {
      assertThat("Failure should have been due to file removal.", e.getMessage(),
          containsString("MyFirstActivity.java"));
    }
  }

  @Test
  public void whenSourceInputInvalidatedThenRebuildFails()
      throws IOException, InterruptedException {
    AssumeAndroidPlatform.assumeSdkIsAvailable();
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "file_watching", tmp);
    workspace.setUp();

    workspace.runBuckdCommand("build", "//java/com/example/activity:activity").assertSuccess();

    String fileName = "java/com/example/activity/MyFirstActivity.java";
    Files.delete(workspace.getPath(fileName));

    thrown.expect(HumanReadableException.class);
    thrown.expectMessage(containsString("MyFirstActivity.java"));

    workspace.runBuckdCommand("build", "//java/com/example/activity:activity");
  }

  @Test
  public void whenAppBuckFileInvalidatedThenRebuildFails()
      throws IOException, InterruptedException {
    AssumeAndroidPlatform.assumeSdkIsAvailable();
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "file_watching", tmp);
    workspace.setUp();

    workspace.runBuckdCommand("build", "app").assertSuccess();

    String fileName = "apps/myapp/BUCK";
    Files.write(workspace.getPath(fileName), "Some Illegal Python".getBytes(Charsets.US_ASCII));

    ProcessResult result = workspace.runBuckdCommand("build", "app");
    assertThat(
        "Failure should be due to syntax error.",
        result.getStderr(),
        containsString("SyntaxError: invalid syntax"));
    result.assertFailure();
  }

  @Test
  public void whenBuckConfigChangesParserInvalidated()
      throws IOException, InterruptedException {
    ProjectFilesystem filesystem = new ProjectFilesystem(tmp.getRoot().toPath());

    Object daemon = Main.getDaemon(
        new TestRepositoryBuilder().setBuckConfig(
            new FakeBuckConfig(
                ImmutableMap.of("somesection", ImmutableMap.of("somename", "somevalue"))))
            .setFilesystem(filesystem)
            .build(),
        ParserConfig.GlobHandler.PYTHON);
    assertEquals(
        "Daemon should not be replaced when config equal.", daemon,
        Main.getDaemon(
            new TestRepositoryBuilder().setBuckConfig(
                new FakeBuckConfig(
                    ImmutableMap.of("somesection", ImmutableMap.of("somename", "somevalue"))))
                .setFilesystem(filesystem)
                .build(),
            ParserConfig.GlobHandler.PYTHON));

    assertNotEquals(
        "Daemon should be replaced when config not equal.", daemon,
        Main.getDaemon(
            new TestRepositoryBuilder().setBuckConfig(
                new FakeBuckConfig(
                    ImmutableMap.of(
                        "somesection",
                        ImmutableMap.of("somename", "someothervalue"))))
                .setFilesystem(filesystem)
                .build(),
            ParserConfig.GlobHandler.PYTHON));
  }

  @Test
  public void whenBuckBuiltTwiceLogIsPresent()
      throws IOException, InterruptedException {
    AssumeAndroidPlatform.assumeSdkIsAvailable();
    final ProjectWorkspace workspace = TestDataHelper.createProjectWorkspaceForScenario(
        this, "file_watching", tmp);
    workspace.setUp();

    workspace.runBuckdCommand("build", "//java/com/example/activity:activity").assertSuccess();

    Path buildLogFile = workspace.getPath("buck-out/bin/build.log");

    assertTrue(Files.isRegularFile(buildLogFile));
    Files.delete(buildLogFile);

    ProcessResult rebuild =
        workspace.runBuckdCommand("build", "//java/com/example/activity:activity");
    rebuild.assertSuccess();

    buildLogFile = workspace.getPath("buck-out/bin/build.log");
    assertTrue(Files.isRegularFile(buildLogFile));
  }

  @Test
  public void whenAndroidDirectoryResolverChangesParserInvalidated()
      throws IOException, InterruptedException {
    ProjectFilesystem filesystem = new ProjectFilesystem(tmp.getRoot().toPath());

    Object daemon = Main.getDaemon(
        new TestRepositoryBuilder()
            .setAndroidDirectoryResolver(
                new FakeAndroidDirectoryResolver(
                    Optional.<Path>absent(),
                    Optional.<Path>absent(),
                    Optional.of("something")))
            .setFilesystem(filesystem)
            .build(),
        ParserConfig.GlobHandler.PYTHON);

    assertNotEquals(
        "Daemon should be replaced when not equal.", daemon,
        Main.getDaemon(
            new TestRepositoryBuilder()
                .setAndroidDirectoryResolver(
                    new FakeAndroidDirectoryResolver(
                        Optional.<Path>absent(),
                        Optional.<Path>absent(),
                        Optional.of("different")))
                .setFilesystem(filesystem)
                .build(),
            ParserConfig.GlobHandler.PYTHON));
  }
}


text:
package org.apache.hadoop.hdfs.server.namenode;

import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;

import java.util.HashSet;
import java.util.Set;

/**
 * Dynamically assign ids to users/groups as they appear in the external
 * filesystem.
 */
@InterfaceAudience.Public
@InterfaceStability.Unstable
public class FsUGIResolver extends UGIResolver {

  private int id;
  private final Set<String> usernames;
  private final Set<String> groupnames;

  FsUGIResolver() {
    super();
    id = 0;
    usernames = new HashSet<String>();
    groupnames = new HashSet<String>();
  }

  @Override
  public synchronized void addUser(String name) {
    if (!usernames.contains(name)) {
      addUser(name, id);
      id++;
      usernames.add(name);
    }
  }

  @Override
  public synchronized void addGroup(String name) {
    if (!groupnames.contains(name)) {
      addGroup(name, id);
      id++;
      groupnames.add(name);
    }
  }

}

redpajama_set_name: RedPajamaGithub


text:
Argot Message Format
============================

Argot Messages are a special form of data that contains a dictionary definition along with the payload matching that definition. The aim is to provide streamlined use in consumers by bypassing the need for them to explicitly maintain and load their own dictionaries. Internally, the messages are read using a version of the meta dictionary augmented with additional types from the core dictionary.

Further details can be found in the blog post [Argot Message Format - A self describing binary message format](http://blog.argot-sdk.org/2014/02/argot-message-format-self-describing.html)

## Header

Argot messages use a 8 bit magic number to mark their identity, the magic number is `65`. The magic number is then followed by a version identifier, this should be `1.3` (four bits each for major and minor).

## Body

The body comes in two parts, the first containing definitions and the second containing the message payload.

### Definitions

The definitions are in the same standard format that is used for reading dictionary files. Any number of definitions can be included in this payload, although for practical reasons it is prudent to keep the number of definitions and ensuing bandwidth low.

### Message

The message segment is read as standard messages are, starting with a Stream Id indicating the type, and then the type reader for that type will be able to handle the rest.


text:
[![CI](https://github.com/aik099/CodingStandard/actions/workflows/tests.yml/badge.svg)](https://github.com/aik099/CodingStandard/actions/workflows/tests.yml)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/aik099/CodingStandard/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/aik099/CodingStandard/?branch=master)
[![codecov](https://codecov.io/gh/aik099/CodingStandard/branch/master/graph/badge.svg)](https://codecov.io/gh/aik099/CodingStandard)

[![Latest Stable Version](https://poser.pugx.org/aik099/coding-standard/v/stable.png)](https://packagist.org/packages/aik099/coding-standard)
[![Total Downloads](https://poser.pugx.org/aik099/coding-standard/downloads.png)](https://packagist.org/packages/aik099/coding-standard)

The PHP_CodeSniffer coding standard I'm using on all of my projects.

Standard itself and it's test suite complies [PHPCS standard](https://github.com/squizlabs/PHP_CodeSniffer/tree/master/CodeSniffer/Standards/PHPCS).

# Usage
Only PHP_CodeSniffer 3.x and later versions are supported. For PHP_CodeSniffer 1.x and 2.x use "1.0" branch.

1. clone this repository
2. run following command in project directory:
```bash
$> phpcs --standard="/path/to/CodingStandard/CodingStandard" library tests
```
or by make your IDE ([instructions for PhpStorm](http://www.jetbrains.com/phpstorm/webhelp/using-php-code-sniffer-tool.html)) to check them automatically.


text:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

//#define DEBUG_RENDERING_FEEDBACK

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Ink;
using System.Windows.Media;
using System.Windows.Input;
using System.Diagnostics;

using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
using MS.Internal;
using MS.Internal.PresentationCore;

namespace MS.Internal.Ink
{
    /// <summary>
    /// An internal utility class that knows how to render a stroke
    /// into an Avalon's DrawingContext.
    /// </summary>
    internal static class StrokeRenderer
    {
        #region Static API

        /// <summary>
        /// Calculate the StreamGeometry for the StrokeNodes.
        /// This method is one of our most sensitive perf paths.  It has been optimized to
        /// create the minimum path figures in the StreamGeometry.  There are two structures
        /// we create for each point in a stroke, the strokenode and the connecting quad.  Adding
        /// strokenodes is very expensive later when MIL renders it, so this method has been optimized
        /// to only add strokenodes when either pressure changes, or the angle of the stroke changes.
        /// </summary>
        internal static void CalcGeometryAndBoundsWithTransform(StrokeNodeIterator iterator,
                                                               DrawingAttributes drawingAttributes,
                                                               MatrixTypes stylusTipMatrixType,
                                                               bool calculateBounds,
                                                               out Geometry geometry,
                                                               out Rect bounds)
        {
            Debug.Assert(iterator != null);
            Debug.Assert(drawingAttributes != null);

            StreamGeometry streamGeometry = new StreamGeometry();
            streamGeometry.FillRule = FillRule.Nonzero;

            StreamGeometryContext context = streamGeometry.Open();
            geometry = streamGeometry;
            bounds = Rect.Empty;
            try
            {
                List<Point> connectingQuadPoints = new List<Point>(iterator.Count * 4);

                //the index that the cb quad points are copied to
                int cdIndex = iterator.Count * 2;
                //the index that the ab quad points are copied to
                int abIndex = 0;
                for (int x = 0; x < cdIndex; x++)
                {
                    //initialize so we can start copying to cdIndex later
                    connectingQuadPoints.Add(new Point(0d, 0d));
                }

                List<Point> strokeNodePoints = new List<Point>();
                double lastAngle = 0.0d;
                bool previousPreviousNodeRendered = false;

                Rect lastRect = new Rect(0, 0, 0, 0);

                for (int index = 0; index < iterator.Count; index++)
                {
                    StrokeNode strokeNode = iterator[index];
                    System.Diagnostics.Debug.Assert(true == strokeNode.IsValid);

                    //the only code that calls this with !calculateBounds
                    //is dynamic rendering, which already draws enough strokeNodes
                    //to hide any visual artifacts.
                    //static rendering calculatesBounds, and we use those
                    //bounds below to figure out what angle to lay strokeNodes down for.
                    Rect strokeNodeBounds = strokeNode.GetBounds();
                    if (calculateBounds)
                    {
                        bounds.Union(strokeNodeBounds);
                    }

                    //if the angle between this and the last position has changed
                    //too much relative to the angle between the last+1 position and the last position
                    //we need to lay down stroke node
                    double delta = Math.Abs(GetAngleDeltaFromLast(strokeNode.PreviousPosition, strokeNode.Position, ref lastAngle));

                    double angleTolerance = 45d;
                    if (stylusTipMatrixType == MatrixTypes.TRANSFORM_IS_UNKNOWN)
                    {
                        //probably a skew is thrown in, we need to fall back to being very conservative
                        //about how many strokeNodes we prune
                        angleTolerance = 10d;
                    }
                    else if (strokeNodeBounds.Height > 40d || strokeNodeBounds.Width > 40d)
                    {
                        //if the strokeNode gets above a certain size, we need to lay down more strokeNodes
                        //to prevent visual artifacts
                        angleTolerance = 20d;
                    }
                    bool directionChanged = delta > angleTolerance && delta < (360d - angleTolerance);

                    double prevArea = lastRect.Height * lastRect.Width;
                    double currArea = strokeNodeBounds.Height * strokeNodeBounds.Width;
                    bool areaChangedOverThreshold = false;
                    if ((Math.Min(prevArea, currArea) / Math.Max(prevArea, currArea)) <= 0.70d)
                    {
                        //the min area is < 70% of the max area
                        areaChangedOverThreshold = true;
                    }

                    lastRect = strokeNodeBounds;

                    //render the stroke node for the first two nodes and last two nodes always
                    if (index <= 1 || index >= iterator.Count - 2 || directionChanged || areaChangedOverThreshold)
                    {
                        //special case... the direction has changed and we need to
                        //insert a stroke node in the StreamGeometry before we render the current one
                        if (directionChanged && !previousPreviousNodeRendered && index > 1 && index < iterator.Count - 1)
                        {
                            //insert a stroke node for the previous node
                            strokeNodePoints.Clear();
                            strokeNode.GetPreviousContourPoints(strokeNodePoints);
                            AddFigureToStreamGeometryContext(context, strokeNodePoints, strokeNode.IsEllipse/*isBezierFigure*/);

                            previousPreviousNodeRendered = true;
                        }

                        //render the stroke node
                        strokeNodePoints.Clear();
                        strokeNode.GetContourPoints(strokeNodePoints);
                        AddFigureToStreamGeometryContext(context, strokeNodePoints, strokeNode.IsEllipse/*isBezierFigure*/);
                    }

                    if (!directionChanged)
                    {
                        previousPreviousNodeRendered = false;
                    }

                    //add the end points of the connecting quad
                    Quad quad = strokeNode.GetConnectingQuad();
                    if (!quad.IsEmpty)
                    {
                        connectingQuadPoints[abIndex++] = quad.A;
                        connectingQuadPoints[abIndex++] = quad.B;
                        connectingQuadPoints.Add(quad.D);
                        connectingQuadPoints.Add(quad.C);
                    }

                    if (strokeNode.IsLastNode)
                    {
                        Debug.Assert(index == iterator.Count - 1);
                        if (abIndex > 0)
                        {
                            //we added something to the connecting quad points.
                            //now we need to do three things
                            //1) Shift the dc points down to the ab points
                            int cbStartIndex = iterator.Count * 2;
                            int cbEndIndex = connectingQuadPoints.Count - 1;
                            for (int i = abIndex, j = cbStartIndex; j <= cbEndIndex; i++, j++)
                            {
                                connectingQuadPoints[i] = connectingQuadPoints[j];
                            }

                            //2) trim the exess off the end of the array
                            int countToRemove = cbStartIndex - abIndex;
                            connectingQuadPoints.RemoveRange((cbEndIndex - countToRemove) + 1, countToRemove);

                            //3) reverse the dc points to make them cd points
                            for (int i = abIndex, j = connectingQuadPoints.Count - 1; i < j; i++, j--)
                            {
                                Point temp = connectingQuadPoints[i];
                                connectingQuadPoints[i] = connectingQuadPoints[j];
                                connectingQuadPoints[j] = temp;
                            }

                            //now render away!
                            AddFigureToStreamGeometryContext(context, connectingQuadPoints, false/*isBezierFigure*/);
                        }
                    }
                }
            }
            finally
            {
                context.Close();
                geometry.Freeze();
            }
        }

        /// <summary>
        /// Calculate the StreamGeometry for the StrokeNodes.
        /// This method is one of our most sensitive perf paths.  It has been optimized to
        /// create the minimum path figures in the StreamGeometry.  There are two structures
        /// we create for each point in a stroke, the strokenode and the connecting quad.  Adding
        /// strokenodes is very expensive later when MIL renders it, so this method has been optimized
        /// to only add strokenodes when either pressure changes, or the angle of the stroke changes.
        /// </summary>
        [FriendAccessAllowed]
        internal static void CalcGeometryAndBounds(StrokeNodeIterator iterator,
                                                   DrawingAttributes drawingAttributes,
#if DEBUG_RENDERING_FEEDBACK
                                                   DrawingContext debugDC,
                                                   double feedbackSize,
                                                   bool showFeedback,
#endif
                                                   bool calculateBounds,
                                                   out Geometry geometry,
                                                   out Rect bounds)
        {
            Debug.Assert(iterator != null && drawingAttributes != null);

            //we can use our new algorithm for identity only.
            Matrix stylusTipTransform = drawingAttributes.StylusTipTransform;
            if (stylusTipTransform != Matrix.Identity && stylusTipTransform._type != MatrixTypes.TRANSFORM_IS_SCALING)
            {
                //second best optimization
                CalcGeometryAndBoundsWithTransform(iterator, drawingAttributes, stylusTipTransform._type, calculateBounds, out geometry, out bounds);
            }
            else
            {
                StreamGeometry streamGeometry = new StreamGeometry();
                streamGeometry.FillRule = FillRule.Nonzero;

                StreamGeometryContext context = streamGeometry.Open();
                geometry = streamGeometry;
                Rect empty = Rect.Empty;
                bounds = empty;
                try
                {
                    //
                    // We keep track of three StrokeNodes as we iterate across
                    // the Stroke. Since these are structs, the default ctor will
                    // be called and .IsValid will be false until we initialize them
                    //
                    StrokeNode emptyStrokeNode = new StrokeNode();
                    StrokeNode prevPrevStrokeNode = new StrokeNode();
                    StrokeNode prevStrokeNode = new StrokeNode();
                    StrokeNode strokeNode = new StrokeNode();

                    Rect prevPrevStrokeNodeBounds = empty;
                    Rect prevStrokeNodeBounds = empty;
                    Rect strokeNodeBounds = empty;

                    //percentIntersect is a function of drawingAttributes height / width
                    double percentIntersect = 95d;
                    double maxExtent = Math.Max(drawingAttributes.Height, drawingAttributes.Width);
                    percentIntersect += Math.Min(4.99999d, ((maxExtent / 20d) * 5d));

                    double prevAngle = double.MinValue;
                    bool isStartOfSegment = true;
                    bool isEllipse = drawingAttributes.StylusTip == StylusTip.Ellipse;
                    bool ignorePressure = drawingAttributes.IgnorePressure;
                    //
                    // Two List<Point>'s that get reused for adding figures
                    // to the streamgeometry.
                    //
                    List<Point> pathFigureABSide = new List<Point>();//don't prealloc.  It causes Gen2 collections to rise and doesn't help execution time
                    List<Point> pathFigureDCSide = new List<Point>();
                    List<Point> polyLinePoints =  new List<Point>(4);

                    int iteratorCount = iterator.Count;
                    for (int index = 0, previousIndex = -1; index < iteratorCount; )
                    {
                        if (!prevPrevStrokeNode.IsValid)
                        {
                            if (prevStrokeNode.IsValid)
                            {
                                //we're sliding our pointers forward
                                prevPrevStrokeNode = prevStrokeNode;
                                prevPrevStrokeNodeBounds = prevStrokeNodeBounds;
                                prevStrokeNode = emptyStrokeNode;
                            }
                            else
                            {
                                prevPrevStrokeNode = iterator[index++, previousIndex++];
                                prevPrevStrokeNodeBounds = prevPrevStrokeNode.GetBounds();
                                continue; //so we always check if index < iterator.Count
                            }
                        }

                        //we know prevPrevStrokeNode is valid
                        if (!prevStrokeNode.IsValid)
                        {
                            if (strokeNode.IsValid)
                            {
                                //we're sliding our pointers forward
                                prevStrokeNode = strokeNode;
                                prevStrokeNodeBounds = strokeNodeBounds;
                                strokeNode = emptyStrokeNode;
                            }
                            else
                            {
                                //get the next strokeNode, but don't automatically update previousIndex
                                prevStrokeNode = iterator[index++, previousIndex];
                                prevStrokeNodeBounds = prevStrokeNode.GetBounds();

                                RectCompareResult result =
                                    FuzzyContains(  prevStrokeNodeBounds,
                                                    prevPrevStrokeNodeBounds,
                                                    isStartOfSegment ? 99.99999d : percentIntersect);

                                if (result == RectCompareResult.Rect1ContainsRect2)
                                {
                                    // this node already contains the prevPrevStrokeNodeBounds (PP):
                                    //
                                    //  |------------|
                                    //  | |----|     |
                                    //  | | PP |  P  |
                                    //  | |----|     |
                                    //  |------------|
                                    //
                                    prevPrevStrokeNode = iterator[index - 1, prevPrevStrokeNode.Index - 1]; ;
                                    prevPrevStrokeNodeBounds = Rect.Union(prevStrokeNodeBounds, prevPrevStrokeNodeBounds);

                                    // at this point prevPrevStrokeNodeBounds already contains this node
                                    // we can just ignore this node
                                    prevStrokeNode = emptyStrokeNode;

                                    // update previousIndex to point to this node
                                    previousIndex = index - 1;

                                    // go back to our main loop
                                    continue;
                                }
                                else if (result == RectCompareResult.Rect2ContainsRect1)
                                {
                                    // this prevPrevStrokeNodeBounds (PP) already contains this node:
                                    //
                                    //  |------------|
                                    //  |      |----||
                                    //  |  PP  | P  ||
                                    //  |      |----||
                                    //  |------------|
                                    //

                                    //prevPrevStrokeNodeBounds already contains this node
                                    //we can just ignore this node
                                    prevStrokeNode = emptyStrokeNode;

                                    // go back to our main loop, but do not update previousIndex
                                    // because it should continue to point to previousPrevious
                                    continue;
                                }

                                Debug.Assert(!prevStrokeNode.GetConnectingQuad().IsEmpty, "prevStrokeNode.GetConnectingQuad() is Empty!");

                                // if neither was true, we now have two of our three nodes required to
                                // start our computation, we need to update previousIndex to point
                                // to our current, valid prevStrokeNode
                                previousIndex = index - 1;
                                continue; //so we always check if index < iterator.Count
                            }
                        }

                        //we know prevPrevStrokeNode and prevStrokeNode are both valid
                        if (!strokeNode.IsValid)
                        {
                            strokeNode = iterator[index++, previousIndex];
                            strokeNodeBounds = strokeNode.GetBounds();

                            RectCompareResult result =
                                    FuzzyContains(  strokeNodeBounds,
                                                    prevStrokeNodeBounds,
                                                    isStartOfSegment ? 99.99999 : percentIntersect);

                            RectCompareResult result2 =
                                    FuzzyContains(  strokeNodeBounds,
                                                    prevPrevStrokeNodeBounds,
                                                    isStartOfSegment ? 99.99999 : percentIntersect);

                            if ( isStartOfSegment &&
                                 result == RectCompareResult.Rect1ContainsRect2 &&
                                 result2 == RectCompareResult.Rect1ContainsRect2)
                            {
                                if (pathFigureABSide.Count > 0)
                                {
                                    //we've started a stroke, we need to end it before resetting
                                    //prevPrev
#if DEBUG_RENDERING_FEEDBACK
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                    //render
                                    ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/);
                                }
                                //we're resetting
                                //prevPrevStrokeNode.  We need to gen one
                                //without a connecting quad
                                prevPrevStrokeNode = iterator[index - 1, prevPrevStrokeNode.Index - 1];
                                prevPrevStrokeNodeBounds = prevPrevStrokeNode.GetBounds();
                                prevStrokeNode = emptyStrokeNode;
                                strokeNode = emptyStrokeNode;

                                // increment previousIndex to to point to this node
                                previousIndex = index - 1;
                                continue;
}
                            else if (result == RectCompareResult.Rect1ContainsRect2)
                            {
                                // this node (C) already contains the prevStrokeNodeBounds (P):
                                //
                                //          |------------|
                                //  |----|  | |----|     |
                                //  | PP |  | | P  |  C  |
                                //  |----|  | |----|     |
                                //          |------------|
                                //
                                //we have to generate a new stroke node that points
                                //to pp since the connecting quad from C to P could be empty
                                //if they have the same point
                                strokeNode = iterator[index - 1, prevStrokeNode.Index - 1];
                                if (!strokeNode.GetConnectingQuad().IsEmpty)
                                {
                                    //only update prevStrokeNode if we have a valid connecting quad
                                    prevStrokeNode = strokeNode;
                                    prevStrokeNodeBounds = Rect.Union(strokeNodeBounds, prevStrokeNodeBounds);

                                    // update previousIndex, since it should point to this node now
                                    previousIndex = index - 1;
                                }

                                // at this point we can just ignore this node
                                strokeNode = emptyStrokeNode;
                                //strokeNodeBounds = empty;

                                prevAngle = double.MinValue; //invalidate

                                // go back to our main loop
                                continue;
                            }
                            else if (result == RectCompareResult.Rect2ContainsRect1)
                            {
                                // this prevStrokeNodeBounds (P) already contains this node (C):
                                //
                                //          |------------|
                                // |----|   |      |----||
                                // | PP |   |  P   | C  ||
                                // |----|   |      |----||
                                //          |------------|
                                //
                                //prevStrokeNodeBounds already contains this node
                                //we can just ignore this node
                                strokeNode = emptyStrokeNode;

                                // go back to our main loop, but do not update previousIndex
                                // because it should continue to point to previous
                                continue;
                            }

                            Debug.Assert(!strokeNode.GetConnectingQuad().IsEmpty, "strokeNode.GetConnectingQuad was empty, this is unexpected");

                            //
                            // NOTE: we do not check if C contains PP, or PP contains C because
                            // that indicates a change in direction, which we handle below
                            //
                            // if neither was true P and C are separate,
                            // we now have all three nodes required to
                            // start our computation, we need to update previousIndex to point
                            // to our current, valid prevStrokeNode
                            previousIndex = index - 1;
                        }

                        // see if we have an overlap between the first and third node
                        bool overlap = prevPrevStrokeNodeBounds.IntersectsWith(strokeNodeBounds);

                        // prevPrevStrokeNode, prevStrokeNode and strokeNode are all
                        // valid nodes now.  Now we need to figure out what do add to our
                        // PathFigure.  First calc bounds on the strokeNode we know we need to render
                        if (calculateBounds)
                        {
                            bounds.Union(prevStrokeNodeBounds);
                        }

                        // determine what points to add to pathFigureABSide and pathFigureDCSide
                        // from prevPrevStrokeNode
                        if (pathFigureABSide.Count == 0)
                        {
                            Debug.Assert(pathFigureDCSide.Count == 0);
                            if (calculateBounds)
                            {
                                bounds.Union(prevPrevStrokeNodeBounds);
                            }

                            if (isStartOfSegment && overlap)
                            {
                                //render a complete first stroke node or we can get artifacts
                                prevPrevStrokeNode.GetContourPoints(polyLinePoints);
                                AddFigureToStreamGeometryContext(context, polyLinePoints, prevPrevStrokeNode.IsEllipse/*isBezierFigure*/);
                                polyLinePoints.Clear();
                            }

                            // we're starting a new pathfigure
                            // we need to add parts of the prevPrevStrokeNode contour
                            // to pathFigureABSide and pathFigureDCSide
#if DEBUG_RENDERING_FEEDBACK
                            prevStrokeNode.GetPointsAtStartOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                            prevStrokeNode.GetPointsAtStartOfSegment(pathFigureABSide, pathFigureDCSide);
#endif

                            //set our marker, we're no longer at the start of the stroke
                            isStartOfSegment = false;
                        }

                        if (prevAngle == double.MinValue)
                        {
                            //prevAngle is no longer valid
                            prevAngle = GetAngleBetween(prevPrevStrokeNode.Position, prevStrokeNode.Position);
                        }
                        double delta = GetAngleDeltaFromLast(prevStrokeNode.Position, strokeNode.Position, ref prevAngle);
                        bool directionChangedOverAbsoluteThreshold = Math.Abs(delta) > 90d && Math.Abs(delta) < (360d - 90d);
                        bool directionChangedOverOverlapThreshold = overlap && !(ignorePressure || strokeNode.PressureFactor == 1f) && Math.Abs(delta) > 30d && Math.Abs(delta) < (360d - 30d);

                        double prevArea = prevStrokeNodeBounds.Height * prevStrokeNodeBounds.Width;
                        double currArea = strokeNodeBounds.Height * strokeNodeBounds.Width;

                        bool areaChanged = !(prevArea == currArea && prevArea == (prevPrevStrokeNodeBounds.Height * prevPrevStrokeNodeBounds.Width));
                        bool areaChangeOverThreshold = false;
                        if (overlap && areaChanged)
                        {
                            if ((Math.Min(prevArea, currArea) / Math.Max(prevArea, currArea)) <= 0.90d)
                            {
                                //the min area is < 70% of the max area
                                areaChangeOverThreshold = true;
                            }
                        }

                        if (areaChanged || delta != 0.0d || index >= iteratorCount)
                        {
                            //the area changed between the three nodes OR there was an angle delta OR we're at the end
                            //of the stroke...  either way, this is a significant node.  If not, we're going to drop it.
                            if ((overlap && (directionChangedOverOverlapThreshold || areaChangeOverThreshold)) ||
                                directionChangedOverAbsoluteThreshold)
                            {
                                //
                                // we need to stop the pathfigure at P
                                // and render the pathfigure
                                //
                                //  |--|      |--|    |--||--|   |------|
                                //  |PP|------|P |    |PP||P |   |PP P C|
                                //  |--|      |--|    |--||--|   |------|
                                //           /           |C |
                                //      |--|             |--|
                                //      |C |
                                //      |--|

#if DEBUG_RENDERING_FEEDBACK
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                //end the figure
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                //render
                                ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/);

                                if (areaChangeOverThreshold)
                                {
                                    //render a complete stroke node or we can get artifacts
                                    prevStrokeNode.GetContourPoints(polyLinePoints);
                                    AddFigureToStreamGeometryContext(context, polyLinePoints, prevStrokeNode.IsEllipse/*isBezierFigure*/);
                                    polyLinePoints.Clear();
                                }
                            }
                            else
                            {
                                //
                                // direction didn't change over the threshold, add the midpoint data
                                //  |--|      |--|
                                //  |PP|------|P |
                                //  |--|      |--|
                                //                \
                                //                  |--|
                                //                  |C |
                                //                  |--|
                                bool endSegment; //flag that tell us if we missed an intersection
#if DEBUG_RENDERING_FEEDBACK
                                strokeNode.GetPointsAtMiddleSegment(prevStrokeNode, delta, pathFigureABSide, pathFigureDCSide, out endSegment, debugDC, feedbackSize, showFeedback);
#else
                                strokeNode.GetPointsAtMiddleSegment(prevStrokeNode, delta, pathFigureABSide, pathFigureDCSide, out endSegment);
#endif
                                if (endSegment)
                                {
                                    //we have a missing intersection, we need to end the
                                    //segment at P
#if DEBUG_RENDERING_FEEDBACK
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                    //end the figure
                                    prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                    //render
                                    ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, true/*clear the point collections*/);
                                }
                             }
                        }

                        //
                        // either way... slide our pointers forward, to do this, we simply mark
                        // our first pointer as 'empty'
                        //
                        prevPrevStrokeNode = emptyStrokeNode;
                        prevPrevStrokeNodeBounds = empty;
}

                    //
                    // anything left to render?
                    //
                    if (prevPrevStrokeNode.IsValid)
                    {
                        if (prevStrokeNode.IsValid)
                        {
                            if (calculateBounds)
                            {
                                bounds.Union(prevPrevStrokeNodeBounds);
                                bounds.Union(prevStrokeNodeBounds);
                            }
                            Debug.Assert(!strokeNode.IsValid);
                            //
                            // we never made it to strokeNode, render two points, OR
                            // strokeNode was a dupe
                            //
                            if (pathFigureABSide.Count > 0)
                            {
#if DEBUG_RENDERING_FEEDBACK
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                                //
                                // strokeNode was a dupe, we just need to render the end of the stroke
                                // which is at prevStrokeNode
                                //
                                prevStrokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif
                                //render
                                ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, false/*clear the point collections*/);
                            }
                            else
                            {
                                // we've only seen two points to render
                                Debug.Assert(pathFigureDCSide.Count == 0);
                                //contains all the logic to render two stroke nodes
                                RenderTwoStrokeNodes(   context,
                                                        prevPrevStrokeNode,
                                                        prevPrevStrokeNodeBounds,
                                                        prevStrokeNode,
                                                        prevStrokeNodeBounds,
                                                        pathFigureABSide,
                                                        pathFigureDCSide,
                                                        polyLinePoints
#if DEBUG_RENDERING_FEEDBACK
                                                       ,debugDC,
                                                       feedbackSize,
                                                       showFeedback
#endif
                                                    );
}
                        }
                        else
                        {
                            if (calculateBounds)
                            {
                                bounds.Union(prevPrevStrokeNodeBounds);
                            }

                            // we only have a single point to render
                            Debug.Assert(pathFigureABSide.Count == 0);
                            prevPrevStrokeNode.GetContourPoints(pathFigureABSide);
                            AddFigureToStreamGeometryContext(context, pathFigureABSide, prevPrevStrokeNode.IsEllipse/*isBezierFigure*/);
}
                    }
                    else if (prevStrokeNode.IsValid && strokeNode.IsValid)
                    {
                        if (calculateBounds)
                        {
                            bounds.Union(prevStrokeNodeBounds);
                            bounds.Union(strokeNodeBounds);
                        }

                        // typical case, we hit the end of the stroke
                        // see if we need to start a stroke, or just end one
                        if (pathFigureABSide.Count > 0)
                        {
#if DEBUG_RENDERING_FEEDBACK
                            strokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide, debugDC, feedbackSize, showFeedback);
#else
                            strokeNode.GetPointsAtEndOfSegment(pathFigureABSide, pathFigureDCSide);
#endif

                            //render
                            ReverseDCPointsRenderAndClear(context, pathFigureABSide, pathFigureDCSide, polyLinePoints, isEllipse, false/*clear the point collections*/);

                            if (FuzzyContains(strokeNodeBounds, prevStrokeNodeBounds, 70d) != RectCompareResult.NoItersection)
                            {
                                //render a complete stroke node or we can get artifacts
                                strokeNode.GetContourPoints(polyLinePoints);
                                AddFigureToStreamGeometryContext(context, polyLinePoints, strokeNode.IsEllipse/*isBezierFigure*/);
                            }
                        }
                        else
                        {
                            Debug.Assert(pathFigureDCSide.Count == 0);
                            //contains all the logic to render two stroke nodes
                            RenderTwoStrokeNodes(   context,
                                                    prevStrokeNode,
                                                    prevStrokeNodeBounds,
                                                    strokeNode,
                                                    strokeNodeBounds,
                                                    pathFigureABSide,
                                                    pathFigureDCSide,
                                                    polyLinePoints
#if DEBUG_RENDERING_FEEDBACK
                                                   ,debugDC,
                                                   feedbackSize,
                                                   showFeedback
#endif
                                                );
}
                    }
                }
                finally
                {
                    context.Close();
                    geometry.Freeze();
                }
            }
        }

        /// <summary>
        /// Helper routine to render two distinct stroke nodes
        /// </summary>
        private static void RenderTwoStrokeNodes(   StreamGeometryContext context,
                                                    StrokeNode strokeNodePrevious,
                                                    Rect strokeNodePreviousBounds,
                                                    StrokeNode strokeNodeCurrent,
                                                    Rect strokeNodeCurrentBounds,
                                                    List<Point> pointBuffer1,
                                                    List<Point> pointBuffer2,
                                                    List<Point> pointBuffer3
#if DEBUG_RENDERING_FEEDBACK
                                                   ,DrawingContext debugDC,
                                                   double feedbackSize,
                                                   bool showFeedback
#endif
                                                    )
        {
            Debug.Assert(pointBuffer1 != null);
            Debug.Assert(pointBuffer2 != null);
            Debug.Assert(pointBuffer3 != null);
            Debug.Assert(context != null);

            //see if we need to render a quad - if there is not at least a 70% overlap
            if (FuzzyContains(strokeNodePreviousBounds, strokeNodeCurrentBounds, 70d) != RectCompareResult.NoItersection)
            {
                //we're between 100% and 70% overlapped
                //just render two distinct figures with a connecting quad (if needed)
                strokeNodePrevious.GetContourPoints(pointBuffer1);
                AddFigureToStreamGeometryContext(context, pointBuffer1, strokeNodePrevious.IsEllipse/*isBezierFigure*/);

                Quad quad = strokeNodeCurrent.GetConnectingQuad();
                if (!quad.IsEmpty)
                {
                    pointBuffer3.Add(quad.A);
                    pointBuffer3.Add(quad.B);
                    pointBuffer3.Add(quad.C);
                    pointBuffer3.Add(quad.D);
                    AddFigureToStreamGeometryContext(context, pointBuffer3, false/*isBezierFigure*/);
                }

                strokeNodeCurrent.GetContourPoints(pointBuffer2);
                AddFigureToStreamGeometryContext(context, pointBuffer2, strokeNodeCurrent.IsEllipse/*isBezierFigure*/);
            }
            else
            {
                //we're less than 70% overlapped, it's safe to run our optimization
#if DEBUG_RENDERING_FEEDBACK
                strokeNodeCurrent.GetPointsAtStartOfSegment(pointBuffer1, pointBuffer2, debugDC, feedbackSize, showFeedback);
                strokeNodeCurrent.GetPointsAtEndOfSegment(pointBuffer1, pointBuffer2, debugDC, feedbackSize, showFeedback);
#else
                strokeNodeCurrent.GetPointsAtStartOfSegment(pointBuffer1, pointBuffer2);
                strokeNodeCurrent.GetPointsAtEndOfSegment(pointBuffer1, pointBuffer2);
#endif
                //render
                ReverseDCPointsRenderAndClear(context, pointBuffer1, pointBuffer2, pointBuffer3, strokeNodeCurrent.IsEllipse, false/*clear the point collections*/);
            }
        }

        /// <summary>
        /// ReverseDCPointsRenderAndClear
        /// </summary>
        private static void ReverseDCPointsRenderAndClear(StreamGeometryContext context, List<Point> abPoints, List<Point> dcPoints, List<Point> polyLinePoints, bool isEllipse, bool clear)
        {
            //we need to reverse the cd side points
            Point temp;
            for (int i = 0, j = dcPoints.Count - 1; i < j; i++, j--)
            {
                temp = dcPoints[i];
                dcPoints[i] = dcPoints[j];
                dcPoints[j] = temp;
            }
            if (isEllipse)
            {
                AddArcToFigureToStreamGeometryContext(context, abPoints, dcPoints, polyLinePoints);
            }
            else
            {
                //for rectangles, render a single path figure by combining both sides
                AddPolylineFigureToStreamGeometryContext(context, abPoints, dcPoints);
            }

            if (clear)
            {
                abPoints.Clear();
                dcPoints.Clear();
            }
        }
        /// <summary>
        /// FuzzyContains for two rects
        /// </summary>
        private static RectCompareResult FuzzyContains(Rect rect1, Rect rect2, double percentIntersect)
        {
            Debug.Assert(percentIntersect >= 0.0 && percentIntersect <= 100.0d);

            double intersectLeft = Math.Max(rect1.Left, rect2.Left);
            double intersectTop = Math.Max(rect1.Top, rect2.Top);
            double intersectWidth = Math.Max((double)(Math.Min(rect1.Right, rect2.Right) - intersectLeft), (double)0);
            double intersectHeight = Math.Max((double)(Math.Min(rect1.Bottom, rect2.Bottom) - intersectTop), (double)0);

            if (intersectWidth == 0.0d || intersectHeight == 0.0d)
            {
                return RectCompareResult.NoItersection;
            }

            //we have an intersection, see if it is enough
            double rect1Area = rect1.Height * rect1.Width;
            double rect2Area = rect2.Height * rect2.Width;
            double minArea = Math.Min(rect1Area, rect2Area);
            double intersectionArea = intersectWidth * intersectHeight;
            double intersect = (intersectionArea / minArea) * 100d;
            if (intersect >= percentIntersect)
            {
                if (rect1Area >= rect2Area)
                {
                    return RectCompareResult.Rect1ContainsRect2;
                }
                return RectCompareResult.Rect2ContainsRect1;
            }

            return RectCompareResult.NoItersection;
        }

        /// <summary>
        /// Private helper to render a path figure to the SGC
        /// </summary>
        private static void AddFigureToStreamGeometryContext(StreamGeometryContext context, List<Point> points, bool isBezierFigure)
        {
            Debug.Assert(context != null);
            Debug.Assert(points != null);
            Debug.Assert(points.Count > 0);

            context.BeginFigure(points[points.Count - 1], //start point
                                        true,   //isFilled
                                        true);  //IsClosed

            if (isBezierFigure)
            {
                context.PolyBezierTo(points,
                                     true,      //isStroked
                                     true);     //isSmoothJoin
            }
            else
            {
                context.PolyLineTo(points,
                                     true,      //isStroked
                                     true);     //isSmoothJoin
            }
        }

        /// <summary>
        /// Private helper to render a path figure to the SGC
        /// </summary>
        private static void AddPolylineFigureToStreamGeometryContext(StreamGeometryContext context, List<Point> abPoints, List<Point> dcPoints)
        {
            Debug.Assert(context != null);
            Debug.Assert(abPoints != null && dcPoints != null);
            Debug.Assert(abPoints.Count > 0 && dcPoints.Count > 0);

            context.BeginFigure(abPoints[0], //start point
                                        true,   //isFilled
                                        true);  //IsClosed

            context.PolyLineTo(abPoints,
                                 true,      //isStroked
                                 true);     //isSmoothJoin

            context.PolyLineTo(dcPoints,
                                 true,      //isStroked
                                 true);     //isSmoothJoin
}

        /// <summary>
        /// Private helper to render a path figure to the SGC
        /// </summary>
        private static void AddArcToFigureToStreamGeometryContext(StreamGeometryContext context, List<Point> abPoints, List<Point> dcPoints, List<Point> polyLinePoints)
        {
            Debug.Assert(context != null);
            Debug.Assert(abPoints != null && dcPoints != null);
            Debug.Assert(polyLinePoints != null);
            //Debug.Assert(abPoints.Count > 0 && dcPoints.Count > 0);
            if (abPoints.Count == 0 || dcPoints.Count == 0)
            {
                return;
            }

            context.BeginFigure(abPoints[0], //start point
                                        true,   //isFilled
                                        true);  //IsClosed

            for (int j = 0; j < 2; j++)
            {
                List<Point> points = j == 0 ? abPoints : dcPoints;
                int startIndex = j == 0 ? 1 : 0;
                for (int i = startIndex; i < points.Count; )
                {
                    Point next = points[i];
                    if (next == StrokeRenderer.ArcToMarker)
                    {
                        if (polyLinePoints.Count > 0)
                        {
                            //polyline first
                            context.PolyLineTo(  polyLinePoints,
                                                 true,      //isStroked
                                                 true);     //isSmoothJoin
                            polyLinePoints.Clear();
                        }
                        //we're arcing, pull out height, width and the arc to point
                        Debug.Assert(i + 2 < points.Count);
                        if (i + 2 < points.Count)
                        {
                            Point sizePoint = points[i + 1];
                            Size ellipseSize = new Size(sizePoint.X / 2/*width*/, sizePoint.Y / 2/*height*/);
                            Point arcToPoint = points[i + 2];

                            bool isLargeArc = false; //>= 180

                            context.ArcTo(  arcToPoint,
                                            ellipseSize,
                                            0d,             //rotation
                                            isLargeArc,     //isLargeArc
                                            SweepDirection.Clockwise,
                                            true,           //isStroked
                                            true);          //isSmoothJoin
                        }
                        i += 3; //advance past this arcTo block
                    }
                    else
                    {
                        //walk forward until we find an arc marker or the end
                        polyLinePoints.Add(next);
                        i++;
                    }
                }
                if (polyLinePoints.Count > 0)
                {
                    //polyline
                    context.PolyLineTo(polyLinePoints,
                                         true,      //isStroked
                                         true);     //isSmoothJoin
                    polyLinePoints.Clear();
                }
            }
        }

        /// <summary>
        /// calculates the angle between the previousPosition and the current one and then computes the delta between
        /// the lastAngle.  lastAngle is also updated
        /// </summary>
        private static double GetAngleDeltaFromLast(Point previousPosition, Point currentPosition, ref double lastAngle)
        {
            double delta = 0.0d;

            //input points typically come in very close to each other
            double dx = (currentPosition.X * 1000) - (previousPosition.X * 1000);
            double dy = (currentPosition.Y * 1000) - (previousPosition.Y * 1000);
            if ((Int64)dx == 0 && (Int64)dy == 0)
            {
                //the points are close enough not to matter
                //don't update lastAngle
                return delta;
            }

            double angle = GetAngleBetween(previousPosition, currentPosition);

            //special case when angle / lastAngle span 0 degrees
            if (lastAngle >= 270 && angle <= 90)
            {
                delta = lastAngle - (360d + angle);
            }
            else if (lastAngle <= 90 && angle >= 270)
            {
                delta = (360d + lastAngle) - angle;
            }
            else
            {
                delta = (lastAngle - angle);
            }
            lastAngle = angle;

            // Return
            return delta;
        }

        /// <summary>
        /// calculates the angle between the previousPosition and the current one and then computes the delta between
        /// the lastAngle.  lastAngle is also updated
        /// </summary>
        private static double GetAngleBetween(Point previousPosition, Point currentPosition)
        {
            double angle = 0.0d;

            //input points typically come in very close to each other
            double dx = (currentPosition.X * 1000) - (previousPosition.X * 1000);
            double dy = (currentPosition.Y * 1000) - (previousPosition.Y * 1000);
            if ((Int64)dx == 0 && (Int64)dy == 0)
            {
                //the points are close enough not to matter
                return angle;
            }

            // Calculate angle
            if (dx == 0.0)
            {
                if (dy == 0.0)
                {
                    angle = 0.0;
                }
                else if (dy > 0.0)
                {
                    angle = Math.PI / 2.0;
                }
                else
                {
                    angle = Math.PI * 3.0 / 2.0;
                }
            }
            else if (dy == 0.0)
            {
                if (dx > 0.0)
                {
                    angle = 0.0;
                }
                else
                {
                    angle = Math.PI;
                }
            }
            else
            {
                if (dx < 0.0)
                {
                    angle = Math.Atan(dy / dx) + Math.PI;
                }
                else if (dy < 0.0)
                {
                    angle = Math.Atan(dy / dx) + (2 * Math.PI);
                }
                else
                {
                    angle = Math.Atan(dy / dx);
                }
            }

            // Convert to degrees
            angle = angle * 180 / Math.PI;

            // Return
            return angle;
        }

        /// <summary>
        /// Get the DrawingAttributes to use for a highlighter stroke. The return value is a copy of
        /// the DA passed in if color.A != 255 with color.A overriden to be 255. Otherwise it returns
        /// the DA passed in.
        /// </summary>
        internal static DrawingAttributes GetHighlighterAttributes(Stroke stroke, DrawingAttributes da)
        {
            System.Diagnostics.Debug.Assert(da.IsHighlighter = true);
            if (da.Color.A != SolidStrokeAlpha)
            {
                DrawingAttributes copy = stroke.DrawingAttributes.Clone();
                copy.Color = GetHighlighterColor(copy.Color);
                return copy;
            }

            return da;
        }

        /// <summary>
        /// Get the color used to draw a highlighter.
        /// </summary>
        internal static Color GetHighlighterColor(Color color)
        {
            // For a highlighter stroke, the color.A is overriden to be 255
            color.A = SolidStrokeAlpha;
            return color;
        }

        // Opacity for highlighter container visuals
        internal static readonly double HighlighterOpacity = 0.5;
        internal static readonly byte SolidStrokeAlpha = 0xFF;
        internal static readonly Point ArcToMarker = new Point(Double.MinValue, Double.MinValue);

        /// <summary>
        /// Simple helper enum
        /// </summary>
        private enum RectCompareResult
        {
            Rect1ContainsRect2,
            Rect2ContainsRect1,
            NoItersection,
        }
        #endregion
    }
}


text:
#import <Foundation/Foundation.h>

#define kPendingPushActionDefaultsKey @"com.urbanairship.action.pending"

typedef NS_ENUM(NSInteger, UASituation) {
    /**
     * Represents a situation in which the action was invoked manually.
     */
    UASituationManualInvocation,

    /**
     * Represents a situation in which the application was launched from a push notification.
     */
    UASituationLaunchedFromPush,

    /**
     * Represents a situation in which a push notification was received in the foreground.
     */
    UASituationForegroundPush,

    /**
     * Represents a situation in which a push notification was received in the background.
     */
    UASituationBackgroundPush,

    /**
     * Represents a situation in which the action was triggered from a
     * web view
     */
    UASituationWebViewInvocation
};

/**
 * Contains the arguments passed into an action during execution.
 */
@interface UAActionArguments : NSObject

/**
 * UAActionArguments factory method.
 *
 * @param value The value associated with the arguments.
 * @param situation The situation of the action.
 */
+ (instancetype)argumentsWithValue:(id)value withSituation:(UASituation)situation;

/**
 * Situation of the action
 */
@property(nonatomic, assign) UASituation situation;

/**
 * The value associated with the action
 */
@property(nonatomic, strong) id value;

/**
 * The name the action was invoked with.
 */
@property(nonatomic, copy) NSString *name;

@end


text:
class Post < ActiveRecord::Base
  belongs_to :user
  belongs_to :board

  acts_as_voteable

  has_many :comments

  attr_accessor :parent

  scope :recent, ->(limit = 10) { order('created_at DESC').limit(limit) }

  validates :title,    presence: true, length: { maximum: 255 }
  validates :user_id,  presence: true
  validates :board_id, presence: true
end


text:
// Code generated by client-gen. DO NOT EDIT.

package fake

import (
	v1 "github.com/google/knative-gcp/pkg/client/clientset/versioned/typed/events/v1"
	rest "k8s.io/client-go/rest"
	testing "k8s.io/client-go/testing"
)

type FakeEventsV1 struct {
	*testing.Fake
}

func (c *FakeEventsV1) CloudAuditLogsSources(namespace string) v1.CloudAuditLogsSourceInterface {
	return &FakeCloudAuditLogsSources{c, namespace}
}

func (c *FakeEventsV1) CloudBuildSources(namespace string) v1.CloudBuildSourceInterface {
	return &FakeCloudBuildSources{c, namespace}
}

func (c *FakeEventsV1) CloudPubSubSources(namespace string) v1.CloudPubSubSourceInterface {
	return &FakeCloudPubSubSources{c, namespace}
}

func (c *FakeEventsV1) CloudSchedulerSources(namespace string) v1.CloudSchedulerSourceInterface {
	return &FakeCloudSchedulerSources{c, namespace}
}

func (c *FakeEventsV1) CloudStorageSources(namespace string) v1.CloudStorageSourceInterface {
	return &FakeCloudStorageSources{c, namespace}
}

// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeEventsV1) RESTClient() rest.Interface {
	var ret *rest.RESTClient
	return ret
}


text:
package laika.parse.rst

import laika.tree.Elements._
import laika.util.Builders._

/** API for creating directives, the extension mechanism of reStructuredText.
 *  The API did not aim to mimic the API of the original Python reference implementation.
 *  Instead the goal was to create an API that is idiomatic Scala, fully typesafe and as concise as possible.
 *  Yet it should be flexible enough to semantically support the options of the Python directives, so that
 *  ideally most existing Python directives could theoretically get ported to Laika.
 *
 *  Entry points are the `BlockDirective` and `SpanDirective` objects. The Python reference parser does
 *  not make this distinction on the API level, but does this internally based on the context a
 *  directive is parsed in. Since Laika APIs are typesafe, the distinction is necessary since
 *  block level and span level directives create different types of document tree nodes.
 *  A `SpanDirective` can only be used in a substitution definition which can then be used
 *  within flow elements. A `BlockDirective` can be used directly in any location other block
 *  level content like paragraphs or lists can be used.
 *
 *  A directive may consist of any combination of arguments, fields and body elements:
 *
 *  {{{
 *  .. myDirective:: arg1 arg2
 *   :field1: value1
 *   :field2: value2
 *
 *   This is the body of the directive. It may consist of any standard or custom
 *   block-level and inline markup.
 *  }}}
 *
 *  In the example above `arg1` and `arg2` are arguments, `field1` and `field2` are fields,
 *  and followed by body elements after a blank line. If there are no arguments or fields
 *  the blank line may be omitted. For the full specification, see
 *  [[http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#directives]].
 *
 *  For each of these directive elements, the API offers a method to specify whether the
 *  element is required or optional, and an optional function to convert or validate the
 *  parsed value.
 *
 *  Consider the following simple example of a directive with just one argument and
 *  a body:
 *
 *  {{{
 *  .. note:: This is the title
 *
 *   This is the body of the note.
 *  }}}
 *
 *  The implementation of this directive could look like this:
 *
 *  {{{
 *  case class Note (title: String,
 *                   content: Seq[Block],
 *                   options: Options = NoOpt) extends Block
 *                                             with BlockContainer[Note]
 *
 *  val rst = ReStructuredText withBlockDirectives
 *    BlockDirective("note") {
 *      (argument(withWS = true) ~ blockContent)(Note(_,_))
 *    }
 *
 *  Transform from rst to HTML fromFile "hello.rst" toFile "hello.html"
 *  }}}
 *
 *  The `argument()` method specifies a required argument of type `String` (since no conversion
 *  function was supplied). We need to set the `withWS` flag to true as an argument cannot have
 *  whitespace per default. The `blockContent` method specifies standard block content (any block-level
 *  elements that are supported in normal blocks, too) which results in a parsed value of type
 *  `Seq[Block]`. Finally you need to provide a function that accepts the results of the specified
 *  directive elements as parameters (of the corresponding type). Here we created a case class
 *  with a matching signature so can pass it directly as the target function. For a block directive
 *  the final result has to be of type `Block` which the `Note` class satisfies. Finally the directive
 *  gets registered with the `ReStructuredText` parser.
 *
 *  If any conversion or validation is required on the individual parts of the directive they can
 *  be passed to the corresponding function:
 *
 *  {{{
 *  def nonNegativeInt (value: String) =
 *    try {
 *      val num = value.toInt
 *      Either.cond(num >= 0, num, s"not a positive int: \$num")
 *    }
 *    catch {
 *      case e: NumberFormatException => Left(s"not a number: \$value")
 *    }
 *
 *  case class Message (severity: Int,
 *                      content: Seq[Block],
 *                      options: Options = NoOpt) extends Block
 *                                                with BlockContainer[Message]
 *
 *  val rst = ReStructuredText withBlockDirectives (
 *    BlockDirective("message") {
 *      (argument(nonNegativeInt) ~ blockContent)(Message(_,_))
 *    }
 *  )
 *  }}}
 *
 *  The function has to provide an `Either[String, T]` as a result. A `Left` result will be interpreted
 *  as an error by the parser with the string being used as the message and an instance of `InvalidBlock`
 *  containing the validator message and the raw source of the directive will be inserted into the document
 *  tree. In this case the final function (`Message`) will never be invoked. A `Right` result will be
 *  used as an argument to the final function. Note how the case class now expects an `Int` as the first
 *  parameter.
 *
 *  Finally arguments and fields can also be optional. In case they are missing, the directive is still
 *  considered valid and `None` will be passed to your function:
 *
 *  {{{
 *  case class Message (severity: Option[Int],
 *                      content: Seq[Block],
 *                      options: Options = NoOpt) extends Block
 *                                                with BlockContainer[Message]
 *
 *  val rst = ReStructuredText withBlockDirectives (
 *    BlockDirective("message") {
 *      (optArgument(nonNegativeInt) ~ blockContent)(Message(_,_))
 *    }
 *  )
 *  }}}
 *
 *  The argument may be missing, but if it is present it has to pass the specified validator.
 *
 *  In case of multiple arguments, the order you specify them is also the order in which they
 *  are parsed from the directive markup, with the only exception being that required arguments
 *  will always be parsed before optional ones, and arguments with whitespace need to come last.
 *
 *  @author Jens Halm
 */
object Directives {

  /** API to implement by the actual directive parser.
   *  The methods of this trait correspond to the methods of the `Parts` object,
   *  only differing in return type.
   */
  trait DirectiveParser {

    def argument [T](convert: String => Either[String,T] = {s:String => Right(s)},
                     withWS: Boolean = false): Result[T]

    def optArgument [T](convert: String => Either[String,T] = {s:String => Right(s)},
                     withWS: Boolean = false): Result[Option[T]]

    def field [T](name: String, convert: String => Either[String,T]): Result[T]

    def optField [T](name: String, convert: String => Either[String,T]): Result[Option[T]]

    def blockContent: Result[Seq[Block]]

    def spanContent: Result[Seq[Span]]

    def content [T](f: String => Either[String,T]): Result[T]

  }

  /** Represents a single part (argument, field or body) of a directive.
   */
  abstract class DirectivePart[+A] extends (DirectiveParser => Result[A]) { self =>

    def map [B](f: A => B): DirectivePart[B] = new DirectivePart[B] {
      def apply (p: DirectiveParser) = self(p) map f
    }

  }

  /** Type class required for using the generic `Builders` API with directives.
   */
  implicit object CanBuildDirectivePart extends CanBuild[DirectivePart] {

    def apply [A,B](ma: DirectivePart[A], mb: DirectivePart[B]): DirectivePart[A~B] = new DirectivePart[A~B] {
      def apply (p: DirectiveParser) = {
        val a = ma(p)
        val b = mb(p)
        new Result(new ~(a.get,b.get))
      }
    }

    def map [A,B](m: DirectivePart[A], f: A => B): DirectivePart[B] = m map f

  }

  /** The public user API for specifying the required and optional parts of a directive
   *  (arguments, fields or body) together with optional converter/validator functions.
   */
  object Parts {

    private def part [T](f: DirectiveParser => Result[T]): DirectivePart[T] = new DirectivePart[T] {
      def apply (p: DirectiveParser) = f(p)
    }

    /** Specifies a required argument.
     *
     *  @param convert the function to use for converting and validating the parsed value
     *  @param withWS whether the argument supports whitespace characters (only one of these
     *  can exist in any single directive markup)
     *  @return a directive part that can be combined with further parts with the `~` operator
     */
    def argument [T](convert: String => Either[String,T] = { s:String => Right(s) },
                     withWS: Boolean = false): DirectivePart[T] = part(_.argument(convert, withWS))

    /** Specifies an optional argument.
     *
     *  @param convert the function to use for converting and validating the parsed value
     *  if it is present
     *  @param withWS whether the argument supports whitespace characters (only one of these
     *  can exist in any single directive markup)
     *  @return a directive part that can be combined with further parts with the `~` operator
     */
    def optArgument [T](convert: String => Either[String,T] = { s:String => Right(s) },
                        withWS: Boolean = false): DirectivePart[Option[T]] = part(_.optArgument(convert, withWS))

    /** Specifies a required named field.
     *
     *  @param name the name of the field as used in the directive markup (without the colons)
     *  @param convert the function to use for converting and validating the parsed value
     *  @return a directive part that can be combined with further parts with the `~` operator
     */
    def field [T](name: String,
                  convert: String => Either[String,T] = { s:String => Right(s) }): DirectivePart[T] =
                    part(_.field(name, convert))

    /** Specifies an optional named field.
     *
     *  @param name the name of the field as used in the directive markup (without the colons)
     *  @param convert the function to use for converting and validating the parsed value
     *  if it is present
     *  @return a directive part that can be combined with further parts with the `~` operator
     */
    def optField [T](name: String,
                     convert: String => Either[String,T] = { s:String => Right(s) }): DirectivePart[Option[T]] =
                     part(_.optField(name, convert))

    /** Specifies standard block-level content as the body of the directive.
     *
     *  @return a directive part that can be combined with further parts with the `~` operator
     */
    def blockContent: DirectivePart[Seq[Block]] = part(_.blockContent)

    def spanContent: DirectivePart[Seq[Span]] = part(_.spanContent)

    /** Specifies that the body of the directive markup should get passed to the conversion function as a raw string.
     *
     *  @param f the function to use for converting and validating the parsed value
     *  @return a directive part that can be combined with further parts with the `~` operator
     */
    def content [T](f: String => Either[String,T]): DirectivePart[T] = part(_.content(f))

  }

  /** Represents a single directive implementation.
   */
  class Directive [E <: Element] private[Directives] (val name: String, val part: BlockParsers with InlineParsers => DirectivePart[E])

  /** API entry point for setting up a span directive that can be used
   *  in substitution definitions.
   */
  object SpanDirective {

    /** Creates a new directive with the specified name and implementation.
     *  The `DirectivePart` can be created by using the methods of the `Parts`
     *  object.
     *
     *  @param name the name the directive can be used with in reStructuredText markup
     *  @param part the implementation of the directive that can be created by using the combinators of the `Parts` object
     *  @return a new directive that can be registered with the reStructuredText parser
     */
    def apply (name: String)(part: DirectivePart[Span]): Directive[Span] = new Directive(name.toLowerCase, _ => part)

    /** Creates a new directive with the specified name and implementation.
     *  The `DirectivePart` can be created by using the methods of the `Parts`
     *  object. In contrast to the `apply` function, this function allows to
     *  depend on the standard block and span parsers. This is necessary if
     *  the directive does both, require a custom parser for arguments or body
     *  and allow for nested directives in those parsers.
     *
     *  @param name the name the directive can be used with in reStructuredText markup
     *  @param part a function returning the implementation of the directive that can be created by using the combinators of the `Parts` object
     *  @return a new directive that can be registered with the reStructuredText parser
     */
    def recursive (name: String)(part: BlockParsers with InlineParsers => DirectivePart[Span]): Directive[Span] = new Directive(name.toLowerCase, part)

  }

  /** API entry point for setting up a block directive.
   */
  object BlockDirective {

    /** Creates a new directive with the specified name and implementation.
     *  The `DirectivePart` can be created by using the methods of the `Parts`
     *  object.
     *
     *  @param name the name the directive can be used with in reStructuredText markup
     *  @param part the implementation of the directive that can be created by using the combinators of the `Parts` object
     *  @return a new directive that can be registered with the reStructuredText parser
     */
    def apply (name: String)(part: DirectivePart[Block]): Directive[Block] = new Directive(name.toLowerCase, _ => part)

    /** Creates a new directive with the specified name and implementation.
     *  The `DirectivePart` can be created by using the methods of the `Parts`
     *  object. In contrast to the `apply` function, this function allows to
     *  depend on the standard block and span parsers. This is necessary if
     *  the directive does both, require a custom parser for arguments or body
     *  and allow for nested directives in those parsers.
     *
     *  @param name the name the directive can be used with in reStructuredText markup
     *  @param part a function returning the implementation of the directive that can be created by using the combinators of the `Parts` object
     *  @return a new directive that can be registered with the reStructuredText parser
     */
    def recursive (name: String)(part: BlockParsers with InlineParsers => DirectivePart[Block]): Directive[Block] = new Directive(name.toLowerCase, part)

  }

}