/**
  * @param string[][][] $fieldsAsMultiDimensionalArrays
  * @param string[] $metadata
  * @param string[] $files
  * @return Dictionary
  */
 protected function generateDictionary(array $fieldsAsMultiDimensionalArrays, array $metadata, array $files) : Dictionary
 {
     if ($files) {
         $tempDirectory = (new \esperecyan\dictionary_php\parser\GenericDictionaryParser())->generateTempDirectory();
         foreach ($files as $filename => $file) {
             file_put_contents("{$tempDirectory}/{$filename}", $file);
         }
     }
     $dictionary = new Dictionary(isset($tempDirectory) ? new \FilesystemIterator($tempDirectory) : null);
     foreach ($fieldsAsMultiDimensionalArrays as $fieldsAsMultiDimensionalArray) {
         $dictionary->addWord($fieldsAsMultiDimensionalArray);
     }
     $dictionary->setMetadata($metadata);
     return $dictionary;
 }
 /**
  * @param \SplFileInfo $file
  * @param string|null $filename
  * @param string|null $title
  * @throws SyntaxException お題の数、辞書全体の文字数が制限範囲外であるとき。
  * @return Dictionary
  */
 public function parse(\SplFileInfo $file, string $filename = null, string $title = null) : Dictionary
 {
     $dictionary = new Dictionary();
     $words = [];
     if (!$file instanceof \SplFileObject) {
         $file = $file->openFile();
     } else {
         $file->rewind();
     }
     $file->setFlags(\SplFileObject::DROP_NEW_LINE | \SplFileObject::READ_AHEAD | \SplFileObject::SKIP_EMPTY);
     foreach ($file as $line) {
         $word = $this->parseLine($line);
         if (!in_array($word, $words)) {
             $dictionary->addWord(['text' => [str_replace('ヴ', 'ゔ', $line)]]);
             $words[] = $word;
         }
     }
     $wordsLength = count($words);
     if ($wordsLength < self::WORDS_MIN) {
         throw new SyntaxException(sprintf(_('お題が%1$d個しかありません。%2$d個以上必要です。'), $wordsLength, self::WORDS_MIN));
     }
     if ($wordsLength > self::WORDS_MAX) {
         throw new SyntaxException(sprintf(_('お題が%1$d個あります。%2$d個以内にする必要があります。'), $wordsLength, self::WORDS_MAX));
     }
     $dictionaryCodePoints = mb_strlen(implode('', $words), 'UTF-8');
     if ($dictionaryCodePoints > self::DICTIONARY_CODE_POINTS_MAX) {
         throw new SyntaxException(sprintf(_('辞書全体で%1$d文字あります。%2$d文字以内にする必要があります。'), $dictionaryCodePoints, self::DICTIONARY_CODE_POINTS_MAX));
     }
     if (!is_null($title) || !is_null($filename)) {
         if (!is_null($title) && $title !== '') {
             $trimedTitle = preg_replace('/^[  \\t]+|[  \\t]+$/u', '', $title);
         }
         if (!is_null($filename) && (!isset($trimedTitle) || $trimedTitle === '')) {
             $trimedTitle = preg_replace('/^[  \\t]+|[  \\t]+$/u', '', (new GenericDictionaryParser())->getTitleFromFilename($filename));
         }
         if (isset($trimedTitle) && $trimedTitle !== '') {
             $dictionary->setMetadata(['@title' => $trimedTitle]);
             $titleLength = $this->getLengthAs16BitCodeUnits($trimedTitle);
             if ($titleLength > self::TITLE_MAX) {
                 $this->logger->error(sprintf(_('辞書名が%1$d文字 (補助文字は2文字扱い) あります。ピクトセンスにおける辞書名の最大文字数は%2$d文字です。'), $titleLength, self::TITLE_MAX));
             }
         } else {
             $this->logger->error(_('辞書名が空です。先頭末尾の空白は取り除かれます。'));
         }
     }
     return $dictionary;
 }
 /**
  * CSVの一レコードを表す2つの配列によってお題を追加します。
  * @param Dictionary $dictionary
  * @param string[] $fieldNames
  * @param string[] $fields
  * @param bool $first ヘッダ行を除く最初のレコードであれば真。
  * @throws SyntaxException
  */
 protected function addRecord(Dictionary $dictionary, array $fieldNames, array $fields, bool $first)
 {
     if (!in_array('text', $fieldNames)) {
         throw new SyntaxException(sprintf(_('ヘッダ行「%s」にフィールド名「text」が存在しません'), $this->convertToCSVRecord($fieldNames)));
     }
     if (count($fields) > count($fieldNames)) {
         throw new SyntaxException(sprintf(_('「%s」のフィールド数は、ヘッダ行のフィールド名の数を超えています。'), $this->convertToCSVRecord($fields)));
     }
     foreach ($fields as $i => $field) {
         if ($field !== '') {
             if ($fieldNames[$i][0] === '@') {
                 if ($first) {
                     $metaFields[$fieldNames[$i]] = $field;
                 } else {
                     $this->logger->error(sprintf(_('メタフィールド%sの内容は、最初のレコードにのみ記述可能です。'), $fieldNames[$i]));
                 }
             } else {
                 $fieldsAsMultiDimensionalArray[$fieldNames[$i]][] = $field;
             }
         }
     }
     if (!isset($fieldsAsMultiDimensionalArray['text'][0])) {
         throw new SyntaxException(sprintf(_('「%s」にはtextフィールドが存在しません。'), $this->convertToCSVRecord($fields)));
     }
     $dictionary->addWord($fieldsAsMultiDimensionalArray);
     if (isset($metaFields)) {
         $dictionary->setMetadata($metaFields);
     }
 }