2 * Copyright (c) 2010-2021 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.io.ByteArrayInputStream;
16 import java.io.InputStream;
17 import java.util.HashMap;
19 import java.util.Optional;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23 import java.util.stream.Collectors;
25 import javax.xml.bind.JAXBContext;
26 import javax.xml.bind.JAXBException;
27 import javax.xml.bind.Unmarshaller;
28 import javax.xml.transform.stream.StreamSource;
30 import org.eclipse.jdt.annotation.NonNullByDefault;
31 import org.eclipse.jetty.client.HttpClient;
32 import org.eclipse.jetty.client.api.ContentResponse;
33 import org.eclipse.jetty.http.HttpMethod;
34 import org.openhab.binding.tr064.internal.dto.additions.PhonebooksType;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * The {@link Tr064PhonebookImpl} class implements a phonebook
41 * @author Jan N. Klug - Initial contribution
44 public class Tr064PhonebookImpl implements Phonebook {
45 private final Logger logger = LoggerFactory.getLogger(Tr064PhonebookImpl.class);
47 private Map<String, String> phonebook = new HashMap<>();
49 private final HttpClient httpClient;
50 private final String phonebookUrl;
52 private String phonebookName = "";
54 public Tr064PhonebookImpl(HttpClient httpClient, String phonebookUrl) {
55 this.httpClient = httpClient;
56 this.phonebookUrl = phonebookUrl;
60 private void getPhonebook() {
62 ContentResponse contentResponse = httpClient.newRequest(phonebookUrl).method(HttpMethod.GET)
63 .timeout(2, TimeUnit.SECONDS).send();
64 InputStream xml = new ByteArrayInputStream(contentResponse.getContent());
66 JAXBContext context = JAXBContext.newInstance(PhonebooksType.class);
67 Unmarshaller um = context.createUnmarshaller();
68 PhonebooksType phonebooksType = um.unmarshal(new StreamSource(xml), PhonebooksType.class).getValue();
70 phonebookName = phonebooksType.getPhonebook().getName();
72 phonebook = phonebooksType.getPhonebook().getContact().stream().map(contact -> {
73 String contactName = contact.getPerson().getRealName();
74 return contact.getTelephony().getNumber().stream()
75 .collect(Collectors.toMap(number -> normalizeNumber(number.getValue()), number -> contactName,
76 this::mergeSameContactNames));
77 }).collect(HashMap::new, HashMap::putAll, HashMap::putAll);
78 logger.debug("Downloaded phonebook {}: {}", phonebookName, phonebook);
79 } catch (JAXBException | InterruptedException | ExecutionException | TimeoutException e) {
80 logger.warn("Failed to get phonebook with URL {}:", phonebookUrl, e);
84 // in case there are multiple phone entries with same number -> name mapping, i.e. in phonebooks exported from
85 // mobiles containing multiple accounts like: local, cloudprovider1, messenger1, messenger2,...
86 private String mergeSameContactNames(String nameA, String nameB) {
87 if (nameA != null && nameA.equals(nameB)) {
90 throw new IllegalStateException(
91 "Found different names for the same number: '" + nameA + "' and '" + nameB + "'");
95 public String getName() {
100 public Optional<String> lookupNumber(String number, int matchCount) {
101 String normalized = normalizeNumber(number);
102 String matchString = matchCount > 0 && matchCount < normalized.length()
103 ? normalized.substring(normalized.length() - matchCount)
105 logger.trace("Normalized '{}' to '{}', matchString is '{}'", number, normalized, matchString);
106 return matchString.isBlank() ? Optional.empty()
107 : phonebook.keySet().stream().filter(n -> n.endsWith(matchString)).findFirst().map(phonebook::get);
111 public String toString() {
112 return "Phonebook{" + "phonebookName='" + phonebookName + "', phonebook=" + phonebook + '}';
115 private String normalizeNumber(String number) {
116 // Naive normalization: remove all non-digit characters
117 return number.replaceAll("[^0-9]\\+\\*", "");