// Copyright 2017 Google 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 codeu.chat.client.commandline; import java.lang.Math; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Scanner; import codeu.chat.common.ListViewable; // Page up and down through a list of objects and allow user to select one. // T must implement ListViewable. This gives access to listView(), which produces // the human-readable string that identifies the object in the list. public final class ListNavigator<T extends ListViewable> { private final List<T> selection = new ArrayList<T>(); private final Scanner lineScanner; private int top; private int bottom; private int pageSize; private boolean hasInt; private int intValue; private String choice; // Creates a ListNavigator of the desired ListViewable subclass public ListNavigator(Iterable<T> objectList, Scanner scanner, int pageSize) { this.pageSize = pageSize; this.lineScanner = scanner; for (final T e : objectList) { this.selection.add(e); } moveUp(); // set top and bottom for first page. } // Run the chooser. Returns true when a selection has been made, or false on an error // or if the user cancels the operation. public boolean chooseFromList() { if (selection.size() == 0) { System.out.println("ERROR: selection is empty - cannot select"); return false; } else { while (true) { displayChoices(); issuePrompt(); getChoice(); if (indexSelected()) { return true; } else if (moveDownSelected()) { moveDown(); } else if (moveUpSelected()) { moveUp(); } else if (cancelSelected()) { return false; } else { System.out.println("Poor choice - try again."); } } } } // Get the selectes object from the chooser. // Should be called after chooseFromList returns true (otherwise returns null). public T getSelectedChoice() { return (hasInt) ? selection.get(intValue - 1) : null; } // Print a prompt that tells the user their options: // 1) <n> the index of a list item that is currently on view. Return the object. // 2) '+' page down // 3) '-' page back up // 4) '*' cancel (return null) private void issuePrompt() { System.out.format("Enter index (%s)%s%s or '*' to cancel: ", (top != bottom) ? String.format("%d-%d", top, bottom) : String.format("%d", top), (canMoveUp()) ? " or '-' to back up" : "", (canMoveDown()) ? " or '+' to advance" : ""); } // Display a set of entries with index numbers. // Number of entries displayed is determined by pageSize. private void displayChoices() { for (int i = top; i <= bottom; i++) { System.out.format(" [%d]: %s\n", i, selection.get(i - 1).listView()); } } // Process user's response to issuePrompt() prompt. private void getChoice() { final Scanner tokenScanner = new Scanner(lineScanner.nextLine()); choice = tokenScanner.nextLine().trim(); try { intValue = Integer.parseInt(choice); hasInt = true; } catch (NumberFormatException ex) { hasInt = false; } tokenScanner.close(); } // Unseen entries precede the current view. private boolean canMoveUp() { return top > 1; } // Unseen entries follow the current view. private boolean canMoveDown() { return bottom < selection.size(); } private boolean indexSelected() { return hasInt && (intValue >= top) && (intValue <= bottom); } private final boolean moveDownSelected() { return choice.equals("+") && canMoveDown(); } private final boolean moveUpSelected() { return choice.equals("-") && canMoveUp(); } private final boolean cancelSelected() { return choice.equals("*"); } // Move the view up one page (or to top of page). private void moveUp() { top = Math.max(top - pageSize, 1); bottom = Math.min(top + pageSize - 1, selection.size()); } // Move the view down one page (or to bottom of page). private void moveDown() { if (selection.size() == 0) return; bottom = Math.min(bottom + pageSize, selection.size()); top = bottom - pageSize + 1; } }