blob: 057e61d4fc44a3770493f9c15d3f775138b0efe1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 EclipseSource Muenchen GmbH and others.
*
*
* 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:
* Eugen Neufeld - initial API and implementation
******************************************************************************/
package org.eclipse.e4.ui.internal.workbench;
import java.util.Comparator;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.MApplicationElement;
import org.eclipse.e4.ui.model.fragment.MStringModelFragment;
import org.eclipse.e4.ui.model.internal.ModelUtils;
import org.eclipse.e4.ui.model.internal.PositionInfo;
/**
* Custom {@link Comparator} that operates on {@link MStringModelFragment} model
* fragments wrapped in a {@link ModelFragmentWrapper}. The comparator will
* order the fragments in the logical way in which they should be merged, based
* on their defined {@link MStringModelFragment#getPositionInList()
* positionInList} , as follows:
* <ul>
* <li><b>Index</b> - all elements with an index are merged first. Lowest to
* highest index.
* <li><b>No position defined</b> - all contributions without any information
* are sorted in the order in which they are read from the extension points and
* added to the end of the list
* <li><b>First/Last</b> - all elements with a first/last tag. If there are
* several elements in the same container with the same tag, then those elements
* are sorted in the order as they come form the extension registry.
* <li><b> After/Before</b> - the after and before contributions are ordered
* considering potential dependencies between fragments. If the dependency is
* not resolvable eg points to the application model the element is sorted to
* the front of the after/before list.
* </ul>
*/
public class ModelFragmentComparator implements Comparator<ModelFragmentWrapper> {
private final MApplication application;
public ModelFragmentComparator(MApplication application) {
super();
this.application = application;
}
@Override
public int compare(ModelFragmentWrapper o1, ModelFragmentWrapper o2) {
if (o1 == o2)
return 0;
PositionDescription posInfo1 = getPositionDescription(o1);
PositionDescription posInfo2 = getPositionDescription(o2);
switch (posInfo1.getPlace()) {
case NONE:
switch (posInfo2.getPlace()) {
case INDEX:
case NONE:
return 1;
default:
return -1;
}
case ABSOLUTE:
switch (posInfo2.getPlace()) {
case RELATIVE:
return -1;
default:
return 1;
}
case INDEX:
switch (posInfo2.getPlace()) {
case INDEX:
int result = posInfo1.getPositionReferenceAsInteger() - posInfo2.getPositionReferenceAsInteger();
if (result == 0)
return 1;
return result;
default:
return -1;
}
case RELATIVE:
switch (posInfo2.getPlace()) {
case RELATIVE:
boolean hasElement = false;
for (MApplicationElement element : o2.getModelFragment().getElements()) {
hasElement |= ModelUtils.findElementById(element, posInfo1.getReference()) != null;
if (hasElement)
break;
}
if (hasElement)
return 1;
hasElement = false;
for (MApplicationElement element : o1.getModelFragment().getElements()) {
hasElement |= ModelUtils.findElementById(element, posInfo2.getReference()) != null;
if (hasElement)
break;
}
if (hasElement)
return -1;
hasElement = ModelUtils.findElementById(application, posInfo1.getReference()) != null;
if (hasElement)
return -1;
return 1;
default:
return 1;
}
default:
// should never be reached
return 1;
}
}
private PositionDescription getPositionDescription(ModelFragmentWrapper wrapper) {
if (!MStringModelFragment.class.isInstance(wrapper.getModelFragment()))
return new PositionDescription(PositionPlace.NONE, null);
MStringModelFragment stringFragment = (MStringModelFragment) wrapper.getModelFragment();
if (stringFragment.getPositionInList() == null)
return new PositionDescription(PositionPlace.NONE, null);
String posInList = stringFragment.getPositionInList().trim();
PositionInfo posInfo = PositionInfo.parse(posInList);
if (posInfo == null)
return new PositionDescription(PositionPlace.NONE, null);
switch (posInfo.getPosition()) {
case AFTER:
case BEFORE:
return new PositionDescription(PositionPlace.RELATIVE, posInfo.getPositionReference());
case FIRST:
case LAST:
return new PositionDescription(PositionPlace.ABSOLUTE, posInfo.getPositionReference());
case INDEX:
return new PositionDescription(PositionPlace.INDEX, posInfo.getPositionReference());
default:
return new PositionDescription(PositionPlace.NONE, null);
}
}
/**
* Inner Class used to describe the Position to which the fragment should be
* sorted.
*/
private static class PositionDescription {
private final PositionPlace place;
private final String reference;
PositionDescription(PositionPlace place, String reference) {
this.place = place;
this.reference = reference;
}
public PositionPlace getPlace() {
return place;
}
public String getReference() {
return reference;
}
public int getPositionReferenceAsInteger() {
return Integer.parseInt(reference);
}
}
/**
* Inner class used to describe the position to merge to.
*/
private enum PositionPlace {
NONE, ABSOLUTE, INDEX, RELATIVE
}
}