+/*
+ * Written by Dawid Kurzyniec and released to the public domain, as explained
+ * at http://creativecommons.org/licenses/publicdomain
+ */
+
+//package edu.emory.mathcs.util.net;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.*;
+
+/**
+ * Wrapper for sockets which enables to add functionality in subclasses
+ * on top of existing, connected sockets. It is useful when direct subclassing
+ * of delegate socket class is not possible, e.g. if the delegate socket is
+ * created by a library. Possible usage example is socket factory chaining.
+ * This class delegates all socket-related requests to the wrapped delegate,
+ * as of JDK 1.4.
+ *
+ * @author Dawid Kurzyniec
+ * @version 1.4
+ */
+public abstract class SocketWrapper extends Socket {
+
+ /**
+ * the wrapped delegate socket.
+ */
+ protected final Socket delegate;
+
+ /**
+ * Creates new socket wrapper for a given socket. The delegate
+ * must be connected and bound and it must not be closed.
+ * @param delegate the delegate socket to wrap
+ * @throws SocketException if the delegate socket is closed, not bound,
+ * or not connected
+ */
+ protected SocketWrapper(Socket delegate) throws SocketException {
+ super(new WrappingSocketImpl(delegate));
+ this.delegate = delegate;
+ }
+
+ public SocketChannel getChannel() {
+ return delegate.getChannel();
+ }
+
+ /**
+ * Returns true, indicating that the socket is bound.
+ *
+ * @return true
+ */
+ public boolean isBound() {
+ return true;
+ }
+
+ public boolean isClosed() {
+ return super.isClosed() || delegate.isClosed();
+ }
+
+ /**
+ * Returns true, indicating that the socket is connected.
+ *
+ * @return true
+ */
+ public boolean isConnected() {
+ return true;
+ }
+
+ public boolean isInputShutdown() {
+ return super.isInputShutdown() || delegate.isInputShutdown();
+ }
+
+ public boolean isOutputShutdown() {
+ return super.isInputShutdown() || delegate.isOutputShutdown();
+ }
+
+ private static class WrappingSocketImpl extends SocketImpl {
+ private final Socket delegate;
+ WrappingSocketImpl(Socket delegate) throws SocketException {
+ if (delegate == null) {
+ throw new NullPointerException();
+ }
+ if (delegate.isClosed()) {
+ throw new SocketException("Delegate server socket is closed");
+ }
+ if (!(delegate.isBound())) {
+ throw new SocketException("Delegate server socket is not bound");
+ }
+ if (!(delegate.isConnected())) {
+ throw new SocketException("Delegate server socket is not connected");
+ }
+ this.delegate = delegate;
+ }
+
+ protected void create(boolean stream) {}
+
+ protected void connect(String host, int port) {
+ // delegate is always connected, thus this method is never called
+ throw new UnsupportedOperationException();
+ }
+
+ protected void connect(InetAddress address, int port) {
+ // delegate is always connected, thus this method is never called
+ throw new UnsupportedOperationException();
+ }
+
+ protected void connect(SocketAddress address, int timeout) {
+ // delegate is always connected, thus this method is never called
+ throw new UnsupportedOperationException();
+ }
+
+ protected void bind(InetAddress host, int port) {
+ // delegate is always bound, thus this method is never called
+ throw new UnsupportedOperationException();
+ }
+
+ protected void listen(int backlog) {
+ // this wrapper is never used by a ServerSocket
+ throw new UnsupportedOperationException();
+ }
+
+ protected void accept(SocketImpl s) {
+ // this wrapper is never used by a ServerSocket
+ throw new UnsupportedOperationException();
+ }
+
+ protected InputStream getInputStream() throws IOException {
+ return delegate.getInputStream();
+ }
+
+ protected OutputStream getOutputStream() throws IOException {
+ return delegate.getOutputStream();
+ }
+
+ protected int available() throws IOException {
+ return getInputStream().available();
+ }
+
+ protected void close() throws IOException {
+ delegate.close();
+ }
+
+ protected void shutdownInput() throws IOException {
+ delegate.shutdownInput();
+ }
+
+ protected void shutdownOutput() throws IOException {
+ delegate.shutdownOutput();
+ }
+
+ protected FileDescriptor getFileDescriptor() {
+ // this wrapper is never used by a ServerSocket
+ throw new UnsupportedOperationException();
+ }
+
+ protected InetAddress getInetAddress() {
+ return delegate.getInetAddress();
+ }
+
+ protected int getPort() {
+ return delegate.getPort();
+ }
+
+ protected boolean supportsUrgentData() {
+ return false; // must be overridden in sub-class
+ }
+
+ protected void sendUrgentData (int data) throws IOException {
+ delegate.sendUrgentData(data);
+ }
+
+ protected int getLocalPort() {
+ return delegate.getLocalPort();
+ }
+
+ public Object getOption(int optID) throws SocketException {
+ switch (optID) {
+ case SocketOptions.IP_TOS:
+ return new Integer(delegate.getTrafficClass());
+ case SocketOptions.SO_BINDADDR:
+ return delegate.getLocalAddress();
+ case SocketOptions.SO_KEEPALIVE:
+ return Boolean.valueOf(delegate.getKeepAlive());
+ case SocketOptions.SO_LINGER:
+ return new Integer(delegate.getSoLinger());
+ case SocketOptions.SO_OOBINLINE:
+ return Boolean.valueOf(delegate.getOOBInline());
+ case SocketOptions.SO_RCVBUF:
+ return new Integer(delegate.getReceiveBufferSize());
+ case SocketOptions.SO_REUSEADDR:
+ return Boolean.valueOf(delegate.getReuseAddress());
+ case SocketOptions.SO_SNDBUF:
+ return new Integer(delegate.getSendBufferSize());
+ case SocketOptions.SO_TIMEOUT:
+ return new Integer(delegate.getSoTimeout());
+ case SocketOptions.TCP_NODELAY:
+ return Boolean.valueOf(delegate.getTcpNoDelay());
+ case SocketOptions.SO_BROADCAST:
+ default:
+ throw new IllegalArgumentException("Unsupported option type");
+ }
+ }
+
+ public void setOption(int optID, Object value) throws SocketException {
+ switch (optID) {
+ case SocketOptions.SO_BINDADDR:
+ throw new IllegalArgumentException("Socket is bound");
+ case SocketOptions.SO_KEEPALIVE:
+ delegate.setKeepAlive(((Boolean)value).booleanValue());
+ break;
+ case SocketOptions.SO_LINGER:
+ if (value instanceof Boolean) {
+ delegate.setSoLinger(((Boolean)value).booleanValue(), 0);
+ }
+ else {
+ delegate.setSoLinger(true, ((Integer)value).intValue());
+ }
+ break;
+ case SocketOptions.SO_OOBINLINE:
+ delegate.setOOBInline(((Boolean)value).booleanValue());
+ break;
+ case SocketOptions.SO_RCVBUF:
+ delegate.setReceiveBufferSize(((Integer)value).intValue());
+ break;
+ case SocketOptions.SO_REUSEADDR:
+ delegate.setReuseAddress(((Boolean)value).booleanValue());
+ break;
+ case SocketOptions.SO_SNDBUF:
+ delegate.setSendBufferSize(((Integer)value).intValue());
+ break;
+ case SocketOptions.SO_TIMEOUT:
+ delegate.setSoTimeout(((Integer)value).intValue());
+ break;
+ case SocketOptions.TCP_NODELAY:
+ delegate.setTcpNoDelay(((Boolean)value).booleanValue());
+ break;
+ case SocketOptions.SO_BROADCAST:
+ default:
+ throw new IllegalArgumentException("Unsupported option type");
+ }
+ }
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SocketWrapper)) return false;
+ SocketWrapper that = (SocketWrapper)obj;
+ return this.delegate.equals(that.delegate);
+ }
+
+ public int hashCode() {
+ return delegate.hashCode() ^ 0x01010101;
+ }
+}
\ No newline at end of file