Skip to content

Package: RankingHelper

RankingHelper

nameinstructionbranchcomplexitylinemethod
RankingHelper(Class, double)
M: 0 C: 6
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 2
100%
M: 0 C: 1
100%
RankingHelper(Class, double, Double)
M: 0 C: 12
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 5
100%
M: 0 C: 1
100%
createWarning(List, double)
M: 0 C: 41
100%
M: 0 C: 0
100%
M: 0 C: 1
100%
M: 0 C: 8
100%
M: 0 C: 1
100%
getHighestRankingElement(Collection, RankingHelper.RankTester)
M: 0 C: 69
100%
M: 0 C: 14
100%
M: 0 C: 8
100%
M: 0 C: 16
100%
M: 0 C: 1
100%
reportClashingPriorities(String)
M: 24 C: 10
29%
M: 1 C: 1
50%
M: 1 C: 1
50%
M: 8 C: 4
33%
M: 0 C: 1
100%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2011-2018 EclipseSource Muenchen GmbH and others.
3: *
4: * All rights reserved. This program and the accompanying materials
5: * are made available under the terms of the Eclipse Public License 2.0
6: * which accompanies this distribution, and is available at
7: * https://www.eclipse.org/legal/epl-2.0/
8: *
9: * SPDX-License-Identifier: EPL-2.0
10: *
11: * Contributors:
12: * Mat Hansen - initial API and implementation
13: * Christian W. Damus - bug 533522
14: ******************************************************************************/
15: package org.eclipse.emfforms.common;
16:
17: import static java.lang.Double.doubleToLongBits;
18:
19: import java.text.MessageFormat;
20: import java.util.ArrayList;
21: import java.util.Collection;
22: import java.util.List;
23:
24: import org.eclipse.emfforms.spi.common.report.AbstractReport;
25: import org.eclipse.emfforms.spi.common.report.ReportService;
26: import org.osgi.framework.Bundle;
27: import org.osgi.framework.BundleContext;
28: import org.osgi.framework.FrameworkUtil;
29: import org.osgi.framework.ServiceReference;
30:
31: /**
32: * Ranking helper class to generically handle prioritization use cases.
33: * This class assumes that priorities are in ascending order (higher rank means higher priority).
34: *
35: * @author Mat Hansen <mhansen@eclipsesource.com>
36: * @since 1.14
37: *
38: * @param <T> the type for the rank comparison
39: */
40: public class RankingHelper<T> {
41:
42:         /**
43:          * Value used to initialize the rank comparison in case no initialRank value is passed.
44:          */
45:         public static final double DEFAULT_INITIAL_RANK = -1d;
46:
47:         private final Class<?> elementClass;
48:         private final double initialRank;
49:         private final Double ignoreRankValue;
50:
51:         /**
52:          * Constructor which disables ignore rank feature.
53:          *
54:          * @param elementClass the class the prioritization is based on, required for logging purposes
55:          * @param initialRank the initialization value for the rank comparison (see INITIAL_RANK)
56:          */
57:         public RankingHelper(Class<?> elementClass, double initialRank) {
58:                 this(elementClass, initialRank, null);
59:         }
60:
61:         /**
62:          * The main constructor.
63:          *
64:          * @param elementClass the class the prioritization is based on, required for logging purposes
65:          * @param initialRank the initialization value for the rank comparison (see INITIAL_RANK)
66:          * @param ignoreRankValue a specific value which will result in the given element being omitted.
67:          * Must be non-null otherwise this feature is not enabled.
68:          */
69:         public RankingHelper(Class<?> elementClass, double initialRank, Double ignoreRankValue) {
70:                 this.elementClass = elementClass;
71:                 this.initialRank = initialRank;
72:                 this.ignoreRankValue = ignoreRankValue;
73:         }
74:
75:         /**
76:          * Helper callback interface for rank testing.
77:          *
78:          * @param <T> the type for the rank comparison.
79:          * @see RankingHelper
80:          */
81:         public interface RankTester<T> {
82:
83:                 /**
84:                  * Callback method which should delegate to the actual ranking element.
85:                  *
86:                  * @param element the element to get the rank/priority for
87:                  * @return numeric priority value. Higher value means higher priority.
88:                  */
89:                 double getRank(T element);
90:
91:         }
92:
93:         /**
94:          * Determine the highest ranking element by iterating over all elements and calling rankTester.getRank().
95:          *
96:          * @param elements the elements to consider
97:          * @param rankTester the tester instance to fetch a rank/priority for a given element
98:          * @return the highest ranking element found (may be null)
99:          */
100:         public T getHighestRankingElement(final Collection<T> elements,
101:                 final RankTester<T> rankTester) {
102:
103:                 final List<T> highestRankingElements = new ArrayList<T>();
104:                 double highestRank = initialRank;
105:
106:•                for (final T element : elements) {
107:
108:                         final double rank = rankTester.getRank(element);
109:
110:                         // Don't use Double.equals(Double) because that requires creating a boxed Double
111:                         // every time (as seen in a profiler), and we do this operation a lot in large models
112:•                        if (ignoreRankValue != null && doubleToLongBits(ignoreRankValue.doubleValue()) == doubleToLongBits(rank)) {
113:                                 continue;
114:•                        } else if (rank >= highestRank) {
115:•                                if (rank > highestRank) {
116:                                         highestRank = rank;
117:                                         highestRankingElements.clear();
118:                                 }
119:                                 highestRankingElements.add(element);
120:                         }
121:
122:                 }
123:
124:•                if (highestRankingElements.size() > 1) {
125:                         reportClashingPriorities(createWarning(highestRankingElements, highestRank));
126:                 }
127:
128:•                if (highestRankingElements.isEmpty()) {
129:                         return null;
130:                 }
131:                 return highestRankingElements.get(0);
132:         }
133:
134:         /**
135:          * Create warning about clashing priorities.
136:          *
137:          * @param highestRankingElements list of clashing elements
138:          * @param rank the rank of the elements
139:          * @return the warning message
140:          */
141:         protected String createWarning(List<T> highestRankingElements, double rank) {
142:                 final String warning = "Priority clash while trying to find highest ranking instance of {1}.\n" + //$NON-NLS-1$
143:                         "Instance {2} clashes with {3}{4,choice, 0#|0< and {4,number,integer} more}, reporting rank {0,number}."; //$NON-NLS-1$
144:
145:                 final Object[] arguments = new Object[] {
146:                         rank,
147:                         elementClass,
148:                         highestRankingElements.get(0).getClass(),
149:                         highestRankingElements.get(1).getClass(),
150:                         highestRankingElements.size() - 2
151:                 };
152:
153:                 return MessageFormat.format(warning, arguments);
154:         }
155:
156:         /**
157:          * Report clashing priorities using ReportService.
158:          *
159:          * @param warningMessage the warning message
160:          */
161:         protected void reportClashingPriorities(String warningMessage) {
162:
163:                 final Bundle bundle = FrameworkUtil.getBundle(getClass());
164:•                if (bundle != null) {
165:
166:                         // we fetch the report service for every error individually
167:                         // because this shouldn't happen very often
168:                         final BundleContext bundleContext = bundle.getBundleContext();
169:                         final ServiceReference<ReportService> serviceReference = bundleContext
170:                                 .getServiceReference(ReportService.class);
171:                         final ReportService reportService = bundleContext.getService(serviceReference);
172:
173:                         reportService
174:                                 .report(new AbstractReport(warningMessage, 2));
175:
176:                         bundleContext.ungetService(serviceReference);
177:
178:                 } else {
179:                         // we still want to complain in case we have no OSGi context
180:                         System.err.println(warningMessage);
181:                 }
182:
183:         }
184:
185: }