package net.enilink.komma.edit.properties;
import java.util.Iterator;
import net.enilink.komma.common.adapter.IAdapterFactory;
import net.enilink.komma.common.command.CommandResult;
import net.enilink.komma.common.command.CompositeCommand;
import net.enilink.komma.common.command.ICommand;
import net.enilink.komma.common.command.ICompositeCommand;
import net.enilink.komma.core.IEntity;
import net.enilink.komma.core.IEntityManager;
import net.enilink.komma.core.ILiteral;
import net.enilink.komma.core.IReference;
import net.enilink.komma.core.IStatement;
import net.enilink.komma.core.ITransaction;
import net.enilink.komma.core.Statement;
import net.enilink.komma.core.URI;
import net.enilink.komma.core.URIs;
import net.enilink.komma.edit.command.CommandParameter;
import net.enilink.komma.edit.domain.AdapterFactoryEditingDomain;
import net.enilink.komma.edit.domain.IEditingDomain;
import net.enilink.komma.edit.util.PropertyUtil;
import net.enilink.komma.em.concepts.IClass;
import net.enilink.komma.em.concepts.IProperty;
import net.enilink.komma.em.concepts.IResource;
import net.enilink.komma.em.util.ISparqlConstants;
import net.enilink.vocab.owl.DatatypeProperty;
import net.enilink.vocab.owl.OWL;
import net.enilink.vocab.rdfs.RDFS;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
/**
* Helper class to simplify the usage of {@link IEditingSupport} implementations
* that are supplied by an adapter factory.
*/
public abstract class EditingHelper {
public static URI NULL_URI = URIs.createURI("komma:null");
public static enum Type {
PROPERTY, VALUE, LITERAL_LANG_TYPE
}
protected IAdapterFactory delegatingAdapterFactory = new IAdapterFactory() {
@Override
public Object adapt(Object object, Object type) {
IAdapterFactory factory = getAdapterFactory();
return factory == null ? null : getAdapterFactory().adapt(object,
type);
}
@Override
public boolean isFactoryForType(Object type) {
IAdapterFactory factory = getAdapterFactory();
return factory == null ? false : getAdapterFactory()
.isFactoryForType(type);
}
};
protected final Type type;
public EditingHelper(Type type) {
this.type = type;
}
public boolean canEdit(Object element) {
// forbid changing the predicate of existing statements
if (type == Type.PROPERTY) {
return element instanceof IStatement
&& ((IStatement) element).getObject() == null;
}
if (element instanceof IStatement
&& (((IStatement) element).getPredicate() == null || ((IStatement) element)
.isInferred())) {
return false;
}
IEditingSupport editingSupport = getEditingSupport(element);
if (editingSupport != null) {
return editingSupport.canEdit(element);
}
return false;
}
protected IAdapterFactory getAdapterFactory() {
IEditingDomain editingDomain = getEditingDomain();
if (editingDomain instanceof AdapterFactoryEditingDomain) {
return ((AdapterFactoryEditingDomain) editingDomain)
.getAdapterFactory();
}
return null;
}
/**
* Returns the editing domain for executing commands.
*/
abstract protected IEditingDomain getEditingDomain();
protected IEditingSupport getEditingSupport(Object element) {
if (element instanceof IStatement) {
IStatement stmt = (IStatement) element;
// use the resource editor for predicates
if (type == Type.PROPERTY) {
return new ResourceEditingSupport(delegatingAdapterFactory,
true);
} else if (type == Type.LITERAL_LANG_TYPE) {
return new LiteralLangTypeEditingSupport(
delegatingAdapterFactory);
}
return getEditingSupport(delegatingAdapterFactory,
(IEntity) stmt.getSubject(), stmt.getPredicate(),
stmt.getObject());
}
return null;
}
public IProposalSupport getProposalSupport(Object element) {
IEditingSupport editingSupport = getEditingSupport(element);
if (editingSupport != null) {
return editingSupport.getProposalSupport(element);
}
return null;
}
public Object getValue(Object element) {
IEditingSupport editingSupport = getEditingSupport(element);
if (editingSupport != null) {
return editingSupport.getEditorValue(element);
}
return null;
}
/**
* Assigns a property to an element if operating in property editing mode.
*/
protected void setProperty(Object element, IProperty property) {
}
protected Object unwrap(Object element) {
if (element instanceof IStatement) {
IStatement stmt = (IStatement) element;
return new Statement(stmt.getSubject(), stmt.getPredicate(),
unwrap(stmt.getObject()));
}
return NULL_URI.equals(element)
|| (element instanceof ILiteral && NULL_URI
.equals(((ILiteral) element).getDatatype())) ? null
: element;
}
/**
* Assigns the given <code>value</code> to the <code>element</code> and
* returns the resulting status of the editing operation and related
* commands.
*/
public CommandResult setValue(final Object element,
IEntityManager entityManager, final Object value) {
if (value == null || !(value instanceof IResource)
&& value.equals(getValue(element))) {
return CommandResult.newOKCommandResult();
}
IEditingSupport editingSupport = getEditingSupport(element);
if (editingSupport == null) {
return CommandResult.newCancelledCommandResult();
}
ICommand newObjectCommand = editingSupport.convertEditorValue(value,
entityManager, unwrap(element));
if (type == Type.PROPERTY && newObjectCommand != null) {
try {
newObjectCommand.execute(new NullProgressMonitor(), null);
} catch (ExecutionException e) {
return CommandResult.newErrorCommandResult(e);
}
Object newPredicate = newObjectCommand.getCommandResult()
.getReturnValue();
if (newPredicate instanceof IProperty) {
setProperty(element, (IProperty) newPredicate);
}
return CommandResult.newOKCommandResult();
}
if (newObjectCommand != null) {
CommandResult result;
if (element instanceof IStatement) {
final IStatement stmt = (IStatement) unwrap(element);
ICompositeCommand command = new CompositeCommand() {
@Override
protected CommandResult doExecuteWithResult(
IProgressMonitor progressMonitor, IAdaptable info)
throws ExecutionException {
final IResource subject = (IResource) stmt.getSubject();
ITransaction transaction = subject.getEntityManager()
.getTransaction();
transaction.begin();
try {
CommandResult result = super.doExecuteWithResult(
progressMonitor, info);
if (!result.getStatus().isOK()) {
return result;
}
// ensure that previously created data is readable
// (if
// isolation != READ UNCOMMITTED)
transaction.commit();
transaction.begin();
final IProperty predicate = subject
.getEntityManager().find(
stmt.getPredicate(),
IProperty.class);
// if stmt.getObject() == null then this is a new
// statement and therefore must not be removed
Object obj = stmt.getObject();
IStatus status;
int index = CommandParameter.NO_INDEX;
if (obj == null) {
status = Status.OK_STATUS;
} else {
ICommand removeCmd = PropertyUtil
.getRemoveCommand(getEditingDomain(),
(IResource) stmt.getSubject(),
predicate, obj);
status = addAndExecute(removeCmd,
progressMonitor, info);
index = PropertyUtil.getRemovedIndex(removeCmd);
}
Object returnValue = null;
if (status.isOK()
&& !result.getReturnValues().isEmpty()) {
// use last return value in case of composite
// commands
Iterator<?> it = result.getReturnValues()
.iterator();
while (it.hasNext()) {
returnValue = it.next();
}
status = addAndExecute(
PropertyUtil.getAddCommand(
getEditingDomain(), subject,
predicate, returnValue, index),
progressMonitor, info);
}
if (status.isOK()) {
transaction.commit();
}
return new CommandResult(status, returnValue);
} finally {
if (transaction.isActive()) {
transaction.rollback();
}
}
}
};
command.add(newObjectCommand);
result = execute(command);
} else {
result = execute(newObjectCommand);
}
return result == null ? CommandResult.newOKCommandResult() : result;
}
return CommandResult.newCancelledCommandResult();
}
protected CommandResult execute(ICommand command) {
try {
getEditingDomain().getCommandStack().execute(command, null, null);
return command.getCommandResult();
} catch (Exception exc) {
return CommandResult.newErrorCommandResult(exc);
}
}
/**
* Find the best matching {@link IStatementEditingSupport} for the given
* statement.
*/
public IEditingSupport getEditingSupport(IAdapterFactory adapterFactory,
final IEntity subject, final IReference predicate,
final Object object) {
IEditingSupport support = adapterFactory == null ? null
: (IEditingSupport) adapterFactory.adapt(predicate,
IEditingSupport.class);
if (support != null) {
return support;
}
// assume that property values are literals if current object is
// already a literal
if (object != null && !(object instanceof IReference)) {
support = new LiteralEditingSupport();
}
if (support == null) {
IProperty property = predicate instanceof IProperty ? (IProperty) predicate
: subject.getEntityManager().find(predicate,
IProperty.class);
if (property instanceof DatatypeProperty
|| property.getRdfsRanges().contains(RDFS.TYPE_LITERAL)
|| property
.getEntityManager()
.createQuery(
ISparqlConstants.PREFIX
+ "ASK { ?p rdfs:range ?r . filter (exists { ?r a rdfs:DataType } || regex(str(?r), 'http://www.w3.org/2001/XMLSchema#')) }")
.setParameter("p", property).getBooleanResult()) {
support = new LiteralEditingSupport();
} else if (object instanceof IClass
|| property.getRdfsRanges().contains(RDFS.TYPE_CLASS)
|| property.getRdfsRanges().contains(OWL.TYPE_CLASS)) {
support = new ManchesterEditingSupport(adapterFactory);
} else {
support = new ResourceEditingSupport(adapterFactory);
}
}
return support;
}
}