All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sputnikdev.bluetooth.URL Maven / Gradle / Ivy

package org.sputnikdev.bluetooth;

/*-
 * #%L
 * org.sputnikdev:bluetooth-utils
 * %%
 * Copyright (C) 2017 Sputnik Dev
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Class {@code URL} represents a Uniform Resource Locator for bluetooth resources,
 * e.g. bluetooth adapters, bluetooth devices, GATT services, GATT characteristics and GATT fields.
 * For example, if you have an adapter with MAC address B8:27:EB:60:0C:43,
 * a device with MAC address 54:60:09:95:86:01 is connected to the adapter,
 * the device has a GATT service with UUID 0000180f-0000-1000-8000-00805f9b34fb,
 * the service has a characteristic with UUID 00002a19-0000-1000-8000-00805f9b34fb
 * and the characteristic has a field called "Level", then a URL for the field can be:
 * /B8:27:EB:60:0C:43/54:60:09:95:86:01/0000180f-0000-1000-8000-00805f9b34fb/00002a19-0000-1000-8000-00805f9b34fb/Level
 * Or a URL for the service can be:
 * /B8:27:EB:60:0C:43/54:60:09:95:86:01/0000180f-0000-1000-8000-00805f9b34fb
 * Similarly, it is easy to define a URL for other components, e.g. adapters, devices and characteristics.
 *
 * 

If there are more than one protocol used to access Bluetooth devices (e.g. DBus, serial interface etc), * then it is possible to define protocol as well: * tinyb://B8:27:EB:60:0C:43/54:60:09:95:86:01/0000180f-0000-1000-8000-00805f9b34fb/ * 00002a19-0000-1000-8000-00805f9b34fb/Level * * @author Vlad Kolotov */ public class URL implements Comparable { public static final Pattern URL_PATTERN = Pattern.compile("^((?\\w*):/)?/(?(\\w\\w:){5}\\w\\w)" + "?(/(?(\\w\\w:){5}\\w\\w))" + "?(/(?[0-9a-f]{4,8}(-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?))" + "?(/(?[0-9a-f]{4,8}(-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})?))" + "?(/(?\\w+))?$"); public static final URL ROOT = new URL("/"); private final String protocol; private final String adapterAddress; private final String deviceAddress; private final String serviceUUID; private final String characteristicUUID; private final String fieldName; /** * Constructor to build a URL object from its text representation. * E.g. / bluetooth adapter / bluetooth device / GATT service / GATT characteristic / Characteristic Field Name * *

The following are some examples of a valid URL: * *

URL pointing to a field: * /B8:27:EB:60:0C:43/54:60:09:95:86:01/0000180f-0000-1000-8000-00805f9b34fb/ * 00002a19-0000-1000-8000-00805f9b34fb/Level * *

URL pointing to a characteristic: * /B8:27:EB:60:0C:43/54:60:09:95:86:01/0000180f-0000-1000-8000-00805f9b34fb/00002a19-0000-1000-8000-00805f9b34fb * *

URL pointing to a service: * /B8:27:EB:60:0C:43/54:60:09:95:86:01/0000180f-0000-1000-8000-00805f9b34fb * *

URL pointing to a bluetooth device: * /B8:27:EB:60:0C:43/54:60:09:95:86:01 * *

URL point to a bluetooth adapter: * /B8:27:EB:60:0C:43 * *

URL pointing to the "root": * / * *

Services and characteristic UUIDs can be in a short form: * /B8:27:EB:60:0C:43/54:60:09:95:86:01/0000180f/00002a19/Level * /B8:27:EB:60:0C:43/54:60:09:95:86:01/180f/2a19/Level * * * @param url text representation of a URL */ public URL(String url) { Matcher matcher = URL_PATTERN.matcher(url); if (matcher.find()) { this.protocol = matcher.group("protocol"); this.adapterAddress = matcher.group("adapter"); this.deviceAddress = matcher.group("device"); this.serviceUUID = matcher.group("service"); this.characteristicUUID = matcher.group("characteristic"); this.fieldName = matcher.group("field"); validate(); } else { throw new IllegalArgumentException("Invalid URL: " + url); } } /** * Constructs the "root" URL object. All components of the such URL are nulls. * A text representation of the "root" URL is "/" */ public URL() { this(null, null); } /** * Constructs a URL pointing to a bluetooth device. * @param adapterAddress bluetooth adapter MAC address * @param deviceAddress bluetooth device MAC address */ public URL(String adapterAddress, String deviceAddress) { this(adapterAddress, deviceAddress, null, null, null); } /** * Constructs a URL pointing to a GATT service. * @param protocol protocol name * @param adapterAddress bluetooth adapter MAC address * @param deviceAddress bluetooth device MAC address */ public URL(String protocol, String adapterAddress, String deviceAddress) { this(protocol, adapterAddress, deviceAddress, null, null, null); } /** * Constructs a URL pointing to a GATT characteristic. * @param adapterAddress bluetooth adapter MAC address * @param deviceAddress bluetooth device MAC address * @param serviceUUID UUID of a GATT service * @param characteristicUUID UUID of a GATT characteristic */ public URL(String adapterAddress, String deviceAddress, String serviceUUID, String characteristicUUID) { this(adapterAddress, deviceAddress, serviceUUID, characteristicUUID, null); } /** * Constructs a URL pointing to a GATT field. * @param adapterAddress bluetooth adapter MAC address * @param deviceAddress bluetooth device MAC address * @param serviceUUID UUID of a GATT service * @param characteristicUUID UUID of a GATT characteristic * @param fieldName name of a field of the characteristic */ public URL(String adapterAddress, String deviceAddress, String serviceUUID, String characteristicUUID, String fieldName) { this(null, adapterAddress, deviceAddress, serviceUUID, characteristicUUID, fieldName); } /** * Constructs a URL pointing to a GATT field. * @param protocol protocol name * @param adapterAddress bluetooth adapter MAC address * @param deviceAddress bluetooth device MAC address * @param serviceUUID UUID of a GATT service * @param characteristicUUID UUID of a GATT characteristic * @param fieldName name of a field of the characteristic */ public URL(String protocol, String adapterAddress, String deviceAddress, String serviceUUID, String characteristicUUID, String fieldName) { this.protocol = protocol; this.adapterAddress = adapterAddress; this.deviceAddress = deviceAddress; this.serviceUUID = serviceUUID; this.characteristicUUID = characteristicUUID; this.fieldName = fieldName; validate(); } /** * Makes a copy of a given URL with some additional components. * @param protocol protocol name * @return a copy of a given URL with some additional components */ public URL copyWithProtocol(String protocol) { return new URL(protocol, this.adapterAddress, this.deviceAddress, this.serviceUUID, this.characteristicUUID, this.fieldName); } /** * Makes a copy of a given URL with some additional components. * @param adapterAddress bluetooth adapter MAC address * @return a copy of a given URL with some additional components */ public URL copyWithAdapter(String adapterAddress) { return new URL(this.protocol, adapterAddress, this.deviceAddress, this.serviceUUID, this.characteristicUUID, this.fieldName); } /** * Makes a copy of a given URL with some additional components. * @param deviceAddress bluetooth device MAC address * @return a copy of a given URL with some additional components */ public URL copyWithDevice(String deviceAddress) { return new URL(this.protocol, this.adapterAddress, deviceAddress, this.serviceUUID, this.characteristicUUID, this.fieldName); } /** * Makes a copy of a given URL with some additional components. * @param serviceUUID UUID of a GATT service * @return a copy of a given URL with some additional components */ public URL copyWithService(String serviceUUID) { return new URL(this.protocol, this.adapterAddress, this.deviceAddress, serviceUUID, null, null); } /** * Makes a copy of a given URL with some additional components. * @param characteristicUUID UUID of a GATT characteristic * @return a copy of a given URL with some additional components */ public URL copyWithCharacteristic(String characteristicUUID) { return new URL(this.protocol, this.adapterAddress, this.deviceAddress, serviceUUID, characteristicUUID, null); } /** * Makes a copy of a given URL with some additional components. * @param serviceUUID UUID of a GATT service * @param characteristicUUID UUID of a GATT characteristic * @return a copy of a given URL with some additional components */ public URL copyWith(String serviceUUID, String characteristicUUID) { return new URL(this.protocol, this.adapterAddress, this.deviceAddress, serviceUUID, characteristicUUID, null); } /** * Makes a copy of a given URL with some additional components. * @param serviceUUID UUID of a GATT service * @param characteristicUUID UUID of a GATT characteristic * @param fieldName name of a field of the characteristic * @return a copy of a given URL with some additional components */ public URL copyWith(String serviceUUID, String characteristicUUID, String fieldName) { return new URL(this.protocol, this.adapterAddress, this.deviceAddress, serviceUUID, characteristicUUID, fieldName); } /** * Makes a copy of a given URL with some additional components. * @param fieldName name of a field of the characteristic * @return a copy of a given URL with some additional components */ public URL copyWithField(String fieldName) { return new URL(this.protocol, this.adapterAddress, this.deviceAddress, this.serviceUUID, this.characteristicUUID, fieldName); } /** * Returns a copy of a given URL truncated to its device component. * Service, characteristic and field components will be null. * @return a copy of a given URL truncated to the device component */ public URL getDeviceURL() { return new URL(this.protocol, this.adapterAddress, this.deviceAddress); } /** * Returns a copy of a given URL truncated to its service component. * Characteristic and field components will be null. * @return a copy of a given URL truncated to the service component */ public URL getServiceURL() { return new URL(this.protocol, this.adapterAddress, this.deviceAddress, this.serviceUUID, null, null); } /** * Returns a copy of a given URL truncated to its characteristic component. * The field component will be null. * @return a copy of a given URL truncated to the service component */ public URL getCharacteristicURL() { return new URL(this.protocol, this.adapterAddress, this.deviceAddress, this.serviceUUID, this.characteristicUUID, null); } /** * Returns a copy of a given URL truncated to its adapter component. * Device, Service, characteristic and field components will be null. * @return a copy of a given URL truncated to the adapter component */ public URL getAdapterURL() { return new URL(this.protocol, this.adapterAddress, null); } /** * Returns a copy of a given URL truncated to its protocol component. * Adapter, Device, Service, characteristic and field components will be null. * @return a copy of a given URL truncated to the adapter component */ public URL getProtocolURL() { return new URL(this.protocol, null, null); } /** * Returns bluetooth protocol. * @return bluetooth protocol */ public String getProtocol() { return protocol; } /** * Returns bluetooth adapter address (MAC address). * @return adapter address (MAC address) */ public String getAdapterAddress() { return adapterAddress; } /** * Returns bluetooth device address. * @return bluetooth device address */ public String getDeviceAddress() { return deviceAddress; } /** * Returns service UUID. * @return service UUID */ public String getServiceUUID() { return serviceUUID; } /** * Return characteristic UUID. * @return characteristic UUID */ public String getCharacteristicUUID() { return characteristicUUID; } /** * Returns field name. * @return field name */ public String getFieldName() { return fieldName; } /** * Checks whether a given URL is the "root" URL object. * @return true if it is the "root" URL, false otherwise */ public boolean isProtocol() { return protocol != null && adapterAddress == null; } /** * Checks whether a given URL is the "root" URL object. * @return true if it is the "root" URL, false otherwise */ public boolean isRoot() { return adapterAddress == null; } /** * Checks whether a given URL is pointing to a bluetooth adapter. * @return true if a given URL is pointing to a bluetooth adapter, false otherwise */ public boolean isAdapter() { return adapterAddress != null && deviceAddress == null; } /** * Checks whether a given URL is pointing to a bluetooth device. * @return true if a given URL is pointing to a bluetooth device, false otherwise */ public boolean isDevice() { return deviceAddress != null && serviceUUID == null; } /** * Checks whether a given URL is pointing to a GATT service. * @return true if a given URL is pointing to a GATT service, false otherwise */ public boolean isService() { return serviceUUID != null && characteristicUUID == null; } /** * Checks whether a given URL is pointing to a GATT characteristic. * @return true if a given URL is pointing to a GATT characteristic, false otherwise */ public boolean isCharacteristic() { return characteristicUUID != null && fieldName == null; } /** * Checks whether a given URL is pointing to a field of a characteristic. * @return true if a given URL is pointing to a field of a characteristic, false otherwise */ public boolean isField() { return fieldName != null; } /** * Returns a new URL representing its "parent" component. * @return a new URL representing its "parent" component */ public URL getParent() { if (isField()) { return getCharacteristicURL(); } else if (isCharacteristic()) { return getServiceURL(); } else if (isService()) { return getDeviceURL(); } else if (isDevice()) { return getAdapterURL(); } else if (isAdapter()) { return getProtocolURL(); } else { return null; } } /** * Checks whether the URL is a descendant of a URL provided as an argument. * If the provided URL does not specify protocol, then it will match any protocol of the URL * @param url a bluetooth URL * @return true if the URL is a descendant of a provided URL */ public boolean isDescendant(URL url) { if (url.protocol != null && protocol != null && !protocol.equals(url.protocol)) { return false; } if (adapterAddress != null && url.adapterAddress == null) { return true; } if (adapterAddress != null && !adapterAddress.equals(url.adapterAddress)) { return false; } if (deviceAddress != null && url.deviceAddress == null) { return true; } if (deviceAddress != null && !deviceAddress.equals(url.deviceAddress)) { return false; } if (serviceUUID != null && url.serviceUUID == null) { return true; } if (serviceUUID != null ? !serviceUUID.equals(url.serviceUUID) : url.serviceUUID != null) { return false; } if (characteristicUUID != null && url.characteristicUUID == null) { return true; } if (characteristicUUID != null ? !characteristicUUID.equals(url.characteristicUUID) : url.characteristicUUID != null) { return false; } return fieldName != null && url.fieldName == null; } @Override public String toString() { LinkedList fields = new LinkedList<>(); fields.add(adapterAddress); fields.add(deviceAddress); fields.add(serviceUUID); fields.add(characteristicUUID); fields.add(fieldName); while (!fields.isEmpty() && fields.getLast() == null) { fields.removeLast(); } return (protocol != null ? protocol + ":/" : "") + "/" + String.join("/", fields); } @Override public boolean equals(Object that) { if (this == that) { return true; } if (that == null || getClass() != that.getClass()) { return false; } URL url = (URL) that; if (protocol != null ? !protocol.equals(url.protocol) : url.protocol != null) { return false; } if (adapterAddress != null ? !adapterAddress.equals(url.adapterAddress) : url.adapterAddress != null) { return false; } if (deviceAddress != null ? !deviceAddress.equals(url.deviceAddress) : url.deviceAddress != null) { return false; } if (serviceUUID != null ? !serviceUUID.equals(url.serviceUUID) : url.serviceUUID != null) { return false; } if (characteristicUUID != null ? !characteristicUUID.equals(url.characteristicUUID) : url.characteristicUUID != null) { return false; } return fieldName != null ? fieldName.equals(url.fieldName) : url.fieldName == null; } @Override public int hashCode() { int result = protocol != null ? protocol.hashCode() : 0; result = 31 * result + (adapterAddress != null ? adapterAddress.hashCode() : 0); result = 31 * result + (deviceAddress != null ? deviceAddress.hashCode() : 0); result = 31 * result + (serviceUUID != null ? serviceUUID.hashCode() : 0); result = 31 * result + (characteristicUUID != null ? characteristicUUID.hashCode() : 0); result = 31 * result + (fieldName != null ? fieldName.hashCode() : 0); return result; } @Override public int compareTo(URL that) { int result = compareFields(adapterAddress, that.adapterAddress); if (result != 0) { return result; } result = compareFields(deviceAddress, that.deviceAddress); if (result != 0) { return result; } result = compareFields(serviceUUID, that.serviceUUID); if (result != 0) { return result; } result = compareFields(characteristicUUID, that.characteristicUUID); if (result != 0) { return result; } return compareFields(fieldName, that.fieldName); } private void validate() { if (fieldName != null && characteristicUUID == null || characteristicUUID != null && serviceUUID == null || serviceUUID != null && deviceAddress == null || deviceAddress != null && adapterAddress == null) { throw new IllegalArgumentException("Invalid url: " + toString()); } } private int compareFields(String field1, String field2) { if (field1 == null && field2 == null) { return 0; } else if (field2 == null) { return 1; } else if (field1 == null) { return -1; } else { return field1.compareTo(field2); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy