org.unitime.timetable.gwt.client.widgets.CourseSelectionSuggestBox.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.timetable.gwt.client.widgets.CourseSelectionSuggestBox.java

Source

/*
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for
 * additional information regarding copyright ownership.
 *
 * The Apereo Foundation licenses this file to you 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.unitime.timetable.gwt.client.widgets;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.unitime.timetable.gwt.client.aria.AriaStatus;
import org.unitime.timetable.gwt.client.aria.AriaSuggestBox;
import org.unitime.timetable.gwt.client.aria.ImageButton;
import org.unitime.timetable.gwt.client.page.UniTimeNotifications;
import org.unitime.timetable.gwt.resources.GwtAriaMessages;
import org.unitime.timetable.gwt.resources.StudentSectioningConstants;
import org.unitime.timetable.gwt.resources.StudentSectioningMessages;
import org.unitime.timetable.gwt.resources.StudentSectioningResources;
import org.unitime.timetable.gwt.shared.ClassAssignmentInterface;
import org.unitime.timetable.gwt.shared.CourseRequestInterface;
import org.unitime.timetable.gwt.shared.ClassAssignmentInterface.CourseAssignment;
import org.unitime.timetable.gwt.shared.CourseRequestInterface.FreeTime;

import com.google.gwt.aria.client.Roles;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
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.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
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.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.HasCloseHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
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.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.SuggestOracle.Callback;
import com.google.gwt.user.client.ui.SuggestOracle.Request;
import com.google.gwt.user.client.ui.SuggestOracle.Response;
import com.google.gwt.user.client.ui.SuggestOracle.Suggestion;

/**
 * @author Tomas Muller
 */
public class CourseSelectionSuggestBox extends Composite implements CourseSelection {
    protected static final StudentSectioningResources RESOURCES = GWT.create(StudentSectioningResources.class);
    protected static final StudentSectioningMessages MESSAGES = GWT.create(StudentSectioningMessages.class);
    protected static final StudentSectioningConstants CONSTANTS = GWT.create(StudentSectioningConstants.class);
    protected static final GwtAriaMessages ARIA = GWT.create(GwtAriaMessages.class);

    protected AriaSuggestBox iSuggest;
    private String iLastSuggestion;
    protected ImageButton iFinderButton;
    private HorizontalPanel iHPanel;
    private VerticalPanel iVPanel;
    private Label iError;

    private String iHint = "";
    private Set<String> iValidCourseNames = new HashSet<String>();

    private DataProvider<String, Collection<CourseAssignment>> iDataProvider;
    private FreeTimeParser iFreeTimeParser = null;
    private CourseFinder iCourseFinder = null;
    private CourseFinderFactory iCourseFinderFactory = null;
    private List<Validator<CourseSelection>> iValidators = new ArrayList<Validator<CourseSelection>>();

    private boolean iShowCourses = false, iShowDefaultSuggestions = false;
    private boolean iSaved = false;

    public CourseSelectionSuggestBox() {
        this(false, false);
    }

    public CourseSelectionSuggestBox(boolean showCourses, boolean showDefaultSuggestions) {
        iShowCourses = showCourses;
        iShowDefaultSuggestions = showDefaultSuggestions;

        SuggestOracle courseOfferingOracle = new SuggestOracle() {
            public void requestSuggestions(Request request, Callback callback) {
                if (request.getQuery().equals(iHint))
                    return;
                iDataProvider.getData(request.getQuery(), new SuggestCallback(request, callback));
            }

            public void requestDefaultSuggestions(Request request, Callback callback) {
                if (iShowDefaultSuggestions)
                    iDataProvider.getData("", new SuggestCallback(request, callback));
                else
                    super.requestDefaultSuggestions(request, callback);

            }

            public boolean isDisplayStringHTML() {
                return true;
            }
        };

        iSuggest = new AriaSuggestBox(courseOfferingOracle);
        iSuggest.setStyleName("unitime-TextBoxHint");

        iFinderButton = new ImageButton(RESOURCES.search_picker(), RESOURCES.search_picker_Down(),
                RESOURCES.search_picker_Over(), RESOURCES.search_picker_Disabled());
        iFinderButton.setTabIndex(-1);

        iVPanel = new VerticalPanel();

        iHPanel = new HorizontalPanel();
        iHPanel.add(iSuggest);
        iHPanel.add(iFinderButton);
        iVPanel.add(iHPanel);

        iError = new Label();
        iError.setStyleName("unitime-ErrorHint");
        iError.setVisible(false);
        Roles.getPresentationRole().setAriaHiddenState(iError.getElement(), true);
        iVPanel.add(iError);

        iFinderButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                if (iSuggest.isEnabled()) {
                    openDialogAsync();
                }
            }
        });

        iSuggest.addSelectionHandler(new SelectionHandler<Suggestion>() {
            public void onSelection(SelectionEvent<Suggestion> event) {
                String text = event.getSelectedItem().getReplacementString();
                iLastSuggestion = text;
                CourseSelectionEvent.fire(CourseSelectionSuggestBox.this, text, !text.isEmpty());
            }
        });
        iSuggest.getValueBox().addChangeHandler(new ChangeHandler() {
            public void onChange(ChangeEvent event) {
                boolean valid = false;
                String text = iSuggest.getText();
                if (text.equalsIgnoreCase(iLastSuggestion))
                    valid = true;
                else
                    for (String course : iValidCourseNames) {
                        if (course.equalsIgnoreCase(text)) {
                            valid = true;
                            break;
                        }
                    }
                CourseSelectionEvent.fire(CourseSelectionSuggestBox.this, text, valid);
            }
        });
        iSuggest.getValueBox().addKeyDownHandler(new KeyDownHandler() {
            public void onKeyDown(KeyDownEvent event) {
                if (!iSuggest.isEnabled())
                    return;
                if ((event.getNativeEvent().getKeyCode() == 'F' || event.getNativeEvent().getKeyCode() == 'f')
                        && (event.isControlKeyDown() || event.isAltKeyDown())) {
                    iSuggest.hideSuggestionList();
                    openDialogAsync();
                }
                if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) {
                    iSuggest.hideSuggestionList();
                }
                if ((event.getNativeEvent().getKeyCode() == 'L' || event.getNativeEvent().getKeyCode() == 'l')
                        && (event.isControlKeyDown() || event.isAltKeyDown())) {
                    iSuggest.showSuggestionList();
                }
            }
        });
        iSuggest.getValueBox().addBlurHandler(new BlurHandler() {
            public void onBlur(BlurEvent event) {
                if (iSuggest.getText().isEmpty()) {
                    if (iError.isVisible())
                        iError.setVisible(false);
                    if (!iHint.isEmpty()) {
                        iSuggest.setText(iHint);
                        iSuggest.setStyleName("unitime-TextBoxHint");
                    }
                }
            }
        });
        iSuggest.getValueBox().addFocusHandler(new FocusHandler() {
            public void onFocus(FocusEvent event) {
                iSuggest.setStyleName("gwt-SuggestBox");
                if (!iHint.isEmpty() && iSuggest.getText().equals(iHint))
                    iSuggest.setText("");
                if (!iError.getText().isEmpty())
                    AriaStatus.getInstance().setText(iError.getText());
            }
        });

        initWidget(iVPanel);
    }

    @Override
    public String getValue() {
        return (iSuggest.getText().equals(iHint) ? "" : iSuggest.getText());
    }

    @Override
    public void setValue(String value) {
        setValue(value, false);
    }

    private void openDialogAsync() {
        GWT.runAsync(new RunAsyncCallback() {
            public void onSuccess() {
                openDialog();
            }

            public void onFailure(Throwable reason) {
                UniTimeNotifications.error(MESSAGES.failedToLoadTheApp(reason.getMessage()));
            }
        });
    }

    private void openDialog() {
        getCourseFinder().setValue(iSuggest.getText().equals(iHint) ? "" : iSuggest.getText(), true);
        getCourseFinder().findCourse();
    }

    @Override
    public void setValue(String value, boolean fireEvents) {
        if (value == null || value.isEmpty()) {
            iSuggest.setText(iHint);
            if (!iHint.isEmpty())
                iSuggest.setStyleName("unitime-TextBoxHint");
            else
                iSuggest.setStyleName("gwt-SuggestBox");
            setError(null);
        } else {
            iSuggest.setText(value);
            if (iSuggest.getText().isEmpty()) {
                if (!iHint.isEmpty()) {
                    iSuggest.setText(iHint);
                    iSuggest.setStyleName("unitime-TextBoxHint");
                } else {
                    iSuggest.setStyleName("gwt-SuggestBox");
                }
            } else {
                iSuggest.setStyleName("gwt-SuggestBox");
            }
        }
        if (fireEvents)
            CourseSelectionEvent.fire(CourseSelectionSuggestBox.this, value, value != null && !value.isEmpty());
    }

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

    @Override
    public void setHint(String hint) {
        if (iSuggest.getText().equals(iHint)) {
            iSuggest.setText(hint);
            if (!hint.isEmpty())
                iSuggest.setStyleName("unitime-TextBoxHint");
        }
        iHint = hint;
    }

    @Override
    public String getHint() {
        return iHint;
    }

    @Override
    public boolean isEnabled() {
        return iSuggest.isEnabled();
    }

    @Override
    public void setEnabled(boolean enabled) {
        if (enabled) {
            iSuggest.setEnabled(true);
            iFinderButton.setEnabled(true);
            iFinderButton.setTabIndex(0);
        } else {
            iSuggest.setEnabled(false);
            iFinderButton.setEnabled(false);
            iFinderButton.setTabIndex(-1);
            if (iSaved)
                iFinderButton.setFace(RESOURCES.search_picker_Assigned());
        }
    }

    public void setSaved(boolean saved) {
        iSaved = saved;
        if (!isEnabled())
            iFinderButton.setFace(saved ? RESOURCES.search_picker_Assigned() : RESOURCES.search_picker_Disabled());
        iFinderButton.setTitle(saved ? MESSAGES.saved(getValue()) : iFinderButton.getAltText());
    }

    public boolean isSaved() {
        return iSaved;
    }

    @Override
    public void setSuggestions(DataProvider<String, Collection<CourseAssignment>> provider) {
        iDataProvider = provider;
    }

    @Override
    public void setFreeTimes(FreeTimeParser parser) {
        iFreeTimeParser = parser;
        iFreeTimeParser.setValidCourseNames(iValidCourseNames);
    }

    @Override
    public FreeTimeParser getFreeTimes() {
        return iFreeTimeParser;
    }

    public boolean isAllowFreeTime() {
        return iFreeTimeParser != null;
    }

    public class SuggestCallback implements AsyncCallback<Collection<ClassAssignmentInterface.CourseAssignment>> {
        private Request iRequest;
        private Callback iCallback;

        public SuggestCallback(Request request, Callback callback) {
            iRequest = request;
            iCallback = callback;
        }

        public void onFailure(final Throwable caught) {
            iValidCourseNames.clear();
            final ArrayList<Suggestion> suggestions = new ArrayList<Suggestion>();
            if (iFreeTimeParser != null) {
                iFreeTimeParser.getData(iRequest.getQuery(), new AsyncCallback<List<FreeTime>>() {
                    @Override
                    public void onFailure(Throwable e) {
                        if (iRequest.getQuery().toLowerCase().startsWith(CONSTANTS.freePrefix().toLowerCase())) {
                            suggestions.add(new SimpleSuggestion("<font color='red'>" + e.getMessage() + "</font>",
                                    "", e.getMessage()));
                            // setStatus(e.getMessage());
                        } else {
                            suggestions.add(
                                    new SimpleSuggestion("<font color='red'>" + caught.getMessage() + "</font>", "",
                                            caught.getMessage()));
                            // setStatus(caught.getMessage());
                        }
                    }

                    @Override
                    public void onSuccess(List<FreeTime> freeTimes) {
                        String status = "";
                        for (CourseRequestInterface.FreeTime ft : freeTimes) {
                            status += ft.toAriaString(CONSTANTS.longDays(), CONSTANTS.useAmPm()) + " ";
                        }
                        String ft = iFreeTimeParser.freeTimesToString(freeTimes);
                        Suggestion suggestion = new SimpleSuggestion(ft, ft,
                                ARIA.courseFinderSelectedFreeTime(status));
                        suggestions.add(suggestion);
                        // setStatus(status + " matches the entered text. Press enter to select it.");
                    }
                });
            } else {
                suggestions.add(new SimpleSuggestion("<font color='red'>" + caught.getMessage() + "</font>", "",
                        caught.getMessage()));
                // setStatus(caught.getMessage());
            }
            iCallback.onSuggestionsReady(iRequest, new Response(suggestions));
        }

        public void onSuccess(Collection<ClassAssignmentInterface.CourseAssignment> result) {
            ArrayList<Suggestion> suggestions = new ArrayList<Suggestion>();
            iValidCourseNames.clear();
            for (ClassAssignmentInterface.CourseAssignment suggestion : result) {
                String courseName = MESSAGES.courseName(suggestion.getSubject(), suggestion.getCourseNbr());
                String courseNameWithTitle = (suggestion.getTitle() == null ? courseName
                        : MESSAGES.courseNameWithTitle(suggestion.getSubject(), suggestion.getCourseNbr(),
                                suggestion.getTitle()));
                if (suggestion.hasUniqueName() && !iShowCourses) {
                    suggestions.add(new SimpleSuggestion(courseNameWithTitle, courseName,
                            suggestion.getTitle() == null ? courseName : courseName + " " + suggestion.getTitle()));
                    iValidCourseNames.add(courseName);
                } else {
                    suggestions.add(new SimpleSuggestion(courseNameWithTitle, courseNameWithTitle,
                            suggestion.getTitle() == null ? courseName : courseName + " " + suggestion.getTitle()));
                    iValidCourseNames.add(courseNameWithTitle);
                }
            }
            iCallback.onSuggestionsReady(iRequest, new Response(suggestions));
        }
    }

    public static class SimpleSuggestion implements Suggestion, AriaSuggestBox.HasStatus {
        private String iDisplay, iReplace, iStatus;

        public SimpleSuggestion(String display, String replace, String status) {
            iDisplay = display;
            iReplace = replace;
            iStatus = status;
        }

        public SimpleSuggestion(String display, String replace) {
            this(display, replace, display);
        }

        public SimpleSuggestion(String replace) {
            this(replace, replace, replace);
        }

        @Override
        public String getDisplayString() {
            return iDisplay;
        }

        @Override
        public String getReplacementString() {
            return iReplace;
        }

        @Override
        public String getStatusString() {
            return iStatus;
        }
    }

    @Override
    public void setCourseFinderFactory(CourseFinderFactory factory) {
        iCourseFinderFactory = factory;
    }

    @Override
    public CourseFinder getCourseFinder() {
        if (iCourseFinder == null) {
            iCourseFinder = iCourseFinderFactory.createCourseFinder();
            iCourseFinder.addSelectionHandler(new SelectionHandler<String>() {
                @Override
                public void onSelection(SelectionEvent<String> event) {
                    setValue(event.getSelectedItem(), true);
                }
            });
            if (iCourseFinder instanceof HasCloseHandlers) {
                ((HasCloseHandlers) iCourseFinder).addCloseHandler(new CloseHandler<PopupPanel>() {
                    public void onClose(CloseEvent<PopupPanel> event) {
                        Scheduler.get().scheduleDeferred(new ScheduledCommand() {
                            public void execute() {
                                iSuggest.setFocus(true);
                            }
                        });
                    }
                });
            }
        }
        return iCourseFinder;
    }

    @Override
    public void setLabel(String title, String finderTitle) {
        iSuggest.setAriaLabel(title);
        iFinderButton.setAltText(finderTitle);
    }

    @Override
    public void hideSuggestionList() {
        iSuggest.hideSuggestionList();
    }

    @Override
    public void showSuggestionList() {
        iSuggest.showSuggestionList();
    }

    @Override
    public void setWidth(String width) {
        iSuggest.setWidth(width);
    }

    @Override
    public void setError(String error) {
        if (error == null || error.isEmpty()) {
            iError.setText("");
            iError.setVisible(false);
        } else {
            iError.setText(error);
            iError.setVisible(true);
            iSuggest.setStatus(error);
            // AriaStatus.getInstance().setText(error);
        }
    }

    @Override
    public String getError() {
        return (iError.isVisible() ? iError.getText() : null);
    }

    @Override
    public void setAccessKey(char a) {
        iSuggest.setAccessKey(a);
    }

    @Override
    public int getTabIndex() {
        return iSuggest.getTabIndex();
    }

    @Override
    public void setFocus(boolean focused) {
        iSuggest.setFocus(focused);
        if (focused)
            iSuggest.getValueBox().selectAll();
    }

    @Override
    public void setTabIndex(int index) {
        iSuggest.setTabIndex(index);
    }

    @Override
    public HandlerRegistration addCourseSelectionHandler(CourseSelectionHandler handler) {
        return addHandler(handler, CourseSelectionEvent.getType());
    }

    @Override
    public boolean isFreeTime() {
        if (iFreeTimeParser == null || getValue().isEmpty())
            return false;
        try {
            iFreeTimeParser.parseFreeTime(getValue());
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }

    @Override
    public void addValidator(Validator<CourseSelection> validator) {
        iValidators.add(validator);
    }

    public String validate() {
        if (getValue().isEmpty()) {
            setError(null);
            return null;
        }
        if (iFreeTimeParser != null) {
            try {
                iFreeTimeParser.parseFreeTime(getValue());
                setError(null);
                return null;
            } catch (IllegalArgumentException e) {
                if (getValue().toLowerCase().startsWith(CONSTANTS.freePrefix().toLowerCase())) {
                    setError(MESSAGES.invalidFreeTime());
                    return e.getMessage();
                }
            }
        }
        for (Validator<CourseSelection> validator : iValidators) {
            String message = validator.validate(this);
            if (message != null) {
                setError(message);
                return message;
            }
        }
        setError(null);
        return null;
    }
}