From 76f62b98a3a071f1e366bb245aadf0336f92d075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dinis=20Ferreira?= Date: Sat, 30 May 2026 23:14:50 +0200 Subject: [PATCH] chore: enable CPD detection (pmd.cpd.min 100000 -> 100), de-dup and baseline pmd.cpd.min=100000 effectively disabled PMD's copy/paste detector: cpd-check passed on virtually any duplication. Lower the threshold to 100 (PMD's default) so CPD detects duplication going forward. At 100 the reactor surfaces pre-existing duplications; each was assessed for whether it is genuinely extractable logic or incidental/deliberate similarity: - Refactored: the identical isExtensionUpdateRequired guard chain in the Check validator/quickfix extension helpers lifted to AbstractCheckExtensionHelper (keyed on getExtensionPointId() plus a new protected getTargetClassName() hook); the identical private featureIterable helper in Abstract{,Streaming}FingerprintComputer extracted to a shared package-private FingerprintFeatures utility. - Baselined with per-site CPD-OFF markers and reasons: test scaffolding in AbstractValidationTest, sibling test cases in ParameterListMatcherTest, incidental token overlap in DispatchingCheckImpl, migrated Xtend dispatch/emission code in FormatJvmModelInferrer (file-scoped) and FormatFragment2, and sibling test-class scaffolding in the two CheckCfg test classes. The lifted guard chain keeps the overrides' public visibility: the hook is deliberately public so tests can exercise it directly on injected helpers. Full-reactor pmd:cpd-check is clean at threshold 100. Closes #1339. --- .../runtime/issue/DispatchingCheckImpl.java | 4 ++ ...ractCheckDocumentationExtensionHelper.java | 14 ++++ .../util/AbstractCheckExtensionHelper.java | 35 ++++++++++ .../util/CheckPreferencesExtensionHelper.java | 3 +- .../util/CheckQuickfixExtensionHelper.java | 14 +--- .../util/CheckValidatorExtensionHelper.java | 15 +--- .../CheckCfgContentAssistTest.java | 2 + ...CfgConfiguredParameterValidationsTest.java | 2 + .../typesystem/ParameterListMatcherTest.java | 2 + .../format/generator/FormatFragment2.java | 4 ++ .../jvmmodel/FormatJvmModelInferrer.java | 1 + .../test/jupiter/AbstractValidationTest.java | 4 ++ .../resource/AbstractFingerprintComputer.java | 47 +------------ .../AbstractStreamingFingerprintComputer.java | 47 +------------ .../xtext/resource/FingerprintFeatures.java | 70 +++++++++++++++++++ ddk-parent/pom.xml | 2 +- 16 files changed, 153 insertions(+), 113 deletions(-) create mode 100644 com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/FingerprintFeatures.java diff --git a/com.avaloq.tools.ddk.check.runtime.core/src/com/avaloq/tools/ddk/check/runtime/issue/DispatchingCheckImpl.java b/com.avaloq.tools.ddk.check.runtime.core/src/com/avaloq/tools/ddk/check/runtime/issue/DispatchingCheckImpl.java index ada56e5946..59ebacf991 100644 --- a/com.avaloq.tools.ddk.check.runtime.core/src/com/avaloq/tools/ddk/check/runtime/issue/DispatchingCheckImpl.java +++ b/com.avaloq.tools.ddk.check.runtime.core/src/com/avaloq/tools/ddk/check/runtime/issue/DispatchingCheckImpl.java @@ -35,6 +35,7 @@ @SuppressWarnings({"checkstyle:AbstractClassName"}) public abstract class DispatchingCheckImpl extends AbstractCheckImpl { + // CPD-OFF — incidental token overlap (DI field + trace/try idiom), not an extractable unit (#1339) @Inject private ITraceSet traceSet; @@ -72,6 +73,7 @@ public boolean validate(final EClass eClass, final EObject object, final Diagnos State state = new State(); state.chain = diagnostics; + // CPD-ON state.eventCollector = eventCollector; validate(checkMode, object, state); @@ -123,6 +125,7 @@ protected void validate(final String contextName, final String qContextName, fin if (!disabledMethodTracker.isDisabled(contextName)) { Collector eventCollector = diagnosticCollector.getEventCollector(); try { + // CPD-OFF — incidental token overlap (DI field + trace/try idiom), not an extractable unit (#1339) traceStart(qContextName, object, eventCollector); checkAction.run(); } catch (Exception e) { @@ -160,6 +163,7 @@ protected static class State implements ValidationMessageAcceptorMixin, Diagnost // CHECKSTYLE:OFF public DiagnosticChain chain; public CheckType currentCheckType; + // CPD-ON public boolean hasErrors; public ResourceValidationRuleSummaryEvent.Collector eventCollector; // CHECKSTYLE:ON diff --git a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckDocumentationExtensionHelper.java b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckDocumentationExtensionHelper.java index cb48bd7009..3ef2e3c6e2 100644 --- a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckDocumentationExtensionHelper.java +++ b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckDocumentationExtensionHelper.java @@ -44,4 +44,18 @@ protected boolean isExtensionEnabled(final IPluginModelBase base, final CheckCat return !config.isGenerateLanguageInternalChecks(); } + /** + * Documentation extensions do not reference a generated target class, so documentation helpers have no target class name and + * provide their own {@link #isExtensionUpdateRequired} logic that never consults it. + * + * @param catalog + * the check catalog + * @return never returns normally + */ + @SuppressWarnings("PMD.UnusedFormalParameter") + @Override + protected String getTargetClassName(final CheckCatalog catalog) { + throw new UnsupportedOperationException("Documentation extension helpers have no target class"); //$NON-NLS-1$ + } + } diff --git a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckExtensionHelper.java b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckExtensionHelper.java index bdbab27825..ab98d01b5d 100644 --- a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckExtensionHelper.java +++ b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/AbstractCheckExtensionHelper.java @@ -147,6 +147,41 @@ protected boolean isExtensionUpdateRequired(final CheckCatalog catalog, final IP return extension.getPoint().equals(getExtensionPointId()); // if points are different, given extension must not be updated } + /** + * Checks whether a class-referencing extension (validator / quickfix) needs updating: it must point at this helper's + * extension point and reference exactly one element whose target class, language and catalog name still match the + * check catalog. Shared by the validator and quickfix helpers, whose only difference is {@link #getTargetClassName}. + * + * @param catalog + * the catalog + * @param extension + * the extension + * @param elements + * the elements + * @return true, if the extension must be regenerated + */ + protected boolean isTargetClassExtensionUpdateRequired(final CheckCatalog catalog, final IPluginExtension extension, final Iterable elements) { + // CHECKSTYLE:OFF + // @Format-Off + return getExtensionPointId().equals(extension.getPoint()) + && (!extensionNameMatches(extension, catalog) + || Iterables.size(elements) != 1 + || !targetClassMatches(Iterables.get(elements, 0), getTargetClassName(catalog)) + || catalog.getGrammar() == null && Iterables.get(elements, 0).getAttribute(LANGUAGE_ELEMENT_TAG) != null + || catalog.getGrammar() != null && !languageNameMatches(Iterables.get(elements, 0), catalog.getGrammar().getName())); + // @Format-On + // CHECKSTYLE:ON + } + + /** + * Gets the target class name based on the package path of given check catalog. + * + * @param catalog + * the check catalog + * @return the target class FQN + */ + protected abstract String getTargetClassName(CheckCatalog catalog); + /** * Updates a given extension to values calculated using given check catalog. * diff --git a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckPreferencesExtensionHelper.java b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckPreferencesExtensionHelper.java index 01935d770a..fc54260f59 100644 --- a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckPreferencesExtensionHelper.java +++ b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckPreferencesExtensionHelper.java @@ -160,7 +160,8 @@ public String getExtensionPointName(final CheckCatalog catalog) { * the check catalog * @return the target class FQN */ - private String getTargetClassName(final CheckCatalog catalog) { + @Override + protected String getTargetClassName(final CheckCatalog catalog) { return getFromServiceProvider(CheckGeneratorNaming.class, catalog).qualifiedPreferenceInitializerClassName(catalog); } diff --git a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckQuickfixExtensionHelper.java b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckQuickfixExtensionHelper.java index ddb5a3e49f..1761e76dd9 100644 --- a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckQuickfixExtensionHelper.java +++ b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckQuickfixExtensionHelper.java @@ -120,22 +120,14 @@ protected void doUpdateExtension(final CheckCatalog catalog, final IPluginExtens * the check catalog * @return the target class FQN */ - private String getTargetClassName(final CheckCatalog catalog) { + @Override + protected String getTargetClassName(final CheckCatalog catalog) { return getFromServiceProvider(CheckGeneratorNaming.class, catalog).qualifiedQuickfixClassName(catalog); } @Override public boolean isExtensionUpdateRequired(final CheckCatalog catalog, final IPluginExtension extension, final Iterable elements) { - // CHECKSTYLE:OFF - // @Format-Off - return QUICKFIX_EXTENSION_POINT_ID.equals(extension.getPoint()) - && (!extensionNameMatches(extension, catalog) - || Iterables.size(elements) != 1 - || !targetClassMatches(Iterables.get(elements, 0), getTargetClassName(catalog)) - || catalog.getGrammar() == null && Iterables.get(elements, 0).getAttribute(LANGUAGE_ELEMENT_TAG) != null - || catalog.getGrammar() != null && !languageNameMatches(Iterables.get(elements, 0), catalog.getGrammar().getName())); - // @Format-On - // CHECKSTYLE:ON + return isTargetClassExtensionUpdateRequired(catalog, extension, elements); } } diff --git a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckValidatorExtensionHelper.java b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckValidatorExtensionHelper.java index 95fea23bb7..99402fb767 100644 --- a/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckValidatorExtensionHelper.java +++ b/com.avaloq.tools.ddk.check.ui/src/com/avaloq/tools/ddk/check/ui/builder/util/CheckValidatorExtensionHelper.java @@ -112,23 +112,14 @@ private String getCatalogResourceName(final CheckCatalog catalog) { * the check catalog * @return the target class FQN */ - private String getTargetClassName(final CheckCatalog catalog) { + @Override + protected String getTargetClassName(final CheckCatalog catalog) { return getFromServiceProvider(CheckGeneratorNaming.class, catalog).qualifiedValidatorClassName(catalog); } @Override public boolean isExtensionUpdateRequired(final CheckCatalog catalog, final IPluginExtension extension, final Iterable elements) { - // CHECKSTYLE:OFF - // @Format-Off - return CHECK_EXTENSION_POINT_ID.equals(extension.getPoint()) - && (!extensionNameMatches(extension, catalog) - || Iterables.size(elements) != 1 - || !targetClassMatches(Iterables.get(elements, 0), getTargetClassName(catalog)) - || catalog.getGrammar() == null && Iterables.get(elements, 0).getAttribute(LANGUAGE_ELEMENT_TAG) != null - || catalog.getGrammar() != null && !languageNameMatches(Iterables.get(elements, 0), catalog.getGrammar().getName()) - ); - // @Format-On - // CHECKSTYLE:ON + return isTargetClassExtensionUpdateRequired(catalog, extension, elements); } } diff --git a/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/contentassist/CheckCfgContentAssistTest.java b/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/contentassist/CheckCfgContentAssistTest.java index 54b19545d0..20fc4232a9 100644 --- a/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/contentassist/CheckCfgContentAssistTest.java +++ b/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/contentassist/CheckCfgContentAssistTest.java @@ -45,6 +45,7 @@ default Test ( } """; + // CPD-OFF — sibling test-class scaffolding, kept explicit (#1339) @Override protected AbstractXtextTestUtil getXtextTestUtil() { return CheckCfgTestUtil.getInstance(); @@ -70,6 +71,7 @@ protected void afterAllTests() { @Test public void testConfiguredParameterProposals() { + // CPD-ON final String source = SOURCE_TEMPLATE.formatted(TestPropertySpecificationWithExpectedValues.INSTANCE.getName(), expected(TestPropertySpecificationWithExpectedValues.INSTANCE.getExpectedValues())); assertKernelSourceProposals("ConfiguredParameterProposals.checkcfg", source); } diff --git a/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgConfiguredParameterValidationsTest.java b/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgConfiguredParameterValidationsTest.java index cffcfbea44..37b416555e 100644 --- a/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgConfiguredParameterValidationsTest.java +++ b/com.avaloq.tools.ddk.checkcfg.core.test/src/com/avaloq/tools/ddk/checkcfg/validation/CheckCfgConfiguredParameterValidationsTest.java @@ -28,6 +28,7 @@ public class CheckCfgConfiguredParameterValidationsTest extends AbstractValidationTest { + // CPD-OFF — sibling test-class scaffolding, kept explicit (#1339) @Override protected AbstractXtextTestUtil getXtextTestUtil() { return CheckCfgTestUtil.getInstance(); @@ -53,6 +54,7 @@ protected void afterAllTests() { @Test public void testConfiguredParameterValues() { + // CPD-ON final TestPropertySpecificationWithExpectedValues allowedOnly = TestPropertySpecificationWithExpectedValues.INSTANCE; final TestPropertySpecificationWithOutExpectedValues acceptsAny = TestPropertySpecificationWithOutExpectedValues.INSTANCE; final String source = """ diff --git a/com.avaloq.tools.ddk.typesystem.test/src/com/avaloq/tools/ddk/typesystem/ParameterListMatcherTest.java b/com.avaloq.tools.ddk.typesystem.test/src/com/avaloq/tools/ddk/typesystem/ParameterListMatcherTest.java index 84d22023b2..6f3946ace1 100644 --- a/com.avaloq.tools.ddk.typesystem.test/src/com/avaloq/tools/ddk/typesystem/ParameterListMatcherTest.java +++ b/com.avaloq.tools.ddk.typesystem.test/src/com/avaloq/tools/ddk/typesystem/ParameterListMatcherTest.java @@ -893,6 +893,7 @@ void testUnnamedFormalAfterNamed4() { assertSame(unnamedFormal2, matchResult.getUnnamedFormalsAfterNamed().get(1), UNNAMED_FORMAL_AFTER_NAMED_NOT_LOCATED); } + // CPD-OFF — explicit parameterized test cases, kept readable over shared (#1339) @Test void testForceMatchByPosition1() { List formals = new ArrayList(); @@ -946,5 +947,6 @@ void testForceMatchByPosition3() { checkParameterMatch(IParameterMatchChecker.MatchStatus.MATCH, actuals.get(1), formals.get(1), matches.get(1)); checkParameterMatch(IParameterMatchChecker.MatchStatus.MATCH, actuals.get(2), formals.get(2), matches.get(2)); } + // CPD-ON } diff --git a/com.avaloq.tools.ddk.xtext.format.generator/src/com/avaloq/tools/ddk/xtext/format/generator/FormatFragment2.java b/com.avaloq.tools.ddk.xtext.format.generator/src/com/avaloq/tools/ddk/xtext/format/generator/FormatFragment2.java index 241a18250f..044ee384ee 100644 --- a/com.avaloq.tools.ddk.xtext.format.generator/src/com/avaloq/tools/ddk/xtext/format/generator/FormatFragment2.java +++ b/com.avaloq.tools.ddk.xtext.format.generator/src/com/avaloq/tools/ddk/xtext/format/generator/FormatFragment2.java @@ -164,6 +164,7 @@ protected XtendFileAccess doGetXtendStubFile() { protected void appendTo(final TargetStringConcatenation builder) { builder.append("import com.avaloq.tools.ddk.xtext.formatting.ExtendedLineEntry"); builder.newLine(); + // CPD-OFF — parallel Xtend/Java formatter-stub emission, kept explicit (#1339) builder.append("import java.util.List"); builder.newLine(); builder.newLine(); @@ -184,6 +185,7 @@ protected void appendTo(final TargetStringConcatenation builder) { builder.append(" */"); builder.newLine(); builder.append("class "); + // CPD-ON builder.append(getFormatterStub(getGrammar()).getSimpleName()); builder.append(" extends "); builder.append(FormatGeneratorUtil.getFormatterName(getGrammar(), "Abstract")); @@ -251,6 +253,7 @@ protected void appendTo(final TargetStringConcatenation builder) { builder.append("import java.util.List;"); builder.newLine(); builder.newLine(); + // CPD-OFF — parallel Xtend/Java formatter-stub emission, kept explicit (#1339) builder.append("import org.eclipse.xtext.TerminalRule;"); builder.newLine(); builder.newLine(); @@ -271,6 +274,7 @@ protected void appendTo(final TargetStringConcatenation builder) { builder.append(" */"); builder.newLine(); builder.append("public class "); + // CPD-ON builder.append(getFormatterStub(getGrammar()).getSimpleName()); builder.append(" extends "); builder.append(FormatGeneratorUtil.getFormatterName(getGrammar(), "Abstract")); diff --git a/com.avaloq.tools.ddk.xtext.format/src/com/avaloq/tools/ddk/xtext/format/jvmmodel/FormatJvmModelInferrer.java b/com.avaloq.tools.ddk.xtext.format/src/com/avaloq/tools/ddk/xtext/format/jvmmodel/FormatJvmModelInferrer.java index c9e0462fd8..3abd284dce 100644 --- a/com.avaloq.tools.ddk.xtext.format/src/com/avaloq/tools/ddk/xtext/format/jvmmodel/FormatJvmModelInferrer.java +++ b/com.avaloq.tools.ddk.xtext.format/src/com/avaloq/tools/ddk/xtext/format/jvmmodel/FormatJvmModelInferrer.java @@ -110,6 +110,7 @@ */ @SuppressWarnings({"nls", "checkstyle:MethodName", "PMD.UnusedFormalParameter"}) public class FormatJvmModelInferrer extends AbstractModelInferrer { + // CPD-OFF — migrated Xtend dispatch/emission code, kept faithful; de-dup is a migration follow-up (#1339) // CHECKSTYLE:CONSTANTS-OFF the repeated literals are Java source fragments emitted by this generator, not nameable constants // CHECKSTYLE:CHECK-OFF LambdaBodyLength the model-inference closures mirror the Xtext JvmTypesBuilder API and are kept whole diff --git a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jupiter/AbstractValidationTest.java b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jupiter/AbstractValidationTest.java index 822311aa6e..47bf6ce854 100644 --- a/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jupiter/AbstractValidationTest.java +++ b/com.avaloq.tools.ddk.xtext.test.core/src/com/avaloq/tools/ddk/xtext/test/jupiter/AbstractValidationTest.java @@ -377,6 +377,7 @@ public void apply(final EObject root, final Integer pos) { * actual message */ private void createErrorMessage(final Integer pos, final List diagnosticsOnTargetPosition, final boolean issueFound, final boolean expectedSeverityMatches, final int actualSeverity, final boolean expectedMessageMatches, final String actualMessage) { + // CPD-OFF — test scaffolding; per-test clarity beats extraction (#1339) StringBuilder errorMessage = new StringBuilder(200); if (issueMustBeFound && !issueFound) { errorMessage.append("Expected issue not found. Code '").append(issueCode).append('\n'); @@ -398,6 +399,7 @@ private void createErrorMessage(final Integer pos, final List linkingErrors = object.eResource().getErrors().stream().filter(error -> error instanceof XtextLinkingDiagnostic).collect(Collectors.toList()); final List errorMessages = Lists.transform(linkingErrors, Resource.Diagnostic::getMessage); for (final String referenceName : referenceNames) { @@ -1012,6 +1015,7 @@ public static void assertLinkingErrorsOnResourceExist(final EObject object, fina break; } } + // CPD-ON assertTrue(found, NLS.bind("Expected linking error on \"{0}\" but could not find it", referenceName)); } } diff --git a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractFingerprintComputer.java b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractFingerprintComputer.java index 3c6aefbf57..fbed766c6c 100644 --- a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractFingerprintComputer.java +++ b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractFingerprintComputer.java @@ -14,19 +14,15 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Collections; -import java.util.Iterator; import java.util.List; -import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.emf.ecore.util.InternalEList; import org.eclipse.xtext.linking.lazy.LazyLinkingResource; import com.google.common.collect.Iterables; @@ -209,43 +205,6 @@ protected final CharSequence encodeFingerprint(final ExportItem export) { } } - /** - * Return an Iterable containing all the contents of the given feature of the given object. If the - * feature is not many-valued, the resulting iterable will have one element. If the feature is an EReference, - * the iterable may contain proxies. The iterable may contain null values. - * - * @param - * The Generic type of the objects in the iterable - * @param obj - * The object - * @param feature - * The feature - * @return An iterable over all the contents of the feature of the object. - */ - @SuppressWarnings("unchecked") - private Iterable featureIterable(final EObject obj, final EStructuralFeature feature) { - if (feature == null) { - return Collections.emptyList(); - } - if (feature.isMany()) { - if (feature instanceof EAttribute || ((EReference) feature).isContainment()) { - return (Iterable) obj.eGet(feature); - } - return new Iterable() { - @Override - public Iterator iterator() { - EList list = (EList) obj.eGet(feature); - if (list instanceof InternalEList internalList) { - return internalList.basicIterator(); // Don't resolve - } else { - return list.iterator(); - } - } - }; - } - return Collections.singletonList((T) obj.eGet(feature, false)); // Don't resolve - } - /** * Generate a fingerprint for the target object using its URI. * @@ -319,7 +278,7 @@ protected CharSequence fingerprintFeature(final EObject obj, final EReference re if (obj == null) { return NULL_STRING; } - final Iterable targets = featureIterable(obj, ref); + final Iterable targets = FingerprintFeatures.featureIterable(obj, ref); if (targets == null) { return NULL_STRING; } @@ -370,7 +329,7 @@ protected CharSequence fingerprintFeature(final EObject obj, final EAttribute at if (obj == null) { return NULL_STRING; } - final Iterable values = featureIterable(obj, attr); + final Iterable values = FingerprintFeatures.featureIterable(obj, attr); if (values == null) { return NULL_STRING; } @@ -468,7 +427,7 @@ protected ExportItem fingerprintRef(final EObject obj, final EReference ref, fin if (obj == null) { return NO_EXPORT; } - final Iterable targets = featureIterable(obj, ref); + final Iterable targets = FingerprintFeatures.featureIterable(obj, ref); if (targets == null) { return NO_EXPORT; } diff --git a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractStreamingFingerprintComputer.java b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractStreamingFingerprintComputer.java index 24d0712e0e..47594323b2 100644 --- a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractStreamingFingerprintComputer.java +++ b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/AbstractStreamingFingerprintComputer.java @@ -11,19 +11,15 @@ package com.avaloq.tools.ddk.xtext.resource; import java.util.Collections; -import java.util.Iterator; import java.util.List; -import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EAttribute; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; -import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.InternalEObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.emf.ecore.util.InternalEList; import org.eclipse.xtext.linking.lazy.LazyLinkingResource; import com.google.common.collect.Iterables; @@ -133,43 +129,6 @@ protected String computeFingerprint(final Iterable objects) { return hasher.hash().toString(); } - /** - * Return an Iterable containing all the contents of the given feature of the given object. If the - * feature is not many-valued, the resulting iterable will have one element. If the feature is an EReference, - * the iterable may contain proxies. The iterable may contain null values. - * - * @param - * The Generic type of the objects in the iterable - * @param obj - * The object - * @param feature - * The feature - * @return An iterable over all the contents of the feature of the object. - */ - @SuppressWarnings("unchecked") - private Iterable featureIterable(final EObject obj, final EStructuralFeature feature) { - if (feature == null) { - return Collections.emptyList(); - } - if (feature.isMany()) { - if (feature instanceof EAttribute || ((EReference) feature).isContainment()) { - return (Iterable) obj.eGet(feature); - } - return new Iterable() { - @Override - public Iterator iterator() { - EList list = (EList) obj.eGet(feature); - if (list instanceof InternalEList internalList) { - return internalList.basicIterator(); // Don't resolve - } else { - return list.iterator(); - } - } - }; - } - return Collections.singletonList((T) obj.eGet(feature, false)); // Don't resolve - } - /** * Generate a fingerprint for the target object using its URI. * @@ -249,7 +208,7 @@ protected void fingerprintFeature(final EObject obj, final EReference ref, final hasher.putUnencodedChars(NULL_STRING); return; } - final Iterable targets = featureIterable(obj, ref); + final Iterable targets = FingerprintFeatures.featureIterable(obj, ref); if (targets == null) { hasher.putUnencodedChars(NULL_STRING); return; @@ -306,7 +265,7 @@ protected void fingerprintFeature(final EObject obj, final EAttribute attr, fina hasher.putUnencodedChars(NULL_STRING); return; } - final Iterable values = featureIterable(obj, attr); + final Iterable values = FingerprintFeatures.featureIterable(obj, attr); if (values == null) { hasher.putUnencodedChars(NULL_STRING); return; @@ -409,7 +368,7 @@ protected void fingerprintRef(final EObject obj, final EReference ref, final Fin if (obj == null) { return; } - final Iterable targets = featureIterable(obj, ref); + final Iterable targets = FingerprintFeatures.featureIterable(obj, ref); if (targets == null) { return; } diff --git a/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/FingerprintFeatures.java b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/FingerprintFeatures.java new file mode 100644 index 0000000000..cb840d8dfc --- /dev/null +++ b/com.avaloq.tools.ddk.xtext/src/com/avaloq/tools/ddk/xtext/resource/FingerprintFeatures.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2016 Avaloq Group AG 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: + * Avaloq Group AG - initial API and implementation + *******************************************************************************/ +package com.avaloq.tools.ddk.xtext.resource; + +import java.util.Collections; +import java.util.Iterator; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.ecore.util.InternalEList; + + +/** + * Shared helpers for iterating over the contents of {@link EStructuralFeature}s when computing fingerprints. + */ +final class FingerprintFeatures { + + private FingerprintFeatures() { + // utility class + } + + /** + * Return an Iterable containing all the contents of the given feature of the given object. If the + * feature is not many-valued, the resulting iterable will have one element. If the feature is an EReference, + * the iterable may contain proxies. The iterable may contain null values. + * + * @param + * The Generic type of the objects in the iterable + * @param obj + * The object + * @param feature + * The feature + * @return An iterable over all the contents of the feature of the object. + */ + @SuppressWarnings("unchecked") + static Iterable featureIterable(final EObject obj, final EStructuralFeature feature) { + if (feature == null) { + return Collections.emptyList(); + } + if (feature.isMany()) { + if (feature instanceof EAttribute || ((EReference) feature).isContainment()) { + return (Iterable) obj.eGet(feature); + } + return new Iterable() { + @Override + public Iterator iterator() { + EList list = (EList) obj.eGet(feature); + if (list instanceof InternalEList internalList) { + return internalList.basicIterator(); // Don't resolve + } else { + return list.iterator(); + } + } + }; + } + return Collections.singletonList((T) obj.eGet(feature, false)); // Don't resolve + } + +} diff --git a/ddk-parent/pom.xml b/ddk-parent/pom.xml index acf55302f5..24e0e03818 100644 --- a/ddk-parent/pom.xml +++ b/ddk-parent/pom.xml @@ -32,7 +32,7 @@ true ${workspace}/ddk-configuration/pmd/ruleset.xml - 100000 + 100 ${workspace}/ddk-configuration/checkstyle/avaloq.xml ${workspace}/ddk-configuration/checkstyle/avaloq-test.xml ${workspace}/ddk-configuration/findbugs/exclusion-filter.xml