1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2022 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees\Factories; 21 22use Fisharebest\Webtrees\Contracts\ElementFactoryInterface; 23use Fisharebest\Webtrees\Contracts\ElementInterface; 24use Fisharebest\Webtrees\Elements\AddressCity; 25use Fisharebest\Webtrees\Elements\AddressCountry; 26use Fisharebest\Webtrees\Elements\AddressEmail; 27use Fisharebest\Webtrees\Elements\AddressFax; 28use Fisharebest\Webtrees\Elements\AddressLine; 29use Fisharebest\Webtrees\Elements\AddressLine1; 30use Fisharebest\Webtrees\Elements\AddressLine2; 31use Fisharebest\Webtrees\Elements\AddressLine3; 32use Fisharebest\Webtrees\Elements\AddressPostalCode; 33use Fisharebest\Webtrees\Elements\AddressState; 34use Fisharebest\Webtrees\Elements\AddressWebPage; 35use Fisharebest\Webtrees\Elements\AdoptedByWhichParent; 36use Fisharebest\Webtrees\Elements\Adoption; 37use Fisharebest\Webtrees\Elements\AdultChristening; 38use Fisharebest\Webtrees\Elements\AgeAtEvent; 39use Fisharebest\Webtrees\Elements\AncestralFileNumber; 40use Fisharebest\Webtrees\Elements\Annulment; 41use Fisharebest\Webtrees\Elements\ApprovedSystemId; 42use Fisharebest\Webtrees\Elements\AutomatedRecordId; 43use Fisharebest\Webtrees\Elements\Baptism; 44use Fisharebest\Webtrees\Elements\BarMitzvah; 45use Fisharebest\Webtrees\Elements\BasMitzvah; 46use Fisharebest\Webtrees\Elements\Birth; 47use Fisharebest\Webtrees\Elements\Blessing; 48use Fisharebest\Webtrees\Elements\Burial; 49use Fisharebest\Webtrees\Elements\CasteName; 50use Fisharebest\Webtrees\Elements\CauseOfEvent; 51use Fisharebest\Webtrees\Elements\Census; 52use Fisharebest\Webtrees\Elements\CertaintyAssessment; 53use Fisharebest\Webtrees\Elements\Change; 54use Fisharebest\Webtrees\Elements\ChangeDate; 55use Fisharebest\Webtrees\Elements\CharacterSet; 56use Fisharebest\Webtrees\Elements\ChildLinkageStatus; 57use Fisharebest\Webtrees\Elements\Christening; 58use Fisharebest\Webtrees\Elements\Confirmation; 59use Fisharebest\Webtrees\Elements\ContentDescription; 60use Fisharebest\Webtrees\Elements\Coordinates; 61use Fisharebest\Webtrees\Elements\CopyrightFile; 62use Fisharebest\Webtrees\Elements\CopyrightSourceData; 63use Fisharebest\Webtrees\Elements\CountOfChildren; 64use Fisharebest\Webtrees\Elements\CountOfMarriages; 65use Fisharebest\Webtrees\Elements\Cremation; 66use Fisharebest\Webtrees\Elements\CustomElement; 67use Fisharebest\Webtrees\Elements\CustomEvent; 68use Fisharebest\Webtrees\Elements\CustomFact; 69use Fisharebest\Webtrees\Elements\CustomFamilyEvent; 70use Fisharebest\Webtrees\Elements\CustomIndividualEvent; 71use Fisharebest\Webtrees\Elements\DateLdsOrd; 72use Fisharebest\Webtrees\Elements\DateValue; 73use Fisharebest\Webtrees\Elements\Death; 74use Fisharebest\Webtrees\Elements\DescriptiveTitle; 75use Fisharebest\Webtrees\Elements\Divorce; 76use Fisharebest\Webtrees\Elements\DivorceFiled; 77use Fisharebest\Webtrees\Elements\Emigration; 78use Fisharebest\Webtrees\Elements\EmptyElement; 79use Fisharebest\Webtrees\Elements\Engagement; 80use Fisharebest\Webtrees\Elements\EntryRecordingDate; 81use Fisharebest\Webtrees\Elements\EventAttributeType; 82use Fisharebest\Webtrees\Elements\EventOrFactClassification; 83use Fisharebest\Webtrees\Elements\EventsRecorded; 84use Fisharebest\Webtrees\Elements\EventTypeCitedFrom; 85use Fisharebest\Webtrees\Elements\FamilyCensus; 86use Fisharebest\Webtrees\Elements\FamilyRecord; 87use Fisharebest\Webtrees\Elements\FamilyResidence; 88use Fisharebest\Webtrees\Elements\FamilySearchFamilyTreeId; 89use Fisharebest\Webtrees\Elements\FamilyStatusText; 90use Fisharebest\Webtrees\Elements\FileName; 91use Fisharebest\Webtrees\Elements\FirstCommunion; 92use Fisharebest\Webtrees\Elements\Form; 93use Fisharebest\Webtrees\Elements\GedcomElement; 94use Fisharebest\Webtrees\Elements\GenerationsOfAncestors; 95use Fisharebest\Webtrees\Elements\GenerationsOfDescendants; 96use Fisharebest\Webtrees\Elements\GovIdentifier; 97use Fisharebest\Webtrees\Elements\Graduation; 98use Fisharebest\Webtrees\Elements\HeaderRecord; 99use Fisharebest\Webtrees\Elements\HierarchicalRelationship; 100use Fisharebest\Webtrees\Elements\Immigration; 101use Fisharebest\Webtrees\Elements\IndividualRecord; 102use Fisharebest\Webtrees\Elements\LanguageId; 103use Fisharebest\Webtrees\Elements\LdsBaptism; 104use Fisharebest\Webtrees\Elements\LdsBaptismDateStatus; 105use Fisharebest\Webtrees\Elements\LdsChildSealing; 106use Fisharebest\Webtrees\Elements\LdsChildSealingDateStatus; 107use Fisharebest\Webtrees\Elements\LdsConfirmation; 108use Fisharebest\Webtrees\Elements\LdsEndowment; 109use Fisharebest\Webtrees\Elements\LdsEndowmentDateStatus; 110use Fisharebest\Webtrees\Elements\LdsSpouseSealing; 111use Fisharebest\Webtrees\Elements\LdsSpouseSealingDateStatus; 112use Fisharebest\Webtrees\Elements\LocationRecord; 113use Fisharebest\Webtrees\Elements\MaidenheadLocator; 114use Fisharebest\Webtrees\Elements\Marriage; 115use Fisharebest\Webtrees\Elements\MarriageBanns; 116use Fisharebest\Webtrees\Elements\MarriageContract; 117use Fisharebest\Webtrees\Elements\MarriageLicence; 118use Fisharebest\Webtrees\Elements\MarriageSettlement; 119use Fisharebest\Webtrees\Elements\MarriageType; 120use Fisharebest\Webtrees\Elements\MediaRecord; 121use Fisharebest\Webtrees\Elements\MultimediaFileReference; 122use Fisharebest\Webtrees\Elements\MultimediaFormat; 123use Fisharebest\Webtrees\Elements\NameOfBusiness; 124use Fisharebest\Webtrees\Elements\NameOfFamilyFile; 125use Fisharebest\Webtrees\Elements\NameOfProduct; 126use Fisharebest\Webtrees\Elements\NameOfRepository; 127use Fisharebest\Webtrees\Elements\NameOfSourceData; 128use Fisharebest\Webtrees\Elements\NamePersonal; 129use Fisharebest\Webtrees\Elements\NamePhoneticVariation; 130use Fisharebest\Webtrees\Elements\NamePieceGiven; 131use Fisharebest\Webtrees\Elements\NamePieceNickname; 132use Fisharebest\Webtrees\Elements\NamePiecePrefix; 133use Fisharebest\Webtrees\Elements\NamePieceSuffix; 134use Fisharebest\Webtrees\Elements\NamePieceSurname; 135use Fisharebest\Webtrees\Elements\NamePieceSurnamePrefix; 136use Fisharebest\Webtrees\Elements\NameRomanizedVariation; 137use Fisharebest\Webtrees\Elements\NameType; 138use Fisharebest\Webtrees\Elements\NationalIdNumber; 139use Fisharebest\Webtrees\Elements\NationOrTribalOrigin; 140use Fisharebest\Webtrees\Elements\Naturalization; 141use Fisharebest\Webtrees\Elements\NobilityTypeTitle; 142use Fisharebest\Webtrees\Elements\NoteRecord; 143use Fisharebest\Webtrees\Elements\NoteStructure; 144use Fisharebest\Webtrees\Elements\Occupation; 145use Fisharebest\Webtrees\Elements\OrdinanceProcessFlag; 146use Fisharebest\Webtrees\Elements\Ordination; 147use Fisharebest\Webtrees\Elements\PafUid; 148use Fisharebest\Webtrees\Elements\PedigreeLinkageType; 149use Fisharebest\Webtrees\Elements\PermanentRecordFileNumber; 150use Fisharebest\Webtrees\Elements\PhoneNumber; 151use Fisharebest\Webtrees\Elements\PhoneticType; 152use Fisharebest\Webtrees\Elements\PhysicalDescription; 153use Fisharebest\Webtrees\Elements\PlaceHierarchy; 154use Fisharebest\Webtrees\Elements\PlaceLatitude; 155use Fisharebest\Webtrees\Elements\PlaceLivingOrdinance; 156use Fisharebest\Webtrees\Elements\PlaceLongtitude; 157use Fisharebest\Webtrees\Elements\PlaceName; 158use Fisharebest\Webtrees\Elements\PlacePhoneticVariation; 159use Fisharebest\Webtrees\Elements\PlaceRomanizedVariation; 160use Fisharebest\Webtrees\Elements\Possessions; 161use Fisharebest\Webtrees\Elements\Probate; 162use Fisharebest\Webtrees\Elements\PublicationDate; 163use Fisharebest\Webtrees\Elements\ReceivingSystemName; 164use Fisharebest\Webtrees\Elements\RelationIsDescriptor; 165use Fisharebest\Webtrees\Elements\ReligiousAffiliation; 166use Fisharebest\Webtrees\Elements\RepositoryRecord; 167use Fisharebest\Webtrees\Elements\ResearchTask; 168use Fisharebest\Webtrees\Elements\ResearchTaskPriority; 169use Fisharebest\Webtrees\Elements\ResearchTaskStatus; 170use Fisharebest\Webtrees\Elements\ResearchTaskType; 171use Fisharebest\Webtrees\Elements\Residence; 172use Fisharebest\Webtrees\Elements\ResponsibleAgency; 173use Fisharebest\Webtrees\Elements\RestrictionNotice; 174use Fisharebest\Webtrees\Elements\Retirement; 175use Fisharebest\Webtrees\Elements\RoleInEvent; 176use Fisharebest\Webtrees\Elements\RomanizedType; 177use Fisharebest\Webtrees\Elements\ScholasticAchievement; 178use Fisharebest\Webtrees\Elements\SexValue; 179use Fisharebest\Webtrees\Elements\SexXValue; 180use Fisharebest\Webtrees\Elements\SocialSecurityNumber; 181use Fisharebest\Webtrees\Elements\SourceCallNumber; 182use Fisharebest\Webtrees\Elements\SourceData; 183use Fisharebest\Webtrees\Elements\SourceFiledByEntry; 184use Fisharebest\Webtrees\Elements\SourceJurisdictionPlace; 185use Fisharebest\Webtrees\Elements\SourceMediaType; 186use Fisharebest\Webtrees\Elements\SourceOriginator; 187use Fisharebest\Webtrees\Elements\SourcePublicationFacts; 188use Fisharebest\Webtrees\Elements\SourceRecord; 189use Fisharebest\Webtrees\Elements\SubmissionRecord; 190use Fisharebest\Webtrees\Elements\SubmitterName; 191use Fisharebest\Webtrees\Elements\SubmitterRecord; 192use Fisharebest\Webtrees\Elements\SubmitterRegisteredRfn; 193use Fisharebest\Webtrees\Elements\SubmitterText; 194use Fisharebest\Webtrees\Elements\TempleCode; 195use Fisharebest\Webtrees\Elements\TextFromSource; 196use Fisharebest\Webtrees\Elements\TimeValue; 197use Fisharebest\Webtrees\Elements\TransmissionDate; 198use Fisharebest\Webtrees\Elements\UnknownElement; 199use Fisharebest\Webtrees\Elements\UserReferenceNumber; 200use Fisharebest\Webtrees\Elements\UserReferenceType; 201use Fisharebest\Webtrees\Elements\VersionNumber; 202use Fisharebest\Webtrees\Elements\WebtreesUser; 203use Fisharebest\Webtrees\Elements\WhereWithinSource; 204use Fisharebest\Webtrees\Elements\Will; 205use Fisharebest\Webtrees\Elements\XrefAssociate; 206use Fisharebest\Webtrees\Elements\XrefFamily; 207use Fisharebest\Webtrees\Elements\XrefIndividual; 208use Fisharebest\Webtrees\Elements\XrefLocation; 209use Fisharebest\Webtrees\Elements\XrefMedia; 210use Fisharebest\Webtrees\Elements\XrefRepository; 211use Fisharebest\Webtrees\Elements\XrefSource; 212use Fisharebest\Webtrees\Elements\XrefSubmission; 213use Fisharebest\Webtrees\Elements\XrefSubmitter; 214use Fisharebest\Webtrees\I18N; 215 216use function array_merge; 217use function preg_match; 218use function var_dump; 219 220/** 221 * Make a GEDCOM element. 222 */ 223class ElementFactory implements ElementFactoryInterface 224{ 225 /** @var array<string,ElementInterface> */ 226 private array $elements = []; 227 228 /** 229 * Create a GEDCOM element that corresponds to a GEDCOM tag. 230 * Finds the correct element for all valid tags. 231 * Finds a likely element for custom tags. 232 * 233 * @param string $tag - Colon delimited hierarchy, e.g. 'INDI:BIRT:PLAC' 234 * 235 * @return ElementInterface 236 */ 237 public function make(string $tag): ElementInterface 238 { 239 return $this->elements[$tag] ?? $this->findElementByWildcard($tag) ?? new UnknownElement($tag); 240 } 241 242 /** 243 * Register GEDCOM tags. 244 * 245 * @param array<string,ElementInterface> $elements 246 */ 247 public function registerTags(array $elements): void 248 { 249 $this->elements = $elements + $this->elements; 250 } 251 252 /** 253 * Register more subtags. 254 * 255 * @param array<string,array<int,array<int,string>>> $subtags 256 */ 257 public function registerSubTags(array $subtags): void 258 { 259 foreach ($subtags as $tag => $children) { 260 foreach ($children as $child) { 261 $this->make($tag)->subtag(...$child); 262 } 263 } 264 } 265 266 /** 267 * @param string $tag 268 * 269 * @return ElementInterface|null 270 */ 271 private function findElementByWildcard(string $tag): ?ElementInterface 272 { 273 foreach ($this->elements as $tags => $element) { 274 if (str_contains($tags, '*')) { 275 $regex = '/^' . strtr($tags, ['*' => '[^:]+']) . '$/'; 276 277 if (preg_match($regex, $tag)) { 278 return $element; 279 } 280 } 281 } 282 283 return null; 284 } 285} 286