Split cpd/pmd specific methods into...

subinterfaces of Language
This commit is contained in:
Clément Fournier
2023-02-13 14:53:14 +01:00
parent d6ec427aa8
commit 9c3434a07b
20 changed files with 115 additions and 95 deletions

View File

@ -4,8 +4,9 @@
package net.sourceforge.pmd.lang.apex;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.PmdCapableLanguage;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageModuleBase;
import net.sourceforge.pmd.lang.LanguageProcessor;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
@ -14,7 +15,7 @@ import net.sourceforge.pmd.lang.apex.cpd.ApexTokenizer;
import apex.jorje.services.Version;
public class ApexLanguageModule extends LanguageModuleBase {
public class ApexLanguageModule extends LanguageModuleBase implements PmdCapableLanguage, CpdCapableLanguage {
public static final String NAME = "Apex";
public static final String TERSE_NAME = "apex";
@ -39,7 +40,7 @@ public class ApexLanguageModule extends LanguageModuleBase {
return new ApexTokenizer((ApexLanguageProperties) bundle);
}
public static Language getInstance() {
return LanguageRegistry.PMD.getLanguageByFullName(NAME);
public static ApexLanguageModule getInstance() {
return (ApexLanguageModule) LanguageRegistry.PMD.getLanguageById(TERSE_NAME);
}
}

View File

@ -136,7 +136,8 @@ public final class CpdAnalysis implements AutoCloseable {
sourceManager.getTextFiles().stream()
.map(it -> it.getLanguageVersion().getLanguage())
.distinct()
.collect(Collectors.toMap(lang -> lang, lang -> lang.createCpdTokenizer(configuration.getLanguageProperties(lang))));
.filter(it -> it instanceof CpdCapableLanguage)
.collect(Collectors.toMap(lang -> lang, lang -> ((CpdCapableLanguage) lang).createCpdTokenizer(configuration.getLanguageProperties(lang))));
Map<String, Integer> numberOfTokensPerFile = new HashMap<>();

View File

@ -0,0 +1,32 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cpd;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
/**
* A language that also supports {@link CpdAnalysis CPD}.
*
* @author Clément Fournier
*/
public interface CpdCapableLanguage extends Language {
/**
* Create a new {@link Tokenizer} for this language, given
* a property bundle with configuration. The bundle was created by
* this instance using {@link #newPropertyBundle()}. It can be assumed
* that the bundle will never be mutated anymore, and this method
* takes ownership of it.
*
* @param bundle A bundle of properties created by this instance.
*
* @return A new language processor
*/
default Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) {
return new AnyTokenizer();
}
}

View File

@ -0,0 +1,31 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.cpd;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageProcessor;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
/**
* A language that also supports {@link CpdAnalysis CPD}.
*
* @author Clément Fournier
*/
public interface PmdCapableLanguage extends Language {
/**
* Create a new {@link LanguageProcessor} for this language, given
* a property bundle with configuration. The bundle was created by
* this instance using {@link #newPropertyBundle()}. It can be assumed
* that the bundle will never be mutated anymore, and this method
* takes ownership of it.
*
* @param bundle A bundle of properties created by this instance.
*
* @return A new language processor
*/
LanguageProcessor createProcessor(LanguagePropertyBundle bundle);
}

View File

@ -8,9 +8,6 @@ import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import net.sourceforge.pmd.cpd.AnyTokenizer;
import net.sourceforge.pmd.cpd.Tokenizer;
/**
* Represents a language module, and provides access to language-specific
* functionality. You can get a language instance from a {@link LanguageRegistry}.
@ -159,50 +156,6 @@ public interface Language extends Comparable<Language> {
return new LanguagePropertyBundle(this);
}
/**
* Return true if this language supports parsing files into an AST.
* In that case {@link #createProcessor(LanguagePropertyBundle)} should
* also be implemented.
*/
default boolean supportsParsing() {
return false;
}
/**
* Create a new {@link LanguageProcessor} for this language, given
* a property bundle with configuration. The bundle was created by
* this instance using {@link #newPropertyBundle()}. It can be assumed
* that the bundle will never be mutated anymore, and this method
* takes ownership of it.
*
* @param bundle A bundle of properties created by this instance.
*
* @return A new language processor
*
* @throws UnsupportedOperationException if this language does not support PMD
*/
default LanguageProcessor createProcessor(LanguagePropertyBundle bundle) {
throw new UnsupportedOperationException(this + " does not support running a PMD analysis.");
}
/**
* Create a new {@link Tokenizer} for this language, given
* a property bundle with configuration. The bundle was created by
* this instance using {@link #newPropertyBundle()}. It can be assumed
* that the bundle will never be mutated anymore, and this method
* takes ownership of it.
*
* @param bundle A bundle of properties created by this instance.
*
* @return A new language processor
*
* @throws UnsupportedOperationException if this language does not support CPD
*/
default Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) {
return new AnyTokenizer();
}
/**
* Returns a set of the IDs of languages that this language instance

View File

@ -18,6 +18,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.cpd.PmdCapableLanguage;
import net.sourceforge.pmd.internal.util.IOUtil;
import net.sourceforge.pmd.properties.PropertyDescriptor;
import net.sourceforge.pmd.properties.PropertySource;
@ -119,6 +120,10 @@ public final class LanguageProcessorRegistry implements AutoCloseable {
MessageReporter messageReporter) {
Set<LanguageProcessor> processors = new HashSet<>();
for (Language language : registry) {
if (!(language instanceof PmdCapableLanguage)) {
LOG.trace("Not instantiating language {} because it does not support PMD", language);
continue;
}
LanguagePropertyBundle properties = languageProperties.getOrDefault(language, language.newPropertyBundle());
if (!properties.getLanguage().equals(language)) {
throw new IllegalArgumentException("Mismatched language");
@ -128,7 +133,7 @@ public final class LanguageProcessorRegistry implements AutoCloseable {
//
readLanguagePropertiesFromEnv(properties, messageReporter);
processors.add(language.createProcessor(properties));
processors.add(((PmdCapableLanguage) language).createProcessor(properties));
} catch (IllegalArgumentException e) {
messageReporter.error(e); // todo
}

View File

@ -25,6 +25,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.PmdCapableLanguage;
import net.sourceforge.pmd.util.CollectionUtil;
/**
@ -44,13 +46,13 @@ public final class LanguageRegistry implements Iterable<Language> {
* Contains the languages that support PMD and are found on the classpath
* of the classloader of this class. This can be used as a "default" registry.
*/
public static final LanguageRegistry PMD = ALL_LANGUAGES.filter(Language::supportsParsing);
public static final LanguageRegistry PMD = ALL_LANGUAGES.filter(it -> it instanceof PmdCapableLanguage);
/**
* Contains the languages that support CPD and are found on the classpath
* of the classloader of this class.
*/
public static final LanguageRegistry CPD = ALL_LANGUAGES;
public static final LanguageRegistry CPD = ALL_LANGUAGES.filter(it -> it instanceof CpdCapableLanguage);
private final Set<Language> languages;

View File

@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.cpd.AnyTokenizer;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.ast.AstInfo;
import net.sourceforge.pmd.lang.ast.Parser;
@ -26,7 +27,7 @@ import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase;
* @since 6.48.0
*/
@Experimental
public final class PlainTextLanguage extends SimpleLanguageModuleBase {
public final class PlainTextLanguage extends SimpleLanguageModuleBase implements CpdCapableLanguage {
private static final Language INSTANCE = new PlainTextLanguage();

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.impl;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.LanguageModuleBase;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
@ -13,7 +14,7 @@ import net.sourceforge.pmd.lang.LanguagePropertyBundle;
*
* @author Clément Fournier
*/
public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase {
public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase implements CpdCapableLanguage {
/**
* Construct a module instance using the given metadata. The metadata must
@ -25,11 +26,6 @@ public abstract class CpdOnlyLanguageModuleBase extends LanguageModuleBase {
super(metadata);
}
@Override
public boolean supportsParsing() {
return false;
}
@Override
public abstract Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle);
}

View File

@ -8,6 +8,8 @@ import java.util.function.Function;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.PmdCapableLanguage;
import net.sourceforge.pmd.lang.LanguageModuleBase;
import net.sourceforge.pmd.lang.LanguageProcessor;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
@ -15,12 +17,13 @@ import net.sourceforge.pmd.lang.LanguageVersionHandler;
/**
* The simplest implementation of a language, where only a {@link LanguageVersionHandler}
* needs to be implemented.
* needs to be implemented. A default {@link CpdCapableLanguage} implementation
* is provided.
*
* @author Clément Fournier
* @since 7.0.0
*/
public abstract class SimpleLanguageModuleBase extends LanguageModuleBase {
public class SimpleLanguageModuleBase extends LanguageModuleBase implements PmdCapableLanguage, CpdCapableLanguage {
private final Function<LanguagePropertyBundle, LanguageVersionHandler> handler;
@ -33,11 +36,6 @@ public abstract class SimpleLanguageModuleBase extends LanguageModuleBase {
this.handler = makeHandler;
}
@Override
public boolean supportsParsing() {
return true;
}
@Override
public LanguageProcessor createProcessor(LanguagePropertyBundle bundle) {
LanguageVersionHandler services = handler.apply(bundle);

View File

@ -14,7 +14,6 @@ import java.util.Iterator;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.lang.DummyLanguageModule;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.document.TextDocument;
import net.sourceforge.pmd.lang.document.TextFile;
@ -36,7 +35,7 @@ class MatchAlgorithmTest {
@Test
void testSimple() throws IOException {
Language dummy = DummyLanguageModule.getInstance();
DummyLanguageModule dummy = DummyLanguageModule.getInstance();
Tokenizer tokenizer = dummy.createCpdTokenizer(dummy.newPropertyBundle());
String fileName = "Foo.dummy";
TextFile textFile = TextFile.forCharSeq(getSampleCode(), fileName, dummy.getDefaultVersion());

View File

@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang;
import java.util.Objects;
import net.sourceforge.pmd.RuleViolation;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.ast.DummyNode;
import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode;
@ -22,7 +23,7 @@ import net.sourceforge.pmd.reporting.ViolationDecorator;
/**
* Dummy language used for testing PMD.
*/
public class DummyLanguageModule extends SimpleLanguageModuleBase {
public class DummyLanguageModule extends SimpleLanguageModuleBase implements CpdCapableLanguage {
public static final String NAME = "Dummy";
public static final String TERSE_NAME = "dummy";

View File

@ -13,6 +13,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import net.sourceforge.pmd.lang.LanguageModuleBase.LanguageMetadata;
import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase;
/**
* @author Clément Fournier
@ -57,12 +58,9 @@ class LanguageModuleBaseTest {
}
private static LanguageModuleBase makeLanguage(LanguageMetadata meta) {
return new LanguageModuleBase(meta) {
@Override
public LanguageProcessor createProcessor(LanguagePropertyBundle bundle) {
throw new UnsupportedOperationException("fake instance");
}
};
return new SimpleLanguageModuleBase(meta, p -> {
throw new UnsupportedOperationException("fake instance");
});
}

View File

@ -4,8 +4,9 @@
package net.sourceforge.pmd.lang.java;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.PmdCapableLanguage;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageModuleBase;
import net.sourceforge.pmd.lang.LanguageProcessor;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
@ -17,7 +18,7 @@ import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties;
/**
* Created by christoferdutz on 20.09.14.
*/
public class JavaLanguageModule extends LanguageModuleBase {
public class JavaLanguageModule extends LanguageModuleBase implements PmdCapableLanguage, CpdCapableLanguage {
public static final String NAME = "Java";
public static final String TERSE_NAME = "java";
@ -61,7 +62,7 @@ public class JavaLanguageModule extends LanguageModuleBase {
return new JavaTokenizer((JavaLanguageProperties) bundle);
}
public static Language getInstance() {
return LanguageRegistry.PMD.getLanguageByFullName(NAME);
public static JavaLanguageModule getInstance() {
return (JavaLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME);
}
}

View File

@ -5,7 +5,6 @@
package net.sourceforge.pmd.lang.ecmascript;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.ecmascript.ast.EcmascriptParser;
@ -26,8 +25,8 @@ public class EcmascriptLanguageModule extends SimpleLanguageModuleBase {
properties -> () -> new EcmascriptParser(properties));
}
public static Language getInstance() {
return LanguageRegistry.PMD.getLanguageByFullName(NAME);
public static EcmascriptLanguageModule getInstance() {
return (EcmascriptLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME);
}
@Override

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.jsp;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase;
@ -12,7 +13,7 @@ import net.sourceforge.pmd.lang.jsp.cpd.JSPTokenizer;
/**
* Created by christoferdutz on 20.09.14.
*/
public class JspLanguageModule extends SimpleLanguageModuleBase {
public class JspLanguageModule extends SimpleLanguageModuleBase implements CpdCapableLanguage {
public static final String NAME = "Java Server Pages";
public static final String TERSE_NAME = "jsp";

View File

@ -24,12 +24,12 @@ import java.util.*
* Baseline files are saved in txt files.
*/
abstract class CpdTextComparisonTest(
val language: Language,
val language: CpdCapableLanguage,
override val extensionIncludingDot: String
) : BaseTextComparisonTest() {
constructor(langId: String, extensionIncludingDot: String) : this(
LanguageRegistry.CPD.getLanguageById(langId)!!,
LanguageRegistry.CPD.getLanguageById(langId) as CpdCapableLanguage,
extensionIncludingDot
)

View File

@ -4,6 +4,7 @@
package net.sourceforge.pmd.lang.ast.test
import net.sourceforge.pmd.*
import net.sourceforge.pmd.cpd.PmdCapableLanguage
import net.sourceforge.pmd.internal.util.IOUtil
import net.sourceforge.pmd.lang.*
import net.sourceforge.pmd.lang.ast.Node
@ -64,9 +65,9 @@ abstract class BaseParsingHelper<Self : BaseParsingHelper<Self, T>, T : RootNode
?: throw AssertionError("Unsupported version $version for language $language")
}
val language: Language
val language: PmdCapableLanguage
get() =
params.languageRegistry.getLanguageByFullName(langName)
params.languageRegistry.getLanguageByFullName(langName) as? PmdCapableLanguage?
?: run {
val langNames = params.languageRegistry.commaSeparatedList { it.name }
throw AssertionError("'$langName' is not a supported language (available $langNames)")

View File

@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.plsql;
import net.sourceforge.pmd.cpd.PLSQLTokenizer;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase;
@ -50,7 +49,7 @@ public class PLSQLLanguageModule extends SimpleLanguageModuleBase {
return new PLSQLTokenizer(bundle);
}
public static Language getInstance() {
return LanguageRegistry.PMD.getLanguageById("plsql");
public static PLSQLLanguageModule getInstance() {
return (PLSQLLanguageModule) LanguageRegistry.PMD.getLanguageById("plsql");
}
}

View File

@ -4,19 +4,20 @@
package net.sourceforge.pmd.lang.vf;
import net.sourceforge.pmd.cpd.CpdCapableLanguage;
import net.sourceforge.pmd.cpd.Tokenizer;
import net.sourceforge.pmd.lang.vf.cpd.VfTokenizer;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.apex.ApexLanguageModule;
import net.sourceforge.pmd.lang.impl.SimpleLanguageModuleBase;
import net.sourceforge.pmd.lang.vf.cpd.VfTokenizer;
/**
* @author sergey.gorbaty
*/
public class VfLanguageModule extends SimpleLanguageModuleBase {
public class VfLanguageModule extends SimpleLanguageModuleBase implements CpdCapableLanguage {
public static final String NAME = "Salesforce VisualForce";
public static final String TERSE_NAME = "vf";