blob: 4a4606382853affceb6beb88a042142ff0d59cdc [file] [log] [blame]
/*******************************************************************************
* 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:
* jfaltermeier - initial API and implementation
******************************************************************************/
package org.eclipse.emf.ecp.view.spi.rule.model.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecp.view.spi.model.VDomainModelReference;
import org.eclipse.emf.ecp.view.spi.rule.model.LeafCondition;
import org.eclipse.emfforms.spi.common.report.AbstractReport;
import org.eclipse.emfforms.spi.common.report.ReportService;
import org.eclipse.emfforms.spi.core.services.databinding.DatabindingFailedException;
import org.eclipse.emfforms.spi.core.services.databinding.emf.EMFFormsDatabindingEMF;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceReference;
/**
* Iterator that returns all settings of the condition's {@link LeafCondition#getDomainModelReference()} and
* {@link LeafCondition#getValueDomainModelReference()}.
*
* @author jfaltermeier
* @since 1.5
*
*/
public class LeafConditionSettingIterator implements Iterator<Setting> {
/**
* Map from a setting iterator to the children of the last returned setting of the iterator.
*/
private final Map<Iterator<Setting>, Iterator<EObject>> iteratorToCurrentChildrenMap;
private int expectedStackSize;
private final Stack<Iterator<Setting>> iterators;
private final boolean includeNonValueRelatedSettings;
private final Set<VDomainModelReference> dmrs;
private final List<Setting> nonValueRelSettings = new LinkedList<EStructuralFeature.Setting>();
private final LeafCondition condition;
private BundleContext bundleContext;
private EMFFormsDatabindingEMF databinding;
private ServiceReference<EMFFormsDatabindingEMF> databindingServiceReference;
private ReportService reportService;
private ServiceReference<ReportService> reportServiceReference;
private boolean isDisposed;
/**
* Constructs a new {@link LeafConditionSettingIterator}.
*
* @param condition the leaf condition
* @param domainModel The root domain object of the given {@link LeafCondition}
* @param includeNonValueRelatedSettings whether to include non value related settings
* @since 1.9
*/
public LeafConditionSettingIterator(LeafCondition condition, EObject domainModel,
boolean includeNonValueRelatedSettings) {
isDisposed = false;
this.condition = condition;
dmrs = new LinkedHashSet<VDomainModelReference>();
this.includeNonValueRelatedSettings = includeNonValueRelatedSettings;
iteratorToCurrentChildrenMap = new LinkedHashMap<Iterator<Setting>, Iterator<EObject>>();
expectedStackSize = 1;
if (condition.getValueDomainModelReference() != null) {
expectedStackSize = expectedStackSize + 1;
}
iterators = new Stack<Iterator<Setting>>();
final VDomainModelReference dmr = condition.getDomainModelReference();
if (dmr != null) {
try {
final Setting setting = getDatabinding().getSetting(dmr, domainModel);
final Iterator<Setting> iterator = Collections.singleton(setting).iterator();
iterators.push(iterator);
} catch (final DatabindingFailedException ex) {
getReportService().report(new AbstractReport(ex,
String.format("Could not get the setting iterator for DMR: %s and domain model: %s", //$NON-NLS-1$
condition.getDomainModelReference(), domainModel)));
}
}
// TODO: remove after segments are introduced.
// iterators.push(condition.getDomainModelReference().getIterator());
organizeStack();
}
/**
* @return The {@link EMFFormsDatabindingEMF} service.
*
*/
private EMFFormsDatabindingEMF getDatabinding() {
if (databinding == null) {
databindingServiceReference = getBundleContext().getServiceReference(EMFFormsDatabindingEMF.class);
databinding = getBundleContext().getService(databindingServiceReference);
}
return databinding;
}
/**
* @return The {@link ReportService}
*/
private ReportService getReportService() {
if (reportService == null) {
reportServiceReference = getBundleContext().getServiceReference(ReportService.class);
reportService = getBundleContext().getService(reportServiceReference);
}
return reportService;
}
/**
* @return The {@link BundleContext} for this class
*/
private BundleContext getBundleContext() {
if (bundleContext == null) {
bundleContext = FrameworkUtil.getBundle(getClass()).getBundleContext();
}
return bundleContext;
}
private void organizeStack() {
if (iterators.size() == 0) {
// end
return;
}
if (iterators.size() == expectedStackSize && iterators.peek().hasNext()) {
// all is ok
return;
}
if (iterators.size() == expectedStackSize) {
// top iterator has reached end.
// since top iterator does not have entries in iteratorToCurrentChildrenMap,
// this is treated as a special case
iterators.pop();
organizeStack();
return;
}
// iterators.size() != expectedStackSize
final Iterator<EObject> domainIterator = iteratorToCurrentChildrenMap.get(iterators.peek());
if (domainIterator == null) {
// move on to next setting
if (iterators.peek().hasNext()) {
final Setting setting = iterators.peek().next();
if (includeNonValueRelatedSettings) {
nonValueRelSettings.add(setting);
}
final Iterator<EObject> childIterator = getChildIterator(setting);
iteratorToCurrentChildrenMap.put(iterators.peek(), childIterator);
organizeStack();
return;
}
// top iterator at end, pop
iterators.pop();
organizeStack();
return;
}
if (domainIterator.hasNext()) {
final EObject nextDomain = domainIterator.next();
try {
final Setting setting = getDatabinding().getSetting(condition.getValueDomainModelReference(),
nextDomain);
final Iterator<Setting> iterator = Collections.singleton(setting).iterator();
iterators.push(iterator);
} catch (final DatabindingFailedException ex) {
getReportService().report(new AbstractReport(ex,
String.format("Could not get the setting iterator for DMR: %s and domain model: %s", //$NON-NLS-1$
condition.getValueDomainModelReference(), nextDomain)));
}
final VDomainModelReference valueDMR = EcoreUtil.copy(condition.getValueDomainModelReference());
// TODO: remove after segments are introduced.
// valueDMR.init(nextDomain);
dmrs.add(valueDMR);
// TODO: remove after segments are introduced.
// iterators.push(valueDMR.getIterator());
organizeStack();
return;
}
// domain iterator at end
// remove from map
iteratorToCurrentChildrenMap.remove(iterators.peek());
organizeStack();
return;
}
@SuppressWarnings("unchecked")
private Iterator<EObject> getChildIterator(Setting setting) {
final List<EObject> children = new ArrayList<EObject>();
if (setting.getEStructuralFeature().isMany()) {
children.addAll((Collection<? extends EObject>) setting.get(true));
} else {
children.add((EObject) setting.get(true));
}
final Iterator<EObject> iterator = children.iterator();
while (iterator.hasNext()) {
if (iterator.next() == null) {
iterator.remove();
}
}
return children.iterator();
}
/**
* {@inheritDoc}
*
* @see java.util.Iterator#hasNext()
*/
@Override
public boolean hasNext() {
if (includeNonValueRelatedSettings && !nonValueRelSettings.isEmpty()) {
return true;
}
if (iterators.isEmpty()) {
return false;
}
return iterators.peek().hasNext();
}
/**
* {@inheritDoc}
*
* @see java.util.Iterator#next()
*/
@Override
public Setting next() {
if (includeNonValueRelatedSettings && !nonValueRelSettings.isEmpty()) {
return nonValueRelSettings.remove(0);
}
final Setting result = iterators.peek().next();
organizeStack();
return result;
}
/**
* {@inheritDoc}
*
* @see java.util.Iterator#remove()
*/
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/**
* <br>
* Returns the used {@link VDomainModelReference VDomainModelReferences}.</br>
* <b>This map will contain all entries when the iterator has reached its end.</b>
*
* @return the map
*/
public Set<VDomainModelReference> getUsedValueDomainModelReferences() {
return dmrs;
}
/**
* Disposes this {@link LeafConditionSettingIterator}.
*
* @since 1.9
*/
public void dispose() {
if (isDisposed) {
return;
}
isDisposed = true;
if (databindingServiceReference != null) {
databinding = null;
getBundleContext().ungetService(databindingServiceReference);
}
if (reportServiceReference != null) {
reportService = null;
getBundleContext().ungetService(reportServiceReference);
}
}
/**
* @return <code>true</code> if this {@link LeafConditionSettingIterator} is disposed, <code>false</code> otherwise
* @since 1.9
*/
public boolean isDisposed() {
return isDisposed;
}
}