/******************************************************************************* * Copyright (c) 2012 xored software, Inc. 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: * xored software, Inc. - initial API and implementation (Yuri Strot) ******************************************************************************/ package com.xored.glance.internal.ui.search; import com.xored.glance.internal.ui.search.SearchJob.ISearchMonitor; import com.xored.glance.ui.sources.ConfigurationManager; import com.xored.glance.ui.sources.ITextBlock; import com.xored.glance.ui.sources.ITextSource; import com.xored.glance.ui.sources.Match; import java.util.regex.Matcher; /** * @author Yuri Strot */ public class SearchEngine extends Thread { private class SearchScope extends AbstractSearchScope implements ISearchMonitor { private boolean firstFound = false; /** * @param source */ public SearchScope(ITextSource source) { super(source); } @Override public void added(SearchScopeEntry entry, Match match) { if (!firstFound) { if (ConfigurationManager.getInstance().incremenstalSearch()) { select(match); } listener.firstFound(match); firstFound = true; } } @Override public void blocksChanged(ITextBlock[] removed, ITextBlock[] added) { synchronized (monitor) { super.blocksChanged(removed, added); cancel = true; interrupt(); } } @Override public void blocksReplaced(ITextBlock[] newBlocks) { synchronized (monitor) { super.blocksReplaced(newBlocks); cancel = true; interrupt(); } } @Override public void cleared(SearchScopeEntry entry) { synchronized (monitor) { cancel = true; interrupt(); } } public SearchJob getJob() { synchronized (monitor) { while (currentEntry < entries.size()) { SearchJob job = (SearchJob) entries.get(currentEntry); if (!job.isFinished()) { return job; } currentEntry++; } return updateEntry(); } } @Override public Match[] getMatches() { synchronized (monitor) { return super.getMatches(); } } @Override public boolean isCanceled() { synchronized (monitor) { return cancel; } } @Override public void select(Match match) { if (match != null) { super.select(match); listener.setMatchIndex(match.getIndex()); } } public void updateResult() { if (!firstFound) { listener.firstFound(null); firstFound = true; } listener.allFound(getMatches()); } @Override protected SearchScopeEntry createEntry(ITextBlock block) { return new SearchJob(block, matcher, this); } protected void updateMatcher(boolean findFirst) { firstFound = !findFirst; for (SearchScopeEntry entry : entries) { SearchJob job = (SearchJob) entry; job.update(matcher); } updateStart(); } private SearchJob updateEntry() { currentEntry = 0; for (SearchScopeEntry entry : entries) { SearchJob job = (SearchJob) entry; if (!job.isFinished()) { return job; } currentEntry++; } currentEntry = 0; return null; } } private static final long PAUSE = 100 * 5;// 100 s private boolean cancel; private SearchRule rule; private Matcher matcher; private Object monitor = new Object(); private boolean exit; private boolean paused; private SearchScope scope; private ISearchListener listener; public SearchEngine(ISearchListener listener) { this.listener = listener; } public void exit() { synchronized (monitor) { exit = true; interrupt(); } } @Override public void run() { while (!exit) { sleepWhileInterrupted(0); if (scope == null) { continue; } if (exit) { break; } while (true) { long start = System.currentTimeMillis(); if (!findMatches()) { continue; } long waitPause = paused ? PAUSE - (System.currentTimeMillis() - start) : 0; if (waitPause > 0) { sleepWhileInterrupted(waitPause); } if (!scope.isCanceled()) { break; } } listener.finished(); scope.showMatches(); } } public void selectNext() { if (scope != null) { scope.selectNext(); } } public void selectPrev() { if (scope != null) { scope.selectPrev(); } } public void setRule(SearchRule rule) { synchronized (monitor) { doSetRule(rule); } } public void setSource(SearchRule rule, ITextSource source, boolean paused) { synchronized (monitor) { if (scope != null) { scope.dispose(); scope = null; } scope = new SearchScope(source); this.paused = paused; doSetRule(rule); } } public void updateSourceSelection() { scope.updateSourceSelection(); } protected void doSetRule(SearchRule rule) { cancel = true; boolean findFirst = false; if (this.rule == null || !this.rule.equals(rule)) { this.rule = rule; findFirst = true; matcher = rule.getText().length() == 0 ? null : rule.getPattern().matcher(new String()); } if (scope != null) { scope.updateMatcher(findFirst); } interrupt(); } private boolean findMatches() { cancel = false; if (matcher == null) { scope.showEmptyText(); return true; } SearchJob job = null; while ((job = scope.getJob()) != null) { if (!job.run()) { return false; } } scope.updateResult(); return true; } private void sleepWhileInterrupted(long millis) { try { if (millis == 0) { while (true) { Thread.sleep(50); } } else { Thread.sleep(millis); } } catch (InterruptedException e1) { } } }