/**
   * Tries to extract a node connector from the node <code>node</code>, throwing an exception if
   * something goes wrong.
   *
   * @param node The node. Not <code>null</code>.
   * @return A connector. Never <code>null</code>.
   * @throws NullPointerException If <code>node==null</code>.
   * @throws XMLExtractorException If something goes wrong while extracting.
   */
  public static Connector<DefaultConstraintAutomaton> tryExtractFrom(final XMLNode node)
      throws XMLExtractorException {

    if (node == null) throw new NullPointerException();

    try {
      if (!node.hasType())
        throw new XMLExtractorException("Every node should have a supported type.");

      return library.newNode(
          node.getName(), node.countSinkEnds(), node.countSourceEnds(), node.getType());
    } catch (final Exception e) {
      throw new XMLExtractorException(
          "I failed to extract a connector from the node named \"" + node.getName() + "\".", e);
    }
  }
  /**
   * Tries to extract a primitive connector from the primitive <code>primitive</code>, throwing an
   * exception if something goes wrong.
   *
   * @param primitive The primitive. Not <code>null</code>.
   * @return A connector. Never <code>null</code>.
   * @throws NullPointerException If <code>primitive==null</code>.
   * @throws XMLExtractorException If something goes wrong while extracting.
   */
  public static Connector<DefaultConstraintAutomaton> tryExtractFrom(final XMLPrimitive primitive)
      throws XMLExtractorException {

    if (primitive == null) throw new NullPointerException();

    try {
      if (!primitive.hasNodes())
        throw new XMLExtractorException("Every primitive should connect to at least one node.");

      if (!primitive.hasType())
        throw new XMLExtractorException("Every primitive should have a supported type.");

      /* Get relevant source nodes and sink nodes. */
      Collection<XMLNode> sourceNodes = Collections.emptyList();
      Collection<XMLNode> sinkNodes = Collections.emptyList();
      switch (primitive.getType()) {
        case ASYNC_DRAIN:
        case SYNC_DRAIN:
          if (!primitive.hasSourceNodes() || (sourceNodes = primitive.getSourceNodes()).size() > 2)
            throw new XMLExtractorException(
                "Every AsyncDrain and SyncDrain should connect to one or two source nodes.");
          break;

        case ASYNC_SPOUT:
        case SYNC_SPOUT:
          if (!primitive.hasSinkNodes() || (sinkNodes = primitive.getSinkNodes()).size() > 2)
            throw new XMLExtractorException(
                "Every AsyncSpout and SyncSpout should connect to one or two sink nodes.");
          break;

        default:
          if (!primitive.hasSourceNodes()
              || !primitive.hasSinkNodes()
              || (sourceNodes = primitive.getSourceNodes()).size() != 1
              || (sinkNodes = primitive.getSinkNodes()).size() != 1)
            throw new XMLExtractorException(
                "Every FIFO, Filter, LossySync, and Sync should connect one source node and one sink node.");
      }

      /* Get node names. */
      final List<XMLNode> nodes = new ArrayList<XMLNode>();
      nodes.addAll(sourceNodes);
      nodes.addAll(sinkNodes);
      final Iterator<XMLNode> iterator = nodes.iterator();

      final String nodeName1 = iterator.next().getName();
      final String nodeName2 = iterator.hasNext() ? iterator.next().getName() : nodeName1;

      /* Return. */
      switch (primitive.getType()) {
        case ASYNC_DRAIN:
          return library.newAsyncDrain(nodeName1, nodeName2);
        case ASYNC_SPOUT:
          return library.newAsyncSpout(nodeName1, nodeName2);
        case FIFO:
          return library.newFIFO(
              nodeName1,
              nodeName2,
              primitive.hasFullBuffer(),
              primitive.hasBufferItem() ? primitive.getBufferItem() : "null");
        case FILTER:
          return library.newFilter(
              nodeName1,
              nodeName2,
              primitive.hasConstraintText() ? primitive.getConstraintText() : "true");
        case LOSSY_SYNC:
          return library.newLossySync(nodeName1, nodeName2);
        case SYNC:
          return library.newSync(nodeName1, nodeName2);
        case SYNC_DRAIN:
          return library.newSyncDrain(nodeName1, nodeName2);
        case SYNC_SPOUT:
          return library.newSyncSpout(nodeName1, nodeName2);
        default:
          throw new XMLExtractorException("Every primitive should have a supported type.");
      }
    } catch (final Exception e) {
      throw new XMLExtractorException(
          "I failed to extract a connector from "
              + (primitive.hasNodes()
                  ? "the primitive between nodes \"" + primitive.getNodes() + "\""
                  : "an empty primitive")
              + ".",
          e);
    }
  }