001// --------------------------------------------------------------------------------
002// Copyright 2002-2025 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.contact.server.indexer;
018
019import com.echothree.model.control.contact.common.ContactMechanismTypes;
020import com.echothree.model.control.contact.server.control.ContactControl;
021import com.echothree.model.control.geo.server.control.GeoControl;
022import com.echothree.model.control.index.common.IndexConstants;
023import com.echothree.model.control.index.common.IndexFieldVariations;
024import com.echothree.model.control.index.common.IndexFields;
025import com.echothree.model.control.contact.server.analyzer.ContactMechanismAnalyzer;
026import com.echothree.model.control.index.server.indexer.BaseIndexer;
027import com.echothree.model.control.index.server.indexer.FieldTypes;
028import com.echothree.model.data.contact.server.entity.ContactMechanism;
029import com.echothree.model.data.contact.server.entity.ContactMechanismPurpose;
030import com.echothree.model.data.core.server.entity.EntityInstance;
031import com.echothree.model.data.geo.server.entity.GeoCode;
032import com.echothree.model.data.index.server.entity.Index;
033import com.echothree.model.data.party.server.entity.Language;
034import com.echothree.model.data.party.server.entity.Party;
035import com.echothree.model.data.party.server.entity.PartyType;
036import com.echothree.util.common.string.Inet4AddressUtils;
037import com.echothree.util.server.message.ExecutionErrorAccumulator;
038import com.echothree.util.server.persistence.Session;
039import java.util.HashSet;
040import java.util.Set;
041import org.apache.lucene.analysis.Analyzer;
042import org.apache.lucene.document.Document;
043import org.apache.lucene.document.Field;
044import org.apache.lucene.document.SortedDocValuesField;
045import org.apache.lucene.util.BytesRef;
046
047public class ContactMechanismIndexer
048        extends BaseIndexer<ContactMechanism> {
049    
050    ContactControl contactControl = Session.getModelController(ContactControl.class);
051    GeoControl geoControl = Session.getModelController(GeoControl.class);
052
053    /** Creates a new instance of ContactMechanismIndexer */
054    public ContactMechanismIndexer(final ExecutionErrorAccumulator eea, final Index index) {
055        super(eea, index);
056    }
057
058    @Override
059    protected Analyzer getAnalyzer() {
060        return new ContactMechanismAnalyzer(eea, language, entityType, entityAliasTypes, entityAttributes, tagScopes);
061    }
062    
063    @Override
064    protected ContactMechanism getEntity(final EntityInstance entityInstance) {
065        return contactControl.getContactMechanismByEntityInstance(entityInstance);
066    }
067    
068    private void addPartyNamesToDocument(Document document, Set<Party> parties) {
069        var partyNamesBuilder = new StringBuilder();
070
071        parties.forEach((party) -> {
072            if(partyNamesBuilder.length() != 0) {
073                partyNamesBuilder.append(' ');
074            }
075
076            partyNamesBuilder.append(party.getLastDetail().getPartyName());
077        });
078
079        document.add(new Field(IndexFields.partyNames.name(), partyNamesBuilder.toString(), FieldTypes.NOT_STORED_TOKENIZED));
080    }
081    
082    private void addPartyTypeNamesToDocument(Document document, Set<PartyType> partyTypes) {
083        var partyTypeNamesBuilder = new StringBuilder();
084
085        partyTypes.forEach((partyType) -> {
086            if(partyTypeNamesBuilder.length() != 0) {
087                partyTypeNamesBuilder.append(' ');
088            }
089
090            partyTypeNamesBuilder.append(partyType.getPartyTypeName());
091        });
092
093        document.add(new Field(IndexFields.partyTypeNames.name(), partyTypeNamesBuilder.toString(), FieldTypes.NOT_STORED_TOKENIZED));
094    }
095    
096    private void addContactMechanismPurposeNamesToDocument(Document document, Set<ContactMechanismPurpose> contactMechanismPurposes) {
097        var contactMechanismPurposesNamesBuilder = new StringBuilder();
098
099        contactMechanismPurposes.forEach((contactMechanismPurpose) -> {
100            if(contactMechanismPurposesNamesBuilder.length() != 0) {
101                contactMechanismPurposesNamesBuilder.append(' ');
102            }
103
104            contactMechanismPurposesNamesBuilder.append(contactMechanismPurpose.getContactMechanismPurposeName());
105        });
106
107        document.add(new Field(IndexFields.contactMechanismPurposeNames.name(), contactMechanismPurposesNamesBuilder.toString(), FieldTypes.NOT_STORED_TOKENIZED));
108    }
109    
110    private void addPartiesToDocument(final Document document, final ContactMechanism contactMechanism) {
111        Set<Party> parties = new HashSet<>();
112        Set<PartyType> partyTypes = new HashSet<>();
113        Set<ContactMechanismPurpose> contactMechanismPurposes = new HashSet<>();
114        
115        contactControl.getPartyContactMechanismsByContactMechanism(contactMechanism).forEach((partyContactMechanism) -> {
116            var partyContactMechanismDetail = partyContactMechanism.getLastDetail();
117            var party = partyContactMechanismDetail.getParty();
118            parties.add(party);
119            partyTypes.add(party.getLastDetail().getPartyType());
120            contactControl.getPartyContactMechanismPurposesByPartyContactMechanism(partyContactMechanism).forEach((partyContactMechanismPurpose) -> {
121                contactMechanismPurposes.add(partyContactMechanismPurpose.getLastDetail().getContactMechanismPurpose());
122            });
123        });
124        
125        addPartyNamesToDocument(document, parties);
126        addPartyTypeNamesToDocument(document, partyTypes);
127        addContactMechanismPurposeNamesToDocument(document, contactMechanismPurposes);
128    }
129    
130    private void addGeoCodeToDocument(final Document document, final Language language, final GeoCode geoCode, String description, final String geoCodeIndexField, final String descriptionIndexField) {
131        if(geoCode != null) {
132            description = geoControl.getBestGeoCodeDescription(geoCode, language);
133
134            document.add(new Field(geoCodeIndexField, geoCode.getLastDetail().getGeoCodeName(), FieldTypes.NOT_STORED_TOKENIZED));
135        }
136
137        if(description != null) {
138            document.add(new Field(descriptionIndexField, description, FieldTypes.NOT_STORED_TOKENIZED));
139        }
140    }
141    
142    private void addContactEmailAddressToDocument(final Document document, final ContactMechanism contactMechanism) {
143        var contactEmailAddress = contactControl.getContactEmailAddress(contactMechanism);
144
145        if(contactEmailAddress != null) {
146            document.add(new Field(IndexFields.emailAddress.name(), contactEmailAddress.getEmailAddress(), FieldTypes.NOT_STORED_TOKENIZED));
147        }
148    }
149
150    private void addContactInet4AddressToDocument(final Document document, final ContactMechanism contactMechanism) {
151        var contactInet4Address = contactControl.getContactInet4Address(contactMechanism);
152
153        if(contactInet4Address != null) {
154            document.add(new Field(IndexFields.inet4Address.name(), Inet4AddressUtils.getInstance().formatInet4Address(contactInet4Address.getInet4Address()), FieldTypes.NOT_STORED_TOKENIZED));
155        }
156    }
157
158    private void addContactPostalAddressToDocument(final Document document, final ContactMechanism contactMechanism, final Language language) {
159        var contactPostalAddress = contactControl.getContactPostalAddress(contactMechanism);
160
161        if(contactPostalAddress != null) {
162            var personalTitle = contactPostalAddress.getPersonalTitle();
163            var firstName = contactPostalAddress.getFirstName();
164            var middleName = contactPostalAddress.getMiddleName();
165            var lastName = contactPostalAddress.getLastName();
166            var nameSuffix = contactPostalAddress.getNameSuffix();
167            var companyName = contactPostalAddress.getCompanyName();
168            var attention = contactPostalAddress.getAttention();
169            var address2 = contactPostalAddress.getAddress2();
170            var address3 = contactPostalAddress.getAddress3();
171
172            if(personalTitle != null) {
173                document.add(new Field(IndexFields.personalTitle.name(), personalTitle.getLastDetail().getDescription(), FieldTypes.NOT_STORED_TOKENIZED));
174            }
175
176            if(firstName != null) {
177                document.add(new Field(IndexFields.firstName.name(), firstName, FieldTypes.NOT_STORED_TOKENIZED));
178            }
179
180            if(middleName != null) {
181                document.add(new Field(IndexFields.middleName.name(), middleName, FieldTypes.NOT_STORED_TOKENIZED));
182            }
183
184            if(lastName != null) {
185                document.add(new Field(IndexFields.lastName.name(), lastName, FieldTypes.NOT_STORED_TOKENIZED));
186            }
187
188            if(nameSuffix != null) {
189                document.add(new Field(IndexFields.nameSuffix.name(), nameSuffix.getLastDetail().getDescription(), FieldTypes.NOT_STORED_TOKENIZED));
190            }
191
192            if(companyName != null) {
193                document.add(new Field(IndexFields.companyName.name(), companyName, FieldTypes.NOT_STORED_TOKENIZED));
194            }
195
196            if(attention != null) {
197                document.add(new Field(IndexFields.attention.name(), attention, FieldTypes.NOT_STORED_TOKENIZED));
198            }
199
200            document.add(new Field(IndexFields.address1.name(), contactPostalAddress.getAddress1(), FieldTypes.NOT_STORED_TOKENIZED));
201
202            if(address2 != null) {
203                document.add(new Field(IndexFields.address2.name(), address2, FieldTypes.NOT_STORED_TOKENIZED));
204            }
205
206            if(address3 != null) {
207                document.add(new Field(IndexFields.address3.name(), address3, FieldTypes.NOT_STORED_TOKENIZED));
208            }
209
210            addGeoCodeToDocument(document, language, contactPostalAddress.getCityGeoCode(), contactPostalAddress.getCity(), IndexFields.cityGeoCodeName.name(), IndexFields.city.name());
211            addGeoCodeToDocument(document, language, contactPostalAddress.getCountyGeoCode(), null, IndexFields.countyGeoCodeName.name(), IndexFields.county.name());
212            addGeoCodeToDocument(document, language, contactPostalAddress.getStateGeoCode(), contactPostalAddress.getState(), IndexFields.stateGeoCodeName.name(), IndexFields.state.name());
213            addGeoCodeToDocument(document, language, contactPostalAddress.getPostalCodeGeoCode(), contactPostalAddress.getPostalCode(), IndexFields.postalCodeGeoCodeName.name(), IndexFields.postalCode.name());
214            addGeoCodeToDocument(document, language, contactPostalAddress.getCountryGeoCode(), null, IndexFields.countryGeoCodeName.name(), IndexFields.country.name());
215
216            document.add(new Field(IndexFields.isCommercial.name(), contactPostalAddress.getIsCommercial().toString(), FieldTypes.NOT_STORED_TOKENIZED));
217        }
218    }
219
220    private void addContactTelephoneToDocument(final Document document, final ContactMechanism contactMechanism) {
221        var contactTelephone = contactControl.getContactTelephone(contactMechanism);
222
223        if(contactTelephone != null) {
224            var areaCode = contactTelephone.getAreaCode();
225            var telephoneExtension = contactTelephone.getTelephoneExtension();
226
227            document.add(new Field(IndexFields.countryGeoCodeName.name(), contactTelephone.getCountryGeoCode().getLastDetail().getGeoCodeName(), FieldTypes.NOT_STORED_TOKENIZED));
228
229            if(areaCode != null) {
230                document.add(new Field(IndexFields.areaCode.name(), areaCode, FieldTypes.NOT_STORED_TOKENIZED));
231            }
232
233            document.add(new Field(IndexFields.telephoneNumber.name(), contactTelephone.getTelephoneNumber(), FieldTypes.NOT_STORED_TOKENIZED));
234
235            if(telephoneExtension != null) {
236                document.add(new Field(IndexFields.telephoneExtension.name(), telephoneExtension, FieldTypes.NOT_STORED_TOKENIZED));
237            }
238        }
239    }
240
241    private void addContactWebAddressToDocument(final Document document, final ContactMechanism contactMechanism) {
242        var contactWebAddress = contactControl.getContactWebAddress(contactMechanism);
243
244        if(contactWebAddress != null) {
245            document.add(new Field(IndexFields.url.name(), contactWebAddress.getUrl(), FieldTypes.NOT_STORED_TOKENIZED));
246        }
247    }
248    
249    private void addContactMechanismToDocument(final Document document, final ContactMechanism contactMechanism, final Language language) {
250        var contactMechanismDetail = contactMechanism.getLastDetail();
251        var contactMechanismTypeName = contactMechanismDetail.getContactMechanismType().getContactMechanismTypeName();
252        
253        document.add(new Field(IndexFields.contactMechanismName.name(), contactMechanismDetail.getContactMechanismName(), FieldTypes.NOT_STORED_TOKENIZED));
254        document.add(new SortedDocValuesField(IndexFields.contactMechanismName.name() + IndexConstants.INDEX_FIELD_VARIATION_SEPARATOR + IndexFieldVariations.sortable.name(),
255                new BytesRef(contactMechanismDetail.getContactMechanismName())));
256
257        document.add(new Field(IndexFields.contactMechanismTypeName.name(), contactMechanismTypeName, FieldTypes.NOT_STORED_TOKENIZED));
258        document.add(new Field(IndexFields.allowSolicitation.name(), contactMechanismDetail.getAllowSolicitation().toString(), FieldTypes.NOT_STORED_TOKENIZED));
259        
260        if(contactMechanismTypeName.equals(ContactMechanismTypes.EMAIL_ADDRESS.name())) {
261            addContactEmailAddressToDocument(document, contactMechanism);
262        } else if(contactMechanismTypeName.equals(ContactMechanismTypes.INET_4.name())) {
263            addContactInet4AddressToDocument(document, contactMechanism);
264        } else if(contactMechanismTypeName.equals(ContactMechanismTypes.INET_6.name())) {
265            // TODO
266        } else if(contactMechanismTypeName.equals(ContactMechanismTypes.POSTAL_ADDRESS.name())) {
267            addContactPostalAddressToDocument(document, contactMechanism, language);
268        } else if(contactMechanismTypeName.equals(ContactMechanismTypes.TELECOM_ADDRESS.name())) {
269            addContactTelephoneToDocument(document, contactMechanism);
270        } else if(contactMechanismTypeName.equals(ContactMechanismTypes.WEB_ADDRESS.name())) {
271            addContactWebAddressToDocument(document, contactMechanism);
272        }
273    }
274    
275    @Override
276    protected Document convertToDocument(final EntityInstance entityInstance, final ContactMechanism contactMechanism) {
277        var document = newDocumentWithEntityInstanceFields(entityInstance, contactMechanism.getPrimaryKey());
278
279        addPartiesToDocument(document, contactMechanism);
280        addContactMechanismToDocument(document, contactMechanism, language);
281
282        return document;
283    }
284
285}