blob: 39cb35f96690bfda85028c899c2c06eac0c332ec [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011-2019 EclipseSource Muenchen GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Lucas Koehler - initial API and implementation
* Christian W. Damus - bug 553224
******************************************************************************/
package org.eclipse.emfforms.core.services.databinding.featurepath;
import java.util.List;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.databinding.IEMFListProperty;
import org.eclipse.emf.databinding.IEMFValueProperty;
import org.eclipse.emf.databinding.edit.EMFEditProperties;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.ENamedElement;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
import org.eclipse.emf.ecp.view.spi.model.VFeaturePathDomainModelReference;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
import org.eclipse.emfforms.spi.core.services.databinding.emf.DomainModelReferenceConverterEMF;
/**
* An implementation of {@link DomainModelReferenceConverterEMF} that converts {@link VFeaturePathDomainModelReference}
* s.
*
* @author Lucas Koehler
*
*/
public class FeaturePathDomainModelReferenceConverter implements DomainModelReferenceConverterEMF {
@Override
public double isApplicable(VDomainModelReference domainModelReference) {
if (domainModelReference == null) {
throw new IllegalArgumentException("The given VDomainModelReference must not be null."); //$NON-NLS-1$
}
if (domainModelReference instanceof VFeaturePathDomainModelReference) {
return 0;
}
return NOT_APPLICABLE;
}
private VFeaturePathDomainModelReference checkAndConvertDMR(VDomainModelReference domainModelReference)
throws DatabindingFailedException {
if (domainModelReference == null) {
throw new IllegalArgumentException("The given VDomainModelReference must not be null."); //$NON-NLS-1$
}
if (!VFeaturePathDomainModelReference.class.isInstance(domainModelReference)) {
throw new IllegalArgumentException(
String.format("DomainModelReference %1$s is not an instance of VFeaturePathDomainModelReference.", //$NON-NLS-1$
identify(domainModelReference)));
}
final VFeaturePathDomainModelReference featurePathReference = (VFeaturePathDomainModelReference) domainModelReference;
if (featurePathReference.getDomainModelEFeature() == null) {
throw new DatabindingFailedException(
"The field domainModelEFeature of the given VFeaturePathDomainModelReference must not be null."); //$NON-NLS-1$
}
if (featurePathReference.getDomainModelEFeature().eIsProxy()) {
throw new DatabindingFailedException(
String.format("The domainModelEFeature %1$s of the given DMR %2$s is a proxy.", //$NON-NLS-1$
identify(featurePathReference.getDomainModelEFeature()), identify(featurePathReference)));
}
for (final EReference path : featurePathReference.getDomainModelEReferencePath()) {
if (path.eIsProxy()) {
throw new DatabindingFailedException(
String.format("The path reference %1$s of the given DMR %2$s is a proxy.", //$NON-NLS-1$
identify(path), identify(featurePathReference)));
}
}
return featurePathReference;
}
@Override
public IEMFValueProperty convertToValueProperty(VDomainModelReference domainModelReference, EObject object)
throws DatabindingFailedException {
final EditingDomain editingDomain = getEditingDomain(object);
return convertToValueProperty(domainModelReference, null, editingDomain);
}
@Override
public IEMFValueProperty convertToValueProperty(VDomainModelReference domainModelReference, EClass rootEClass,
EditingDomain editingDomain) throws DatabindingFailedException {
final VFeaturePathDomainModelReference featurePathReference = checkAndConvertDMR(domainModelReference);
final List<EReference> referencePath = featurePathReference.getDomainModelEReferencePath();
if (referencePath.isEmpty()) {
return EMFEditProperties.value(editingDomain, featurePathReference.getDomainModelEFeature());
}
if (referencePath.get(0).isMany()) {
throw new DatabindingFailedException(String.format(
"The path is not fully resolved. The reference being resolved is not a single reference: %1$s. The DMR is %2$s.", //$NON-NLS-1$
identify(referencePath.get(0)), identify(domainModelReference)));
}
IEMFValueProperty emfValueProperty = EMFEditProperties.value(editingDomain, referencePath.get(0));
for (int i = 1; i < referencePath.size(); i++) {
final EReference eReference = referencePath.get(i);
if (eReference.isMany()) {
throw new DatabindingFailedException(String.format(
"The path is not fully resolved. The reference being resolved is not a single reference: %1$s. The DMR is %2$s.", //$NON-NLS-1$
identify(eReference), identify(domainModelReference)));
}
emfValueProperty = emfValueProperty.value(eReference);
}
return emfValueProperty.value(featurePathReference.getDomainModelEFeature());
}
private EditingDomain getEditingDomain(EObject object) throws DatabindingFailedException {
return AdapterFactoryEditingDomain.getEditingDomainFor(object);
}
@Override
public IEMFListProperty convertToListProperty(VDomainModelReference domainModelReference, EObject object)
throws DatabindingFailedException {
final VFeaturePathDomainModelReference featurePathReference = checkAndConvertDMR(domainModelReference);
final List<EReference> referencePath = featurePathReference.getDomainModelEReferencePath();
final EditingDomain editingDomain = getEditingDomain(object);
if (referencePath.isEmpty()) {
return EMFEditProperties.list(editingDomain, featurePathReference.getDomainModelEFeature());
}
IEMFValueProperty emfValueProperty = EMFEditProperties.value(editingDomain, referencePath.get(0));
for (int i = 1; i < referencePath.size(); i++) {
emfValueProperty = emfValueProperty.value(referencePath.get(i));
}
return emfValueProperty.list(featurePathReference.getDomainModelEFeature());
}
@Override
public Setting getSetting(VDomainModelReference domainModelReference, EObject object)
throws DatabindingFailedException {
final VFeaturePathDomainModelReference featurePathReference = checkAndConvertDMR(domainModelReference);
EObject currentObject = object;
for (final EReference eReference : featurePathReference.getDomainModelEReferencePath()) {
if (eReference.isMany()) {
throw new DatabindingFailedException(String.format(
"The path is not fully resolved. The reference being resolved is not a single reference: %1$s. The DMR is %2$s. Last resolved EObject is %3$s.", //$NON-NLS-1$
identify(eReference), identify(domainModelReference), identify(currentObject)));
}
if (currentObject.eClass().getFeatureID(eReference) == -1) {
throw new DatabindingFailedException(String.format(
"Given EClass %1$s has no such feature %2$s. The DMR is %3$s. Last resolved EObject is %4$s.", //$NON-NLS-1$
identify(currentObject.eClass()), identify(eReference),
identify(domainModelReference), identify(currentObject)));
}
final EObject nextObject = (EObject) currentObject.eGet(eReference);
if (nextObject == null) {
throw new DatabindingFailedException(String.format(
"The path is not fully resolved. The DMR is %1$s. Last resolved EObject is %2$s. Reference being resolved is %3$s.", //$NON-NLS-1$
identify(domainModelReference), identify(currentObject), identify(eReference)));
}
currentObject = nextObject;
}
final EStructuralFeature structuralFeature = featurePathReference.getDomainModelEFeature();
if (structuralFeature.getEType() == null) {
throw new DatabindingFailedException(
String.format("The eType of the feature %1$s is null in DMR %2$s.", identify(structuralFeature), //$NON-NLS-1$
identify(domainModelReference)));
}
if (currentObject.eClass().getEAllStructuralFeatures().contains(structuralFeature)) {
return InternalEObject.class.cast(currentObject).eSetting(structuralFeature);
}
throw new DatabindingFailedException(String.format("The resolved EObject %1$s doesn't have the feature %2$s.", //$NON-NLS-1$
identify(currentObject), identify(structuralFeature)));
}
private static String identify(EObject eObject) {
if (eObject == null) {
return "<null>"; //$NON-NLS-1$
}
final StringBuilder result = new StringBuilder();
// First, a trimmed variant of the to-string representation (omit Java package)
result.append(eObject.toString());
final int hashCodePos = result.indexOf("@"); //$NON-NLS-1$
if (hashCodePos > 0) {
final int lastDotPos = result.lastIndexOf(".", hashCodePos); //$NON-NLS-1$
if (lastDotPos > 0) {
result.delete(0, lastDotPos + 1);
}
}
final Resource resource = eObject.eResource();
if (resource != null) {
result.append('<');
result.append(EcoreUtil.getURI(eObject));
result.append('>');
}
return result.toString();
}
private static String identify(ENamedElement element) {
if (element == null) {
return "<null>"; //$NON-NLS-1$
}
final StringBuilder result = new StringBuilder();
if (element.eIsProxy()) {
// Infer as much as possible
final URI proxyURI = EcoreUtil.getURI(element);
// Get the qualified ENamedElement name
String fragment = proxyURI.fragment();
while (!fragment.isEmpty() && fragment.charAt(0) == '/') {
fragment = fragment.substring(1);
}
result.append(fragment.replace('/', '.'));
// Get the package namespace (or Ecore path)
result.append(" ("); //$NON-NLS-1$
result.append(proxyURI.trimFragment());
result.append(')');
} else {
result.append(element.getName());
for (EObject container = element.eContainer(); container instanceof ENamedElement; container = container
.eContainer()) {
final ENamedElement parent = (ENamedElement) container;
if (parent instanceof EPackage) {
result.append(" ("); //$NON-NLS-1$
result.append(((EPackage) container).getNsURI());
result.append(')');
} else {
result.insert(0, '.');
result.insert(0, parent.getName());
}
}
}
return result.toString();
}
}