AliasesShouldBeListedInDictionaryListener.java
package org.thewonderlemming.c4plantuml.linter.rules.builtin;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.thewonderlemming.c4plantuml.commons.Reporter;
import org.thewonderlemming.c4plantuml.grammars.C4BaseListener;
import org.thewonderlemming.c4plantuml.grammars.SourceType;
import org.thewonderlemming.c4plantuml.grammars.generated.C4L1Parser;
import org.thewonderlemming.c4plantuml.grammars.generated.C4L2Parser;
import org.thewonderlemming.c4plantuml.grammars.generated.C4L3Parser;
/**
* An ANTLR 4 {@link ParseTreeListener} implementation that used aliases are declared in a dictionary file.
*
* @author thewonderlemming
*
*/
public class AliasesShouldBeListedInDictionaryListener extends C4BaseListener {
private static final String MESSAGE_FORMAT = "Alias <%s> was not found in the provided dictionary file: %s";
private final Set<String> aliasDictionary = new HashSet<>();
private final String dictionaryFilename;
private boolean isCheckEnabled = true;
private final Reporter reporter;
private final AliasesShouldBeListedInDictionaryRule rule;
/**
* Default constructor.
*
* @param rule the {@link AliasesShouldBeUniqueRule} instance to refer to.
* @param reporter {@link Reporter} instance to report to.
* @param aliasDictionaryFilename the filename of the alias dictionary to read from.
*/
public AliasesShouldBeListedInDictionaryListener(final AliasesShouldBeListedInDictionaryRule rule,
final Reporter reporter, final String aliasDictionaryFilename) {
this.rule = rule;
this.reporter = reporter;
this.dictionaryFilename = aliasDictionaryFilename;
checkGivenFilenameAndPopulateDictionary(aliasDictionaryFilename);
}
/**
* Checks that the aliases of a boundary definition in a {@link SourceType#C4_L1} grammar are listed in the current
* dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterBoundary(final C4L1Parser.BoundaryContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the aliases of a boundary definition in a {@link SourceType#C4_L2} grammar are listed in the current
* dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterBoundary(final C4L2Parser.BoundaryContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the aliases of a boundary definition in a {@link SourceType#C4_L3} grammar are listed in the current
* dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterBoundary(final C4L3Parser.BoundaryContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the alias of a cloud definition in a {@link SourceType#C4_L1} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterCloud(final C4L1Parser.CloudContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a cloud definition in a {@link SourceType#C4_L2} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterCloud(final C4L2Parser.CloudContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a cloud definition in a {@link SourceType#C4_L3} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterCloud(final C4L3Parser.CloudContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a component definition in a {@link SourceType#C4_L3} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterComponent(final C4L3Parser.ComponentContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a container definition in a {@link SourceType#C4_L2} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterContainer(final C4L2Parser.ContainerContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a container definition in a {@link SourceType#C4_L3} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterContainer(final C4L3Parser.ContainerContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a container boundary definition in a {@link SourceType#C4_L3} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterContainer_boundary(final C4L3Parser.Container_boundaryContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an enterprise boundary definition in a {@link SourceType#C4_L1} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterEnterprise_boundary(final C4L1Parser.Enterprise_boundaryContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an enterprise boundary definition in a {@link SourceType#C4_L2} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterEnterprise_boundary(final C4L2Parser.Enterprise_boundaryContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an enterprise boundary definition in a {@link SourceType#C4_L3} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterEnterprise_boundary(final C4L3Parser.Enterprise_boundaryContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the aliases of a layout definition in a {@link SourceType#C4_L1} grammar are listed in the current
* dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterLayout(final C4L1Parser.LayoutContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the aliases of a layout definition in a {@link SourceType#C4_L2} grammar are listed in the current
* dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterLayout(final C4L2Parser.LayoutContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the aliases of a layout definition in a {@link SourceType#C4_L3} grammar are listed in the current
* dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterLayout(final C4L3Parser.LayoutContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the alias of a person definition in a {@link SourceType#C4_L1} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterPerson(final C4L1Parser.PersonContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a person definition in a {@link SourceType#C4_L2} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterPerson(final C4L2Parser.PersonContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a person definition in a {@link SourceType#C4_L3} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterPerson(final C4L3Parser.PersonContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an external person definition in a {@link SourceType#C4_L1} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterPerson_ext(final C4L1Parser.Person_extContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an external person definition in a {@link SourceType#C4_L2} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterPerson_ext(final C4L2Parser.Person_extContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an external person definition in a {@link SourceType#C4_L3} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterPerson_ext(final C4L3Parser.Person_extContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the aliases of a relationship definition in a {@link SourceType#C4_L1} grammar are listed in the
* current dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterRelationship(final C4L1Parser.RelationshipContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the aliases of a relationship definition in a {@link SourceType#C4_L2} grammar are listed in the
* current dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterRelationship(final C4L2Parser.RelationshipContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the aliases of a relationship definition in a {@link SourceType#C4_L3} grammar are listed in the
* current dictionary or reports them.
* <p>
* {@inheritDoc}
*/
@Override
public void enterRelationship(final C4L3Parser.RelationshipContext ctx) {
ctx.Alias().forEach(this::checkAliasExistsInDictionary);
}
/**
* Checks that the alias of a system definition in a {@link SourceType#C4_L1} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterSystem(final C4L1Parser.SystemContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a system definition in a {@link SourceType#C4_L2} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterSystem(final C4L2Parser.SystemContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a system definition in a {@link SourceType#C4_L3} grammar is listed in the current
* dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterSystem(final C4L3Parser.SystemContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of a system boundary definition in a {@link SourceType#C4_L2} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterSystem_boundary(final C4L2Parser.System_boundaryContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an external system definition in a {@link SourceType#C4_L1} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterSystem_ext(final C4L1Parser.System_extContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an external system definition in a {@link SourceType#C4_L2} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterSystem_ext(final C4L2Parser.System_extContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
/**
* Checks that the alias of an external system definition in a {@link SourceType#C4_L3} grammar is listed in the
* current dictionary or reports it.
* <p>
* {@inheritDoc}
*/
@Override
public void enterSystem_ext(final C4L3Parser.System_extContext ctx) {
checkAliasExistsInDictionary(ctx.Alias());
}
private void checkAliasExistsInDictionary(final TerminalNode alias) {
if (this.isCheckEnabled && !this.aliasDictionary.contains(alias.getText())) {
final String message = String
.format(MESSAGE_FORMAT,
alias.getText(),
new File(this.dictionaryFilename).getName());
this.reporter.report(message);
}
}
private void checkGivenFilenameAndPopulateDictionary(final String aliasDictionaryFilename) {
if (aliasDictionaryFilename.trim().isEmpty()) {
final String message = "Aliases dictionary filename is empty. The current rule will be disabled.";
this.reporter.report(message);
this.rule.getLogger().warn(message);
this.isCheckEnabled = false;
return;
}
final File dictionary = new File(aliasDictionaryFilename);
if (dictionary.exists()) {
populateAliasDictionaryFromFile(dictionary);
} else {
final String message = String
.format("Could not find expected dictionary: %s", dictionary.getName());
this.reporter.report(message);
this.rule.getLogger().error(message);
this.isCheckEnabled = false;
}
}
private void populateAliasDictionaryFromFile(final File dictionary) {
try (Stream<String> lines = Files.lines(dictionary.toPath())) {
lines.forEach(this.aliasDictionary::add);
} catch (final IOException e) {
this.rule
.getLogger()
.error("An error occurred while reading the aliases dictionary file: {}", e.getMessage(), e);
}
}
}