001/* 002 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * bstefanescu 016 */ 017package org.nuxeo.shell.swing; 018 019import java.io.IOException; 020import java.util.ArrayList; 021import java.util.HashSet; 022import java.util.Iterator; 023import java.util.List; 024import java.util.Set; 025 026import javax.swing.JOptionPane; 027 028import jline.CompletionHandler; 029import jline.ConsoleReader; 030import jline.CursorBuffer; 031 032import org.nuxeo.shell.Shell; 033 034/** 035 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 036 */ 037@SuppressWarnings({ "unchecked", "rawtypes" }) 038public class SwingCompletionHandler implements CompletionHandler { 039 040 protected Console console; 041 042 public SwingCompletionHandler(Console console) { 043 this.console = console; 044 } 045 046 public boolean complete(ConsoleReader reader, List candidates, int position) throws IOException { 047 CursorBuffer buf = reader.getCursorBuffer(); 048 049 // if there is only one completion, then fill in the buffer 050 if (candidates.size() == 1) { 051 String value = candidates.get(0).toString(); 052 053 // fail if the only candidate is the same as the current buffer 054 if (value.equals(buf.toString())) { 055 console.beep(); 056 return false; 057 } 058 059 position = console.getCmdLine().setCompletionWord(value); 060 console.setCaretPosition(console.getCmdLine().getCmdStart() + position); 061 return true; 062 } else if (candidates.size() > 1) { 063 String value = getUnambiguousCompletions(candidates); 064 position = console.getCmdLine().setCompletionWord(value); 065 } else if (candidates.isEmpty()) { 066 console.beep(); 067 return false; 068 } 069 String text = console.getCmdLine().getText(); 070 console.append("\n"); 071 printCandidates(candidates); 072 console.append("\n"); 073 console.append(Shell.get().getActiveRegistry().getPrompt(Shell.get())); 074 console.cline = null; 075 console.getCmdLine().setText(text); 076 console.setCaretPosition(console.getCmdLine().getCmdStart() + position); 077 078 return true; 079 } 080 081 /** 082 * Returns a root that matches all the {@link String} elements of the specified {@link List}, or null if there are 083 * no commalities. For example, if the list contains <i>foobar</i>, <i>foobaz</i>, <i>foobuz</i>, the method will 084 * return <i>foob</i>. 085 */ 086 private final String getUnambiguousCompletions(final List candidates) { 087 if ((candidates == null) || (candidates.size() == 0)) { 088 return null; 089 } 090 091 // convert to an array for speed 092 String[] strings = (String[]) candidates.toArray(new String[candidates.size()]); 093 094 String first = strings[0]; 095 StringBuffer candidate = new StringBuffer(); 096 097 for (int i = 0; i < first.length(); i++) { 098 if (startsWith(first.substring(0, i + 1), strings)) { 099 candidate.append(first.charAt(i)); 100 } else { 101 break; 102 } 103 } 104 105 return candidate.toString(); 106 } 107 108 /** 109 * @return true is all the elements of <i>candidates</i> start with <i>starts</i> 110 */ 111 private final boolean startsWith(final String starts, final String[] candidates) { 112 for (int i = 0; i < candidates.length; i++) { 113 if (!candidates[i].startsWith(starts)) { 114 return false; 115 } 116 } 117 118 return true; 119 } 120 121 protected void printCandidates(List<String> candidates) { 122 Set<String> distinct = new HashSet<String>(candidates); 123 124 if (distinct.size() > console.reader.getAutoprintThreshhold()) { 125 // if (!eagerNewlines) 126 if (JOptionPane.showConfirmDialog(console, "Display all " + distinct.size() + " possibilities? (y or n) ", 127 "Completion Warning", JOptionPane.YES_NO_OPTION) == 1) { 128 return; 129 } 130 } 131 132 // copy the values and make them distinct, without otherwise 133 // affecting the ordering. Only do it if the sizes differ. 134 if (distinct.size() != candidates.size()) { 135 List<String> copy = new ArrayList<String>(); 136 137 for (Iterator<String> i = candidates.iterator(); i.hasNext();) { 138 String next = i.next(); 139 140 if (!(copy.contains(next))) { 141 copy.add(next); 142 } 143 } 144 145 candidates = copy; 146 } 147 148 console.printColumns(candidates); 149 150 } 151}