/**
  * Processes each required or optional tag.
  *
  * @param int $commentStart Position in the stack where the comment started.
  * @param int $commentEnd   Position in the stack where the comment ended.
  *
  * @return void
  */
 protected function processTags($commentStart, $commentEnd)
 {
     $docBlock = get_class($this) === 'Joomla_Sniffs_Commenting_FileCommentSniff' ? 'file' : 'class';
     $foundTags = $this->commentParser->getTagOrders();
     $orderIndex = 0;
     $indentation = array();
     $longestTag = 0;
     foreach ($this->tags as $tag => $info) {
         // Required tag missing.
         if ($info['required'] === true && !in_array($tag, $foundTags)) {
             $error = 'Missing @%s tag in %s comment';
             $data = array($tag, $docBlock);
             $this->currentFile->addError($error, $commentEnd, 'MissingTag', $data);
             continue;
         }
         // Get the line number for current tag.
         $tagName = ucfirst($tag);
         if ($info['allow_multiple'] === true) {
             $tagName .= 's';
         }
         $getMethod = 'get' . $tagName;
         /** @var PHP_CodeSniffer_CommentParser_SingleElement|array $tagElement  */
         $tagElement = $this->commentParser->{$getMethod}();
         if (empty($tagElement)) {
             continue;
         }
         $errorPos = $commentStart;
         if (!is_array($tagElement)) {
             $errorPos = $commentStart + $tagElement->getLine();
         }
         // Get the tag order.
         $foundIndexes = array_keys($foundTags, $tag);
         if (count($foundIndexes) > 1) {
             // Multiple occurance not allowed.
             if ($info['allow_multiple'] === false) {
                 $error = 'Only 1 @%s tag is allowed in a %s comment';
                 $data = array($tag, $docBlock);
                 $this->currentFile->addError($error, $errorPos, 'DuplicateTag', $data);
             } else {
                 // Make sure same tags are grouped together.
                 $i = 0;
                 $count = $foundIndexes[0];
                 foreach ($foundIndexes as $index) {
                     if ($index !== $count) {
                         $errorPosIndex = $errorPos + $tagElement[$i]->getLine();
                         $error = '@%s tags must be grouped together';
                         $data = array($tag);
                         $this->currentFile->addError($error, $errorPosIndex, 'TagsNotGrouped', $data);
                     }
                     $i++;
                     $count++;
                 }
             }
         }
         // Check tag order.
         if ($foundIndexes[0] > $orderIndex) {
             $orderIndex = $foundIndexes[0];
         } else {
             if (is_array($tagElement) && !empty($tagElement)) {
                 $errorPos += $tagElement[0]->getLine();
             }
             $error = 'The @%s tag is in the wrong order; the tag %s';
             $data = array($tag, $info['order_text']);
             $this->currentFile->addError($error, $errorPos, 'WrongTagOrder', $data);
         }
         // Store the indentation for checking.
         $len = strlen($tag);
         if ($len > $longestTag) {
             $longestTag = $len;
         }
         if (is_array($tagElement)) {
             /** @var PHP_CodeSniffer_CommentParser_SingleElement $element */
             foreach ($tagElement as $element) {
                 $indentation[] = array('tag' => $tag, 'space' => $this->getIndentation($tag, $element), 'line' => $element->getLine());
             }
         } else {
             $indentation[] = array('tag' => $tag, 'space' => $this->getIndentation($tag, $tagElement));
         }
         $method = 'process' . $tagName;
         if (method_exists($this, $method)) {
             // Process each tag if a method is defined.
             call_user_func(array($this, $method), $errorPos);
         } else {
             if (is_array($tagElement)) {
                 /** @var PHP_CodeSniffer_CommentParser_SingleElement $element */
                 foreach ($tagElement as $element) {
                     $element->process($this->currentFile, $commentStart, $docBlock);
                 }
             } else {
                 $tagElement->process($this->currentFile, $commentStart, $docBlock);
             }
         }
     }
     foreach ($indentation as $indentInfo) {
         if ($indentInfo['space'] !== 0 && $indentInfo['space'] !== $longestTag + 2) {
             $expected = $longestTag - strlen($indentInfo['tag']) + 1;
             $space = $indentInfo['space'] - strlen($indentInfo['tag']);
             if ($space == $expected) {
                 continue;
             }
             $error = '@%s tag comment indented incorrectly; expected %s spaces but found %s';
             $data = array($indentInfo['tag'], $expected, $space);
             $getTagMethod = 'get' . ucfirst($indentInfo['tag']);
             if ($this->tags[$indentInfo['tag']]['allow_multiple'] === true) {
                 $line = $indentInfo['line'];
             } else {
                 /** @var PHP_CodeSniffer_CommentParser_SingleElement $tagElem  */
                 $tagElem = $this->commentParser->{$getTagMethod}();
                 $line = $tagElem->getLine();
             }
             $this->currentFile->addError($error, $commentStart + $line, 'TagIndent', $data);
         }
     }
 }