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