Package com.google.testing.testify.risk.frontend.client.view.impl

Source Code of com.google.testing.testify.risk.frontend.client.view.impl.RiskViewImpl

// Copyright 2010 Google Inc. All Rights Reseved.
//
// Licensed under 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.

package com.google.testing.testify.risk.frontend.client.view.impl;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiFactory;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLTable;
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.testing.testify.risk.frontend.client.riskprovider.RiskProvider;
import com.google.testing.testify.risk.frontend.client.view.RiskView;
import com.google.testing.testify.risk.frontend.client.view.widgets.EasyDisclosurePanel;
import com.google.testing.testify.risk.frontend.client.view.widgets.PageSectionVerticalPanel;
import com.google.testing.testify.risk.frontend.model.Attribute;
import com.google.testing.testify.risk.frontend.model.Capability;
import com.google.testing.testify.risk.frontend.model.CapabilityIntersectionData;
import com.google.testing.testify.risk.frontend.model.Component;
import com.google.testing.testify.risk.frontend.model.Pair;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
* Base Widget for displaying Risk and/or Mitigation. (A glorified 2D heat map.) The control acts
* as a repository of project Attributes, Components, and Capabilities so that future
* risk providers can rely on that data to do visualization.
*
* @author chrsmith@google.com (Chris Smith)
* @author jimr@google.com (Jim Reardon)
*/
public abstract class RiskViewImpl extends Composite
    implements RiskView, HasValueChangeHandlers<Pair<Integer, Integer>> {

  /** Enum for tracking the required pieces of information to fully initialize a risk view. */
  private enum RequiredDataType {
    ATTRIBUTES,
    COMPONENTS,
    CAPABILITIES
  }

  /**
   * Used to wire parent class to associated UI Binder.
   */
  interface RiskViewImplUiBinder extends UiBinder<Widget, RiskViewImpl> { }
  private static final RiskViewImplUiBinder uiBinder = GWT.create(RiskViewImplUiBinder.class);

  @UiField
  public PageSectionVerticalPanel pageSectionPanel;

  @UiField
  public Label introTextLabel;

  @UiField
  public Grid baseGrid;

  /** Panel to hold custom content from a derived class. */
  @UiField
  public VerticalPanel content;

  /** Panel to hold custom content at the bottom of the widget. */
  @UiField
  public SimplePanel bottomContent;

  /**
   * Map of intersection key to data stored inside a CapabilityIntersectionData object.
   */
  private final Map<Integer, CapabilityIntersectionData> dataMap = Maps.newHashMap();

  /**
   * Map of intersection key to capability list.
   */
  private final Multimap<Integer, Capability> capabilityMap = HashMultimap.create();
  private final HashSet<RequiredDataType> initializedDataTypes = Sets.newHashSet();
  private final ArrayList<Component> components = Lists.newArrayList();
  private final ArrayList<Attribute> attributes = Lists.newArrayList();

  private Pair<Integer, Integer> selectedCell;

  /**
   * Constructs a new instance of the RiskViewImpl widget. For the UI to display something, call
   * {@link #setComponents(List)}, {@link #setAttributes(List)}, and {@link #setCapabilities(List)}
   * next.
   */
  public RiskViewImpl() {
    initWidget(uiBinder.createAndBindUi(this));

    setPageText("", "");
  }

  @UiFactory
  public EasyDisclosurePanel createDisclosurePanel() {
    Label header = new Label("Risk displayed by Attribute and Component");
    header.addStyleName("tty-DisclosureHeader");

    return new EasyDisclosurePanel(header);
  }

  /**
   * Sets the risk page's introductory text.
   *
   * @param titleText the text displayed on the top, for example "Risk Factors".
   * @param introText the page text, explaining what the data illustrates.
   */
  protected void setPageText(String titleText, String introText) {
    pageSectionPanel.setHeaderText(titleText);
    introTextLabel.setText(introText);
  }

  @Override
  public void setComponents(List<Component> components) {
    this.components.clear();
    this.components.addAll(components);

    initializedDataTypes.add(RequiredDataType.COMPONENTS);
    initializeGrid();
    initializeRiskCells();
  }

  @Override
  public void setAttributes(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);

    initializedDataTypes.add(RequiredDataType.ATTRIBUTES);
    initializeGrid();
    initializeRiskCells();
  }

  @Override
  public void setCapabilities(List<Capability> newCapabilities) {
    capabilityMap.clear();
    for (Capability capability : newCapabilities) {
      capabilityMap.put(capability.getCapabilityIntersectionKey(), capability);
    }

    initializedDataTypes.add(RequiredDataType.CAPABILITIES);
    initializeRiskCells();
  }

   /**
    * Called for derived classes once the risk view has been fully initilzied. (All Attributes,
    * Components, and Capabilities have been specified.)
    */
  protected abstract void onInitialized();

  @Override
  public Widget asWidget() {
    return this;
  }

  /**
   * Initializes the Risk grid headers.
   */
   void initializeGrid() {
    // We need both attributes and components for this control to make sense.
    if ((!initializedDataTypes.contains(RequiredDataType.ATTRIBUTES))
        || (!initializedDataTypes.contains(RequiredDataType.COMPONENTS))) {
      return;
    }

    baseGrid.clear();
    baseGrid.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
          cellClicked(baseGrid.getCellForEvent(event));
        }
      });
    baseGrid.resize(components.size() + 1, attributes.size() + 1);

    CellFormatter formatter = baseGrid.getCellFormatter();
    formatter.setStyleName(0, 0, "tty-GridXHeaderCell");

    // Initialize the column and row headers.
    for (int cIndex = 0; cIndex < components.size(); cIndex++) {
      Label headerLabel = new Label(components.get(cIndex).getName());
      formatter.setStyleName(cIndex + 1, 0, "tty-GridXHeaderCell");
      baseGrid.setWidget(cIndex + 1, 0,  headerLabel);
    }
    for (int aIndex = 0; aIndex < attributes.size(); aIndex++) {
      Label headerLabel = new Label(attributes.get(aIndex).getName());
      formatter.setStyleName(0, aIndex + 1, "tty-GridYHeaderCell");
      baseGrid.setWidget(0, aIndex + 1, headerLabel);
    }

    // Initialize the data rows.
    for (int cIndex = 0; cIndex < components.size(); cIndex++) {
      for (int aIndex = 0; aIndex < attributes.size(); aIndex++) {
        HTML html = new HTML("&nbsp;");
        baseGrid.setWidget(cIndex + 1, aIndex + 1, html);
      }
    }
  }

  /**
   * Determines if all information necessary for the grid has been loaded from the server.
   *
   * @return true if fully loaded, false if not.
   */
  protected boolean isInitialized() {
    // Make sure all required pieces of data are available.
    for (RequiredDataType type : RequiredDataType.values()) {
      if (!initializedDataTypes.contains(type)) {
        return false;
      }
    }
    return true;
  }

  /**
   * Executes on clicking a cell.
   *
   * @param cell the cell clicked on.
   */
  private void cellClicked(HTMLTable.Cell cell) {
    if (cell != null) {
      int row = cell.getRowIndex();
      int column = cell.getCellIndex();

      // Ignore headers.
      if (row > 0 && column > 0) {
        // Unhighlight currently selected cell.
        if (selectedCell != null) {
          baseGrid.getCellFormatter().removeStyleName(selectedCell.getFirst(),
              selectedCell.getSecond(), "tty-RiskCellSelected");
        }
        baseGrid.getCellFormatter().addStyleName(row, column, "tty-RiskCellSelected");
        selectedCell = new Pair<Integer, Integer>(row, column);
        ValueChangeEvent.fire(this, selectedCell);
      }
    }
  }

  /**
   * Returns the CapabilityIntersectionData for a given row and column.
   *
   * @param row the row of the table you're interested in.
   * @param column the column of the table you're interested in.
   * @return data.
   */
  protected CapabilityIntersectionData getDataForCell(int row, int column) {
    int cIndex = row - 1;
    int aIndex = column - 1;
    if (aIndex < 0 || aIndex >= attributes.size() || cIndex < 0 || cIndex >= components.size()) {
      return null;
    }

    Attribute attribute = attributes.get(aIndex);
    Component component = components.get(cIndex);
    Integer key = Capability.getCapabilityIntersectionKey(component, attribute);
    return dataMap.get(key);
  }

  /**
   * Initialize the riskProviderCells field. (Maybe called more than once as asynchronous calls get
   * returned.)
   */
  private void initializeRiskCells() {
    if (!isInitialized()) {
      return;
    }

    for (Attribute attribute : attributes) {
      for (Component component : components) {
        Integer key = Capability.getCapabilityIntersectionKey(component, attribute);

        CapabilityIntersectionData data = new CapabilityIntersectionData(
            attribute, component, capabilityMap.get(key));

        dataMap.put(key, data);
      }
    }

    // Notify derived classes the risk view has been fully initialized, and is ready for painting.
    onInitialized();
  }

  /**
   * Refreshes the risk data for all cells, based on the risk provided by the passed in risk
   * provider.
   *
   * @param provider provider that determines risk.
   */
  protected void refreshRiskCalculation(RiskProvider provider) {
    if (provider == null) {
      return;
    }
    refreshRiskCalculation(Lists.newArrayList(provider));
  }

  /**
   * Refreshes the risk data for all cells, based on the risk provided by the passed in risk
   * providers.
   *
   * @param providers providers that determines risk (risk is additive).
   */
  protected void refreshRiskCalculation(List<RiskProvider> providers) {
    for (int cIndex = 0; cIndex < components.size(); cIndex++) {
      for (int aIndex = 0; aIndex < attributes.size(); aIndex++) {
        int row = cIndex + 1;
        int column = aIndex + 1;
        Attribute attribute = attributes.get(aIndex);
        Component component = components.get(cIndex);
        Integer key = Capability.getCapabilityIntersectionKey(component, attribute);
        CapabilityIntersectionData data = dataMap.get(key);

        double risk = 0.0;
        double mitigations = 0.0;
        // TODO(jimr): RiskProvider would be better off exposing a type instead of doing it based
        // off the returned positive/negative.
        for (RiskProvider provider : providers) {
          double sourceRisk = provider.calculateRisk(data);
          if (sourceRisk < 0) {
            mitigations += sourceRisk;
          } else {
            risk += sourceRisk;
          }
        }

        updateCell(row, column, risk, mitigations);
      }
    }
  }

  /**
   * Updates a cell with a new risk value.
   *
   * @param row the cell's row.
   * @param column the cell's column.
   * @param risk the new risk value.
   * @param mitigations the new mitigation value.
   */
  private void updateCell(int row, int column, double risk, double mitigations) {
    // Mitigations and risk don't need to be separate, but this gives us flexibility in the future.
    double totalRisk = risk + mitigations;
    int intensity = (int) (totalRisk * 10.0) * 10;
    if (intensity > 100) {
      intensity = 100;
    } else if (intensity < -100) {
      intensity = -100;
    }

    String intensityCss = "tty-RiskIntensity_" + Integer.toString(intensity);
    baseGrid.getCellFormatter().setStyleName(row, column, "tty-GridCell");
    baseGrid.getCellFormatter().addStyleName(row, column, intensityCss);
  }

  /**
   * Retreive a list of data for all intersection points.
   *
   * @return list of CapabilityIntersectionData objects for all intersections on the grid.
   */
  protected List<CapabilityIntersectionData> getIntersectionData() {
    return Lists.newArrayList(dataMap.values());
  }

  @Override
  public HandlerRegistration addValueChangeHandler(
      ValueChangeHandler<Pair<Integer, Integer>> handler) {
    return addHandler(handler, ValueChangeEvent.getType());
  }
}
TOP

Related Classes of com.google.testing.testify.risk.frontend.client.view.impl.RiskViewImpl

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.