/*
* $Id$
* This file is a part of the Arakhne Foundation Classes, http://www.arakhne.org/afc
*
* Copyright (c) 2000-2012 Stephane GALLAND.
* Copyright (c) 2005-10, Multiagent Team, Laboratoire Systemes et Transports,
* Universite de Technologie de Belfort-Montbeliard.
* Copyright (c) 2013-2016 The original authors, and other authors.
*
* 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.
*/
package org.arakhne.afc.vmutil;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.eclipse.xtext.xbase.lib.Pure;
import org.arakhne.afc.vmutil.locale.Locale;
/** A MACNumber is the unique number associated to a network interface.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
* @since 4.2
* @see NetworkInterface
*/
public final class MACNumber {
/**
* Character that is used as separator inside MAC addresses.
*/
public static final char MACNUMBER_SEPARATOR = '/';
/** Constant ethernet address object which has the "null address".
*
* <p>This constant can be used when you want a non-null
* MACAddress object reference, but want a invalid (or null)
* MAC address contained.
*
* @see #isNull()
*/
public static final MACNumber NULL = new MACNumber();
/**
* Content of the MAC address.
*/
private final byte[] bytes;
/** Constructs object with "null values" (address of "0:0:0:0:0:0").
*/
@SuppressWarnings("checkstyle:magicnumber")
public MACNumber() {
this.bytes = new byte[6];
}
/** Build a MACNumber from a set of bytes.
* The byte array must contains 6 elements.
*
* @param bytes is the list of bytes from which the address must be built.
* @throws IllegalArgumentException if the byte array does not corresponds to a valid MAC Number.
*/
@SuppressWarnings("checkstyle:magicnumber")
public MACNumber(byte[] bytes) {
if (bytes == null || bytes.length != 6) {
throw new IllegalArgumentException(Locale.getString("E1")); //$NON-NLS-1$
}
this.bytes = new byte[6];
System.arraycopy(bytes, 0, this.bytes, 0, 6);
}
/** Build a MACNumber from a string representation.
*
* @param address is the string representation of a MAC address
* @throws IllegalArgumentException if the byte array does not corresponds to a valid MAC Number.
* @see #toString()
*/
@SuppressWarnings("checkstyle:magicnumber")
public MACNumber(String address) {
if (address == null) {
throw new IllegalArgumentException(Locale.getString("E1")); //$NON-NLS-1$
}
final String[] parts = address.split(Pattern.quote(":")); //$NON-NLS-1$
if (parts.length != 6) {
throw new IllegalArgumentException(Locale.getString("E1")); //$NON-NLS-1$
}
this.bytes = new byte[6];
try {
int val;
for (int i = 0; i < 6; ++i) {
val = Integer.parseInt(parts[i], 16);
this.bytes[i] = (byte) val;
}
} catch (Exception exception) {
throw new IllegalArgumentException(Locale.getString("E1"), exception); //$NON-NLS-1$
}
}
/** Parse the specified string an repleis the corresponding MAC numbers.
*
* @param addresses is the string to parse
* @return a list of addresses.
* @throws IllegalArgumentException is the argument has not the right syntax.
*/
@Pure
public static MACNumber[] parse(String addresses) {
if ((addresses == null) || ("".equals(addresses))) { //$NON-NLS-1$
return new MACNumber[0];
}
final String[] adrs = addresses.split(Pattern.quote(Character.toString(MACNUMBER_SEPARATOR)));
final List<MACNumber> list = new ArrayList<>();
for (final String adr : adrs) {
list.add(new MACNumber(adr));
}
final MACNumber[] tab = new MACNumber[list.size()];
list.toArray(tab);
list.clear();
return tab;
}
/** Parse the specified string an repleis the corresponding MAC numbers.
*
* @param addresses is the string to parse
* @return a list of addresses.
* @throws IllegalArgumentException is the argument has not the right syntax.
*/
@Pure
public static String[] parseAsString(String addresses) {
if ((addresses == null) || ("".equals(addresses))) { //$NON-NLS-1$
return new String[0];
}
final String[] adrs = addresses.split(Pattern.quote(Character.toString(MACNUMBER_SEPARATOR)));
final List<String> list = new ArrayList<>();
for (final String adr : adrs) {
list.add(new MACNumber(adr).toString());
}
final String[] tab = new String[list.size()];
list.toArray(tab);
list.clear();
return tab;
}
/** Join the specified MAC numbers to reply a string.
*
* @param addresses is the list of mac addresses to join.
* @return the joined string.
*/
@Pure
public static String join(MACNumber... addresses) {
if ((addresses == null) || (addresses.length == 0)) {
return null;
}
final StringBuilder buf = new StringBuilder();
for (final MACNumber number : addresses) {
if (buf.length() > 0) {
buf.append(MACNUMBER_SEPARATOR);
}
buf.append(number);
}
return buf.toString();
}
/** Get all of the ethernet addresses associated with the local machine.
*
* <p>This method will try and find ALL of the ethernet adapters
* which are currently available on the system. This is heavily OS
* dependent and may not be supported on all platforms. When not
* supported, you should still get back a collection with the {@link
* #getPrimaryAdapter primary adapter} in it.
*
* @return the list of MAC numbers associated to the physical devices.
* @see #getPrimaryAdapter
*/
@Pure
public static Collection<MACNumber> getAllAdapters() {
final List<MACNumber> av = new ArrayList<>();
final Enumeration<NetworkInterface> interfaces;
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException exception) {
return av;
}
if (interfaces != null) {
NetworkInterface inter;
while (interfaces.hasMoreElements()) {
inter = interfaces.nextElement();
try {
final byte[] addr = inter.getHardwareAddress();
if (addr != null) {
av.add(new MACNumber(addr));
}
} catch (SocketException exception) {
// Continue to the next loop.
}
}
}
return av;
}
/** Get all of the internet address and ethernet address mappings
* on the local machine.
*
* <p>This method will try and find ALL of the ethernet adapters
* which are currently available on the system. This is heavily OS
* dependent and may not be supported on all platforms. When not
* supported, you should still get back a collection with the {@link
* #getPrimaryAdapterAddresses primary adapter} in it.
*
* @return the map internet address and ethernet address mapping.
* @see #getPrimaryAdapterAddresses
*/
@Pure
public static Map<InetAddress, MACNumber> getAllMappings() {
final Map<InetAddress, MACNumber> av = new HashMap<>();
final Enumeration<NetworkInterface> interfaces;
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException exception) {
return av;
}
if (interfaces != null) {
NetworkInterface inter;
MACNumber mac;
InetAddress inet;
while (interfaces.hasMoreElements()) {
inter = interfaces.nextElement();
try {
final byte[] addr = inter.getHardwareAddress();
if (addr != null) {
mac = new MACNumber(addr);
final Enumeration<InetAddress> inets = inter.getInetAddresses();
while (inets.hasMoreElements()) {
inet = inets.nextElement();
av.put(inet, mac);
}
}
} catch (SocketException exception) {
// Continue to the next loop.
}
}
}
return av;
}
/** Try to determine the primary ethernet address of the machine.
*
* @return the primary MACNumber or <code>null</code>
*/
@Pure
public static MACNumber getPrimaryAdapter() {
final Enumeration<NetworkInterface> interfaces;
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException exception) {
return null;
}
if (interfaces != null) {
NetworkInterface inter;
while (interfaces.hasMoreElements()) {
inter = interfaces.nextElement();
try {
final byte[] addr = inter.getHardwareAddress();
if (addr != null) {
return new MACNumber(addr);
}
} catch (SocketException exception) {
// Continue to the next loop.
}
}
}
return null;
}
/** Try to determine the primary ethernet address of the machine and
* replies the associated internet addresses.
*
* @return the internet addresses of the primary network interface.
*/
@Pure
public static Collection<InetAddress> getPrimaryAdapterAddresses() {
final Enumeration<NetworkInterface> interfaces;
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException exception) {
return Collections.emptyList();
}
if (interfaces != null) {
NetworkInterface inter;
while (interfaces.hasMoreElements()) {
inter = interfaces.nextElement();
try {
final byte[] addr = inter.getHardwareAddress();
if (addr != null) {
final Collection<InetAddress> inetList = new ArrayList<>();
final Enumeration<InetAddress> inets = inter.getInetAddresses();
while (inets.hasMoreElements()) {
inetList.add(inets.nextElement());
}
return inetList;
}
} catch (SocketException exception) {
// Continue to the next loop.
}
}
}
return Collections.emptyList();
}
@Override
@Pure
public boolean equals(Object object) {
if (!(object instanceof MACNumber)) {
return false;
}
final byte[] bao = ((MACNumber) object).bytes;
if (bao.length != this.bytes.length) {
return false;
}
for (int i = 0; i < bao.length; ++i) {
if (bao[i] != this.bytes[i]) {
return false;
}
}
return true;
}
@Override
@Pure
public int hashCode() {
final int blen = this.bytes.length;
int hash = 1;
for (int i = 0; i < blen; ++i) {
hash = 31 * hash + Byte.hashCode(this.bytes[i]);
}
return hash ^ (hash >> 31);
}
@SuppressWarnings("checkstyle:magicnumber")
@Override
@Pure
public String toString() {
final int blen = this.bytes.length;
final StringBuilder sb = new StringBuilder(blen * 3);
for (int i = 0; i < blen; ++i) {
int lo = this.bytes[i];
final int hi = (lo >> 4) & 0xF;
lo &= 0xF;
if (i != 0) {
sb.append(':');
}
sb.append(Character.forDigit(hi, 16));
sb.append(Character.forDigit(lo, 16));
}
return sb.toString();
}
/** Replies if all the MAC address number are equal to zero.
*
* @return <code>true</code> if all the bytes are zero.
* @see #NULL
*/
@Pure
public boolean isNull() {
for (int i = 0; i < this.bytes.length; ++i) {
if (this.bytes[i] != 0) {
return false;
}
}
return true;
}
/** Replies the bytes that compose this MAC Address.
*
* @return a copy of the current bytes.
*/
@Pure
public byte[] getBytes() {
return this.bytes.clone();
}
}