/* * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.controller.netconf.cli.io; import static org.opendaylight.controller.netconf.cli.io.IOUtil.PATH_SEPARATOR; import static org.opendaylight.controller.netconf.cli.io.IOUtil.PROMPT_SUFIX; import com.google.common.base.Optional; import com.google.common.collect.Lists; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Deque; import java.util.Iterator; import jline.console.ConsoleReader; import jline.console.completer.Completer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Jline based IO implementation */ public class ConsoleIOImpl implements ConsoleIO { private static final Logger LOG = LoggerFactory.getLogger(ConsoleIOImpl.class); private final ConsoleReader console; private final Deque contexts = new ArrayDeque<>(); public ConsoleIOImpl() throws IOException { console = new ConsoleReader(); console.setHandleUserInterrupt(true); console.setPaginationEnabled(true); console.setHistoryEnabled(true); // TODO trifferedActions not supported by jline in current version // https://github.com/jline/jline2/issues/149 console.addTriggeredAction('?', new QuestionMarkActionListener()); } @Override public String read() throws IOException { return console.readLine().trim(); } @Override public String read(final Character mask) throws IOException { return console.readLine(mask).trim(); } @Override public void write(final CharSequence data) throws IOException { console.print(data); console.flush(); } @Override public void writeLn(final CharSequence data) throws IOException { console.println(data); console.flush(); } @Override public void formatLn(final String format, final Object... args) throws IOException { console.println(String.format(format, args)); console.flush(); } @Override public void enterContext(final ConsoleContext consoleContext) { contexts.push(consoleContext); enterCtx(consoleContext); } @Override public void enterRootContext(final ConsoleContext consoleContext) { contexts.addLast(consoleContext); enterCtx(consoleContext); } private void enterCtx(final ConsoleContext consoleContext) { setCompleter(consoleContext.getCompleter()); console.setPrompt(buildPrompt()); } @Override public void leaveContext() { contexts.pollFirst(); leaveCtx(); } @Override public void leaveRootContext() { contexts.pollLast(); leaveCtx(); } private void leaveCtx() { console.setPrompt(buildPrompt()); if (contexts.peek() != null) { setCompleter(contexts.peek().getCompleter()); } } protected String buildPrompt() { final StringBuilder newPrompt = new StringBuilder(); final Iterator descendingIterator = contexts.descendingIterator(); while (descendingIterator.hasNext()) { final ConsoleContext consoleContext = descendingIterator.next(); final Optional promptPart = consoleContext.getPrompt(); if (promptPart.isPresent()) { newPrompt.append(PATH_SEPARATOR); newPrompt.append(promptPart.get()); } } if (newPrompt.length() ==0) { newPrompt.append(PATH_SEPARATOR); } newPrompt.append(PROMPT_SUFIX); return newPrompt.toString(); } private void setCompleter(final Completer newCompleter) { for (final Completer concreteCompleter : console.getCompleters()) { console.removeCompleter(concreteCompleter); } console.addCompleter(newCompleter); } private class QuestionMarkActionListener implements ActionListener { @Override public void actionPerformed(final ActionEvent e) { ConsoleIOImpl.this.complete(); } } public void complete() { final ArrayList candidates = Lists.newArrayList(); contexts.peek().getCompleter().complete("", 0, candidates); try { console.getCompletionHandler().complete(console, candidates, 0); } catch (final IOException ex) { throw new IllegalStateException("Unable to write to output", ex); } } }