/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation * Semantum Oy - index based searching (#4255) *******************************************************************************/ package org.simantics.debug.ui; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.Statement; import org.simantics.db.common.request.Queries; import org.simantics.db.common.request.ReadRequest; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.request.Read; import org.simantics.layer0.Layer0; import org.simantics.utils.strings.EString; import org.simantics.utils.threads.IThreadWorkQueue; public class ResourceSearch extends ReadRequest { public static final IResourceFilter FILTER_ALL = new IResourceFilter() { @Override public boolean acceptResource(ReadGraph g, Resource r) { return true; } }; public static final IResourceFilter FILTER_RELATIONS = new IResourceFilter() { @Override public boolean acceptResource(ReadGraph g, Resource r) throws DatabaseException { return g.isInstanceOf(r, Layer0.getInstance(g).Relation); } }; public static final IResourceFilter FILTER_TYPES = new IResourceFilter() { @Override public boolean acceptResource(ReadGraph g, Resource r) throws DatabaseException { Layer0 L0 = Layer0.getInstance(g); return g.isInstanceOf(r, L0.Type) && !g.isInstanceOf(r, L0.Relation); } }; /** * Create filter that matches URI, ID and NAME using wildcard compare * @param txt * @return */ public static final IResourceFilter createFilter(String txt) { final Pattern p = EString.compileSimplePattern(txt); return new IResourceFilter() { @Override public boolean acceptResource(ReadGraph g, Resource r) { try { String uri = g.syncRequest(Queries.possibleUri(r)); if (uri != null && p.matcher(uri).matches()) return true; } catch (Throwable t) {} try { String name = NameUtils.getSafeName(g, r); if (p.matcher(name).matches()) return true; } catch (Throwable t) {} String id = Long.toString(r.getResourceId()); if (p.matcher(id).matches()) return true; return false; } }; } public interface IResourceFilter { boolean acceptResource(ReadGraph g, Resource r) throws DatabaseException; } public interface SearchListener { /** * * @param s * @param r * @param g graph if listening is not async, if so then null */ void onResourceFound(Read s, Collection r, ReadGraph g) throws DatabaseException; void onSearchComplete(Read s); void onError(Read s, Throwable e); } boolean canceled = false; IResourceFilter filter; SearchListener listener; IThreadWorkQueue listenerThread; ResFoundQueue resFoundQueue; boolean asyncListening; public ResourceSearch(IResourceFilter f, SearchListener l, IThreadWorkQueue listenerThread, boolean asyncListening) { assert(f!=null && l!=null); this.filter = f; this.listener = l; this.listenerThread = listenerThread; this.asyncListening = asyncListening; if (listenerThread!=null) resFoundQueue = new ResFoundQueue(); } public void cancel() { canceled = true; } public boolean isCanceled() { return canceled; } private class ResFoundQueue implements Runnable { ReadGraph g; List list = new ArrayList(); /** * add res to queue * @param r * @return true if other resources are still unhandled */ synchronized boolean addResource(Resource r) { list.add(r); return list.size()>1; } @Override public void run() { try { Collection l = null; synchronized(this) { if (list.size()==0) return; if (list.size()<10) { listener.onResourceFound(ResourceSearch.this, list, g); list.clear(); return; } l = new ArrayList(list); list.clear(); } listener.onResourceFound(ResourceSearch.this, l, g); } catch (DatabaseException e) { } } } @Override public void run(ReadGraph g) { try { if (!asyncListening && resFoundQueue!=null) resFoundQueue.g = g; Resource root = g.getResource("http:/"); Layer0 L0 = Layer0.getInstance(g); LinkedList queue = new LinkedList(); queue.add(root); Set queued = new HashSet(); while(!queue.isEmpty() && !canceled) { Resource r = queue.removeFirst(); if (filter.acceptResource(g, r)) { if (listenerThread==null) listener.onResourceFound(ResourceSearch.this, Collections.singletonList(r), g); else { if (!resFoundQueue.addResource(r)) { if (asyncListening) listenerThread.asyncExec(resFoundQueue); else listenerThread.syncExec(resFoundQueue); } } } for (Statement stm : g.getStatements(r, L0.IsWeaklyRelatedTo)) { Resource n = stm.getPredicate(); if (!queued.contains(n)) { queue.add(n); queued.add(n); } n = stm.getObject(); if (!queued.contains(n)) { queue.add(n); queued.add(n); } } if (listenerThread==null) listener.onSearchComplete(ResourceSearch.this); else { Runnable run = new Runnable() { @Override public void run() { ResourceSearch.this.listener.onSearchComplete(ResourceSearch.this); }}; if (asyncListening) listenerThread.asyncExec(run); else listenerThread.syncExec(run); } } } catch (final Throwable e) { if (listenerThread==null) listener.onError(ResourceSearch.this, e); else { Runnable r = new Runnable() { @Override public void run() { listener.onError(ResourceSearch.this, e); }}; if (asyncListening) listenerThread.asyncExec(r); else listenerThread.syncExec(r); } } } }