| /******************************************************************************* |
| * Copyright (c) 2011-2016 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 |
| ******************************************************************************/ |
| package org.eclipse.emfforms.internal.core.services.segments.mapping; |
| |
| import org.eclipse.emf.databinding.internal.EMFValuePropertyDecorator; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.ecore.EStructuralFeature.Setting; |
| import org.eclipse.emf.ecp.common.spi.asserts.Assert; |
| import org.eclipse.emf.ecp.view.spi.model.VDomainModelReferenceSegment; |
| import org.eclipse.emf.edit.domain.EditingDomain; |
| import org.eclipse.emfforms.internal.core.services.databinding.SegmentConverterValueResultImpl; |
| import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException; |
| import org.eclipse.emfforms.spi.core.services.databinding.emf.DomainModelReferenceSegmentConverterEMF; |
| import org.eclipse.emfforms.spi.core.services.databinding.emf.SegmentConverterListResultEMF; |
| import org.eclipse.emfforms.spi.core.services.databinding.emf.SegmentConverterValueResultEMF; |
| import org.eclipse.emfforms.spi.view.mappingsegment.model.VMappingDomainModelReferenceSegment; |
| import org.osgi.service.component.annotations.Component; |
| |
| /** |
| * Converts {@link VMappingDomainModelReferenceSegment VMappingDomainModelReferenceSegments} to value and list |
| * properties, and to {@link Setting settings}. |
| * |
| * @author Lucas Koehler |
| * |
| */ |
| @SuppressWarnings("restriction") |
| @Component(name = "MappingSegmentConverter") |
| public class MappingSegmentConverter implements DomainModelReferenceSegmentConverterEMF { |
| |
| @Override |
| public double isApplicable(VDomainModelReferenceSegment segment) { |
| Assert.create(segment).notNull(); |
| if (segment instanceof VMappingDomainModelReferenceSegment) { |
| return 10d; |
| } |
| return NOT_APPLICABLE; |
| } |
| |
| @Override |
| public SegmentConverterValueResultEMF convertToValueProperty(VDomainModelReferenceSegment segment, |
| EClass segmentRoot, EditingDomain editingDomain) throws DatabindingFailedException { |
| |
| final VMappingDomainModelReferenceSegment mappingSegment = checkAndConvertSegment(segment); |
| |
| final EStructuralFeature structuralFeature = segmentRoot |
| .getEStructuralFeature(mappingSegment.getDomainModelFeature()); |
| if (structuralFeature == null) { |
| throw new DatabindingFailedException(String.format( |
| "The given EOject does not contain the segment's feature. The segment was %1$s. The EObject was %2$s.", //$NON-NLS-1$ |
| segment, segmentRoot)); |
| } |
| checkMapType(structuralFeature); |
| |
| // no checks necessary as they are already done in checkMapType() |
| final EClass eClass = (EClass) structuralFeature.getEType(); |
| final EReference valueReference = (EReference) eClass.getEStructuralFeature("value"); //$NON-NLS-1$ |
| |
| final EMFMappingValueProperty mappingProperty = new EMFMappingValueProperty(editingDomain, |
| mappingSegment.getMappedClass(), structuralFeature); |
| final EMFValuePropertyDecorator resultProperty = new EMFValuePropertyDecorator(mappingProperty, |
| structuralFeature); |
| |
| if (valueReference.getEReferenceType().isSuperTypeOf(mappingSegment.getMappedClass())) { |
| return new SegmentConverterValueResultImpl(resultProperty, mappingSegment.getMappedClass()); |
| } |
| return new SegmentConverterValueResultImpl(resultProperty, valueReference.getEReferenceType()); |
| |
| } |
| |
| @Override |
| public SegmentConverterListResultEMF convertToListProperty(VDomainModelReferenceSegment segment, EClass segmentRoot, |
| EditingDomain editingDomain) throws DatabindingFailedException { |
| throw new UnsupportedOperationException( |
| "A VMappingDomainModelReferenceSegment cannot be converted to a list property, only to a value property."); //$NON-NLS-1$ |
| } |
| |
| @Override |
| public Setting getSetting(VDomainModelReferenceSegment segment, EObject eObject) throws DatabindingFailedException { |
| |
| final VMappingDomainModelReferenceSegment mappingSegment = checkAndConvertSegment(segment); |
| |
| final EStructuralFeature structuralFeature = eObject.eClass() |
| .getEStructuralFeature(mappingSegment.getDomainModelFeature()); |
| if (structuralFeature == null) { |
| throw new DatabindingFailedException(String.format( |
| "The given EOject does not contain the segment's feature. The segment was %1$s. The EObject was %2$s.", //$NON-NLS-1$ |
| segment, eObject)); |
| } |
| checkMapType(structuralFeature); |
| |
| return new MappedSetting(eObject, structuralFeature, mappingSegment.getMappedClass()); |
| } |
| |
| /** |
| * Checks whether the given segment is a valid {@link VMappingDomainModelReferenceSegment}. If yes, convert and |
| * return it. If no, throw an exception. |
| * |
| * @param segment The segment to check and convert |
| * @return The converted segment |
| * @throws DatabindingFailedException If the given segment is not a valid mapping segment |
| */ |
| private VMappingDomainModelReferenceSegment checkAndConvertSegment(VDomainModelReferenceSegment segment) |
| throws DatabindingFailedException { |
| Assert.create(segment).notNull(); |
| Assert.create(segment).ofClass(VMappingDomainModelReferenceSegment.class); |
| |
| final VMappingDomainModelReferenceSegment mappingSegment = (VMappingDomainModelReferenceSegment) segment; |
| |
| if (mappingSegment.getDomainModelFeature() == null) { |
| throw new DatabindingFailedException("The segment's domain model feature must not be null."); //$NON-NLS-1$ |
| } |
| if (mappingSegment.getDomainModelFeature().isEmpty()) { |
| throw new DatabindingFailedException("The segment's domain model feature must not be an empty string."); //$NON-NLS-1$ |
| } |
| if (mappingSegment.getMappedClass() == null) { |
| throw new DatabindingFailedException("The mapping segment's mapped class must not be null."); //$NON-NLS-1$ |
| } |
| |
| return mappingSegment; |
| } |
| |
| /** |
| * Checks whether the given structural feature references a proper map. |
| * |
| * @param structuralFeature The feature to check |
| * @throws IllegalMapTypeException if the structural feature doesn't reference a proper map. |
| */ |
| private void checkMapType(EStructuralFeature structuralFeature) throws IllegalMapTypeException { |
| checkStructuralFeature(structuralFeature); |
| |
| final EClass eClass = (EClass) structuralFeature.getEType(); |
| final EStructuralFeature keyFeature = eClass.getEStructuralFeature("key"); //$NON-NLS-1$ |
| final EStructuralFeature valueFeature = eClass.getEStructuralFeature("value"); //$NON-NLS-1$ |
| if (keyFeature == null || valueFeature == null) { |
| throw new IllegalMapTypeException( |
| "The segment's structural feature must reference a map."); //$NON-NLS-1$ |
| } |
| if (!EReference.class.isInstance(keyFeature)) { |
| throw new IllegalMapTypeException( |
| "The keys of the map referenced by the segment's structural feature must be referenced EClasses."); //$NON-NLS-1$ |
| } |
| if (!EClass.class.isAssignableFrom(((EReference) keyFeature).getEReferenceType().getInstanceClass())) { |
| throw new IllegalMapTypeException( |
| "The keys of the map referenced by the segment's structural feature must be referenced EClasses."); //$NON-NLS-1$ |
| } |
| if (!EReference.class.isInstance(valueFeature)) { |
| throw new IllegalMapTypeException( |
| "The values of the map referenced by the segment's structural feature must be referenced EObjects."); //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Checks basic required properties of the given {@link EStructuralFeature}. |
| * |
| * @param structuralFeature The {@link EStructuralFeature} to check |
| * @throws IllegalMapTypeException if something's wrong with the feature |
| */ |
| private void checkStructuralFeature(EStructuralFeature structuralFeature) throws IllegalMapTypeException { |
| if (structuralFeature.getEType() == null) { |
| throw new IllegalMapTypeException( |
| "The EType of the segment's structural feature was null."); //$NON-NLS-1$ |
| } |
| if (structuralFeature.getEType().getInstanceClassName() == null) { |
| throw new IllegalMapTypeException( |
| "The InstanceClassName of the segment's structural feature's EType was null."); //$NON-NLS-1$ |
| } |
| if (!structuralFeature.getEType().getInstanceClassName().equals("java.util.Map$Entry")) { //$NON-NLS-1$ |
| throw new IllegalMapTypeException( |
| "The segment's structural feature must reference a map."); //$NON-NLS-1$ |
| } |
| if (structuralFeature.getLowerBound() != 0 || structuralFeature.getUpperBound() != -1) { |
| throw new IllegalMapTypeException( |
| "The segment's structural feature must reference a map."); //$NON-NLS-1$ |
| } |
| } |
| } |