/*******************************************************************************
* Copyright (c) 2009 Fraunhofer IWU 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
*
* Contributors:
* Fraunhofer IWU - initial API and implementation
*******************************************************************************/
package net.enilink.komma.edit.ui.provider.reflective;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import net.enilink.commons.iterator.IExtendedIterator;
import net.enilink.commons.iterator.NiceIterator;
import net.enilink.komma.core.IEntityManager;
import net.enilink.komma.core.IQuery;
import net.enilink.komma.core.IReference;
import net.enilink.komma.core.IStatement;
import net.enilink.komma.core.IStatementPattern;
import net.enilink.komma.core.IValue;
import net.enilink.komma.core.Statements;
import net.enilink.komma.edit.provider.ISearchableItemProvider;
import net.enilink.komma.edit.provider.SparqlSearchableItemProvider;
import net.enilink.komma.model.IModel;
import net.enilink.komma.model.IObject;
import org.eclipse.jface.viewers.AbstractTableViewer;
import org.eclipse.jface.viewers.ColumnViewer;
import org.eclipse.jface.viewers.IIndexableLazyContentProvider;
import org.eclipse.jface.viewers.ILazyContentProvider;
import org.eclipse.jface.viewers.ILazyTreeContentProvider;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
public class StatementPatternContentProvider extends ModelContentProvider
implements IStructuredContentProvider, ILazyContentProvider,
IIndexableLazyContentProvider, ILazyTreeContentProvider,
ISearchableItemProvider {
protected Set<IStatementPattern> patterns = new HashSet<IStatementPattern>();
protected Object[] instanceReferences;
protected Map<Object, Integer> instanceToIndex;
protected Viewer viewer;
protected boolean isVirtualViewer;
protected IReference descendantProperty;
public void setDescendantProperty(IReference descendantProperty) {
this.descendantProperty = descendantProperty;
}
@Override
protected boolean addedStatement(IStatement stmt,
Collection<Runnable> runnables) {
for (IStatementPattern pattern : patterns) {
if (Statements.matchesIgnoreContext(stmt, pattern)) {
postRefresh(runnables);
return false;
}
}
doUpdate(stmt, runnables);
return true;
}
protected void doUpdate(IStatement stmt, Collection<Runnable> runnables) {
if (instanceToIndex != null) {
int index = findElement(stmt.getSubject());
if (index >= 0) {
postRefresh(Arrays.asList(stmt.getSubject()), true, runnables);
}
index = findElement(stmt.getPredicate());
if (index >= 0) {
postRefresh(Arrays.asList(stmt.getPredicate()), true, runnables);
}
index = findElement(stmt.getObject());
if (index >= 0) {
postRefresh(Arrays.asList(stmt.getObject()), true, runnables);
}
}
}
@Override
public IExtendedIterator<?> find(Object expression, Object parent, int limit) {
IExtendedIterator<Object> results = NiceIterator.emptyIterator();
if (!patterns.isEmpty()) {
SparqlSearchableItemProvider searchableProvider = new SparqlSearchableItemProvider() {
@Override
protected IEntityManager getEntityManager(Object parent) {
return model.getManager();
}
@Override
protected String getQueryFindPatterns(Object parent) {
StringBuilder sb = getTriplePatterns(descendantProperty != null ? "?root"
: "?s");
if (descendantProperty != null) {
sb.append(" ?root ?descendantProperty ?s .");
}
return sb.toString();
}
@Override
protected void setQueryParameters(IQuery<?> query, Object parent) {
setParameters(query);
}
};
results = results.andThen(searchableProvider.find(expression, null,
10));
}
return results;
}
protected StringBuilder getTriplePatterns(String subject) {
StringBuilder sb = new StringBuilder();
int i = 0;
for (IStatementPattern pattern : patterns) {
sb.append("{ ");
if (pattern.getSubject() != null) {
sb.append("?s").append(i);
} else {
sb.append(subject);
}
sb.append(' ');
if (pattern.getPredicate() != null) {
sb.append("?p").append(i);
} else {
sb.append(subject);
}
sb.append(' ');
if (pattern.getObject() != null) {
sb.append("?o").append(i);
} else {
sb.append(subject);
}
sb.append(" }");
i++;
if (i < patterns.size()) {
sb.append(" UNION ");
}
}
return sb;
}
protected void setParameters(IQuery<?> query) {
int i = 0;
for (IStatementPattern pattern : patterns) {
if (pattern.getSubject() != null) {
query.setParameter("s" + i, pattern.getSubject());
}
if (pattern.getPredicate() != null) {
query.setParameter("p" + i, pattern.getPredicate());
}
if (pattern.getObject() != null) {
query.setParameter("o" + i, pattern.getObject());
}
i++;
}
if (descendantProperty != null) {
query.setParameter("descendantProperty", descendantProperty);
}
}
protected IQuery<?> createQuery() {
final IEntityManager em = model.getManager();
StringBuilder querySb = new StringBuilder();
querySb.append("SELECT ?s WHERE { ");
querySb.append(getTriplePatterns("?s"));
if (descendantProperty != null) {
querySb.append(" FILTER NOT EXISTS { ");
querySb.append(getTriplePatterns("?other"));
querySb.append(" ?other ?descendantProperty ?s");
querySb.append(" }");
}
querySb.append(" } ORDER BY ?s");
IQuery<?> query = em.createQuery(querySb.toString());
setParameters(query);
return query;
}
public Object[] getElements(Object inputElement) {
if (!patterns.isEmpty()) {
return createQuery().evaluate().toList().toArray();
}
return new Object[0];
}
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
instanceToIndex = null;
instanceReferences = null;
patterns.clear();
if (newInput instanceof IStatementPattern) {
patterns.add((IStatementPattern) newInput);
} else if (newInput != null && newInput.getClass().isArray()) {
Object[] array = (Object[]) newInput;
for (Object element : array) {
patterns.add((IStatementPattern) element);
}
}
this.viewer = viewer;
this.isVirtualViewer = this.viewer instanceof ColumnViewer
&& (this.viewer.getControl().getStyle() & SWT.VIRTUAL) != 0;
IModel newModel = getModelFromPatterns(patterns);
super.inputChanged(viewer, this.model, newModel);
// virtual table viewers do not ask for element count
if (newInput != null && viewer instanceof AbstractTableViewer
&& (viewer.getControl().getStyle() & SWT.VIRTUAL) != 0) {
updateChildCount(newInput, -1);
}
}
protected IModel getModelFromPatterns(Collection<IStatementPattern> patterns) {
for (IStatementPattern pattern : patterns) {
if (pattern.getContext() instanceof IModel) {
return (IModel) pattern.getContext();
}
if (pattern.getSubject() instanceof IObject) {
return ((IObject) pattern.getSubject()).getModel();
}
if (pattern.getObject() instanceof IObject) {
return ((IObject) pattern.getObject()).getModel();
}
if (pattern.getPredicate() instanceof IObject) {
return ((IObject) pattern.getPredicate()).getModel();
}
}
return null;
}
@Override
protected boolean removedStatement(IStatement stmt,
Collection<Runnable> runnables) {
// TODO
// maybe broken if reasoner is used ...
// listener gets not notified if inferred statements are removed
for (IStatementPattern pattern : patterns) {
if (Statements.matchesIgnoreContext(stmt, pattern)) {
postRefresh(runnables);
return false;
}
}
doUpdate(stmt, runnables);
return true;
}
@Override
protected boolean shouldRegisterListener(Viewer viewer) {
return this.viewer != null && !patterns.isEmpty();
}
protected Object resolve(Object value) {
if (value instanceof IValue) {
return model.getManager().toInstance((IValue) value);
}
return value;
}
@Override
public void updateElement(int index) {
if (instanceReferences != null && index < instanceReferences.length) {
((AbstractTableViewer) viewer).replace(
resolve(instanceReferences[index]), index);
}
}
@Override
public void updateElement(Object parent, int index) {
Object element = resolve(instanceReferences[index]);
((TreeViewer) viewer).replace(parent, index, element);
updateChildCount(element, -1);
}
@Override
public void updateChildCount(Object element, int currentChildCount) {
Collection<Object> values = new LinkedHashSet<Object>();
if (model != null && !patterns.isEmpty()) {
values.addAll(createQuery().evaluateRestricted(IReference.class)
.toList());
}
instanceReferences = values.toArray();
instanceToIndex = new HashMap<Object, Integer>(
instanceReferences.length);
int i = 0;
for (Object instance : instanceReferences) {
instanceToIndex.put(instance, i++);
}
if (viewer instanceof TreeViewer) {
((TreeViewer) viewer).setChildCount(element,
instanceReferences.length);
} else if (viewer instanceof AbstractTableViewer) {
((AbstractTableViewer) viewer)
.setItemCount(instanceReferences.length);
}
}
@Override
protected void postRefresh(Collection<Runnable> runnables) {
// correctly refresh elements in case of virtual viewer
if (isVirtualViewer) {
// remove all previous update operations
runnables.clear();
runnables.add(new Runnable() {
public void run() {
updateChildCount(viewer.getInput(), -1);
((StructuredViewer) viewer).refresh();
// make sure no other update operations are executed after
// this
// one
executedFullRefresh = true;
}
});
} else {
super.postRefresh(runnables);
}
}
@Override
public Object getParent(Object element) {
// TODO find a reasonable pattern based implementation for this method
return null;
}
@Override
public int findElement(Object element) {
if (instanceToIndex == null) {
return -1;
}
Integer index = instanceToIndex.get(element);
return index != null ? index : -1;
}
}