EndpointI.java

// Copyright (c) ZeroC, Inc.

package com.zeroc.IceBT;

import com.zeroc.Ice.Acceptor;
import com.zeroc.Ice.Connector;
import com.zeroc.Ice.EndpointI_connectors;
import com.zeroc.Ice.HashUtil;
import com.zeroc.Ice.InputStream;
import com.zeroc.Ice.OutputStream;
import com.zeroc.Ice.ParseException;
import com.zeroc.Ice.SSL.SSLEngineFactory;
import com.zeroc.Ice.Transceiver;

import android.bluetooth.BluetoothAdapter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

final class EndpointI extends com.zeroc.Ice.EndpointI {
    private static final int defaultTimeout = 60_000; // 60,000 milliseconds (1 minute)

    public EndpointI(
            Instance instance,
            String addr,
            String uuid,
            String name,
            int channel,
            int timeout,
            String connectionId,
            boolean compress) {
        _instance = instance;
        _addr = addr;
        _uuid = uuid;
        _name = name;
        _channel = channel;
        _timeout = timeout;
        _connectionId = connectionId;
        _compress = compress;
    }

    public EndpointI(Instance instance) {
        _instance = instance;
        _addr = "";
        _uuid = "";
        _name = "";
        _channel = 0;
        // This timeout is not used in Ice 3.8 or greater.
        _timeout = defaultTimeout;
        _connectionId = "";
        _compress = false;
    }

    public EndpointI(Instance instance, InputStream s) {
        _instance = instance;

        // _name and _channel are not marshaled.
        _name = "";
        _channel = 0;
        _connectionId = "";
        _addr = s.readString().toUpperCase();
        _uuid = s.readString();
        _timeout = s.readInt();
        _compress = s.readBool();
    }

    @Override
    public void streamWriteImpl(OutputStream s) {
        // _name and _channel are not marshaled.
        s.writeString(_addr);
        s.writeString(_uuid);
        s.writeInt(_timeout);
        s.writeBool(_compress);
    }

    @Override
    public short type() {
        return _instance.type();
    }

    @Override
    public String protocol() {
        return _instance.protocol();
    }

    @Override
    public int timeout() {
        return _timeout;
    }

    @Override
    public com.zeroc.Ice.EndpointI timeout(int timeout) {
        if (timeout == _timeout) {
            return this;
        } else {
            return new EndpointI(
                _instance, _addr, _uuid, _name, _channel, timeout, _connectionId, _compress);
        }
    }

    @Override
    public String connectionId() {
        return _connectionId;
    }

    @Override
    public com.zeroc.Ice.EndpointI connectionId(String connectionId) {
        if (connectionId.equals(_connectionId)) {
            return this;
        } else {
            return new EndpointI(
                _instance, _addr, _uuid, _name, _channel, _timeout, connectionId, _compress);
        }
    }

    @Override
    public boolean compress() {
        return _compress;
    }

    @Override
    public com.zeroc.Ice.EndpointI compress(boolean compress) {
        if (compress == _compress) {
            return this;
        } else {
            return new EndpointI(
                _instance, _addr, _uuid, _name, _channel, _timeout, _connectionId, compress);
        }
    }

    @Override
    public boolean datagram() {
        return false;
    }

    @Override
    public boolean secure() {
        return _instance.secure();
    }

    @Override
    public Transceiver transceiver() {
        return null;
    }

    @Override
    public void connectors_async(EndpointI_connectors callback) {
        List<Connector> conns = new ArrayList<Connector>();
        conns.add(new ConnectorI(_instance, _addr, _uuid, _timeout, _connectionId));
        callback.connectors(conns);
    }

    @Override
    public Acceptor acceptor(
            String adapterName, SSLEngineFactory sslEngineFactory) {
        return new AcceptorI(this, _instance, adapterName, _uuid, _name);
    }

    @Override
    public List<com.zeroc.Ice.EndpointI> expandHost() {
        return Collections.singletonList(this);
    }

    @Override
    public boolean isLoopbackOrMulticast() {
        return false;
    }

    @Override
    public com.zeroc.Ice.EndpointI toPublishedEndpoint(String publishedHost) {
        return this;
    }

    @Override
    public boolean equivalent(com.zeroc.Ice.EndpointI endpoint) {
        if (!(endpoint instanceof EndpointI)) {
            return false;
        }
        EndpointI btEndpointI = (EndpointI) endpoint;
        return btEndpointI.type() == type()
            && btEndpointI._addr.equals(_addr)
            && btEndpointI._uuid.equals(_uuid)
            && btEndpointI._channel == _channel;
    }

    @Override
    public String options() {
        // WARNING: Certain features, such as proxy validation in Glacier2,
        // depend on the format of proxy strings. Changes to toString() and
        // methods called to generate parts of the reference string could break
        // these features. Please review for all features that depend on the
        // format of proxyToString() before changing this and related code.
        String s = "";

        if (!_addr.isEmpty()) {
            s += " -a ";
            boolean addQuote = _addr.indexOf(':') != -1;
            if (addQuote) {
                s += "\"";
            }
            s += _addr;
            if (addQuote) {
                s += "\"";
            }
        }

        if (!_uuid.isEmpty()) {
            s += " -u ";
            boolean addQuote = _uuid.indexOf(':') != -1;
            if (addQuote) {
                s += "\"";
            }
            s += _uuid;
            if (addQuote) {
                s += "\"";
            }
        }

        if (_channel > 0) {
            s += " -c " + _channel;
        }

        if (_timeout != defaultTimeout) {
            if (_timeout == -1) {
                s += " -t infinite";
            } else {
                s += " -t " + _timeout;
            }
        }

        if (_compress) {
            s += " -z";
        }

        return s;
    }

    public void initWithOptions(ArrayList<String> args, boolean oaEndpoint) {
        super.initWithOptions(args);

        if (_addr.isEmpty()) {
            _addr = _instance.defaultHost();
            if (_addr == null) {
                _addr = "";
            }
        } else if ("*".equals(_addr)) {
            if (oaEndpoint) {
                _addr = "";
            } else {
                throw new ParseException(
                    "'-a *' not valid for proxy endpoint '" + toString() + "'");
            }
        }

        if (_name.isEmpty()) {
            _name = "Ice Service";
        }

        if (_uuid.isEmpty()) {
            if (oaEndpoint) {
                // Generate a UUID for object adapters that don't specify one.
                _uuid = UUID.randomUUID().toString();
            } else {
                throw new ParseException("a UUID must be specified using the -u option");
            }
        }
    }

    @Override
    public com.zeroc.Ice.EndpointInfo getInfo() {
        return new EndpointInfo(_compress, _addr, _uuid, type(), secure());
    }

    @Override
    public int compareTo(com.zeroc.Ice.EndpointI obj) {
        if (!(obj instanceof EndpointI)) {
            return type() < obj.type() ? -1 : 1;
        }

        EndpointI p = (EndpointI) obj;
        if (this == p) {
            return 0;
        }

        int v = _addr.compareTo(p._addr);
        if (v != 0) {
            return v;
        }

        v = _uuid.compareTo(p._uuid);
        if (v != 0) {
            return v;
        }

        if (_channel < p._channel) {
            return -1;
        } else if (p._channel < _channel) {
            return 1;
        }

        if (_timeout < p._timeout) {
            return -1;
        } else if (p._timeout < _timeout) {
            return 1;
        }

        v = _connectionId.compareTo(p._connectionId);
        if (v != 0) {
            return v;
        }

        if (!_compress && p._compress) {
            return -1;
        } else if (!p._compress && _compress) {
            return 1;
        }

        return 0;
    }

    @Override
    public int hashCode() {
        int h = 5381;
        h = HashUtil.hashAdd(h, _addr);
        h = HashUtil.hashAdd(h, _uuid);
        h = HashUtil.hashAdd(h, _timeout);
        h = HashUtil.hashAdd(h, _connectionId);
        h = HashUtil.hashAdd(h, _compress);
        return h;
    }

    @Override
    protected boolean checkOption(String option, String argument, String endpoint) {
        if (super.checkOption(option, argument, endpoint)) {
            return true;
        }

        if ("-a".equals(option)) {
            if (argument == null) {
                throw new ParseException(
                    "no argument provided for -a option in endpoint '" + endpoint + "'");
            }
            if (!"*".equals(argument)
                && !BluetoothAdapter.checkBluetoothAddress(argument.toUpperCase())) {
                throw new ParseException(
                    "invalid address provided for -a option in endpoint '" + endpoint + "'");
            }
            _addr = argument.toUpperCase(); // Android requires a hardware address to use upper case
            // letters.
        } else if ("-u".equals(option)) {
            if (argument == null) {
                throw new ParseException(
                    "no argument provided for -u option in endpoint '" + endpoint + "'");
            }
            try {
                UUID.fromString(argument);
            } catch (IllegalArgumentException ex) {
                throw new ParseException("invalid UUID for Bluetooth endpoint", ex);
            }
            _uuid = argument;
        } else if ("-c".equals(option)) {
            if (argument == null) {
                throw new ParseException(
                    "no argument provided for -c option in endpoint '" + endpoint + "'");
            }

            try {
                _channel = Integer.parseInt(argument);
            } catch (NumberFormatException ex) {
                throw new ParseException(
                    "invalid channel value '" + argument + "' in endpoint '" + endpoint + "'",
                    ex);
            }

            // RFCOMM channel limit is 30
            if (_channel < 0 || _channel > 30) {
                throw new ParseException(
                    "channel value '"
                        + argument
                        + "' out of range in endpoint '"
                        + endpoint
                        + "'");
            }
        } else if ("-t".equals(option)) {
            if (argument == null) {
                throw new ParseException(
                    "no argument provided for -t option in endpoint '" + endpoint + "'");
            }

            if ("infinite".equals(argument)) {
                _timeout = -1;
            } else {
                try {
                    _timeout = Integer.parseInt(argument);
                    if (_timeout < 1) {
                        throw new ParseException(
                            "invalid timeout value '"
                                + argument
                                + "' in endpoint '"
                                + endpoint
                                + "'");
                    }
                } catch (NumberFormatException ex) {
                    throw new ParseException(
                        "invalid timeout value '"
                            + argument
                            + "' in endpoint '"
                            + endpoint
                            + "'",
                        ex);
                }
            }
        } else if ("-z".equals(option)) {
            if (argument != null) {
                throw new ParseException(
                    "unexpected argument `"
                        + argument
                        + "' provided for -z option in "
                        + endpoint);
            }

            _compress = true;
        } else if ("--name".equals(option)) {
            if (argument == null) {
                throw new ParseException(
                    "no argument provided for --name option in endpoint " + endpoint);
            }

            _name = argument;
        } else {
            return false;
        }
        return true;
    }

    private final Instance _instance;
    private String _addr;
    private String _uuid;
    private String _name;
    private int _channel;
    private int _timeout;
    private String _connectionId;
    private boolean _compress;
}