/** * Copyright (c) 2010, 2012 Darmstadt University of Technology. * 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 * * Contributors: * Marcel Bruch - initial API and implementation. */ package org.eclipse.recommenders.internal.subwords.rcp; import static java.lang.Character.*; import java.util.Arrays; import java.util.Iterator; import java.util.List; import com.google.common.collect.Lists; public class SequenceFinder { private static final int[] EMPTY_SEQUENCE = new int[0]; private List<int[]> curSequences = Lists.newLinkedList(); private List<int[]> nextSequences = Lists.newLinkedList(); private int pCompletion, pToken; private String completion, token; public SequenceFinder(String completion, String token) { this.completion = completion; this.token = token; } public List<int[]> findSeqeuences() { if (isConstantName(completion)) { rewriteCompletion(); } int[] start = EMPTY_SEQUENCE; curSequences.add(start); for (pToken = 0; pToken < token.length(); pToken++) { char t = token.charAt(pToken); for (int[] activeSequence : curSequences) { boolean mustmatch = false; int startIndex = activeSequence.length == 0 ? 0 : activeSequence[activeSequence.length - 1] + 1; for (pCompletion = startIndex; pCompletion < completion.length(); pCompletion++) { char c = completion.charAt(pCompletion); if (!Character.isLetter(c)) { if (c == t) { addNewSubsequenceForNext(activeSequence); continue; } mustmatch = true; continue; } else if (Character.isUpperCase(c)) { mustmatch = true; } if (mustmatch && !isSameIgnoreCase(c, t)) { jumpToEndOfWord(); } else if (isSameIgnoreCase(c, t)) { addNewSubsequenceForNext(activeSequence); } } } curSequences = nextSequences; nextSequences = Lists.newLinkedList(); } // filter for (Iterator<int[]> it = curSequences.iterator(); it.hasNext();) { int[] candidate = it.next(); if (candidate.length < token.length()) { it.remove(); continue; } } return curSequences; } private void addNewSubsequenceForNext(int[] activeSequence) { int[] copy = Arrays.copyOf(activeSequence, activeSequence.length + 1); copy[pToken] = pCompletion; nextSequences.add(copy); } private void rewriteCompletion() { StringBuilder sb = new StringBuilder(); boolean toUpperCase = false; for (char c : completion.toCharArray()) { if (Character.isLetterOrDigit(c)) { sb.append(toUpperCase ? Character.toUpperCase(c) : Character.toLowerCase(c)); toUpperCase = false; } else { sb.append(c); toUpperCase = true; } } completion = sb.toString(); } private void jumpToEndOfWord() { for (pCompletion++; pCompletion < completion.length(); pCompletion++) { char next = completion.charAt(pCompletion); if (!isLetter(next)) { // . or _ word boundary found: // XXX numbers are also considered as word boundaries then. this may cause some trouble... break; } if (isUpperCase(next)) { pCompletion--; break; } } } private boolean isConstantName(String completion) { for (char c : completion.toCharArray()) { if (Character.isLetter(c) && Character.isLowerCase(c)) { return false; } } return true; } private boolean isSameIgnoreCase(char c1, char c2) { if (c1 == c2) { return true; } c2 = isLowerCase(c2) ? toUpperCase(c2) : toLowerCase(c2); return c1 == c2; } }