/* * This file is part of lanterna (http://code.google.com/p/lanterna/). * * lanterna is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Copyright (C) 2010-2012 Martin */ package com.googlecode.lanterna.input; import com.googlecode.lanterna.LanternaException; import com.googlecode.lanterna.terminal.TerminalPosition; import java.io.IOException; import java.io.Reader; import java.util.*; /** * Used to read the input stream character by character and generate {@code Key} * objects to be put in the input queue. * @author Martin */ public class InputDecoder { private final Reader source; private final Queue<Character> inputBuffer; private final Queue<Character> leftOverQueue; private final Set<CharacterPattern> bytePatterns; private final List<Character> currentMatching; private TerminalPosition lastReportedTerminalPosition; public InputDecoder(final Reader source) { this.source = source; this.inputBuffer = new LinkedList<Character>(); this.leftOverQueue = new LinkedList<Character>(); this.bytePatterns = new HashSet<CharacterPattern>(); this.currentMatching = new ArrayList<Character>(); this.lastReportedTerminalPosition = null; } public InputDecoder(final Reader source, final KeyMappingProfile profile) { this(source); addProfile(profile); } public void addProfile(KeyMappingProfile profile) { for(CharacterPattern pattern: profile.getPatterns()) bytePatterns.add(pattern); } public Key getNextCharacter() { if(leftOverQueue.size() > 0) { Character first = leftOverQueue.poll(); //HACK!!! if(first == 0x1b) return new Key(Key.Kind.Escape); return new Key(first.charValue()); } try { while(source.ready()) { int readChar = source.read(); if(readChar == -1) return null; inputBuffer.add((char)readChar); } } catch(IOException e) { throw new LanternaException(e); } if(inputBuffer.size() == 0) return null; while(true) { //Check all patterns Character nextChar = inputBuffer.poll(); boolean canMatchWithOneMoreChar = false; if(nextChar != null) { currentMatching.add(nextChar); for(CharacterPattern pattern: bytePatterns) { if(pattern.matches(currentMatching)) { if(pattern.isCompleteMatch(currentMatching)) { Key result = pattern.getResult(currentMatching); if(result.getKind() == Key.Kind.CursorLocation) lastReportedTerminalPosition = ScreenInfoCharacterPattern.getCursorPosition(currentMatching); currentMatching.clear(); return result; } else canMatchWithOneMoreChar = true; } } } if(!canMatchWithOneMoreChar) { for(Character c: currentMatching) leftOverQueue.add(c); currentMatching.clear(); Character first = leftOverQueue.poll(); //HACK!!! if(first == 0x1b) return new Key(Key.Kind.Escape); return new Key(first.charValue()); } } } public TerminalPosition getLastReportedTerminalPosition() { return lastReportedTerminalPosition; } }