Security update for permissions_by_term
[yaffs-website] / vendor / behat / behat / src / Behat / Behat / Context / Snippet / Generator / ContextSnippetGenerator.php
diff --git a/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php b/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php
new file mode 100644 (file)
index 0000000..edb2227
--- /dev/null
@@ -0,0 +1,360 @@
+<?php
+
+/*
+ * This file is part of the Behat.
+ * (c) Konstantin Kudryashov <ever.zet@gmail.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Behat\Behat\Context\Snippet\Generator;
+
+use Behat\Behat\Context\Environment\ContextEnvironment;
+use Behat\Behat\Context\Snippet\ContextSnippet;
+use Behat\Behat\Definition\Pattern\PatternTransformer;
+use Behat\Behat\Snippet\Exception\EnvironmentSnippetGenerationException;
+use Behat\Behat\Snippet\Generator\SnippetGenerator;
+use Behat\Gherkin\Node\PyStringNode;
+use Behat\Gherkin\Node\StepNode;
+use Behat\Gherkin\Node\TableNode;
+use Behat\Testwork\Environment\Environment;
+use ReflectionClass;
+
+/**
+ * Generates snippets for a context class.
+ *
+ * @author Konstantin Kudryashov <ever.zet@gmail.com>
+ */
+final class ContextSnippetGenerator implements SnippetGenerator
+{
+    /**
+     * @var string[string]
+     */
+    private static $proposedMethods = array();
+    /**
+     * @var string
+     */
+    private static $templateTemplate = <<<TPL
+    /**
+     * @%%s %s
+     */
+    public function %s(%s)
+    {
+        throw new PendingException();
+    }
+TPL;
+    /**
+     * @var PatternTransformer
+     */
+    private $patternTransformer;
+    /**
+     * @var TargetContextIdentifier
+     */
+    private $contextIdentifier;
+    /**
+     * @var PatternIdentifier
+     */
+    private $patternIdentifier;
+
+    /**
+     * Initializes snippet generator.
+     *
+     * @param PatternTransformer $patternTransformer
+     */
+    public function __construct(PatternTransformer $patternTransformer)
+    {
+        $this->patternTransformer = $patternTransformer;
+
+        $this->setContextIdentifier(new FixedContextIdentifier(null));
+        $this->setPatternIdentifier(new FixedPatternIdentifier(null));
+    }
+
+    /**
+     * Sets target context identifier.
+     *
+     * @param TargetContextIdentifier $identifier
+     */
+    public function setContextIdentifier(TargetContextIdentifier $identifier)
+    {
+        $this->contextIdentifier = new CachedContextIdentifier($identifier);
+    }
+
+    /**
+     * Sets target pattern type identifier.
+     *
+     * @param PatternIdentifier $identifier
+     */
+    public function setPatternIdentifier(PatternIdentifier $identifier)
+    {
+        $this->patternIdentifier = $identifier;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function supportsEnvironmentAndStep(Environment $environment, StepNode $step)
+    {
+        if (!$environment instanceof ContextEnvironment) {
+            return false;
+        }
+
+        if (!$environment->hasContexts()) {
+            return false;
+        }
+
+        return null !== $this->contextIdentifier->guessTargetContextClass($environment);
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function generateSnippet(Environment $environment, StepNode $step)
+    {
+        if (!$environment instanceof ContextEnvironment) {
+            throw new EnvironmentSnippetGenerationException(sprintf(
+                'ContextSnippetGenerator does not support `%s` environment.',
+                get_class($environment)
+            ), $environment);
+        }
+
+        $contextClass = $this->contextIdentifier->guessTargetContextClass($environment);
+        $patternType = $this->patternIdentifier->guessPatternType($contextClass);
+        $stepText = $step->getText();
+        $pattern = $this->patternTransformer->generatePattern($patternType, $stepText);
+
+        $methodName = $this->getMethodName($contextClass, $pattern->getCanonicalText(), $pattern->getPattern());
+        $methodArguments = $this->getMethodArguments($step, $pattern->getPlaceholderCount());
+        $snippetTemplate = $this->getSnippetTemplate($pattern->getPattern(), $methodName, $methodArguments);
+
+        $usedClasses = $this->getUsedClasses($step);
+
+        return new ContextSnippet($step, $snippetTemplate, $contextClass, $usedClasses);
+    }
+
+    /**
+     * Generates method name using step text and regex.
+     *
+     * @param string $contextClass
+     * @param string $canonicalText
+     * @param string $pattern
+     *
+     * @return string
+     */
+    private function getMethodName($contextClass, $canonicalText, $pattern)
+    {
+        $methodName = $this->deduceMethodName($canonicalText);
+        $methodName = $this->getUniqueMethodName($contextClass, $pattern, $methodName);
+
+        return $methodName;
+    }
+
+    /**
+     * Returns an array of method argument names from step and token count.
+     *
+     * @param StepNode $step
+     * @param integer  $tokenCount
+     *
+     * @return string[]
+     */
+    private function getMethodArguments(StepNode $step, $tokenCount)
+    {
+        $args = array();
+        for ($i = 0; $i < $tokenCount; $i++) {
+            $args[] = '$arg' . ($i + 1);
+        }
+
+        foreach ($step->getArguments() as $argument) {
+            $args[] = $this->getMethodArgument($argument);
+        }
+
+        return $args;
+    }
+
+    /**
+     * Returns an array of classes used by the snippet template
+     *
+     * @param StepNode $step
+     *
+     * @return string[]
+     */
+    private function getUsedClasses(StepNode $step)
+    {
+        $usedClasses = array('Behat\Behat\Tester\Exception\PendingException');
+
+        foreach ($step->getArguments() as $argument) {
+            if ($argument instanceof TableNode) {
+                $usedClasses[] = 'Behat\Gherkin\Node\TableNode';
+            } elseif ($argument instanceof PyStringNode) {
+                $usedClasses[] = 'Behat\Gherkin\Node\PyStringNode';
+            }
+        }
+
+        return $usedClasses;
+    }
+
+    /**
+     * Generates snippet template using regex, method name and arguments.
+     *
+     * @param string   $pattern
+     * @param string   $methodName
+     * @param string[] $methodArguments
+     *
+     * @return string
+     */
+    private function getSnippetTemplate($pattern, $methodName, array $methodArguments)
+    {
+        return sprintf(
+            self::$templateTemplate,
+            str_replace('%', '%%', $pattern),
+            $methodName,
+            implode(', ', $methodArguments)
+        );
+    }
+
+    /**
+     * Generates definition method name based on the step text.
+     *
+     * @param string $canonicalText
+     *
+     * @return string
+     */
+    private function deduceMethodName($canonicalText)
+    {
+        // check that method name is not empty
+        if (0 !== strlen($canonicalText)) {
+            $canonicalText[0] = strtolower($canonicalText[0]);
+
+            return $canonicalText;
+        }
+
+        return 'stepDefinition1';
+    }
+
+    /**
+     * Ensures uniqueness of the method name in the context.
+     *
+     * @param string $contextClass
+     * @param string $stepPattern
+     * @param string $name
+     *
+     * @return string
+     */
+    private function getUniqueMethodName($contextClass, $stepPattern, $name)
+    {
+        $reflection = new ReflectionClass($contextClass);
+
+        $number = $this->getMethodNumberFromTheMethodName($name);
+        list($name, $number) = $this->getMethodNameNotExistentInContext($reflection, $name, $number);
+        $name = $this->getMethodNameNotProposedEarlier($contextClass, $stepPattern, $name, $number);
+
+        return $name;
+    }
+
+    /**
+     * Tries to deduct method number from the provided method name.
+     *
+     * @param string $methodName
+     *
+     * @return integer
+     */
+    private function getMethodNumberFromTheMethodName($methodName)
+    {
+        $methodNumber = 2;
+        if (preg_match('/(\d+)$/', $methodName, $matches)) {
+            $methodNumber = intval($matches[1]);
+        }
+
+        return $methodNumber;
+    }
+
+    /**
+     * Tries to guess method name that is not yet defined in the context class.
+     *
+     * @param ReflectionClass $reflection
+     * @param string          $methodName
+     * @param integer         $methodNumber
+     *
+     * @return array
+     */
+    private function getMethodNameNotExistentInContext(ReflectionClass $reflection, $methodName, $methodNumber)
+    {
+        while ($reflection->hasMethod($methodName)) {
+            $methodName = preg_replace('/\d+$/', '', $methodName);
+            $methodName .= $methodNumber++;
+        }
+
+        return array($methodName, $methodNumber);
+    }
+
+    /**
+     * Tries to guess method name that is not yet proposed to the context class.
+     *
+     * @param string  $contextClass
+     * @param string  $stepPattern
+     * @param string  $name
+     * @param integer $number
+     *
+     * @return string
+     */
+    private function getMethodNameNotProposedEarlier($contextClass, $stepPattern, $name, $number)
+    {
+        foreach ($this->getAlreadyProposedMethods($contextClass) as $proposedPattern => $proposedMethod) {
+            if ($proposedPattern === $stepPattern) {
+                continue;
+            }
+
+            while ($proposedMethod === $name) {
+                $name = preg_replace('/\d+$/', '', $name);
+                $name .= $number++;
+            }
+        }
+
+        $this->markMethodAsAlreadyProposed($contextClass, $stepPattern, $name);
+
+        return $name;
+    }
+
+    /**
+     * Returns already proposed method names.
+     *
+     * @param string $contextClass
+     *
+     * @return string[]
+     */
+    private function getAlreadyProposedMethods($contextClass)
+    {
+        return isset(self::$proposedMethods[$contextClass]) ? self::$proposedMethods[$contextClass] : array();
+    }
+
+    /**
+     * Marks method as proposed one.
+     *
+     * @param string $contextClass
+     * @param string $stepPattern
+     * @param string $methodName
+     */
+    private function markMethodAsAlreadyProposed($contextClass, $stepPattern, $methodName)
+    {
+        self::$proposedMethods[$contextClass][$stepPattern] = $methodName;
+    }
+
+    /**
+     * Returns method argument.
+     *
+     * @param string $argument
+     *
+     * @return string
+     */
+    private function getMethodArgument($argument)
+    {
+        $arg = '__unknown__';
+        if ($argument instanceof PyStringNode) {
+            $arg = 'PyStringNode $string';
+        } elseif ($argument instanceof TableNode) {
+            $arg = 'TableNode $table';
+        }
+
+        return $arg;
+    }
+}