/* * Copyright 2011 Stefan Partusch * * 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 de.spartusch.nasfvi.client; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyUpEvent; import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.Response; import com.google.gwt.http.client.URL; 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.uibinder.client.UiHandler; import com.google.gwt.user.client.History; import com.google.gwt.user.client.Random; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.DisclosurePanel; import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.SuggestBox; import com.google.gwt.user.client.ui.TextArea; import com.google.gwt.user.client.ui.Widget; /** * The main widget of the client containing most of the UI. This class * implements the {@link HistoryStateSetter} interface and queries are only * sent to the server when a history token is set using this interface. * Suggestions on the other hand are processed automatically whenever the * user enters text in the widget's <code>SuggestBox</code>. * @author Stefan Partusch * */ public class MainWidget extends Composite implements RequestCallback, ValueChangeHandler<String>, HistoryStateSetter, KeyUpHandler { interface MyUiBinder extends UiBinder<Widget, MainWidget> {} private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); /** Regular expression to detect placeholders in queries. */ private static final String PLACEHOLDERS = ".*\\([A-Z][a-z]+\\).*"; /** Examples of queries. */ private final String[] examples; /** URL stubs to use. */ private final String suggestionsUrl, parseUrl; @UiField SuggestBox suggestBox; @UiField SimplePanel responsePanel; @UiField DisclosurePanel hints; @UiField HTML exampleItems; /** * @param examples Examples of queries to display in the main widget * @param suggestionsUrl URL stub for retrieving suggestions * @param parseUrl URL stub for parsing */ public MainWidget(final String[] examples, final String suggestionsUrl, final String parseUrl) { this.examples = examples; this.suggestionsUrl = suggestionsUrl; this.parseUrl = parseUrl; initWidget(uiBinder.createAndBindUi(this)); setExamples(); suggestBox.addKeyUpHandler(this); Scheduler.get().scheduleDeferred(new Scheduler.ScheduledCommand() { @Override public void execute() { suggestBox.setFocus(true); } }); } @UiFactory protected final SuggestBox createSuggestBox() { TextArea textArea = new TextArea(); textArea.setCharacterWidth(50); textArea.setVisibleLines(4); return new SuggestBox(new Suggestions(suggestionsUrl), textArea); } /** * Selects some examples randomly and displays them. */ private void setExamples() { StringBuilder sb = new StringBuilder(); for (int i = 0; i < 5; i++) { int index = Random.nextInt(examples.length); sb.append("<li>").append(examples[index]).append("</li>"); } exampleItems.setHTML(sb.toString()); } @UiHandler("moreExamples") protected final void moreExamplesClicked(final ClickEvent event) { setExamples(); } @UiHandler("sendButton") protected final void sendButtonClicked(final ClickEvent event) { setHistoryState(null, 0); } /** * Displays a message within the main widget. * @param message Message to display * @param cssClass CSS class to use for styling * @see #clearMessage */ private void setMessage(final String message, final String cssClass) { String msg = message; if (cssClass != null && !cssClass.isEmpty()) { msg = "<span class='" + cssClass + "'>" + message + "</span>"; } responsePanel.setWidget(new HTML(msg)); } /** * Clears a message displayed in the main widget. * @see #setMessage */ private void clearMessage() { responsePanel.clear(); } @Override public final void onError(final Request request, final Throwable exception) { Main.displayError(exception.getLocalizedMessage(), exception.toString()); } @Override public final void onResponseReceived(final Request request, final Response response) { int sc = response.getStatusCode(); String respText = response.getText(); if (sc == Response.SC_OK) { NResponse nresp = NResponse.parse(respText); if (nresp.isSuccess()) { hints.setOpen(false); NResponseWidget widget = new NResponseWidget(nresp, this); responsePanel.setWidget(widget); } else { setMessage("Ihre Anfrage wurde verstanden und verarbeitet." + " Doch das Vorlesungsverzeichnis enthält keine Daten" + " zur Beantwortung der Anfrage.", null); } } else if (sc == Response.SC_BAD_REQUEST) { setMessage("Die Anfrage konnte nicht verarbeitet werden." + " Bitte formulieren Sie um!", "error"); } else { Main.displayError(response.getStatusText(), respText); } } @Override public final void setHistoryState(final String query, final int offset) { String q = query; if (q == null || q.isEmpty()) { q = suggestBox.getText(); } History.newItem(URL.encodeQueryString(q) + "&" + String.valueOf(offset), true); } @Override public final void onValueChange(final ValueChangeEvent<String> event) { String[] state = event.getValue().split("&", -1); if (state.length != 2) { throw new AssertionError(); } String q = URL.decodeQueryString(state[0]) .replaceAll("\\s+", " ").trim(); String offset = state[1]; suggestBox.setText(q); if (q.isEmpty()) { setMessage("Bitte geben Sie eine Anfrage in das Textfeld ein.", "error"); } else if (q.matches(PLACEHOLDERS)) { setMessage("Bitte ersetzen Sie sämtliche Platzhalter durch" + " konkrete Werte!", "error"); } else { clearMessage(); try { String getUrl = parseUrl + URL.encodeQueryString(q) + "&offset=" + offset; RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, getUrl); builder.setCallback(this); builder.send(); } catch (RequestException e) { Main.displayError(e.getLocalizedMessage(), e.toString()); } } } @Override public final void onKeyUp(final KeyUpEvent event) { if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { setHistoryState(null, 0); } } }