/**
   * Gets a <tt>MultiplexedDatagramSocket</tt> which filters <tt>DatagramPacket</tt>s away from this
   * <tt>DatagramSocket</tt> using a specific <tt>DatagramPacketFilter</tt>. If such a
   * <tt>MultiplexedDatagramSocket</tt> does not exist in this instance, it is created.
   *
   * @param filter the <tt>DatagramPacketFilter</tt> to get a <tt>MultiplexedDatagramSocket</tt> for
   * @return a <tt>MultiplexedDatagramSocket</tt> which filters <tt>DatagramPacket</tt>s away from
   *     this <tt>DatagramSocket</tt> using the specified <tt>filter</tt>
   * @throws SocketException if creating the <tt>MultiplexedDatagramSocket</tt> for the specified
   *     <tt>filter</tt> fails
   */
  public MultiplexedDatagramSocket getSocket(DatagramPacketFilter filter) throws SocketException {
    if (filter == null) throw new NullPointerException("filter");

    synchronized (socketsSyncRoot) {
      /*
       * If a socket for the specified filter exists already, do not
       * create a new one and return the existing.
       */
      for (MultiplexedDatagramSocket socket : sockets)
        if (filter.equals(socket.getFilter())) return socket;

      // Create a new socket for the specified filter.
      MultiplexedDatagramSocket socket = new MultiplexedDatagramSocket(this, filter);

      // Remember the new socket.
      int socketCount = sockets.length;

      if (socketCount == 0) sockets = new MultiplexedDatagramSocket[] {socket};
      else {
        MultiplexedDatagramSocket[] newSockets = new MultiplexedDatagramSocket[socketCount + 1];

        System.arraycopy(sockets, 0, newSockets, 0, socketCount);
        newSockets[socketCount] = socket;
        sockets = newSockets;
      }

      return socket;
    }
  }
  /**
   * Receives a <tt>DatagramPacket</tt> from a specific list of <tt>DatagramPacket</tt>s if it is
   * not empty or from the network if the specified list is empty. When this method returns, the
   * <tt>DatagramPacket</tt>'s buffer is filled with the data received. The datagram packet also
   * contains the sender's IP address, and the port number on the sender's machine.
   *
   * @param received the list of previously received <tt>DatagramPacket</tt> from which the first is
   *     to be removed and returned if available
   * @param p the <tt>DatagramPacket</tt> into which to place the incoming data
   * @throws IOException if an I/O error occurs
   */
  private void receive(List<DatagramPacket> received, DatagramPacket p) throws IOException {
    DatagramPacket r = null;

    do {
      boolean doReceive;

      synchronized (receiveSyncRoot) {
        if (received.isEmpty()) {
          if (inReceive) {
            doReceive = false;
            try {
              receiveSyncRoot.wait();
            } catch (InterruptedException iex) {
              continue;
            }
          } else {
            doReceive = true;
            inReceive = true;
          }
        } else {
          doReceive = false;
          r = received.remove(0);
        }
      }
      if (doReceive) {
        try {
          super.receive(p);

          synchronized (receiveSyncRoot) {
            synchronized (socketsSyncRoot) {
              boolean accepted = false;

              for (MultiplexedDatagramSocket socket : sockets)
                if (socket.getFilter().accept(p)) {
                  socket.received.add(clone(p));
                  accepted = true;

                  /*
                   * Emil: Don't break because we want all
                   * filtering sockets to get this.
                   */
                  // break;
                }

              if (!accepted) this.received.add(clone(p));
            }
          }
        } finally {
          synchronized (receiveSyncRoot) {
            inReceive = false;
            receiveSyncRoot.notify();
          }
        }
      }
    } while (r == null);

    copy(r, p);
  }