/******************************************************************************* * Copyright (c) 2011, 2015 Willink Transformations 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: * E.D.Willink - initial API and implementation *******************************************************************************/ package org.eclipse.ocl.pivot.internal.complete; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.ocl.pivot.Property; import org.eclipse.ocl.pivot.Type; import org.eclipse.ocl.pivot.internal.scoping.EnvironmentView; import org.eclipse.ocl.pivot.internal.scoping.EnvironmentView.Disambiguator; import org.eclipse.ocl.pivot.internal.utilities.EnvironmentFactoryInternal; import com.google.common.collect.Iterators; public class PartialProperties implements Iterable<@NonNull Property> { //resolution = null, partials = null or empty => empty // resolution = X, partials = null or empty or [X} => X // resolution = null, partials not empty => lazy unresolved 'ambiguity' private boolean isResolved = false; private @Nullable Property resolution = null; private @Nullable List<@NonNull Property> partials = null; protected final @NonNull EnvironmentFactoryInternal environmentFactory; public PartialProperties(@NonNull EnvironmentFactoryInternal environmentFactory) { this.environmentFactory = environmentFactory; } public synchronized void didAddProperty(@NonNull Property pivotProperty) { List<@NonNull Property> partials2 = partials; @Nullable Property resolution2 = resolution; if (partials2 == null) { if (resolution2 == null) { resolution2 = resolution = pivotProperty; isResolved = true; } else { partials = partials2 = new ArrayList<@NonNull Property>(); partials2.add(resolution2); if (resolution2 != pivotProperty) { partials2.add(pivotProperty); } resolution2 = resolution = null; isResolved = false; } } else if (partials2.isEmpty()) { if (resolution2 == null) { resolution2 = resolution = pivotProperty; isResolved = true; } else { partials2.add(resolution2); if (resolution2 != pivotProperty) { partials2.add(pivotProperty); } resolution2 = resolution = null; isResolved = false; } } else { if (!partials2.contains(pivotProperty)) { partials2.add(pivotProperty); } resolution2 = resolution = null; isResolved = false; } } public boolean didRemoveProperty(@NonNull Property pivotProperty) { remove(pivotProperty); return isEmpty(); } public synchronized @Nullable Property get() { if (isResolved) { return resolution; } resolve(); if (isResolved) { return resolution; } List<@NonNull Property> values = new ArrayList<@NonNull Property>(partials); Map<@NonNull Type, @NonNull Property> primaryProperties = new HashMap<@NonNull Type, @NonNull Property>(); for (@NonNull Property property : values) { org.eclipse.ocl.pivot.Class owningType = property.getOwningClass(); if (owningType != null) { Type domainType = environmentFactory.getMetamodelManager().getPrimaryType(owningType); if (!primaryProperties.containsKey(domainType)) { primaryProperties.put(domainType, property); // FIXME something more deterministic than first } } } if (primaryProperties.size() == 1) { resolution = primaryProperties.values().iterator().next(); isResolved = true; return resolution; } isResolved = true; resolution = null; return resolution; } public synchronized boolean isEmpty() { if (resolution != null) { return false; } List<Property> partials2 = partials; if (partials2 == null) { return true; } return partials2.size() <= 0; } @Override public @NonNull Iterator<@NonNull Property> iterator() { if (!isResolved) { resolve(); } if (resolution != null) { return Iterators.singletonIterator(resolution); } else if (partials != null) { return partials.iterator(); } else { return Iterators.emptyIterator(); } } public synchronized void remove(@NonNull Property pivotProperty) { if (pivotProperty == resolution) { resolution = null; } if (partials != null) { partials.remove(pivotProperty); } } private void resolve() { assert !isResolved; List<Property> partials2 = partials; if (partials2 == null) { return; } int size = partials2.size(); if (size <= 0) { return; } if (size == 1) { isResolved = true; resolution = partials2.get(0); } List<Property> values = new ArrayList<Property>(partials); for (int i = 0; i < values.size()-1;) { boolean iRemoved = false; @SuppressWarnings("null") @NonNull Property iValue = values.get(i); for (int j = i + 1; j < values.size();) { Class<? extends Property> iClass = iValue.getClass(); @SuppressWarnings("null") @NonNull Property jValue = values.get(j); Class<? extends Property> jClass = jValue.getClass(); int verdict = 0; for (Class<?> key : EnvironmentView.getDisambiguatorKeys()) { if (key.isAssignableFrom(iClass) && key.isAssignableFrom(jClass)) { List<Comparator<Object>> disambiguators = EnvironmentView.getDisambiguators(key); if (disambiguators != null) { for (Comparator<Object> comparator : disambiguators) { if (comparator instanceof Disambiguator<?>) { verdict = ((Disambiguator<@NonNull Object>)comparator).compare(environmentFactory, iValue, jValue); } else { verdict = comparator.compare(iValue, jValue); } if (verdict != 0) { break; } } } if (verdict != 0) { break; } } } if (verdict == 0) { j++; } else if (verdict < 0) { values.remove(i); iRemoved = true; break; } else { values.remove(j); } } if (!iRemoved) { i++; } } if (values.size() == 1) { resolution = values.get(0); isResolved = true; return; } } @Override public String toString() { if (resolution != null) { return resolution.toString(); } List<Property> partials2 = partials; if (partials2 == null) { return ""; } StringBuilder s = new StringBuilder(); for (Property dProperty : partials2) { if (s.length() > 0) { s.append(","); } s.append(dProperty.toString()); } return s.toString(); } }