2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.tr064.internal.phonebook;
15 import java.util.HashMap;
17 import java.util.Optional;
18 import java.util.stream.Collectors;
20 import org.eclipse.jdt.annotation.NonNullByDefault;
21 import org.eclipse.jetty.client.HttpClient;
22 import org.openhab.binding.tr064.internal.dto.additions.PhonebooksType;
23 import org.openhab.binding.tr064.internal.util.Util;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
28 * The {@link Tr064PhonebookImpl} class implements a phonebook
30 * @author Jan N. Klug - Initial contribution
33 public class Tr064PhonebookImpl implements Phonebook {
34 private final Logger logger = LoggerFactory.getLogger(Tr064PhonebookImpl.class);
36 protected Map<String, String> phonebook = new HashMap<>();
38 private final HttpClient httpClient;
39 private final String phonebookUrl;
40 private final int httpTimeout;
42 private String phonebookName = "";
44 public Tr064PhonebookImpl(HttpClient httpClient, String phonebookUrl, int httpTimeout) {
45 this.httpClient = httpClient;
46 this.phonebookUrl = phonebookUrl;
47 this.httpTimeout = httpTimeout;
51 private void getPhonebook() {
52 PhonebooksType phonebooksType = Util.getAndUnmarshalXML(httpClient, phonebookUrl, PhonebooksType.class,
54 if (phonebooksType == null) {
55 logger.warn("Failed to get phonebook with URL '{}'", phonebookUrl);
58 phonebookName = phonebooksType.getPhonebook().getName();
60 phonebook = phonebooksType.getPhonebook().getContact().stream().map(contact -> {
61 String contactName = contact.getPerson().getRealName();
62 if (contactName == null || contactName.isBlank()) {
63 return new HashMap<String, String>();
65 return contact.getTelephony().getNumber().stream().collect(Collectors.toMap(
66 number -> normalizeNumber(number.getValue()), number -> contactName, this::mergeSameContactNames));
67 }).collect(HashMap::new, HashMap::putAll, HashMap::putAll);
68 logger.debug("Downloaded phonebook {}: {}", phonebookName, phonebook);
71 // in case there are multiple phone entries with same number -> name mapping, i.e. in phonebooks exported from
72 // mobiles containing multiple accounts like: local, cloudprovider1, messenger1, messenger2,...
73 private String mergeSameContactNames(String nameA, String nameB) {
74 if (nameA.equals(nameB)) {
77 throw new IllegalStateException(
78 "Found different names for the same number: '" + nameA + "' and '" + nameB + "'");
82 public String getName() {
87 public Optional<String> lookupNumber(String number, int matchCount) {
88 String normalized = normalizeNumber(number);
90 if (matchCount > 0 && matchCount < normalized.length()) {
91 matchString = normalized.substring(normalized.length() - matchCount);
92 } else if (matchCount < 0 && (-matchCount) < normalized.length()) {
93 matchString = normalized.substring(-matchCount);
95 matchString = normalized;
97 logger.trace("Normalized '{}' to '{}', matchString is '{}'", number, normalized, matchString);
98 return matchString.isBlank() ? Optional.empty()
99 : phonebook.keySet().stream().filter(n -> n.endsWith(matchString)).findFirst().map(phonebook::get);
103 public String toString() {
104 return "Phonebook{" + "phonebookName='" + phonebookName + "', phonebook=" + phonebook + '}';
108 * normalize a phone number (remove everything except digits and *) for comparison
110 * @param number the input phone number string
111 * @return normalized phone number string
113 public final String normalizeNumber(String number) {
114 return number.replaceAll("[^0-9\\*\\+]", "");