Skip to content

Commit

Permalink
Added test for FXOMImportsRemover and replacement/removal mechanism.
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliver-Loeffler committed Apr 10, 2024
1 parent edd76d9 commit 095dc11
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,50 +31,100 @@
*/
package com.oracle.javafx.scenebuilder.kit.fxom;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

class FXOMImportsRemover implements BiFunction<String, List<String>, String>{
class FXOMImportsRemover {

private static final Logger LOGGER = Logger.getLogger(FXOMImportsRemover.class.getName());

private Consumer<String> unresolvedTypeConsumer;
private Consumer<String> removedTypeConsumer;

FXOMImportsRemover() {
this(type->{});
this(type->{
/* no operation here by default */
});
}
FXOMImportsRemover(Consumer<String> unresolvedTypeConsumer) {
this.unresolvedTypeConsumer = unresolvedTypeConsumer;

FXOMImportsRemover(Consumer<String> matchingTypeConsumer) {
this.removedTypeConsumer = matchingTypeConsumer;
}

/*
* TODO: Rework the way how the import tag is detected and how the import statement is removed.
* This implementation only works fine with one import per line, but actually one can put multiple imports in one line.
* This approach works as of now but is not robust.
/**
* Removes all imports from a given FXML for the given type names.
*
* @param sourceFxml The source FXML.
* @param typesToRemoveThe type names which shall be removed.
* @return FXML text without the previously removed types
*/
public String removeImports(String sourceFxml, Collection<String> typesToRemove) {
var predicate = new IgnoreImportPredicate(removedTypeConsumer,typesToRemove);
if (typesToRemove.isEmpty()) {
return sourceFxml;
}

LOGGER.log(Level.INFO, "Removing FXML imports.");

/*
* The predicate detects specific imports.
* In this case here, all lines which do not match the given types shall be kept.
*/
return sourceFxml.lines()
.filter(predicate)
.collect(Collectors.joining(System.lineSeparator()));
}

/**
* This predicate will yield true for all FXML lines to keep and false for all
* FXML lines to ignore. Lines to be ignored are import statements with the
* given type names.
* <br>
* <br>
* Regex description
* <pre>
* opening import declaration: &lt;?import
* at least one whitespace, more are possible: (\\s\\s*)
* the unresolved type name
* optional whitespace: (\\s*)
* closing import declaration: [?]&gt;
* </pre>
*/
@Override
public String apply(String sourceFxml, List<String> unresolvedTypes) {
List<String> lines = new ArrayList<>(sourceFxml.lines().toList());
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (line.matches("^\s?[<]\s?[?]\s?import\s?.*")) {
for (String unresolvedType : unresolvedTypes) {
if (line.contains(unresolvedType)) {
LOGGER.log(Level.INFO, "Removing FXML import: ", line);
lines.set(i, "");
unresolvedTypeConsumer.accept(unresolvedType);
}
private static class IgnoreImportPredicate implements Predicate<String> {

private final Map<String, Pattern> typesToRemove;

private final Consumer<String> matchingTypeConsumer;

IgnoreImportPredicate(Consumer<String> unresolvedTypeConsumer, Collection<String> unresolvedTypes) {
this.matchingTypeConsumer = unresolvedTypeConsumer;
this.typesToRemove = unresolvedTypes.stream()
.collect(Collectors.toMap(String::valueOf,
IgnoreImportPredicate::createRegex));
}

private static Pattern createRegex(String type) {
return Pattern.compile( "<[?]import(\\s*)" + type.replace(".", "[.]") + "(\\s*)[?]>");
}

@Override
public boolean test(String lineToTest) {
for (Entry<String,Pattern> entry : typesToRemove.entrySet()) {
var pattern = entry.getValue().matcher(lineToTest);
if (pattern.matches()) {
LOGGER.log(Level.INFO, "Import to ignore: {0}", lineToTest);
matchingTypeConsumer.accept(entry.getKey());
return false;
}
}
return true;
}
String modifiedFxml = lines.stream()
.collect(Collectors.joining(System.lineSeparator()));
return modifiedFxml;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public void load(String fxmlText, FXOMDocumentSwitch ... switches) throws java.i

private String removeUnresolvableTypeFromFXML(String fxmlText, String unresolvableType) {
FXOMImportsRemover remover = new FXOMImportsRemover(document::addUnresolvableType);
return remover.apply(fxmlText, List.of(unresolvableType));
return remover.removeImports(fxmlText, List.of(unresolvableType));
}

private void handleFxmlLoadingError(Exception x) throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void refresh(FXOMDocument document) {
if (document.hasUnresolvableImports()) {
LOGGER.log(Level.INFO, "Detected unresolved imports.");
FXOMImportsRemover importRemover = new FXOMImportsRemover();
fxmlText = importRemover.apply(fxmlText, document.getUnresolvableTypes());
fxmlText = importRemover.removeImports(fxmlText, document.getUnresolvableTypes());
}

final FXOMDocument newDocument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,87 @@
*/
package com.oracle.javafx.scenebuilder.kit.fxom;

import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.junit.jupiter.api.Test;

class FXOMImportsRemoverTest {

private FXOMImportsRemover classUnderTest = new FXOMImportsRemover();
private FXOMImportsRemover classUnderTest;

@Test
void test() {
/*
* TODO: implement this test
*/
fail("Not yet implemented");
void that_the_given_imports_are_removed() {
Set<String> detectedUnresolvableTypes = new HashSet<>();
classUnderTest = new FXOMImportsRemover(detectedUnresolvableTypes::add);

String sourceFxmlText = """
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import another.unresolvable.Dependency?>
<?import also.an.unresolvable.Dependency?>
<?import this.namespace.is.unknown.*?>
<AnchorPane>
<children>
<Button layoutX="302.0" layoutY="27.0" text="Button" />
<ComboBox layoutX="46.0" layoutY="175.0" prefWidth="150.0" />
<TextField layoutX="345.0" layoutY="264.0" />
<Button layoutX="84.0" layoutY="252.0" text="Button" />
<UnknownElement layoutX="84.0" layoutY="87.0" text="Some Content" />
</children>
</AnchorPane>
""";

String cleanedFxmlText = classUnderTest.removeImports(sourceFxmlText,
List.of("another.unresolvable.Dependency",
"also.an.unresolvable.Dependency"));

String expectedFxmlText = """
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import this.namespace.is.unknown.*?>
<AnchorPane>
<children>
<Button layoutX="302.0" layoutY="27.0" text="Button" />
<ComboBox layoutX="46.0" layoutY="175.0" prefWidth="150.0" />
<TextField layoutX="345.0" layoutY="264.0" />
<Button layoutX="84.0" layoutY="252.0" text="Button" />
<UnknownElement layoutX="84.0" layoutY="87.0" text="Some Content" />
</children>
</AnchorPane>
""";

assertTrue(detectedUnresolvableTypes.contains("another.unresolvable.Dependency"));
assertTrue(detectedUnresolvableTypes.contains("also.an.unresolvable.Dependency"));
assertEquals(expectedFxmlText.lines().limit(4).toList(), cleanedFxmlText.lines().limit(4).toList());
}

@Test
void that_fxml_is_not_modified_when_nothing_is_to_be_removed() {
String sourceFxmlText = """
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import this.namespace.is.unknown.*?>
<AnchorPane>
<children>
<Button layoutX="302.0" layoutY="27.0" text="Button" />
<ComboBox layoutX="46.0" layoutY="175.0" prefWidth="150.0" />
<TextField layoutX="345.0" layoutY="264.0" />
<Button layoutX="84.0" layoutY="252.0" text="Button" />
<UnknownElement layoutX="84.0" layoutY="87.0" text="Some Content" />
</children>
</AnchorPane>
""";

classUnderTest = new FXOMImportsRemover();
String cleanedFxmlText = classUnderTest.removeImports(sourceFxmlText, List.of());

assertEquals(sourceFxmlText, cleanedFxmlText);

}

}

0 comments on commit 095dc11

Please sign in to comment.