/**
   * Register the given connector.
   *
   * <p>The lookup method {@link #getConnector(String)} only returns registered connectors.
   *
   * @param connector The connector to register.
   */
  public void registerConnector(ClientConnector connector) {
    boolean wasUnregistered = unregisteredConnectors.remove(connector);

    String connectorId = connector.getConnectorId();
    ClientConnector previouslyRegistered = connectorIdToConnector.get(connectorId);
    if (previouslyRegistered == null) {
      connectorIdToConnector.put(connectorId, connector);
      uninitializedConnectors.add(connector);
      if (getLogger().isLoggable(Level.FINE)) {
        getLogger()
            .log(
                Level.FINE,
                "Registered {0} ({1})",
                new Object[] {connector.getClass().getSimpleName(), connectorId});
      }
    } else if (previouslyRegistered != connector) {
      throw new RuntimeException("A connector with id " + connectorId + " is already registered!");
    } else if (!wasUnregistered) {
      getLogger()
          .log(
              Level.WARNING,
              "An already registered connector was registered again: {0} ({1})",
              new Object[] {connector.getClass().getSimpleName(), connectorId});
    }
    dirtyConnectors.add(connector);
  }
  /**
   * Returns a string with the connector name and id. Useful mostly for debugging and logging.
   *
   * @param connector The connector
   * @return A string that describes the connector
   */
  private String getConnectorString(ClientConnector connector) {
    if (connector == null) {
      return "(null)";
    }

    String connectorId;
    try {
      connectorId = connector.getConnectorId();
    } catch (RuntimeException e) {
      // This happens if the connector is not attached to the application.
      // SHOULD not happen in this case but theoretically can.
      connectorId = "@" + Integer.toHexString(connector.hashCode());
    }
    return connector.getClass().getName() + "(" + connectorId + ")";
  }
  private void removeUnregisteredConnectors() {
    GlobalResourceHandler globalResourceHandler = uI.getSession().getGlobalResourceHandler(false);

    for (ClientConnector connector : unregisteredConnectors) {
      ClientConnector removedConnector = connectorIdToConnector.remove(connector.getConnectorId());
      assert removedConnector == connector;

      if (globalResourceHandler != null) {
        globalResourceHandler.unregisterConnector(connector);
      }
      uninitializedConnectors.remove(connector);
      diffStates.remove(connector);
    }
    unregisteredConnectors.clear();
  }
  /**
   * Unregister the given connector.
   *
   * <p>The lookup method {@link #getConnector(String)} only returns registered connectors.
   *
   * @param connector The connector to unregister
   */
  public void unregisterConnector(ClientConnector connector) {
    String connectorId = connector.getConnectorId();
    if (!connectorIdToConnector.containsKey(connectorId)) {
      getLogger()
          .log(
              Level.WARNING,
              "Tried to unregister {0} ({1}) which is not registered",
              new Object[] {connector.getClass().getSimpleName(), connectorId});
      return;
    }
    if (connectorIdToConnector.get(connectorId) != connector) {
      throw new RuntimeException(
          "The given connector with id "
              + connectorId
              + " is not the one that was registered for that id");
    }

    Set<String> unregisteredConnectorIds = syncIdToUnregisteredConnectorIds.get(currentSyncId);
    if (unregisteredConnectorIds == null) {
      unregisteredConnectorIds = new HashSet<String>();
      syncIdToUnregisteredConnectorIds.put(currentSyncId, unregisteredConnectorIds);
    }
    unregisteredConnectorIds.add(connectorId);

    dirtyConnectors.remove(connector);
    if (unregisteredConnectors.add(connector)) {
      if (getLogger().isLoggable(Level.FINE)) {
        getLogger()
            .log(
                Level.FINE,
                "Unregistered {0} ({1})",
                new Object[] {connector.getClass().getSimpleName(), connectorId});
      }
    } else {
      getLogger()
          .log(
              Level.WARNING,
              "Unregistered {0} ({1}) that was already unregistered.",
              new Object[] {connector.getClass().getSimpleName(), connectorId});
    }
  }
 public void setDiffState(ClientConnector connector, JSONObject diffState) {
   assert getConnector(connector.getConnectorId()) == connector;
   diffStates.put(connector, diffState);
 }
 public JSONObject getDiffState(ClientConnector connector) {
   assert getConnector(connector.getConnectorId()) == connector;
   return diffStates.get(connector);
 }
 /**
  * Checks whether the given connector has already been initialized in the browser. The given
  * connector should be registered with this connector tracker.
  *
  * @param connector the client connector to check
  * @return <code>true</code> if the initial state has previously been sent to the browser, <code>
  *     false</code> if the client-side doesn't already know anything about the connector.
  */
 public boolean isClientSideInitialized(ClientConnector connector) {
   assert connectorIdToConnector.get(connector.getConnectorId()) == connector
       : "Connector should be registered with this ConnectorTracker";
   return !uninitializedConnectors.contains(connector);
 }