Package tsd.client

Source Code of tsd.client.MetricForm$MetricChangeHandler

// This file is part of OpenTSDB.
// Copyright (C) 2010-2012  The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 of the License, or (at your
// option) any later version.  This program is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
// General Public License for more details.  You should have received a copy
// of the GNU Lesser General Public License along with this program.  If not,
// see <http://www.gnu.org/licenses/>.
package tsd.client;

import java.util.ArrayList;

import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

final class MetricForm extends HorizontalPanel implements Focusable {

  public static interface MetricChangeHandler extends EventHandler {
    void onMetricChange(MetricForm widget);
  }

  private static final String TSDB_ID_CLASS = "[-_./a-zA-Z0-9]";
  private static final String TSDB_ID_RE = "^" + TSDB_ID_CLASS + "*$";
  private static final String TSDB_TAGVALUE_RE =
    "^(\\*?"                                       // a `*' wildcard or nothing
    + "|" + TSDB_ID_CLASS + "+(\\|" + TSDB_ID_CLASS + "+)*)$"; // `foo|bar|...'

  private final EventsHandler events_handler;
  private MetricChangeHandler metric_change_handler;

  private final CheckBox downsample = new CheckBox("Downsample");
  private final ListBox downsampler = new ListBox();
  private final ValidatedTextBox interval = new ValidatedTextBox();
  private final CheckBox rate = new CheckBox("Rate");
  private final CheckBox rate_counter = new CheckBox("Rate Ctr");
  private final TextBox counter_max = new TextBox();
  private final TextBox counter_reset_value = new TextBox();
  private final CheckBox x1y2 = new CheckBox("Right Axis");
  private final ListBox aggregators = new ListBox();
  private final ValidatedTextBox metric = new ValidatedTextBox();
  private final FlexTable tagtable = new FlexTable();

  public MetricForm(final EventsHandler handler) {
    events_handler = handler;
    setupDownsampleWidgets();
    downsample.addClickHandler(handler);
    downsampler.addChangeHandler(handler);
    interval.addBlurHandler(handler);
    interval.addKeyPressHandler(handler);
    rate.addClickHandler(handler);
    rate_counter.addClickHandler(handler);
    counter_max.addBlurHandler(handler);
    counter_max.addKeyPressHandler(handler);
    counter_reset_value.addBlurHandler(handler);
    counter_reset_value.addKeyPressHandler(handler);
    x1y2.addClickHandler(handler);
    aggregators.addChangeHandler(handler);
    metric.addBlurHandler(handler);
    metric.addKeyPressHandler(handler);
    {
      final EventsHandler metric_handler = new EventsHandler() {
        protected <H extends EventHandler> void onEvent(final DomEvent<H> event) {
          if (metric_change_handler != null) {
            metric_change_handler.onMetricChange(MetricForm.this);
          }
        }
      };
      metric.addBlurHandler(metric_handler);
      metric.addKeyPressHandler(metric_handler);
    }

    metric.setValidationRegexp(TSDB_ID_RE);
    assembleUi();
  }

  public String getMetric() {
    return metric.getText();
  }

  /**
   * Parses the metric and tags out of the given string.
   * @param metric A string of the form "metric" or "metric{tag=value,...}".
   * @return The name of the metric.
   */
  private String parseWithMetric(final String metric) {
    // TODO: Try to reduce code duplication with Tags.parseWithMetric().
    final int curly = metric.indexOf('{');
    if (curly < 0) {
      clearTags();
      return metric;
    }
    final int len = metric.length();
    if (metric.charAt(len - 1) != '}') {  // "foo{"
      clearTags();
      return null// Missing '}' at the end.
    } else if (curly == len - 2) {  // "foo{}"
      clearTags();
      return metric.substring(0, len - 2);
    }
    // substring the tags out of "foo{a=b,...,x=y}" and parse them.
    int i = 0// Tag index.
    final int num_tags_before = getNumTags();
    for (final String tag : metric.substring(curly + 1, len - 1).split(",")) {
      final String[] kv = tag.split("=");
      if (kv.length != 2 || kv[0].isEmpty() || kv[1].isEmpty()) {
        setTag(i, "", "");
        continue// Invalid tag.
      }
      if (i < num_tags_before) {
        setTag(i, kv[0], kv[1]);
      } else {
        addTag(kv[0], kv[1]);
      }
      i++;
    }
    // Leave an empty line at the end.
    if (i < num_tags_before) {
      setTag(i, "", "");
    } else {
      addTag();
    }
    // Remove extra tags.
    for (i++; i < num_tags_before; i++) {
      tagtable.removeRow(i + 1);
    }
    // Return the "foo" part of "foo{a=b,...,x=y}"
    return metric.substring(0, curly);
  }

  public void updateFromQueryString(final String m, final String o) {
    // TODO: Try to reduce code duplication with GraphHandler.parseQuery().
    // m is of the following forms:
    //  agg:[interval-agg:][rate[{counter[,max[,reset]]}:]metric[{tag=value,...}]
    // Where the parts in square brackets `[' .. `]' are optional.
    final String[] parts = m.split(":");
    int i = parts.length;
    if (i < 2 || i > 4) {
      return// Malformed.
    }

    setSelectedItem(aggregators, parts[0]);

    i--;  // Move to the last part (the metric name).
    metric.setText(parseWithMetric(parts[i]));
    metric_change_handler.onMetricChange(this);

    final boolean rate = parts[--i].startsWith("rate");
    this.rate.setValue(rate, false);
    LocalRateOptions rate_options = parseRateOptions(rate, parts[i]);
    this.rate_counter.setValue(rate_options.is_counter, false);
    final long rate_counter_max = rate_options.counter_max;
    this.counter_max.setValue(
        rate_counter_max == Long.MAX_VALUE ? "" : Long.toString(rate_counter_max),
        false);
    this.counter_reset_value
        .setValue(Long.toString(rate_options.reset_value), false);
    if (rate) {
      i--;
    }

    // downsampling function & interval.
    if (i > 0) {
      final int dash = parts[1].indexOf('-', 1)// 1st char can't be `-'.
      if (dash < 0) {
        disableDownsample();
        return// Invalid downsampling specifier.
      }
      downsample.setValue(true, false);

      downsampler.setEnabled(true);
      setSelectedItem(downsampler, parts[1].substring(dash + 1));

      interval.setEnabled(true);
      interval.setText(parts[1].substring(0, dash));
    } else {
      disableDownsample();
    }

    x1y2.setValue(o.contains("axis x1y2"), false);
  }

  private void disableDownsample() {
    downsample.setValue(false, false);
    interval.setEnabled(false);
    downsampler.setEnabled(false);
  }

  public CheckBox x1y2() {
    return x1y2;
  }

  private void assembleUi() {
    setWidth("100%");
    // Left hand-side panel.
      final HorizontalPanel hbox = new HorizontalPanel();
      final InlineLabel l = new InlineLabel();
      l.setText("Metric:");
      hbox.add(l);
      final SuggestBox suggest = RemoteOracle.newSuggestBox("metrics",
                                                            metric);
      suggest.setLimit(40);
      hbox.add(suggest);
      hbox.setWidth("100%");
      metric.setWidth("100%");

      tagtable.setWidget(0, 0, hbox);
      tagtable.getFlexCellFormatter().setColSpan(0, 0, 3);
      addTag();
      tagtable.setText(1, 0, "Tags");
      add(tagtable);
    }
    {  // Right hand-side panel.
      final VerticalPanel vbox = new VerticalPanel();
      {
        final HorizontalPanel hbox = new HorizontalPanel();
        hbox.add(rate);
        hbox.add(rate_counter);
        hbox.add(x1y2);
        vbox.add(hbox);
      }
      {
        final HorizontalPanel hbox = new HorizontalPanel();
        final InlineLabel l = new InlineLabel("Rate Ctr Max:");
        hbox.add(l);
        hbox.add(counter_max);
        vbox.add(hbox);
      }
      {
        final HorizontalPanel hbox = new HorizontalPanel();
        final InlineLabel l = new InlineLabel("Rate Ctr Reset:");
        hbox.add(l);
        hbox.add(counter_reset_value);
        vbox.add(hbox);
      }
      {
        final HorizontalPanel hbox = new HorizontalPanel();
        final InlineLabel l = new InlineLabel();
        l.setText("Aggregator:");
        hbox.add(l);
        hbox.add(aggregators);
        vbox.add(hbox);
      }
      vbox.add(downsample);
      {
        final HorizontalPanel hbox = new HorizontalPanel();
        hbox.add(downsampler);
        hbox.add(interval);
        vbox.add(hbox);
      }
      add(vbox);
    }
  }

  public void setMetricChangeHandler(final MetricChangeHandler handler) {
    metric_change_handler = handler;
  }

  public void setAggregators(final ArrayList<String> aggs) {
    for (final String agg : aggs) {
      aggregators.addItem(agg);
      downsampler.addItem(agg);
    }
    setSelectedItem(aggregators, "sum");
    setSelectedItem(downsampler, "avg");
  }

  public boolean buildQueryString(final StringBuilder url) {
    final String metric = getMetric();
    if (metric.isEmpty()) {
      return false;
    }
    url.append("&m=");
    url.append(selectedValue(aggregators));
    if (downsample.getValue()) {
      url.append(':').append(interval.getValue())
        .append('-').append(selectedValue(downsampler));
    }
    if (rate.getValue()) {
      url.append(":rate");
      if (rate_counter.getValue()) {
        url.append('{').append("counter");
        final String max = counter_max.getValue().trim();
        final String reset = counter_reset_value.getValue().trim();
        if (max.length() > 0 && reset.length() > 0) {
          url.append(',').append(max).append(',').append(reset);
        } else if (max.length() > 0 && reset.length() == 0) {
          url.append(',').append(max);
        } else if (max.length() == 0 && reset.length() > 0){
          url.append(",,").append(reset);
        }
        url.append('}');
      }
    }
    url.append(':').append(metric);
    {
      final int ntags = getNumTags();
      url.append('{');
      for (int tag = 0; tag < ntags; tag++) {
        final String tagname = getTagName(tag);
        final String tagvalue = getTagValue(tag);
        if (tagname.isEmpty() || tagvalue.isEmpty()) {
          continue;
        }
        url.append(tagname).append('=').append(tagvalue)
          .append(',');
      }
      final int last = url.length() - 1;
      if (url.charAt(last) == '{') {  // There was no tag.
        url.setLength(last);          // So remove the `{'.
      } else // Need to replace the last `,' with a `}'.
        url.setCharAt(url.length() - 1, '}');
      }
    }
    url.append("&o=");
    if (x1y2.getValue()) {
      url.append("axis x1y2");
    }
    return true;
  }

  private int getNumTags() {
    return tagtable.getRowCount() - 1;
  }

  private String getTagName(final int i) {
    return ((SuggestBox) tagtable.getWidget(i + 1, 1)).getValue();
  }

  private String getTagValue(final int i) {
    return ((SuggestBox) tagtable.getWidget(i + 1, 2)).getValue();
  }

  private void setTagName(final int i, final String value) {
    ((SuggestBox) tagtable.getWidget(i + 1, 1)).setValue(value);
  }

  private void setTagValue(final int i, final String value) {
    ((SuggestBox) tagtable.getWidget(i + 1, 2)).setValue(value);
  }

  /**
   * Changes the name/value of an existing tag.
   * @param i The index of the tag to change.
   * @param name The new name of the tag.
   * @param value The new value of the tag.
   * Requires: {@code i < getNumTags()}.
   */
  private void setTag(final int i, final String name, final String value) {
    setTagName(i, name);
    setTagValue(i, value);
  }

  private void addTag() {
    addTag(null, null);
  }

  private void addTag(final String default_tagname) {
    addTag(default_tagname, null);
  }

  private void addTag(final String default_tagname,
                      final String default_value) {
    final int row = tagtable.getRowCount();

    final ValidatedTextBox tagname = new ValidatedTextBox();
    final SuggestBox suggesttagk = RemoteOracle.newSuggestBox("tagk", tagname);
    final ValidatedTextBox tagvalue = new ValidatedTextBox();
    final SuggestBox suggesttagv = RemoteOracle.newSuggestBox("tagv", tagvalue);
    tagname.setValidationRegexp(TSDB_ID_RE);
    tagvalue.setValidationRegexp(TSDB_TAGVALUE_RE);
    tagname.setWidth("100%");
    tagvalue.setWidth("100%");
    tagname.addBlurHandler(recompact_tagtable);
    tagname.addBlurHandler(events_handler);
    tagname.addKeyPressHandler(events_handler);
    tagvalue.addBlurHandler(recompact_tagtable);
    tagvalue.addBlurHandler(events_handler);
    tagvalue.addKeyPressHandler(events_handler);

    tagtable.setWidget(row, 1, suggesttagk);
    tagtable.setWidget(row, 2, suggesttagv);
    if (row > 2) {
      final Button remove = new Button("x");
      remove.addClickHandler(removetag);
      tagtable.setWidget(row - 1, 0, remove);
    }
    if (default_tagname != null) {
      tagname.setText(default_tagname);
      if (default_value == null) {
        tagvalue.setFocus(true);
      }
    }
    if (default_value != null) {
      tagvalue.setText(default_value);
    }
  }

  private void clearTags() {
    setTag(0, "", "");
    for (int i = getNumTags() - 1; i > 1; i++) {
      tagtable.removeRow(i + 1);
    }
  }

  public void autoSuggestTag(final String tag) {
    // First try to see if the tag is already in the table.
    final int nrows = tagtable.getRowCount();
    int unused_row = -1;
    for (int row = 1; row < nrows; row++) {
      final SuggestBox tagname = ((SuggestBox) tagtable.getWidget(row, 1));
      final SuggestBox tagvalue = ((SuggestBox) tagtable.getWidget(row, 2));
      final String thistag = tagname.getValue();
      if (thistag.equals(tag)) {
        return// This tag is already in the table.
      } if (thistag.isEmpty() && tagvalue.getValue().isEmpty()) {
        unused_row = row;
        break;
      }
    }
    if (unused_row >= 0) {
      ((SuggestBox) tagtable.getWidget(unused_row, 1)).setValue(tag);
    } else {
      addTag(tag);
    }
  }

  private final BlurHandler recompact_tagtable = new BlurHandler() {
    public void onBlur(final BlurEvent event) {
      int ntags = getNumTags();
      // Is the first line empty?  If yes, move everything up by 1 line.
      if (getTagName(0).isEmpty() && getTagValue(0).isEmpty()) {
        for (int tag = 1; tag < ntags; tag++) {
          final String tagname = getTagName(tag);
          final String tagvalue = getTagValue(tag);
          setTag(tag - 1, tagname, tagvalue);
        }
        setTag(ntags - 1, "", "");
      }
      // Try to remove empty lines from the tag table (but never remove the
      // first line or last line, even if they're empty).  Walk the table
      // from the end to make it easier to delete rows as we iterate.
      for (int tag = ntags - 1; tag >= 1; tag--) {
        final String tagname = getTagName(tag);
        final String tagvalue = getTagValue(tag);
        if (tagname.isEmpty() && tagvalue.isEmpty()) {
          tagtable.removeRow(tag + 1);
        }
      }
      ntags = getNumTags()// How many lines are left?
      // If the last line isn't empty, add another one.
      final String tagname = getTagName(ntags - 1);
      final String tagvalue = getTagValue(ntags - 1);
      if (!tagname.isEmpty() && !tagvalue.isEmpty()) {
        addTag();
      }
    }
  };

  private final ClickHandler removetag = new ClickHandler() {
    public void onClick(final ClickEvent event) {
      if (!(event.getSource() instanceof Button)) {
        return;
      }
      final Widget source = (Widget) event.getSource();
      final int ntags = getNumTags();
      for (int tag = 1; tag < ntags; tag++) {
        if (source == tagtable.getWidget(tag + 1, 0)) {
          tagtable.removeRow(tag + 1);
          events_handler.onClick(event);
          break;
        }
      }
    }
  };

  private void setupDownsampleWidgets() {
    downsampler.setEnabled(false);
    interval.setEnabled(false);
    interval.setMaxLength(5);
    interval.setVisibleLength(5);
    interval.setValue("10m");
    interval.setValidationRegexp("^[1-9][0-9]*[smhdwy]$");
    downsample.addClickHandler(new ClickHandler() {
      public void onClick(final ClickEvent event) {
        final boolean checked = ((CheckBox) event.getSource()).getValue();
        downsampler.setEnabled(checked);
        interval.setEnabled(checked);
        if (checked) {
          downsampler.setFocus(true);
        }
      }
    });
  }

  private static String selectedValue(final ListBox list) {  // They should add
    return list.getValue(list.getSelectedIndex());           // this to GWT...
  }

  /**
   * If the given item is in the list, mark it as selected.
   * @param list The list to manipulate.
   * @param item The item to select if present.
   */
  private void setSelectedItem(final ListBox list, final String item) {
    final int nitems = list.getItemCount();
    for (int i = 0; i < nitems; i++) {
      if (item.equals(list.getValue(i))) {
        list.setSelectedIndex(i);
        return;
      }
    }
  }

  /**
   * Class used for parsing and rate options
   */
  private static class LocalRateOptions {
    public boolean is_counter;
    public long counter_max = Long.MAX_VALUE;
    public long reset_value = 0;
  }
 
  /**
   * Parses the "rate" section of the query string and returns an instance
   * of the LocalRateOptions class that contains the values found.
   * <p/>
   * The format of the rate specification is rate[{counter[,#[,#]]}].
   * If the spec is invalid or we were unable to parse properly, it returns a
   * default options object.
   * @param rate If true, then the query is set as a rate query and the rate
   * specification will be parsed. If false, a default RateOptions instance
   * will be returned and largely ignored by the rest of the processing
   * @param spec The part of the query string that pertains to the rate
   * @return An initialized LocalRateOptions instance based on the specification
   * @since 2.0
   */
  static final public LocalRateOptions parseRateOptions(boolean rate, String spec) {
    if (!rate || spec.length() < 6) {
      return new LocalRateOptions();
    }

    String[] parts = spec.split(spec.substring(5, spec.length() - 1), ',');
    if (parts.length < 1 || parts.length > 3) {
      return new LocalRateOptions();
    }

    try {
      LocalRateOptions options = new LocalRateOptions();
      options.is_counter = "counter".equals(parts[0]);
      options.counter_max = (parts.length >= 2 && parts[1].length() > 0 ? Long
          .parseLong(parts[1]) : Long.MAX_VALUE);
      options.reset_value = (parts.length >= 3 && parts[2].length() > 0 ? Long
          .parseLong(parts[2]) : 0);
      return options;
    } catch (NumberFormatException e) {
      return new LocalRateOptions();
    }
  }
 
  // ------------------- //
  // Focusable interface //
  // ------------------- //

  public int getTabIndex() {
    return metric.getTabIndex();
  }

  public void setTabIndex(final int index) {
    metric.setTabIndex(index);
  }

  public void setAccessKey(final char key) {
    metric.setAccessKey(key);
  }

  public void setFocus(final boolean focused) {
    metric.setFocus(focused);
  }

}
TOP

Related Classes of tsd.client.MetricForm$MetricChangeHandler

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.