xref: /webtrees/app/Factories/ElementFactory.php (revision b63ebfff10c4f748c10374a14a84550d3b653912)
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