Package org.cloudfoundry.ide.eclipse.server.ui.internal

Source Code of org.cloudfoundry.ide.eclipse.server.ui.internal.ValidationEventHandler

/*******************************************************************************
* Copyright (c) 2014 Pivotal Software, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License,
* Version 2.0 (the "License�); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*  Contributors:
*     Pivotal Software, Inc. - initial API and implementation
********************************************************************************/
package org.cloudfoundry.ide.eclipse.server.ui.internal;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import org.cloudfoundry.ide.eclipse.server.core.internal.ValidationEvents;
import org.cloudfoundry.ide.eclipse.server.ui.internal.wizards.WizardStatusHandler;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.operation.IRunnableContext;

/**
* A validation event handler around a {@link ServerValidator} that acts both as
* a listener for incoming validation requests as well as a notifier of
* validation completion.
* <p/>
* As a listener, the validation handler will get notified when there are
* changes to account values (e.g. username changed or different cloud space
* selected), or when a user has explicitly requested a server authorisation
* (e.g., a user has pressed a "Validate" UI control), and if necessary, perform
* an account validation. Validations may be both local or can be server
* authorisations, depending on the type of event received by this wrapper.
* <p/>
* As a notifier, once a validation is completed, the handler will notify
* registered listeners that validation has been performed.
* <p/>
* The handler also keeps tracks of errors based on event types. This allows
* multiple notifiers, like different UI parts or other components, that fire
* events, to log errors.
* <p/>
* IMPORTANT NOTE: errors are tracked by event type. Therefore in order to CLEAR
* an error, an {@link IStatus#OK} event with the SAME event type must be fired,
* whether by the same notifier that fired the initial error, or any other
* notifier.
* <p/>
* For example, a credentials UI part fires an event type VALUE_FILLED, with an
* {@link IStatus#ERROR}, due to a missing value like a password in a UI
* control. Once this error has been corrected (password filled), the same
* VALUE_FILLED event must be fired with an {@link IStatus#OK} in order to clear
* that error. This event need not be fired by the same UI part that generated
* the initial error, it can be fired by some other component (maybe one that
* reads a stored password value), as long as the event type REMAINS the same.
* <p/>
* Otherwise if an OK status is fired with a different event type, or example,
* PASSWORD_FILLED, the error will NOT be cleared.
* <p/>
* Therefore event types in {@link PartChangeEvent} should NOT represent an
* error condition (e.g missing values). Rather they should indicate:
* <p/>
* 1. An action that was performed, like values being entered.
* <p/>
* 2. A validation request (e.g. validate locally or validate against a server).
* <p/>
* To indicate an error status associated with an event type, use the
* {@link IStatus} value in the {@link PartChangeEvent}
*/
public class ValidationEventHandler implements IPartChangeListener {

  private ServerValidator validator;

  private Map<Integer, PartChangeEvent> trackedStatus = new HashMap<Integer, PartChangeEvent>();

  private Set<IPartChangeListener> listeners = new LinkedHashSet<IPartChangeListener>();

  private Set<WizardStatusHandler> statusHandlers = new LinkedHashSet<WizardStatusHandler>();

  public ValidationEventHandler(ServerValidator validator) {
    this.validator = validator;
  }

  public ValidationEventHandler() {

  }

  public synchronized void updateValidator(ServerValidator validator) {
    this.validator = validator;
  }

  /**
   *
   * @param handler wizard status handler that gets notified when an event
   * status is received. This is different than a validation listener, in the
   * sense that the status handler gets notified only when a status is meant
   * to be displayed in a wizard, regardless of the event type, whereas a
   * validation listener only gets notified if a validation process completed.
   */
  public synchronized void addStatusHandler(WizardStatusHandler handler) {
    if (handler != null) {
      statusHandlers.add(handler);
    }
  }

  /*
   *
   * (non-Javadoc)
   *
   * @see org.cloudfoundry.ide.eclipse.server.ui.internal.IPartChangeListener#
   * handleChange
   * (org.cloudfoundry.ide.eclipse.server.ui.internal.PartChangeEvent)
   */
  public synchronized void handleChange(PartChangeEvent event) {
    if (validator == null || event == null) {
      return;
    }

    // See if the incoming event triggers a validation. If so the validation
    // will
    // generate its own event
    PartChangeEvent validationEvent = getValidationEvent(event);

    if (validationEvent != null) {
      fireValidationEvent(validationEvent);
      event = validationEvent;
    }

    // Track the event to see if it is an error, or clears an existing error
    trackEvent(event);

    // Notify status handlers of the event
    handleStatus(event);
  }

  public synchronized void validate(IRunnableContext context) {
    handleChange(new PartChangeEvent(context, Status.OK_STATUS, null, ValidationEvents.SERVER_AUTHORISATION));
  }

  /**
   *
   * @param listener to be notified after a validation event has been
   * completed.
   */
  public synchronized void addValidationListener(IPartChangeListener listener) {
    if (listener != null) {
      listeners.add(listener);
    }
  }

  public synchronized void removeValidationListener(IPartChangeListener listener) {
    listeners.remove(listener);
  }

  public synchronized boolean isOK() {
    return getNextNonOKEvent() == null;
  }

  /**
   *
   * @return the next non {@link IStatus#OK} event that is tracked, or null if
   * there are no remaining non-OK events (i.e all errors have been cleared)
   */
  public synchronized IStatus getNextNonOKEvent() {
    PartChangeEvent event = getNextTrackedEvent();
    return event != null ? event.getStatus() : null;
  }

  protected void fireValidationEvent(PartChangeEvent event) {
    if (event == null) {
      return;
    }

    Object source = event.getSource() != null ? event.getSource().getSource() : null;
    for (IPartChangeListener listener : listeners) {
      // Do not notify the same source that fired the event
      if (source != listener) {
        listener.handleChange(event);
      }
    }
  }

  protected PartChangeEvent getNextTrackedEvent() {
    for (PartChangeEvent event : trackedStatus.values()) {
      return event;
    }
    return null;
  }

  protected ValidationStatus validate(IRunnableContext context, int eventType) {
    ValidationStatus status = null;
    switch (eventType) {

    // Any local credential changes (e.g. changes in username or password in
    // the UI), should only result in credentials validation, but not cloud
    // space
    // validation, as the latter is only performed after an explicit server
    // validation event is received
    case ValidationEvents.CREDENTIALS_FILLED:
      status = validator.validate(false, false, context);
      break;
    // Indicates that a request was made to explicitly validate credentials
    // against the server
    case ValidationEvents.SERVER_AUTHORISATION:
      status = validator.validate(true, true, context);
      break;
    // Any general validation event should only be a local validation.
    // Remove
    // server authorisations are handled separately above.
    case ValidationEvents.VALIDATION:
      status = validator.validate(false, true, context);
      break;
    }
    return status;

  }

  protected void handleStatus(PartChangeEvent event) {
    if (event == null || event.getStatus() == null || event.getStatus().isOK()) {
      PartChangeEvent errorEvent = getNextTrackedEvent();
      // Be sure to check for null error event as to not overwrite the
      // incoming OK
      // status event.
      if (errorEvent != null) {
        event = errorEvent;
      }
    }
    for (WizardStatusHandler handler : statusHandlers) {
      handler.handleChange(event);
    }
  }

  /**
   * Examines the given event to determine if validation should occur. If so,
   * it will generate a separate Validation event after the validation has
   * been completed.
   * <p/>
   * Returns null if the incoming event does NOT trigger a validation
   * operation.
   * <p/>
   * Validation can only occur if the incoming event is {@link IStatus#OK}.
   * <p/>
   * Otherwise, if there is an issue indicated by the incoming event status,
   * that should be resolved first before validation can occur (e.g. missing
   * password, invalid server URL, invalid space selection, etc..)
   * @param event
   * @return Validation event IFF the incoming event triggers a validation
   * operation. Otherwise, return null. It does NOT return the original event.
   */
  protected PartChangeEvent getValidationEvent(PartChangeEvent event) {

    // Do not validate if the incoming event is NOT OK.
    if (event.getStatus() == null || !event.getStatus().isOK()) {
      return null;
    }
    int eventType = event.getType();

    IRunnableContext context = event.getData() instanceof IRunnableContext ? (IRunnableContext) event.getData()
        : null;
    ValidationStatus validationStatus = validate(context, eventType);
    PartChangeEvent validationEvent = null;
    if (validationStatus instanceof IAdaptable) {
      validationEvent = (PartChangeEvent) ((IAdaptable) validationStatus).getAdapter(PartChangeEvent.class);
    }
    else {
      validationEvent = new PartChangeEvent(null, validationStatus != null ? validationStatus.getStatus() : null,
          new EventSource<ValidationEventHandler>(this), ValidationEvents.VALIDATION);
    }
    return validationEvent;
  }

  protected void trackEvent(PartChangeEvent event) {
    // Check the status and determine if it is an error
    IStatus status = event.getStatus();

    // Process the status to determine if it is an error. If so, keep track
    // of it. Errors can only
    // be "cleared" by the same event source that generated the error.
    if (status != null) {
      // Only one error per event type is logged.

      if (!status.isOK()) {
        trackedStatus.put(event.getType(), event);
      }
      else {
        trackedStatus.remove(event.getType());
      }
    }
  }
}
TOP

Related Classes of org.cloudfoundry.ide.eclipse.server.ui.internal.ValidationEventHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.