| /******************************************************************************* |
| * 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 |
| } |
| } |