Change a ton of stuff in CPD
This commit is contained in:
64 files changed
+543
-901
No files matched your search
@@ -1,25 +0,0 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
public class ApexLanguage extends AbstractLanguage {
|
||||
|
||||
public ApexLanguage() {
|
||||
this(new Properties());
|
||||
}
|
||||
|
||||
public ApexLanguage(Properties properties) {
|
||||
super("Apex", "apex", new ApexTokenizer(), ".cls");
|
||||
setProperties(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setProperties(Properties properties) {
|
||||
ApexTokenizer tokenizer = (ApexTokenizer) getTokenizer();
|
||||
tokenizer.setProperties(properties);
|
||||
}
|
||||
}
|
||||
@@ -5,67 +5,30 @@
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.antlr.runtime.ANTLRStringStream;
|
||||
import org.antlr.runtime.Lexer;
|
||||
import org.antlr.runtime.Token;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
|
||||
import net.sourceforge.pmd.lang.apex.ApexJorjeLogging;
|
||||
import net.sourceforge.pmd.lang.ast.TokenMgrError;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
import net.sourceforge.pmd.cpd.internal.AntlrTokenizer;
|
||||
import net.sourceforge.pmd.lang.apex.ApexLanguageProperties;
|
||||
import net.sourceforge.pmd.lang.ast.impl.antlr4.AntlrToken;
|
||||
|
||||
import apex.jorje.parser.impl.ApexLexer;
|
||||
public class ApexTokenizer extends AntlrTokenizer {
|
||||
private final boolean caseSensitive;
|
||||
|
||||
public class ApexTokenizer implements Tokenizer {
|
||||
|
||||
public ApexTokenizer() {
|
||||
ApexJorjeLogging.disableLogging();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the properties is <code>false</code> (default), then the case of any token
|
||||
* is ignored.
|
||||
*/
|
||||
public static final String CASE_SENSITIVE = "net.sourceforge.pmd.cpd.ApexTokenizer.caseSensitive";
|
||||
|
||||
private boolean caseSensitive;
|
||||
|
||||
public void setProperties(Properties properties) {
|
||||
caseSensitive = Boolean.parseBoolean(properties.getProperty(CASE_SENSITIVE, "false"));
|
||||
public ApexTokenizer(ApexLanguageProperties properties) {
|
||||
this.caseSensitive = properties.getProperty(Tokenizer.CPD_CASE_SENSITIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tokenize(TextDocument sourceCode, Tokens tokenEntries) {
|
||||
StringBuilder code = sourceCode.getCodeBuffer();
|
||||
|
||||
ANTLRStringStream ass = new ANTLRStringStream(code.toString());
|
||||
ApexLexer lexer = new ApexLexer(ass) {
|
||||
@Override
|
||||
public void emitErrorMessage(String msg) {
|
||||
throw new TokenMgrError(getLine(), getCharPositionInLine(), getSourceName(), msg, null);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
Token token = lexer.nextToken();
|
||||
|
||||
while (token.getType() != Token.EOF) {
|
||||
if (token.getChannel() != Lexer.HIDDEN) {
|
||||
String tokenText = token.getText();
|
||||
if (!caseSensitive) {
|
||||
tokenText = tokenText.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
TokenEntry tokenEntry = new TokenEntry(tokenText, sourceCode.getFileName(),
|
||||
token.getLine(),
|
||||
token.getCharPositionInLine() + 1,
|
||||
token.getCharPositionInLine() + tokenText.length() + 1);
|
||||
tokenEntries.add(tokenEntry);
|
||||
}
|
||||
token = lexer.nextToken();
|
||||
}
|
||||
} finally {
|
||||
tokenEntries.add(TokenEntry.getEOF());
|
||||
protected String getImage(AntlrToken token) {
|
||||
if (caseSensitive) {
|
||||
return token.getImage();
|
||||
}
|
||||
return token.getImage().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected org.antlr.v4.runtime.Lexer getLexerForSource(CharStream charStream) {
|
||||
return new com.nawforce.runtime.parsers.ApexLexer(charStream);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.apex;
|
||||
|
||||
import net.sourceforge.pmd.cpd.ApexTokenizer;
|
||||
import net.sourceforge.pmd.cpd.Tokenizer;
|
||||
import net.sourceforge.pmd.lang.Language;
|
||||
import net.sourceforge.pmd.lang.LanguageModuleBase;
|
||||
import net.sourceforge.pmd.lang.LanguageProcessor;
|
||||
@@ -32,6 +34,11 @@ public class ApexLanguageModule extends LanguageModuleBase {
|
||||
return new ApexLanguageProcessor((ApexLanguageProperties) bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tokenizer createCpdTokenizer(LanguagePropertyBundle bundle) {
|
||||
return new ApexTokenizer((ApexLanguageProperties) bundle);
|
||||
}
|
||||
|
||||
public static Language getInstance() {
|
||||
return LanguageRegistry.PMD.getLanguageByFullName(NAME);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.apex;
|
||||
|
||||
import net.sourceforge.pmd.cpd.Tokenizer;
|
||||
import net.sourceforge.pmd.lang.LanguagePropertyBundle;
|
||||
import net.sourceforge.pmd.properties.PropertyDescriptor;
|
||||
import net.sourceforge.pmd.properties.PropertyFactory;
|
||||
@@ -23,6 +24,7 @@ public class ApexLanguageProperties extends LanguagePropertyBundle {
|
||||
public ApexLanguageProperties() {
|
||||
super(ApexLanguageModule.getInstance());
|
||||
definePropertyDescriptor(MULTIFILE_DIRECTORY);
|
||||
definePropertyDescriptor(Tokenizer.CPD_CASE_SENSITIVE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
net.sourceforge.pmd.cpd.ApexLanguage
|
||||
@@ -4,16 +4,16 @@
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import net.sourceforge.pmd.cpd.test.CpdTextComparisonTest;
|
||||
import net.sourceforge.pmd.cpd.test.LanguagePropertyConfig;
|
||||
import net.sourceforge.pmd.lang.apex.ApexLanguageModule;
|
||||
|
||||
class ApexTokenizerTest extends CpdTextComparisonTest {
|
||||
|
||||
ApexTokenizerTest() {
|
||||
super(".cls");
|
||||
super(ApexLanguageModule.getInstance(), ".cls");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -21,13 +21,6 @@ class ApexTokenizerTest extends CpdTextComparisonTest {
|
||||
return "../lang/apex/cpd/testdata";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tokenizer newTokenizer(Properties properties) {
|
||||
ApexTokenizer tokenizer = new ApexTokenizer();
|
||||
tokenizer.setProperties(properties);
|
||||
return tokenizer;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testTokenize() {
|
||||
@@ -52,14 +45,12 @@ class ApexTokenizerTest extends CpdTextComparisonTest {
|
||||
doTest("tabWidth");
|
||||
}
|
||||
|
||||
private Properties caseSensitive() {
|
||||
private LanguagePropertyConfig caseSensitive() {
|
||||
return properties(true);
|
||||
}
|
||||
|
||||
private Properties properties(boolean caseSensitive) {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(ApexTokenizer.CASE_SENSITIVE, Boolean.toString(caseSensitive));
|
||||
return properties;
|
||||
private LanguagePropertyConfig properties(boolean caseSensitive) {
|
||||
return properties -> properties.setProperty(Tokenizer.CPD_CASE_SENSITIVE, caseSensitive);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -116,7 +116,7 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand {
|
||||
configuration.setSkipDuplicates(skipDuplicates);
|
||||
configuration.setSkipLexicalErrors(skipLexicalErrors);
|
||||
configuration.setSourceEncoding(encoding.getEncoding().name());
|
||||
configuration.setURI(uri == null ? null : uri.toString());
|
||||
configuration.setURI(uri);
|
||||
|
||||
configuration.postContruct();
|
||||
// Pass extra parameters as System properties to allow language
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Zev Blut zb@ubit.com
|
||||
* @author Romain PELISSE belaran@gmail.com
|
||||
*
|
||||
* @deprecated Use an {@link AnyTokenizer} instead, it's basically as powerful.
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class AbstractTokenizer implements Tokenizer {
|
||||
|
||||
// FIXME depending on subclasses to assign local vars is rather fragile -
|
||||
// better to make private and setup via explicit hook methods
|
||||
|
||||
protected List<String> stringToken; // List<String>, should be set by sub
|
||||
// classes
|
||||
protected List<String> ignorableCharacter; // List<String>, should be set by
|
||||
// sub classes
|
||||
// FIXME:Maybe an array of 'char'
|
||||
// would be better for
|
||||
// performance ?
|
||||
protected List<String> ignorableStmt; // List<String>, should be set by sub
|
||||
// classes
|
||||
protected char oneLineCommentChar = '#'; // Most script languages ( shell,
|
||||
// ruby, python,...) use this
|
||||
// symbol for comment line
|
||||
|
||||
private List<String> code;
|
||||
private int lineNumber = 0;
|
||||
private String currentLine;
|
||||
|
||||
// both zero-based
|
||||
private int tokBeginLine;
|
||||
private int tokBeginCol;
|
||||
|
||||
protected boolean spanMultipleLinesString = true; // Most languages do, so
|
||||
// default is true
|
||||
protected Character spanMultipleLinesLineContinuationCharacter = null;
|
||||
|
||||
private boolean downcaseString = true;
|
||||
|
||||
@Override
|
||||
public void tokenize(TextDocument tokens, Tokens tokenEntries) {
|
||||
code = tokens.getCode();
|
||||
|
||||
for (lineNumber = 0; lineNumber < code.size(); lineNumber++) {
|
||||
currentLine = code.get(lineNumber);
|
||||
int loc = 0;
|
||||
while (loc < currentLine.length()) {
|
||||
StringBuilder token = new StringBuilder();
|
||||
loc = getTokenFromLine(token, loc); // may jump several lines
|
||||
|
||||
if (token.length() > 0 && !isIgnorableString(token.toString())) {
|
||||
final String image;
|
||||
if (downcaseString) {
|
||||
image = token.toString().toLowerCase(Locale.ROOT);
|
||||
} else {
|
||||
image = token.toString();
|
||||
}
|
||||
|
||||
tokenEntries.add(new TokenEntry(image,
|
||||
tokens.getFileName(),
|
||||
tokBeginLine + 1,
|
||||
tokBeginCol + 1,
|
||||
loc + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
tokenEntries.add(TokenEntry.getEOF());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns (0-based) EXclusive offset of the end of the token,
|
||||
* may jump several lines (sets {@link #lineNumber} in this case).
|
||||
*/
|
||||
private int getTokenFromLine(StringBuilder token, int loc) {
|
||||
tokBeginLine = lineNumber;
|
||||
tokBeginCol = loc;
|
||||
|
||||
for (int j = loc; j < currentLine.length(); j++) {
|
||||
char tok = currentLine.charAt(j);
|
||||
if (!Character.isWhitespace(tok) && !ignoreCharacter(tok)) {
|
||||
if (isComment(tok)) {
|
||||
if (token.length() > 0) {
|
||||
return j;
|
||||
} else {
|
||||
return getCommentToken(token, loc);
|
||||
}
|
||||
} else if (isString(tok)) {
|
||||
if (token.length() > 0) {
|
||||
return j; // we need to now parse the string as a
|
||||
// separate token.
|
||||
} else {
|
||||
// we are at the start of a string
|
||||
return parseString(token, j, tok);
|
||||
}
|
||||
} else {
|
||||
token.append(tok);
|
||||
}
|
||||
} else {
|
||||
if (token.length() > 0) {
|
||||
return j;
|
||||
} else {
|
||||
// ignored char
|
||||
tokBeginCol++;
|
||||
}
|
||||
}
|
||||
loc = j;
|
||||
}
|
||||
return loc + 1;
|
||||
}
|
||||
|
||||
private int parseString(StringBuilder token, int loc, char stringDelimiter) {
|
||||
boolean escaped = false;
|
||||
boolean done = false;
|
||||
char tok;
|
||||
while (loc < currentLine.length() && !done) {
|
||||
tok = currentLine.charAt(loc);
|
||||
if (escaped && tok == stringDelimiter) { // Found an escaped string
|
||||
escaped = false;
|
||||
} else if (tok == stringDelimiter && token.length() > 0) {
|
||||
// We are done, we found the end of the string...
|
||||
done = true;
|
||||
} else {
|
||||
// Found an escaped char?
|
||||
escaped = tok == '\\';
|
||||
}
|
||||
// Adding char to String:" + token.toString());
|
||||
token.append(tok);
|
||||
loc++;
|
||||
}
|
||||
// Handling multiple lines string
|
||||
if (!done // ... we didn't find the end of the string (but the end of the line)
|
||||
&& spanMultipleLinesString // ... the language allow multiple line span Strings
|
||||
&& lineNumber < code.size() - 1 // ... there is still more lines to parse
|
||||
) {
|
||||
// removes last character, if it is the line continuation (e.g.
|
||||
// backslash) character
|
||||
if (spanMultipleLinesLineContinuationCharacter != null
|
||||
&& token.length() > 0
|
||||
&& token.charAt(token.length() - 1) == spanMultipleLinesLineContinuationCharacter) {
|
||||
token.setLength(token.length() - 1);
|
||||
}
|
||||
// parsing new line
|
||||
currentLine = code.get(++lineNumber);
|
||||
// Warning : recursive call !
|
||||
loc = parseString(token, 0, stringDelimiter);
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
private boolean ignoreCharacter(char tok) {
|
||||
return ignorableCharacter.contains(String.valueOf(tok));
|
||||
}
|
||||
|
||||
private boolean isString(char tok) {
|
||||
return stringToken.contains(String.valueOf(tok));
|
||||
}
|
||||
|
||||
private boolean isComment(char tok) {
|
||||
return tok == oneLineCommentChar;
|
||||
}
|
||||
|
||||
private int getCommentToken(StringBuilder token, int loc) {
|
||||
while (loc < currentLine.length()) {
|
||||
token.append(currentLine.charAt(loc++));
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
private boolean isIgnorableString(String token) {
|
||||
return ignorableStmt.contains(token);
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import net.sourceforge.pmd.lang.document.Chars;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
import net.sourceforge.pmd.util.StringUtil;
|
||||
|
||||
@@ -61,36 +62,32 @@ public class AnyTokenizer implements Tokenizer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tokenize(TextDocument sourceCode, Tokens tokenEntries) {
|
||||
CharSequence text = sourceCode.getCodeBuffer();
|
||||
public void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) {
|
||||
Chars text = sourceCode.getText();
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
int lineNo = 1;
|
||||
int lastLineStart = 0;
|
||||
try {
|
||||
while (matcher.find()) {
|
||||
String image = matcher.group();
|
||||
if (isComment(image)) {
|
||||
continue;
|
||||
} else if (StringUtils.isWhitespace(image)) {
|
||||
lineNo++;
|
||||
lastLineStart = matcher.end();
|
||||
continue;
|
||||
}
|
||||
|
||||
int bline = lineNo;
|
||||
int bcol = 1 + matcher.start() - lastLineStart; // + 1 because columns are 1 based
|
||||
int ecol = StringUtil.columnNumberAt(image, image.length()); // this already outputs a 1-based column
|
||||
if (ecol == image.length() + 1) {
|
||||
ecol = bcol + image.length(); // single-line token
|
||||
} else {
|
||||
// multiline, need to update the line count
|
||||
lineNo += StringUtil.lineNumberAt(image, image.length()) - 1;
|
||||
lastLineStart = matcher.start() + image.length() - ecol + 1;
|
||||
}
|
||||
tokenEntries.add(new TokenEntry(image, sourceCode.getFileName(), bline, bcol, ecol));
|
||||
while (matcher.find()) {
|
||||
String image = matcher.group();
|
||||
if (isComment(image)) {
|
||||
continue;
|
||||
} else if (StringUtils.isWhitespace(image)) {
|
||||
lineNo++;
|
||||
lastLineStart = matcher.end();
|
||||
continue;
|
||||
}
|
||||
} finally {
|
||||
tokenEntries.add(TokenEntry.getEOF());
|
||||
|
||||
int bline = lineNo;
|
||||
int bcol = 1 + matcher.start() - lastLineStart; // + 1 because columns are 1 based
|
||||
int ecol = StringUtil.columnNumberAt(image, image.length()); // this already outputs a 1-based column
|
||||
if (ecol == image.length() + 1) {
|
||||
ecol = bcol + image.length(); // single-line token
|
||||
} else {
|
||||
// multiline, need to update the line count
|
||||
lineNo += StringUtil.lineNumberAt(image, image.length()) - 1;
|
||||
lastLineStart = matcher.start() + image.length() - ecol + 1;
|
||||
}
|
||||
tokenEntries.recordToken(image, bline, bcol, lineNo, ecol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ import net.sourceforge.pmd.internal.util.FileFinder;
|
||||
import net.sourceforge.pmd.internal.util.FileUtil;
|
||||
import net.sourceforge.pmd.internal.util.IOUtil;
|
||||
import net.sourceforge.pmd.lang.ast.TokenMgrError;
|
||||
import net.sourceforge.pmd.lang.document.SourceCode;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
import net.sourceforge.pmd.lang.document.TextFile;
|
||||
import net.sourceforge.pmd.util.database.DBMSMetadata;
|
||||
import net.sourceforge.pmd.util.database.DBURI;
|
||||
import net.sourceforge.pmd.util.database.SourceObject;
|
||||
@@ -41,7 +44,7 @@ public class CPD {
|
||||
|
||||
private CPDConfiguration configuration;
|
||||
|
||||
private Map<String, SourceCode> source = new TreeMap<>();
|
||||
private final SourceManager sourceManager = new SourceManager();
|
||||
private CPDListener listener = new CPDNullListener();
|
||||
private Tokens tokens = new Tokens();
|
||||
private MatchAlgorithm matchAlgorithm;
|
||||
@@ -128,8 +131,8 @@ public class CPD {
|
||||
}
|
||||
|
||||
public void go() {
|
||||
log.debug("Running match algorithm on {} files...", source.size());
|
||||
matchAlgorithm = new MatchAlgorithm(source, tokens, configuration.getMinimumTileSize(), listener);
|
||||
log.debug("Running match algorithm on {} files...", sourceManager.size());
|
||||
matchAlgorithm = new MatchAlgorithm(sourceManager, tokens, configuration.getMinimumTileSize(), listener);
|
||||
matchAlgorithm.findMatches();
|
||||
log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size());
|
||||
}
|
||||
@@ -216,8 +219,7 @@ public class CPD {
|
||||
}
|
||||
}
|
||||
|
||||
@Experimental
|
||||
public void add(SourceCode sourceCode) throws IOException {
|
||||
private void add(SourceCode sourceCode) throws IOException {
|
||||
if (configuration.isSkipLexicalErrors()) {
|
||||
addAndSkipLexicalErrors(sourceCode);
|
||||
} else {
|
||||
@@ -226,11 +228,13 @@ public class CPD {
|
||||
}
|
||||
|
||||
private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException {
|
||||
log.debug("Tokenizing {}", sourceCode.getFileName());
|
||||
configuration.tokenizer().tokenize(sourceCode, tokens);
|
||||
listener.addedFile(1, new File(sourceCode.getFileName()));
|
||||
source.put(sourceCode.getFileName(), sourceCode);
|
||||
numberOfTokensPerFile.put(sourceCode.getFileName(), tokens.size() - lastTokenSize - 1 /*EOF*/);
|
||||
log.debug("Tokenizing {}", sourceCode.getPathId());
|
||||
try (TextDocument doc = sourceCode.load()) {
|
||||
configuration.tokenizer().tokenize(doc, tokens);
|
||||
}
|
||||
listener.addedFile(1);
|
||||
source.put(sourceCode.getPathId(), sourceCode);
|
||||
numberOfTokensPerFile.put(sourceCode.getPathId(), tokens.size() - lastTokenSize - 1 /*EOF*/);
|
||||
lastTokenSize = tokens.size();
|
||||
}
|
||||
|
||||
@@ -239,7 +243,7 @@ public class CPD {
|
||||
try {
|
||||
addAndThrowLexicalError(sourceCode);
|
||||
} catch (TokenMgrError e) {
|
||||
System.err.println("Skipping " + sourceCode.getFileName() + ". Reason: " + e.getMessage());
|
||||
System.err.println("Skipping " + sourceCode.getDisplayName() + ". Reason: " + e.getMessage());
|
||||
savedState.restore(tokens);
|
||||
}
|
||||
}
|
||||
@@ -253,15 +257,6 @@ public class CPD {
|
||||
return new ArrayList<>(source.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get each Source to be processed.
|
||||
*
|
||||
* @return all Sources to be processed
|
||||
*/
|
||||
public List<SourceCode> getSources() {
|
||||
return new ArrayList<>(source.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry to invoke CPD as command line tool. Note that this will
|
||||
* invoke {@link System#exit(int)}.
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.io.FilenameFilter;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -24,6 +25,8 @@ import net.sourceforge.pmd.AbstractConfiguration;
|
||||
import net.sourceforge.pmd.cpd.renderer.CPDReportRenderer;
|
||||
import net.sourceforge.pmd.internal.util.FileFinder;
|
||||
import net.sourceforge.pmd.internal.util.FileUtil;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
import net.sourceforge.pmd.lang.document.TextFile;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -79,7 +82,7 @@ public class CPDConfiguration extends AbstractConfiguration {
|
||||
|
||||
private boolean nonRecursive;
|
||||
|
||||
private String uri;
|
||||
private URI uri;
|
||||
|
||||
private boolean help;
|
||||
|
||||
@@ -87,7 +90,7 @@ public class CPDConfiguration extends AbstractConfiguration {
|
||||
|
||||
private boolean debug = false;
|
||||
|
||||
public SourceCode sourceCodeFor(File file) {
|
||||
public TextFile sourceCodeFor(File file) {
|
||||
return new SourceCode(new SourceCode.FileCodeLoader(file, getSourceEncoding().name()));
|
||||
}
|
||||
|
||||
@@ -340,11 +343,11 @@ public class CPDConfiguration extends AbstractConfiguration {
|
||||
this.fileListPath = fileListPath;
|
||||
}
|
||||
|
||||
public String getURI() {
|
||||
public URI getURI() {
|
||||
return uri;
|
||||
}
|
||||
|
||||
public void setURI(String uri) {
|
||||
public void setURI(URI uri) {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface CPDListener {
|
||||
|
||||
int INIT = 0;
|
||||
@@ -14,7 +12,7 @@ public interface CPDListener {
|
||||
int GROUPING = 3;
|
||||
int DONE = 4;
|
||||
|
||||
void addedFile(int fileCount, File file);
|
||||
void addedFile(int fileCount);
|
||||
|
||||
void phaseUpdate(int phase);
|
||||
}
|
||||
@@ -4,11 +4,9 @@
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class CPDNullListener implements CPDListener {
|
||||
@Override
|
||||
public void addedFile(int fileCount, File file) {
|
||||
public void addedFile(int fileCount) {
|
||||
// does nothing - override it if necessary
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import net.sourceforge.pmd.internal.util.FileCollectionUtil;
|
||||
import net.sourceforge.pmd.internal.util.FileFinder;
|
||||
import net.sourceforge.pmd.internal.util.FileUtil;
|
||||
import net.sourceforge.pmd.internal.util.IOUtil;
|
||||
import net.sourceforge.pmd.lang.ast.TokenMgrError;
|
||||
import net.sourceforge.pmd.lang.document.FileCollector;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
import net.sourceforge.pmd.util.database.DBMSMetadata;
|
||||
import net.sourceforge.pmd.util.database.DBURI;
|
||||
import net.sourceforge.pmd.util.database.SourceObject;
|
||||
import net.sourceforge.pmd.util.log.MessageReporter;
|
||||
|
||||
/**
|
||||
* @deprecated Use the module pmd-cli for CLI support.
|
||||
*/
|
||||
@Deprecated
|
||||
public class CpdAnalysis {
|
||||
|
||||
private CPDConfiguration configuration;
|
||||
private FileCollector files;
|
||||
private MessageReporter reporter;
|
||||
private CPDListener listener;
|
||||
|
||||
|
||||
public CpdAnalysis(CPDConfiguration theConfiguration) {
|
||||
configuration = theConfiguration;
|
||||
|
||||
// Add all sources
|
||||
try {
|
||||
extractAllSources();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public FileCollector files() {
|
||||
return files;
|
||||
}
|
||||
|
||||
private void extractAllSources() throws IOException {
|
||||
// Add files
|
||||
if (null != configuration.getFiles() && !configuration.getFiles().isEmpty()) {
|
||||
addSourcesFilesToCPD(configuration.getFiles());
|
||||
}
|
||||
|
||||
// Add Database URIS
|
||||
if (null != configuration.getURI()) {
|
||||
FileCollectionUtil.collectDB(files(), configuration.getURI());
|
||||
}
|
||||
|
||||
if (null != configuration.getFileListPath()) {
|
||||
FileCollectionUtil.collectFileList(files(), FileUtil.toExistingPath(configuration.getFileListPath()));
|
||||
}
|
||||
}
|
||||
|
||||
private void addSourcesFilesToCPD(List<File> files) throws IOException {
|
||||
for (File file : files) {
|
||||
files().addFileOrDirectory(file.toPath());
|
||||
}
|
||||
}
|
||||
|
||||
public void setCpdListener(CPDListener cpdListener) {
|
||||
this.listener = cpdListener;
|
||||
}
|
||||
|
||||
private void addAndThrowLexicalError(SourceCode sourceCode) throws IOException {
|
||||
log.debug("Tokenizing {}", sourceCode.getPathId());
|
||||
try (TextDocument doc = sourceCode.load()) {
|
||||
configuration.tokenizer().tokenize(doc, tokens);
|
||||
}
|
||||
listener.addedFile(1);
|
||||
source.put(sourceCode.getPathId(), sourceCode);
|
||||
numberOfTokensPerFile.put(sourceCode.getPathId(), tokens.size() - lastTokenSize - 1 /*EOF*/);
|
||||
lastTokenSize = tokens.size();
|
||||
}
|
||||
|
||||
public CPDReport performAnalysis() {
|
||||
|
||||
try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) {
|
||||
Tokens tokens = new Tokens();
|
||||
|
||||
|
||||
log.debug("Running match algorithm on {} files...", sourceManager.size());
|
||||
MatchAlgorithm matchAlgorithm = new MatchAlgorithm(sourceManager, tokens, configuration.getMinimumTileSize(), listener);
|
||||
matchAlgorithm.findMatches();
|
||||
log.debug("Finished: {} duplicates found", matchAlgorithm.getMatches().size());
|
||||
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
reporter.errorEx("Exception while running CPD", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(File file) throws IOException {
|
||||
|
||||
if (configuration.isSkipDuplicates()) {
|
||||
// TODO refactor this thing into a separate class
|
||||
String signature = file.getName() + '_' + file.length();
|
||||
if (current.contains(signature)) {
|
||||
System.err.println("Skipping " + file.getAbsolutePath()
|
||||
+ " since it appears to be a duplicate file and --skip-duplicate-files is set");
|
||||
return;
|
||||
}
|
||||
current.add(signature);
|
||||
}
|
||||
|
||||
if (!IOUtil.equalsNormalizedPaths(file.getAbsoluteFile().getCanonicalPath(), file.getAbsolutePath())) {
|
||||
System.err.println("Skipping " + file + " since it appears to be a symlink");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.exists()) {
|
||||
System.err.println("Skipping " + file + " since it doesn't exist (broken symlink?)");
|
||||
return;
|
||||
}
|
||||
|
||||
SourceCode sourceCode = configuration.sourceCodeFor(file);
|
||||
add(sourceCode);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -911,7 +911,7 @@ public class GUI implements CPDListener {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addedFile(int fileCount, File file) {
|
||||
public void addedFile(int fileCount) {
|
||||
tokenizingFilesBar.setMaximum(fileCount);
|
||||
tokenizingFilesBar.setValue(tokenizingFilesBar.getValue() + 1);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,14 @@
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import net.sourceforge.pmd.lang.document.Chars;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
|
||||
public class Mark implements Comparable<Mark> {
|
||||
private TokenEntry token;
|
||||
private TokenEntry endToken;
|
||||
private int lineCount;
|
||||
private SourceCode code;
|
||||
private TextDocument code;
|
||||
|
||||
public Mark(TokenEntry token) {
|
||||
this.token = token;
|
||||
@@ -69,11 +72,13 @@ public class Mark implements Comparable<Mark> {
|
||||
}
|
||||
|
||||
/** Newlines are normalized to \n. */
|
||||
public String getSourceCodeSlice() {
|
||||
return this.code.getSlice(getBeginLine(), getEndLine());
|
||||
public Chars getSourceCodeSlice() {
|
||||
return this.code.sliceOriginalText(
|
||||
this.code.createLineRange(getBeginLine(), getEndLine())
|
||||
);
|
||||
}
|
||||
|
||||
public void setSourceCode(SourceCode code) {
|
||||
public void setSourceCode(TextDocument code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.sourceforge.pmd.PMD;
|
||||
import net.sourceforge.pmd.lang.document.Chars;
|
||||
|
||||
public class Match implements Comparable<Match>, Iterable<Mark> {
|
||||
|
||||
@@ -74,7 +75,7 @@ public class Match implements Comparable<Match>, Iterable<Mark> {
|
||||
}
|
||||
|
||||
/** Newlines are normalized to \n. */
|
||||
public String getSourceCodeSlice() {
|
||||
public Chars getSourceCodeSlice() {
|
||||
return this.getMark(0).getSourceCodeSlice();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,23 +11,25 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class MatchAlgorithm {
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
|
||||
class MatchAlgorithm {
|
||||
|
||||
private static final int MOD = 37;
|
||||
private int lastMod = 1;
|
||||
|
||||
private List<Match> matches;
|
||||
private Map<String, SourceCode> source;
|
||||
private Map<String, TextDocument> source;
|
||||
private Tokens tokens;
|
||||
private List<TokenEntry> code;
|
||||
private CPDListener cpdListener;
|
||||
private int min;
|
||||
|
||||
public MatchAlgorithm(Map<String, SourceCode> sourceCode, Tokens tokens, int min) {
|
||||
public MatchAlgorithm(Map<String, TextDocument> sourceCode, Tokens tokens, int min) {
|
||||
this(sourceCode, tokens, min, new CPDNullListener());
|
||||
}
|
||||
|
||||
public MatchAlgorithm(Map<String, SourceCode> sourceCode, Tokens tokens, int min, CPDListener listener) {
|
||||
public MatchAlgorithm(SourceManager sourceCode, Tokens tokens, int min, CPDListener listener) {
|
||||
this.source = sourceCode;
|
||||
this.tokens = tokens;
|
||||
this.code = tokens.getTokens();
|
||||
@@ -85,7 +87,7 @@ public class MatchAlgorithm {
|
||||
|
||||
mark.setLineCount(lineCount);
|
||||
mark.setEndToken(endToken);
|
||||
SourceCode sourceCode = source.get(token.getTokenSrcID());
|
||||
TextDocument sourceCode = source.get(token.getTokenSrcID());
|
||||
assert sourceCode != null : token.getTokenSrcID() + " is not registered in " + source.keySet();
|
||||
mark.setSourceCode(sourceCode);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public class SimpleRenderer implements CPDReportRenderer {
|
||||
|
||||
writer.append(PMD.EOL); // add a line to separate the source from the desc above
|
||||
|
||||
String source = match.getSourceCodeSlice();
|
||||
Chars source = match.getSourceCodeSlice();
|
||||
|
||||
if (trimLeadingWhitespace) {
|
||||
for (Chars line : StringUtil.linesWithTrimIndent(source)) {
|
||||
@@ -70,7 +70,8 @@ public class SimpleRenderer implements CPDReportRenderer {
|
||||
return;
|
||||
}
|
||||
|
||||
writer.append(source).append(PMD.EOL);
|
||||
source.writeFully(writer);
|
||||
writer.append(PMD.EOL);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,190 +4,32 @@
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.internal.util.IOUtil;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
import net.sourceforge.pmd.lang.document.TextFile;
|
||||
|
||||
public class SourceCode {
|
||||
|
||||
public abstract static class CodeLoader {
|
||||
private SoftReference<List<String>> code;
|
||||
private SoftReference<TextDocument> softRef;
|
||||
private final TextFile textFile;
|
||||
|
||||
public List<String> getCode() {
|
||||
List<String> c = null;
|
||||
if (code != null) {
|
||||
c = code.get();
|
||||
}
|
||||
if (c != null) {
|
||||
return c;
|
||||
}
|
||||
this.code = new SoftReference<>(load());
|
||||
return code.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a range of lines.
|
||||
*
|
||||
* @param startLine Start line (inclusive, 1-based)
|
||||
* @param endLine End line (inclusive, 1-based)
|
||||
*/
|
||||
public List<String> getCodeSlice(int startLine, int endLine) {
|
||||
List<String> c = null;
|
||||
if (code != null) {
|
||||
c = code.get();
|
||||
}
|
||||
if (c != null) {
|
||||
return c.subList(startLine - 1, endLine);
|
||||
}
|
||||
return load(startLine, endLine);
|
||||
}
|
||||
|
||||
public abstract String getFileName();
|
||||
|
||||
protected abstract Reader getReader() throws Exception;
|
||||
|
||||
protected List<String> load() {
|
||||
try (BufferedReader reader = new BufferedReader(getReader())) {
|
||||
List<String> lines = new ArrayList<>();
|
||||
String currentLine;
|
||||
while ((currentLine = reader.readLine()) != null) {
|
||||
lines.add(currentLine);
|
||||
}
|
||||
return lines;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Problem while reading " + getFileName() + ":" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a range of lines.
|
||||
*
|
||||
* @param startLine Start line (inclusive, 1-based)
|
||||
* @param endLine End line (inclusive, 1-based)
|
||||
*/
|
||||
protected List<String> load(int startLine, int endLine) {
|
||||
try (BufferedReader reader = new BufferedReader(getReader())) {
|
||||
int linesToRead = 1 + endLine - startLine; // +1 because endLine is inclusive
|
||||
List<String> lines = new ArrayList<>(linesToRead);
|
||||
|
||||
// Skip lines until we reach the start point
|
||||
for (int i = 0; i < startLine - 1; i++) {
|
||||
reader.readLine();
|
||||
}
|
||||
|
||||
String currentLine;
|
||||
while ((currentLine = reader.readLine()) != null) {
|
||||
lines.add(currentLine);
|
||||
|
||||
if (lines.size() == linesToRead) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Problem while reading " + getFileName() + ":" + e.getMessage());
|
||||
}
|
||||
}
|
||||
public SourceCode(TextFile textFile) {
|
||||
this.textFile = textFile;
|
||||
}
|
||||
|
||||
public static class FileCodeLoader extends CodeLoader {
|
||||
private File file;
|
||||
private String encoding;
|
||||
|
||||
public FileCodeLoader(File file, String encoding) {
|
||||
this.file = file;
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader getReader() throws Exception {
|
||||
IOUtil.BomAwareInputStream inputStream = new IOUtil.BomAwareInputStream(Files.newInputStream(file.toPath()));
|
||||
|
||||
if (inputStream.hasBom()) {
|
||||
encoding = inputStream.getBomCharsetName();
|
||||
}
|
||||
return new InputStreamReader(inputStream, encoding);
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return file.getAbsolutePath();
|
||||
public TextDocument load() throws IOException {
|
||||
if (softRef != null && softRef.get() != null) {
|
||||
return softRef.get();
|
||||
}
|
||||
TextDocument doc = TextDocument.create(textFile);
|
||||
softRef = new SoftReference<>(doc);
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static class StringCodeLoader extends CodeLoader {
|
||||
public static final String DEFAULT_NAME = "CODE_LOADED_FROM_STRING";
|
||||
|
||||
private String code;
|
||||
|
||||
private String name;
|
||||
|
||||
public StringCodeLoader(String code) {
|
||||
this(code, DEFAULT_NAME);
|
||||
}
|
||||
|
||||
public StringCodeLoader(String code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader getReader() {
|
||||
return new StringReader(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ReaderCodeLoader extends CodeLoader {
|
||||
public static final String DEFAULT_NAME = "CODE_LOADED_FROM_READER";
|
||||
|
||||
private Reader code;
|
||||
|
||||
private String name;
|
||||
|
||||
public ReaderCodeLoader(Reader code) {
|
||||
this(code, DEFAULT_NAME);
|
||||
}
|
||||
|
||||
public ReaderCodeLoader(Reader code, String name) {
|
||||
this.code = code;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Reader getReader() {
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
private CodeLoader cl;
|
||||
|
||||
public SourceCode(CodeLoader cl) {
|
||||
this.cl = cl;
|
||||
}
|
||||
|
||||
public List<String> getCode() {
|
||||
return cl.getCode();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.sourceforge.pmd.internal.util.IOUtil;
|
||||
import net.sourceforge.pmd.lang.document.TextDocument;
|
||||
import net.sourceforge.pmd.lang.document.TextFile;
|
||||
|
||||
public class SourceManager implements AutoCloseable {
|
||||
|
||||
private final Map<TextFile, SoftReference<TextDocument>> files = new ConcurrentHashMap<>();
|
||||
private final List<TextFile> textFiles;
|
||||
|
||||
public SourceManager(List<? extends TextFile> files) {
|
||||
textFiles = new ArrayList<>(files);
|
||||
}
|
||||
|
||||
|
||||
TextDocument get(String pathId) {
|
||||
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return files.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
Exception exception = IOUtil.closeAll(textFiles);
|
||||
if (exception != null) {
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,19 +25,6 @@ public class TokenEntry implements Comparable<TokenEntry> {
|
||||
private int identifier;
|
||||
private int hashCode;
|
||||
|
||||
private static final ThreadLocal<Map<String, Integer>> TOKENS = new ThreadLocal<Map<String, Integer>>() {
|
||||
@Override
|
||||
protected Map<String, Integer> initialValue() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
};
|
||||
private static final ThreadLocal<AtomicInteger> TOKEN_COUNT = new ThreadLocal<AtomicInteger>() {
|
||||
@Override
|
||||
protected AtomicInteger initialValue() {
|
||||
return new AtomicInteger(0);
|
||||
}
|
||||
};
|
||||
|
||||
private TokenEntry() {
|
||||
this.identifier = 0;
|
||||
this.tokenSrcID = "EOFMarker";
|
||||
@@ -59,14 +46,6 @@ public class TokenEntry implements Comparable<TokenEntry> {
|
||||
this(image, tokenSrcID, beginLine, -1, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new token entry with the given informations.
|
||||
* @param image
|
||||
* @param tokenSrcID
|
||||
* @param beginLine the linenumber, 1-based.
|
||||
* @param beginColumn the column number, 1-based
|
||||
* @param endColumn the column number, 1-based
|
||||
*/
|
||||
public TokenEntry(String image, String tokenSrcID, int beginLine, int beginColumn, int endColumn) {
|
||||
assert isOk(beginLine) && isOk(beginColumn) && isOk(endColumn) : "Coordinates are 1-based";
|
||||
setImage(image);
|
||||
@@ -74,7 +53,6 @@ public class TokenEntry implements Comparable<TokenEntry> {
|
||||
this.beginLine = beginLine;
|
||||
this.beginColumn = beginColumn;
|
||||
this.endColumn = endColumn;
|
||||
this.index = TOKEN_COUNT.get().getAndIncrement();
|
||||
}
|
||||
|
||||
public TokenEntry(String image, FileLocation location) {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import net.sourceforge.pmd.lang.document.FileLocation;
|
||||
import net.sourceforge.pmd.lang.document.TextFile;
|
||||
|
||||
public interface TokenFactory {
|
||||
|
||||
void recordToken(String image, int startLine, int startCol, int endLine, int endCol);
|
||||
|
||||
default void recordToken(String image, FileLocation location) {
|
||||
recordToken(image, location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn());
|
||||
}
|
||||
|
||||
void setImage(TokenEntry entry, String newImage);
|
||||
|
||||
TokenEntry peekLastToken();
|
||||
|
||||
static TokenFactory forFile(TextFile file, Tokens sink) {
|
||||
return new TokenFactory() {
|
||||
final String name = file.getPathId();
|
||||
|
||||
@Override
|
||||
public void recordToken(String image, int startLine, int startCol, int endLine, int endCol) {
|
||||
sink.addToken(image, name, startLine, startCol, endLine, endCol);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImage(TokenEntry entry, String newImage) {
|
||||
sink.setImage(entry, newImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TokenEntry peekLastToken() {
|
||||
return sink.peekLastToken();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,13 @@ public interface Tokenizer {
|
||||
.build();
|
||||
|
||||
|
||||
PropertyDescriptor<Boolean> CPD_CASE_SENSITIVE =
|
||||
PropertyFactory.booleanProperty("cpdCaseSensitive")
|
||||
.defaultValue(true)
|
||||
.desc("Whether CPD should ignore the case of tokens. Affects all tokens.")
|
||||
.build();
|
||||
|
||||
|
||||
String IGNORE_LITERALS = "ignore_literals";
|
||||
String IGNORE_IDENTIFIERS = "ignore_identifiers";
|
||||
String IGNORE_ANNOTATIONS = "ignore_annotations";
|
||||
@@ -75,5 +82,5 @@ public interface Tokenizer {
|
||||
|
||||
String DEFAULT_SKIP_BLOCKS_PATTERN = "#if 0|#endif";
|
||||
|
||||
void tokenize(TextDocument sourceCode, Tokens tokenEntries) throws IOException;
|
||||
void tokenize(TextDocument sourceCode, TokenFactory tokenEntries) throws IOException;
|
||||
}
|
||||
@@ -5,17 +5,47 @@
|
||||
package net.sourceforge.pmd.cpd;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.sourceforge.pmd.lang.document.FileLocation;
|
||||
|
||||
public class Tokens {
|
||||
|
||||
private List<TokenEntry> tokens = new ArrayList<>();
|
||||
private final List<TokenEntry> tokens = new ArrayList<>();
|
||||
|
||||
private static final ThreadLocal<Map<String, Integer>> TOKENS = new ThreadLocal<Map<String, Integer>>() {
|
||||
@Override
|
||||
protected Map<String, Integer> initialValue() {
|
||||
return new HashMap<>();
|
||||
}
|
||||
};
|
||||
private static final ThreadLocal<AtomicInteger> TOKEN_COUNT = new ThreadLocal<AtomicInteger>() {
|
||||
@Override
|
||||
protected AtomicInteger initialValue() {
|
||||
return new AtomicInteger(0);
|
||||
}
|
||||
};
|
||||
|
||||
public void add(TokenEntry tokenEntry) {
|
||||
this.tokens.add(tokenEntry);
|
||||
}
|
||||
|
||||
public void addToken(String image, FileLocation location) {
|
||||
this.tokens.add(new TokenEntry(image, location));
|
||||
}
|
||||
|
||||
public void setImage(TokenEntry entry, String newImage) {
|
||||
entry.setImage(newImage);
|
||||
}
|
||||
|
||||
public TokenEntry peekLastToken() {
|
||||
return get(size() - 1);
|
||||
}
|
||||
|
||||
public Iterator<TokenEntry> iterator() {
|
||||
return tokens.iterator();
|
||||
}
|
||||
@@ -43,4 +73,8 @@ public class Tokens {
|
||||
public List<TokenEntry> getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
void addToken(String image, String fileName, int startLine, int startCol, int endLine, int endCol) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,8 @@ package net.sourceforge.pmd.cpd.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import net.sourceforge.pmd.cpd.TokenEntry;
|
||||
import net.sourceforge.pmd.cpd.TokenFactory;
|
||||
import net.sourceforge.pmd.cpd.Tokenizer;
|
||||
import net.sourceforge.pmd.cpd.Tokens;
|
||||
import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter;
|
||||
import net.sourceforge.pmd.lang.TokenManager;
|
||||
import net.sourceforge.pmd.lang.ast.GenericToken;
|
||||
@@ -22,8 +21,8 @@ public abstract class TokenizerBase<T extends GenericToken<T>> implements Tokeni
|
||||
return new BaseTokenFilter<>(tokenManager);
|
||||
}
|
||||
|
||||
protected TokenEntry processToken(Tokens tokenEntries, T currentToken) {
|
||||
return new TokenEntry(getImage(currentToken), currentToken.getReportLocation());
|
||||
protected void processToken(TokenFactory tokenEntries, T currentToken) {
|
||||
tokenEntries.recordToken(getImage(currentToken), currentToken.getReportLocation());
|
||||
}
|
||||
|
||||
protected String getImage(T token) {
|
||||
@@ -31,11 +30,11 @@ public abstract class TokenizerBase<T extends GenericToken<T>> implements Tokeni
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tokenize(TextDocument document, Tokens tokenEntries) throws IOException {
|
||||
public void tokenize(TextDocument document, TokenFactory tokenEntries) throws IOException {
|
||||
TokenManager<T> tokenManager = filterTokenStream(makeLexerImpl(document));
|
||||
T currentToken = tokenManager.getNextToken();
|
||||
while (currentToken != null) {
|
||||
tokenEntries.add(processToken(tokenEntries, currentToken));
|
||||
processToken(tokenEntries, currentToken);
|
||||
currentToken = tokenManager.getNextToken();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,11 +343,16 @@ public final class FileCollector implements AutoCloseable {
|
||||
* @return True if the directory has been added
|
||||
*/
|
||||
public boolean addDirectory(Path dir) throws IOException {
|
||||
return addDirectory(dir, true);
|
||||
}
|
||||
|
||||
public boolean addDirectory(Path dir, boolean recurse) throws IOException {
|
||||
if (!Files.isDirectory(dir)) {
|
||||
reporter.error("Not a directory {0}", dir);
|
||||
return false;
|
||||
}
|
||||
Files.walkFileTree(dir, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE, new SimpleFileVisitor<Path>() {
|
||||
int maxDepth = recurse ? Integer.MAX_VALUE : 1;
|
||||
Files.walkFileTree(dir, EnumSet.of(FileVisitOption.FOLLOW_LINKS), maxDepth, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (attrs.isRegularFile()) {
|
||||
@@ -367,8 +372,18 @@ public final class FileCollector implements AutoCloseable {
|
||||
* @return True if the file or directory has been added
|
||||
*/
|
||||
public boolean addFileOrDirectory(Path file) throws IOException {
|
||||
return addFileOrDirectory(file, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a file or directory recursively. Language is determined automatically
|
||||
* from the extension/file patterns.
|
||||
*
|
||||
* @return True if the file or directory has been added
|
||||
*/
|
||||
public boolean addFileOrDirectory(Path file, boolean recurseIfDirectory) throws IOException {
|
||||
if (Files.isDirectory(file)) {
|
||||
return addDirectory(file);
|
||||
return addDirectory(file, recurseIfDirectory);
|
||||
} else if (Files.isRegularFile(file)) {
|
||||
return addFile(file);
|
||||
} else {
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package net.sourceforge.pmd.util;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -291,9 +290,8 @@ public final class StringUtil {
|
||||
/**
|
||||
* Returns a list of
|
||||
*/
|
||||
public static List<Chars> linesWithTrimIndent(String source) {
|
||||
List<String> lines = Arrays.asList(source.split("\n"));
|
||||
List<Chars> result = lines.stream().map(Chars::wrap).collect(CollectionUtil.toMutableList());
|
||||
public static List<Chars> linesWithTrimIndent(Chars source) {
|
||||
List<Chars> result = source.lineStream().collect(CollectionUtil.toMutableList());
|
||||
trimIndentInPlace(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -146,10 +146,10 @@ class CPDTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addedFile(int fileCount, File file) {
|
||||
public void addedFile(int fileCount) {
|
||||
files++;
|
||||
if (files > expectedFilesCount) {
|
||||
fail("File was added! - " + file);
|
||||
fail("File was added!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,8 @@ class CSVRendererTest {
|
||||
}
|
||||
|
||||
private Mark createMark(String image, String tokenSrcID, int beginLine, int lineCount, String code) {
|
||||
Tokens tokens = new Tokens();
|
||||
tokens.addToken(image, );
|
||||
Mark result = new Mark(new TokenEntry(image, tokenSrcID, beginLine));
|
||||
|
||||
result.setLineCount(lineCount);
|
||||
|
||||
Loaded 30 of 64 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user