Java tutorial
/* * Copyright 2011 Facebook, Inc. * * 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.facebook.tsdb.tsdash.client.presenter; import java.util.ArrayList; import java.util.HashMap; import com.facebook.tsdb.tsdash.client.event.ErrorEvent; import com.facebook.tsdb.tsdash.client.event.MetricEvent; import com.facebook.tsdb.tsdash.client.event.MetricHeaderEvent; import com.facebook.tsdb.tsdash.client.event.MetricHeaderEventHandler; import com.facebook.tsdb.tsdash.client.event.TagEvent; import com.facebook.tsdb.tsdash.client.model.ApplicationState; import com.facebook.tsdb.tsdash.client.model.Metric; import com.facebook.tsdb.tsdash.client.model.MetricHeader; import com.facebook.tsdb.tsdash.client.model.TimeRange; import com.facebook.tsdb.tsdash.client.service.HTTPService; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.HasChangeHandlers; import com.google.gwt.event.dom.client.HasClickHandlers; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.HasText; import com.google.gwt.user.client.ui.HasValue; import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.Widget; public class MetricPresenter implements Presenter { public interface MetricsFormWidget { void focusSuggest(boolean focus); HasValue<String> typedMetric(); HasClickHandlers addMetricButton(); HasWidgets metricsContainer(); void setMetricSuggestions(ArrayList<String> options); int metricsCount(); void setLoadingVisible(boolean visible); HasClickHandlers viewAllButton(); HasText viewAllButtonText(); } public interface MetricWidget { HasClickHandlers deleteButton(); HasClickHandlers cloneButton(); HasClickHandlers rightAxisButton(); HasClickHandlers rateButton(); HasClickHandlers commitButton(); boolean isPressed(Object toggleButton); String selectedAggregator(); void selectedAggregator(String aggregator); HasChangeHandlers aggregator(); HasValue<Boolean> aggregatorSwitch(); void pressToggleButton(Object toggleButton, boolean pressed); String getName(); HasWidgets tagsContainer(); void setEnabled(boolean enabled); void markPlottable(boolean plottable); void aggregatorEnabled(boolean enabled); void commitEnabled(boolean enabled); void commitVisible(boolean visible); } public interface TagWidget { HasClickHandlers deleteButton(); HasClickHandlers setValueButton(); HasClickHandlers removeValueButton(); String getSelectedValue(); void optionsVisible(boolean visible); void setValue(String value); } public interface MetricOptionWidget { HasText link(); HasClickHandlers linkButton(); } private final HandlerManager eventBus; private final HTTPService service; private final MetricsFormWidget widget; private final HashMap<String, MetricHeader> headers = new HashMap<String, MetricHeader>(); private ApplicationState appState; // this is used only for metric header update private final HashMap<Metric, MetricWidget> metricsWidgets = new HashMap<Metric, MetricWidget>(); private final PopupPanel allMetricsPopup = new PopupPanel(true); private final HTMLPanel allMetricsContainer = new HTMLPanel(""); public MetricPresenter(HandlerManager eventBus, HTTPService service, MetricsFormWidget widget) { this.eventBus = eventBus; this.service = service; this.widget = widget; bindWidget(); listenHeaderUpdates(); } private void setupAllMetricsPopup() { allMetricsPopup.setWidget(allMetricsContainer); Widget w = (Widget) widget; int top = w.getAbsoluteTop() + 10; int left = w.getAbsoluteLeft() + 350; allMetricsPopup.setPopupPosition(left, top); allMetricsContainer.addStyleName("allMetricsPopup"); } private void listenHeaderUpdates() { eventBus.addHandler(MetricHeaderEvent.TYPE, new MetricHeaderEventHandler() { @Override public void onHeadersLoaded(MetricHeaderEvent event) { int i = 0; for (MetricHeader newHeader : event.getHeaders()) { Metric metric = appState.metrics.get(i); if (newHeader.compareTo(metric.header) != 0) { // update the header re-render metric.header = newHeader; MetricWidget metricWidget = metricsWidgets.get(metric); metricWidget.setEnabled(true); loadTagsWidgets(metricWidget, metric); } i++; } } }); } private void loadMetricHeader(final Metric metric, final Command cmd) { TimeRange timeRange = appState.getAndUpdateTimeRange(); widget.setLoadingVisible(true); service.loadMetricHeader(metric, timeRange, new AsyncCallback<MetricHeader>() { @Override public void onFailure(Throwable caught) { eventBus.fireEvent(new ErrorEvent(caught)); } @Override public void onSuccess(MetricHeader result) { metric.header = result; headers.put(metric.getSignature(), result); widget.setLoadingVisible(false); cmd.execute(); } }); } private void getCachedHeader(Metric metric, final Command cmd) { if (headers.containsKey(metric.getSignature())) { metric.header = headers.get(metric.getSignature()); cmd.execute(); } else { loadMetricHeader(metric, cmd); } } private void bindWidget() { // ADD METRIC widget.addMetricButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { final String metricName = widget.typedMetric().getValue(); if (metricName.equals("")) { widget.focusSuggest(true); return; } widget.typedMetric().setValue(""); final Metric newMetric = new Metric(metricName); getCachedHeader(newMetric, new Command() { @Override public void execute() { addMetric(newMetric); } }); } }); // ALL METRICS widget.viewAllButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { allMetricsPopup.show(); } }); } private TagWidget createTagWidget(Metric metric, String tag) { // if this is a tag set by the user if (metric.tags.containsKey(tag)) { return new com.facebook.tsdb.tsdash.client.ui.TagWidget(tag, metric.tags.get(tag)); } // we join the aggregated and multiple options in one case ArrayList<String> options; if (metric.isAggregated() && metric.header.tagsSet.get(tag).size() == 0) { options = new ArrayList<String>(); options.add(metric.aggregator); } else { options = metric.header.tagsSet.get(tag); } return new com.facebook.tsdb.tsdash.client.ui.TagWidget(tag, options); } private TagWidget addTag(final MetricWidget metricWidget, final Metric metric, final String tagName) { final TagWidget tagWidget = createTagWidget(metric, tagName); // TAG SET tagWidget.setValueButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { String tagValue = tagWidget.getSelectedValue(); metric.tags.put(tagName, tagValue); if (!metric.plottable) { getCachedHeader(metric, new Command() { @Override public void execute() { loadTagsWidgets(metricWidget, metric); metricWidget.commitEnabled(true); } }); return; } eventBus.fireEvent(new TagEvent(TagEvent.Operation.SET, metric, tagName, tagValue)); } }); // TAG REMOVE VALUE tagWidget.removeValueButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { metric.tags.remove(tagName); if (!metric.plottable) { getCachedHeader(metric, new Command() { @Override public void execute() { loadTagsWidgets(metricWidget, metric); metricWidget.commitEnabled(true); } }); } eventBus.fireEvent(new TagEvent(TagEvent.Operation.REMOVE, metric, tagName, null)); } }); metricWidget.tagsContainer().add((Widget) tagWidget); return tagWidget; } private void loadTagsWidgets(MetricWidget metricWidget, Metric metric) { metricWidget.tagsContainer().clear(); for (String tagName : metric.header.tagsSet.keySet()) { addTag(metricWidget, metric, tagName); } } private MetricWidget addMetric(final Metric metric) { final MetricWidget metricWidget = new com.facebook.tsdb.tsdash.client.ui.MetricWidget(metric.name); // store the mapping for header update metricsWidgets.put(metric, metricWidget); // DELETE metricWidget.deleteButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { widget.metricsContainer().remove((Widget) metricWidget); if (!metric.isPlottable()) { return; } eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.DELETE, metric)); } }); // CLONE metricWidget.cloneButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { addMetric(metric.dup()); } }); // COMMIT metricWidget.commitButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { metric.plottable = true; metricWidget.markPlottable(true); metricWidget.commitEnabled(false); metricWidget.commitVisible(false); eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.ADD, metric)); } }); // AGGREGATOR SWITCH metricWidget.aggregatorSwitch().addValueChangeHandler(new ValueChangeHandler<Boolean>() { @Override public void onValueChange(ValueChangeEvent<Boolean> event) { if (!metric.plottable) { metricWidget.commitEnabled(true); return; } if (event.getValue() == true) { // the aggregator has just been enabled metric.aggregator = metricWidget.selectedAggregator(); } else { metric.aggregator = null; } eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.AGGREGATE, metric)); } }); // AGGREGATOR CHANGE metricWidget.aggregator().addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { if (!metric.plottable) { metricWidget.commitEnabled(true); return; } if (metric.aggregator != null) { metric.aggregator = metricWidget.selectedAggregator(); eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.AGGREGATE, metric)); } } }); // PARAM TOGGLE ClickHandler toggleButtonHandler = new ClickHandler() { @Override public void onClick(ClickEvent event) { metricWidget.pressToggleButton(event.getSource(), !metricWidget.isPressed(event.getSource())); metric.rightAxis = metricWidget.isPressed(metricWidget.rightAxisButton()); metric.rate = metricWidget.isPressed(metricWidget.rateButton()); eventBus.fireEvent(new MetricEvent(MetricEvent.Operation.PARAM_TOGGLE, metric)); } }; metricWidget.rightAxisButton().addClickHandler(toggleButtonHandler); metricWidget.rateButton().addClickHandler(toggleButtonHandler); if (metric.header.tagsSet.size() == 0) { metricWidget.setEnabled(false); } else { metricWidget.pressToggleButton(metricWidget.rightAxisButton(), metric.rightAxis); metricWidget.pressToggleButton(metricWidget.rateButton(), metric.rate); if (!metric.isPlottable()) { metricWidget.markPlottable(false); } if (metric.aggregator != null) { metricWidget.aggregatorSwitch().setValue(true, false); metricWidget.selectedAggregator(metric.aggregator); } metricWidget.aggregatorEnabled(metric.allowsAggregation()); loadTagsWidgets(metricWidget, metric); } widget.metricsContainer().add((Widget) metricWidget); return metricWidget; } private void loadMetrics(ArrayList<Metric> metrics) { widget.metricsContainer().clear(); for (Metric metric : metrics) { addMetric(metric); } } private void addMetricOption(final String metricName) { MetricOptionWidget optionWidget = new com.facebook.tsdb.tsdash.client.ui.MetricOptionWidget(); optionWidget.link().setText(metricName); optionWidget.linkButton().addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { final Metric newMetric = new Metric(metricName); allMetricsPopup.hide(); getCachedHeader(newMetric, new Command() { @Override public void execute() { addMetric(newMetric); } }); } }); allMetricsContainer.add((Widget) optionWidget); } @Override public void go(final HasWidgets container, final ApplicationState appState) { this.appState = appState; container.add((com.google.gwt.user.client.ui.Widget) widget); widget.focusSuggest(true); loadMetrics(appState.metrics); service.loadMetricsName(new AsyncCallback<ArrayList<String>>() { @Override public void onFailure(Throwable caught) { // fire error event eventBus.fireEvent(new ErrorEvent(caught)); } @Override public void onSuccess(ArrayList<String> metricNames) { widget.setMetricSuggestions(metricNames); widget.viewAllButtonText().setText("view all (" + metricNames.size() + ")"); setupAllMetricsPopup(); allMetricsContainer.clear(); for (String metricName : metricNames) { addMetricOption(metricName); } } }); } }