Java tutorial
/******************************************************************************* * Copyright 2012 Pawe Pszty * * 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 org.rest.client.ui.desktop.widget; import java.util.ArrayList; import java.util.Date; import java.util.List; import org.rest.client.RestClient; import org.rest.client.request.URLParser; import org.rest.client.request.URLParser.QueryParam; import org.rest.client.storage.store.UrlHistoryStoreWebSql; import org.rest.client.suggestion.UrlsSuggestOracle; import org.rest.client.ui.RequestView.Presenter; import com.allen_sauer.gwt.log.client.Log; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.EventTarget; 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.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.logical.shared.AttachEvent; import com.google.gwt.event.logical.shared.SelectionEvent; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.http.client.URL; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.HasText; import com.google.gwt.user.client.ui.SuggestBox; import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay; import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.Widget; /** * * @author Pawe Pszty * */ public class RequestUrlWidget extends Composite implements HasText { interface Binder extends UiBinder<Widget, RequestUrlWidget> { } class UrlsSuggestionDisplay extends DefaultSuggestionDisplay { @Override public boolean isAnimationEnabled() { return false; } } @UiField(provided = true) SuggestBox urlField; @UiField TextBox detailedHostField; @UiField TextBox detailedPathField; @UiField TextBox detailedHashField; @UiField HTMLPanel paramsContainer; @UiField DivElement detailedPanel; @UiField Element toggleView; @UiField Element addParam; @UiField Element urlContextMenu; @UiField Element urlContextMenuButton; private Presenter listener = null; final private ArrayList<QueryDetailRow> queryParamsList = new ArrayList<QueryDetailRow>(); private Date lastEnterTime; private UrlsSuggestionDisplay suggestionsDisplay; private UrlsSuggestOracle suggestOracle; public RequestUrlWidget() { UrlHistoryStoreWebSql historyStore = RestClient.getClientFactory().getUrlHistoryStore(); //create URL simple field (suggestion box) suggestOracle = new UrlsSuggestOracle(historyStore); suggestionsDisplay = new UrlsSuggestionDisplay(); suggestionsDisplay.setAnimationEnabled(false); urlField = new SuggestBox(suggestOracle, new TextBox(), suggestionsDisplay); urlField.getElement().setAttribute("placeholder", "URL"); urlField.setAutoSelectEnabled(false); //init widget initWidget(GWT.<Binder>create(Binder.class).createAndBindUi(this)); detailedHostField.getElement().setAttribute("placeholder", "HOST"); detailedHostField.getElement().setAttribute("id", "detailedHostField"); detailedPathField.getElement().setAttribute("placeholder", "PATH"); detailedPathField.getElement().setAttribute("id", "detailedPathField"); detailedHashField.getElement().setAttribute("placeholder", "HASH"); detailedHashField.getElement().setAttribute("id", "detailedHashField"); setParamsContainerEvents(); observeToggleButton(this); observeAddParamButton(this); observeSettingsMenu(this); Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { } }); } public void setPresenter(Presenter listener) { this.listener = listener; } /** * paramsContainer listen for the change, click and key down events. * On change event it checking if event target is input field and if it is, it update URL value. * On key down event it is looking for ENTER key to starts the request. * On click event it is looking for the remove row button and it will update UTL value. */ private void setParamsContainerEvents() { paramsContainer.addDomHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { EventTarget et = event.getNativeEvent().getEventTarget(); if (Element.is(et)) { Element el = Element.as(et); if (el.getNodeName().toLowerCase().equals("input")) { updateURL(); } } } }, ChangeEvent.getType()); paramsContainer.addDomHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { EventTarget et = event.getNativeEvent().getEventTarget(); if (Element.is(et)) { Element el = Element.as(et); String nName = el.getNodeName().toLowerCase(); if (nName.equals("iron-icon") || nName.equals("paper-button")) { updateURL(); } } } }, ClickEvent.getType()); paramsContainer.addDomHandler(new KeyDownHandler() { @Override public void onKeyDown(KeyDownEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { updateURL(); performEnterKeyAction(); } } }, KeyDownEvent.getType()); } private void suggestionCallback() { String url = urlField.getValue(); if (listener != null) { listener.fireUrlChangeEvent(url); } } public void clearAll() { urlField.setValue(null); detailedHostField.setValue(null); detailedPathField.setValue(null); detailedHashField.setValue(null); clearForm(); if (listener != null) { listener.fireUrlChangeEvent(null); } } @UiHandler({ "detailedHostField", "detailedPathField", "detailedHashField" }) void onAnyValueChange(ValueChangeEvent<String> event) { updateURL(); } @UiHandler("urlField") void onSelectionChange(SelectionEvent<Suggestion> event) { if (suggestionsDisplay.isSuggestionListShowing()) { return; } suggestionCallback(); } @UiHandler({ "urlField", "detailedHostField", "detailedPathField", "detailedHashField" }) void onAnyValueKeyDownHandler(KeyDownEvent event) { if (suggestionsDisplay.isSuggestionListShowing()) { return; } if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { performEnterKeyAction(); } } @UiHandler("urlField") void onUrlValueChange(ValueChangeEvent<String> event) { if (suggestionsDisplay.isSuggestionListShowing()) { return; } listener.fireUrlChangeEvent(event.getValue()); } private void toggleView() { boolean isNowSimpleView = true; if (detailedPanel.getClassName().contains("hidden")) { //opening detailedPanel.removeClassName("hidden"); urlField.setVisible(false); detailedHostField.removeStyleName("hidden"); updateQueryForm(); ensureQueryFormHasRow(); isNowSimpleView = false; } else { updateURL(); detailedPanel.addClassName("hidden"); urlField.setVisible(true); detailedHostField.addStyleName("hidden"); } listener.fireUrlToggleEvent(isNowSimpleView); } /** * Toggle view. * @param expanded true if it should be expanded */ public void setToogleView(boolean expanded) { if (expanded) { if (detailedPanel.getClassName().contains("hidden")) { toggleView(); } } else { if (!detailedPanel.getClassName().contains("hidden")) { toggleView(); } } } /** * Perform action when ENTER key is pressed */ private void performEnterKeyAction() { if (suggestionsDisplay.isSuggestionListShowing()) { return; } if (lastEnterTime != null) { Date c = new Date(); long delta = c.getTime() - lastEnterTime.getTime(); if (delta < 1000) { return; } } lastEnterTime = new Date(); listener.fireRequestStartActionEvent(lastEnterTime); } /** * Add new key-value pair for query parameters. * <p> * Note:<br/> * All value change events for this text box are handled on parent container. * </p> */ private void addDetailedQueryParamRow(String name, String value) { final QueryDetailRow row = new QueryDetailRow(name, value); paramsContainer.add(row); queryParamsList.add(row); row.asWidget().addAttachHandler(new AttachEvent.Handler() { @Override public void onAttachOrDetach(AttachEvent event) { queryParamsList.remove(row); } }); } /** * Ensure that form has at least one row. */ void ensureQueryFormHasRow() { if (paramsContainer.getWidgetCount() > 0) return; addDetailedQueryParamRow(null, null); } /** * Clear form values */ void clearForm() { paramsContainer.clear(); queryParamsList.clear(); detailedHostField.setValue(null); detailedPathField.setValue(null); detailedHashField.setValue(null); } /** * Get current params value and fill up form. */ void updateQueryForm() { clearForm(); //parse given path and place it to proper fields URLParser data = new URLParser().parse(urlField.getValue()); String protocol = data.getProtocol(); String authority = data.getAuthority(); String hostField = ""; if (!(protocol == null || authority == null || protocol.isEmpty() && authority.isEmpty())) { if (!protocol.isEmpty()) { hostField = data.getProtocol() + "://"; } hostField += data.getAuthority(); } detailedHostField.setValue(hostField); detailedPathField.setValue(data.getPath()); detailedHashField.setValue(data.getAnchor()); List<QueryParam> params = data.getParamsList(); int paramsSize = params.size(); for (int i = 0; i < paramsSize; i++) { QueryParam param = params.get(i); String key = param.getKey(); if (key == null || key.trim().isEmpty()) { continue; } addDetailedQueryParamRow(param.getKey(), param.getValue()); } } void updateURL() { String url = getDetailedAsValue(); Log.debug("update url: " + url); urlField.setValue(url); listener.fireUrlChangeEvent(url); } private String getDetailedAsValue() { String url = detailedHostField.getValue(); if (url.endsWith("/")) { //trim last "/" url = url.substring(0, url.length() - 1); } String path = detailedPathField.getValue(); if (!path.trim().isEmpty() && !path.startsWith("/")) { path = "/" + path; } url += path; int paramsSize = queryParamsList.size(); if (paramsSize > 0) { url += "?"; } for (int i = 0; i < paramsSize; i++) { if (i > 0) { url += "&"; } QueryDetailRow fi = queryParamsList.get(i); url += fi.getKeyValue() + "=" + fi.getValue(); } String hash = detailedHashField.getValue(); if (hash != null && !hash.trim().isEmpty()) { url += "#" + hash; } return url; } /** * @return current URL value */ @Override public String getText() { return urlField.getValue(); } /** * Sets URL value */ @Override public void setText(String text) { urlField.setValue(text); listener.fireUrlChangeEvent(text); updateQueryForm(); } private final native void observeSettingsMenu(RequestUrlWidget context) /*-{ var menu = this.@org.rest.client.ui.desktop.widget.RequestUrlWidget::urlContextMenu; if(!menu) return; menu.addEventListener('iron-select',function(e){ var action = e.target.selectedItem.dataset['action']; if(!action) return; context.@org.rest.client.ui.desktop.widget.RequestUrlWidget::callMenuAction(Ljava/lang/String;Z)(action,false); }, false); var button = this.@org.rest.client.ui.desktop.widget.RequestUrlWidget::urlContextMenuButton; if(!button) return; button.addEventListener('iron-overlay-closed',function(e){ menu.selected = -1; }, false); button.addEventListener('tap',function(e){ context.@org.rest.client.ui.desktop.widget.RequestUrlWidget::urlContectMenuOpened()(); }, false); }-*/; private void urlContectMenuOpened() { listener.urlContextMenuOpenedAction(); } private void callMenuAction(String action, boolean isCtrl) { String actionName = ""; if (action.equals("encParamsAction")) { performEncodeParamsAction(isCtrl); actionName = "Encode parameters"; } else if (action.equals("decParamsAction")) { performDecodeParamsAction(isCtrl); actionName = "Decode parameters"; } else if (action.equals("replAmpAction")) { performReplaceAmpAction(); actionName = "Replace & with ;"; } else if (action.equals("openUrlTabAction")) { performOpenUrlAction(urlField.getValue()); actionName = "Replace ; with &"; } else if (action.equals("replSemiAction")) { performReplaceSemicolonAction(); actionName = "Open URL in new tab"; } listener.urlContextMenuActionPerformed(actionName); } private void performEncodeParamsAction(boolean isCtrl) { URLParser data = new URLParser().parse(urlField.getValue()); List<QueryParam> params = data.getParamsList(); int paramsSize = params.size(); for (int i = 0; i < paramsSize; i++) { QueryParam param = params.get(i); String key = param.getKey(); if (key == null || key.trim().isEmpty()) { continue; } if (isCtrl) { key = URL.encodePathSegment(key); } else { key = URL.encodeQueryString(key); } String value = param.getValue(); if (isCtrl) { value = URL.encodePathSegment(value); } else { value = URL.encodeQueryString(value); } QueryParam update = QueryParam.create(key, value); params.set(i, update); } data.setParamsList(params); String newUrl = data.toString(); setText(newUrl); } private void performDecodeParamsAction(boolean isCtrl) { URLParser data = new URLParser().parse(urlField.getValue()); List<QueryParam> params = data.getParamsList(); int paramsSize = params.size(); for (int i = 0; i < paramsSize; i++) { QueryParam param = params.get(i); String key = param.getKey(); if (key == null || key.trim().isEmpty()) { continue; } if (isCtrl) { key = URL.decodePathSegment(key); } else { key = URL.decodeQueryString(key); } String value = param.getValue(); if (isCtrl) { value = URL.decodePathSegment(value); } else { value = URL.decodeQueryString(value); } QueryParam update = QueryParam.create(key, value); params.set(i, update); } data.setParamsList(params); String newUrl = data.toString(); setText(newUrl); } private void performReplaceAmpAction() { URLParser data = new URLParser().parse(urlField.getValue()); data.setParamsDelimiter(";"); data.setQueryFromCurrentParams(); String newUrl = data.toString(); setText(newUrl); } private void performReplaceSemicolonAction() { URLParser data = new URLParser().parse(urlField.getValue()); data.setParamsDelimiter("&"); data.setQueryFromCurrentParams(); String newUrl = data.toString(); setText(newUrl); } private final native void performOpenUrlAction(String url) /*-{ if(chrome && chrome.tabs && chrome.tabs.create){ chrome.tabs.create({url:url}); } else { console.log("Chrome API unavailable. Not in extension?"); } }-*/; private final native void observeToggleButton(RequestUrlWidget context) /*-{ var button = this.@org.rest.client.ui.desktop.widget.RequestUrlWidget::toggleView; if(!button) return; button.addEventListener('tap', function(e){ if(button.classList.contains('active')){ button.classList.remove('active'); context.@org.rest.client.ui.desktop.widget.RequestUrlWidget::setToogleView(Z)(false); } else { button.classList.add('active') context.@org.rest.client.ui.desktop.widget.RequestUrlWidget::setToogleView(Z)(true); } }); }-*/; private final native void observeAddParamButton(RequestUrlWidget context) /*-{ var button = this.@org.rest.client.ui.desktop.widget.RequestUrlWidget::addParam; if(!button) return; button.addEventListener('tap', function(e){ context.@org.rest.client.ui.desktop.widget.RequestUrlWidget::addDetailedQueryParamRow(Ljava/lang/String;Ljava/lang/String;)(null, null); }); }-*/; }