/**
   * Deeply list the opaque ports connected to this port on the outside. Begin by listing the ports
   * that are connected to this port. If any of those are transparent ports that we are connected to
   * from the inside, then list all the ports deeply connected on the outside to that transparent
   * port. If any are transparent ports that we are connected to from the outside, then list opaque
   * ports deeply inside that port. Note that a port may be listed more than once. The path argument
   * is the path from the port that originally calls this method to this port. If this port is
   * already on the list of ports on the path to this port in deeply traversing the topology, then
   * there is a loop in the topology, and an InvalidStateException is thrown. This method not
   * synchronized on the workspace, so the caller should.
   *
   * @param path The list of ports on the path to this port in deeply traversing the topology.
   * @return An unmodifiable list of ComponentPort objects.
   */
  protected List _deepConnectedPortList(LinkedList path) {
    if (_deepLinkedPortsVersion == _workspace.getVersion()) {
      // Cache is valid.  Use it.
      return _deepLinkedPorts;
    }

    if (path == null) {
      path = new LinkedList();
    } else {
      if (path.indexOf(this) >= 0) {
        throw new InvalidStateException(path, "Loop in topology!");
      }
    }

    path.add(0, this);

    Iterator nearRelations = linkedRelationList().iterator();
    LinkedList result = new LinkedList();

    while (nearRelations.hasNext()) {
      ComponentRelation relation = (ComponentRelation) nearRelations.next();

      // A null link (supported since indexed links) might
      // yield a null relation here. EAL 7/19/00.
      if (relation != null) {
        Iterator connectedPorts = relation.linkedPortList(this).iterator();

        while (connectedPorts.hasNext()) {
          ComponentPort port = (ComponentPort) connectedPorts.next();

          // NOTE: If level-crossing transitions are not allowed,
          // then a simpler test than that of the following
          // would work.
          if (port._isInsideLinkable(relation.getContainer())) {
            // We are coming at the port from the inside.
            if (port.isOpaque()) {
              result.add(port);
            } else {
              // Port is transparent
              result.addAll(port._deepConnectedPortList(path));
            }
          } else {
            // We are coming at the port from the outside.
            if (port.isOpaque()) {
              result.add(port);
            } else {
              // It is transparent.
              result.addAll(port._deepInsidePortList(path));
            }
          }
        }
      }
    }

    _deepLinkedPorts = Collections.unmodifiableList(result);
    _deepLinkedPortsVersion = _workspace.getVersion();
    path.remove(0);
    return _deepLinkedPorts;
  }
  /**
   * If this port is transparent, then deeply list the ports connected on the inside. Otherwise,
   * list just this port. All ports listed are opaque. Note that the returned list could conceivably
   * be empty, for example if this port is transparent but has no inside links. Also, a port may be
   * listed more than once if more than one inside connection to it has been established. The path
   * argument is the path from the port that originally calls this method to this port. If this port
   * is already on the list of ports on the path to this port in deeply traversing the topology,
   * then there is a loop in the topology, and an InvalidStateException is thrown. This method is
   * read-synchronized on the workspace.
   *
   * @param path The list of ports on the path to this port in deeply traversing the topology.
   * @return An unmodifiable list of ComponentPort objects.
   */
  protected List _deepInsidePortList(LinkedList path) {
    if (_deepLinkedInPortsVersion == _workspace.getVersion()) {
      // Cache is valid.  Use it.
      return _deepLinkedInPorts;
    }

    if (path == null) {
      path = new LinkedList();
    } else {
      if (path.indexOf(this) >= 0) {
        throw new InvalidStateException(path, "Loop in topology!");
      }
    }

    path.add(0, this);

    LinkedList result = new LinkedList();

    // Port is transparent.
    Iterator relations = insideRelationList().iterator();

    while (relations.hasNext()) {
      Relation relation = (Relation) relations.next();

      // A null link might yield a null relation here.
      if (relation != null) {
        Iterator insidePorts = relation.linkedPortList(this).iterator();

        while (insidePorts.hasNext()) {
          ComponentPort port = (ComponentPort) insidePorts.next();

          // The inside port may not be actually inside,
          // in which case we want to look through it
          // from the inside (this supports transparent
          // entities).
          if (port._isInsideLinkable(relation.getContainer())) {
            // The inside port is not truly inside.
            // Check to see whether it is transparent.
            if (port.isOpaque()) {
              result.add(port);
            } else {
              result.addAll(port._deepConnectedPortList(path));
            }
          } else {
            // We are coming at the port from the outside.
            if (port.isOpaque()) {
              // The inside port is truly inside.
              result.add(port);
            } else {
              result.addAll(port._deepInsidePortList(path));
            }
          }
        }
      }
    }

    _deepLinkedInPorts = Collections.unmodifiableList(result);
    _deepLinkedInPortsVersion = _workspace.getVersion();
    path.remove(0);
    return _deepLinkedInPorts;
  }