Skip to content

Commit

Permalink
More tidy up ... also provides a way to iterate over each discovered …
Browse files Browse the repository at this point in the history
…component in the DSL.
  • Loading branch information
simonbrowndotje committed Aug 25, 2024
1 parent bc93a0f commit 8d57511
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.structurizr.component.filter.TypeFilter;
import com.structurizr.component.matcher.TypeMatcher;
import com.structurizr.component.naming.DefaultNamingStrategy;
import com.structurizr.component.naming.SimpleNamingStrategy;
import com.structurizr.component.naming.NamingStrategy;
import com.structurizr.component.supporting.DefaultSupportingTypesStrategy;
import com.structurizr.component.supporting.SupportingTypesStrategy;
Expand All @@ -26,12 +25,6 @@ public final class ComponentFinderStrategyBuilder {
public ComponentFinderStrategyBuilder() {
}

public ComponentFinderStrategyBuilder forTechnology(String technology) {
this.technology = technology;

return this;
}

public ComponentFinderStrategyBuilder matchedBy(TypeMatcher typeMatcher) {
this.typeMatcher = typeMatcher;

Expand All @@ -56,6 +49,12 @@ public ComponentFinderStrategyBuilder namedBy(NamingStrategy namingStrategy) {
return this;
}

public ComponentFinderStrategyBuilder asTechnology(String technology) {
this.technology = technology;

return this;
}

public ComponentFinderStrategyBuilder forEach(ComponentVisitor componentVisitor) {
this.componentVisitor = componentVisitor;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,11 @@ void setComponentFinderStrategy(ComponentFinderStrategy componentFinderStrategy)
this.componentFinderStrategy = componentFinderStrategy;
}

@Override
public String toString() {
return "DiscoveredComponent{" +
"name='" + name + '\'' +
'}';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ protected String[] getPermittedTokens() {
StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_MATCHER_TOKEN,
StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FILTER_TOKEN,
StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN,
StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_NAMING_TOKEN
StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_NAMING_TOKEN,
StructurizrDslTokens.COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.structurizr.dsl;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

final class ComponentFinderStrategyForEachDslContext extends DslContext {

private final List<String> dslLines = new ArrayList<>();

ComponentFinderStrategyForEachDslContext(ComponentFinderStrategyDslContext dslContext, StructurizrDslParser dslParser) {
dslContext.getComponentFinderStrategyBuilder().forEach(component -> {
try {
dslParser.parse(dslLines, new ComponentDslContext(component));
} catch (StructurizrDslParserException e) {
throw new RuntimeException(e);
}
});
}

void addLine(String line) {
this.dslLines.add(line);
}

@Override
protected String[] getPermittedTokens() {
return new String[] {};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import com.structurizr.component.supporting.AllTypesUnderPackageSupportingTypesStrategy;

import java.io.File;
import java.lang.reflect.Constructor;

final class ComponentFinderStrategyParser extends AbstractParser {

Expand All @@ -35,7 +34,7 @@ void parseTechnology(ComponentFinderStrategyDslContext context, Tokens tokens) {
}

String name = tokens.get(1);
context.getComponentFinderStrategyBuilder().forTechnology(name);
context.getComponentFinderStrategyBuilder().asTechnology(name);
}

void parseMatcher(ComponentFinderStrategyDslContext context, Tokens tokens, File dslFile) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,7 @@ public Workspace getWorkspace() {
}

private String getParsedDsl() {
StringBuilder buf = new StringBuilder();

for (String line : dslSourceLines) {
buf.append(line);
buf.append(System.lineSeparator());
}

return buf.toString();
return String.join(System.lineSeparator(), dslSourceLines);
}

void parse(DslParserContext context, File path) throws StructurizrDslParserException {
Expand All @@ -140,7 +133,7 @@ public void parse(File dslFile) throws StructurizrDslParserException {
}

try {
parse(Files.readAllLines(dslFile.toPath(), characterEncoding), dslFile, false);
parse(Files.readAllLines(dslFile.toPath(), characterEncoding), dslFile, false, true);
} catch (IOException e) {
throw new StructurizrDslParserException(e.getMessage());
}
Expand Down Expand Up @@ -175,7 +168,13 @@ public void parse(String dsl, File dslFile) throws StructurizrDslParserException
}

List<String> lines = Arrays.asList(dsl.split("\\r?\\n"));
parse(lines, dslFile, false);
parse(lines, dslFile, false, true);
}

void parse(List<String> lines, DslContext dslContext) throws StructurizrDslParserException {
startContext(dslContext);
parse(lines, null, true, false);
endContext();
}

/**
Expand All @@ -185,13 +184,12 @@ public void parse(String dsl, File dslFile) throws StructurizrDslParserException
* @param dslFile a File representing the DSL file, and therefore where includes/images/etc should be loaded relative to
* @throws StructurizrDslParserException when something goes wrong
*/
void parse(List<String> lines, File dslFile, boolean include) throws StructurizrDslParserException {
void parse(List<String> lines, File dslFile, boolean fragment, boolean includeInDslSourceLines) throws StructurizrDslParserException {
List<DslLine> dslLines = preProcessLines(lines);

for (DslLine dslLine : dslLines) {
boolean includeInDslSourceLines = true;

String line = dslLine.getSource();
String lineForDslSource = line;

if (line.startsWith(BOM)) {
// this caters for files encoded as "UTF-8 with BOM"
Expand Down Expand Up @@ -255,12 +253,13 @@ void parse(List<String> lines, File dslFile, boolean include) throws Structurizr
paddedLines.add(leadingSpace + unpaddedLine);
}

parse(paddedLines, includedFile.getFile(), true);
parse(paddedLines, includedFile.getFile(), true, true);
}

includeInDslSourceLines = false;
}

// include the !include in the parser DSL as: # !include ...
lineForDslSource = null;

} else if (PLUGIN_TOKEN.equalsIgnoreCase(firstToken)) {
if (!restricted) {
String fullyQualifiedClassName = new PluginParser().parse(getContext(), tokens.withoutContextStartToken());
Expand Down Expand Up @@ -454,6 +453,14 @@ void parse(List<String> lines, File dslFile, boolean include) throws Structurizr
} else if (COMPONENT_FINDER_STRATEGY_NAMING_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) {
new ComponentFinderStrategyParser().parseNaming(getContext(ComponentFinderStrategyDslContext.class), tokens, dslFile);

} else if (COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN.equalsIgnoreCase(firstToken) && inContext(ComponentFinderStrategyDslContext.class)) {
if (shouldStartContext(tokens)) {
startContext(new ComponentFinderStrategyForEachDslContext(getContext(ComponentFinderStrategyDslContext.class), this));
}

} else if (inContext(ComponentFinderStrategyForEachDslContext.class)) {
getContext(ComponentFinderStrategyForEachDslContext.class).addLine(line);

} else if (ENTERPRISE_TOKEN.equalsIgnoreCase(firstToken) && inContext(ModelDslContext.class)) {
throw new RuntimeException("The enterprise keyword was previously deprecated, and has now been removed - please use group instead (https://docs.structurizr.com/dsl/language#group)");

Expand Down Expand Up @@ -998,8 +1005,8 @@ void parse(List<String> lines, File dslFile, boolean include) throws Structurizr
}
}

if (includeInDslSourceLines) {
dslSourceLines.add(line);
if (includeInDslSourceLines && lineForDslSource != null) {
dslSourceLines.add(lineForDslSource);
}
} catch (Exception e) {
if (e.getMessage() != null) {
Expand All @@ -1010,7 +1017,7 @@ void parse(List<String> lines, File dslFile, boolean include) throws Structurizr
}
}

if (!include && !contextStack.empty()) {
if (!fragment && !contextStack.empty()) {
throw new StructurizrDslParserException("Unexpected end of DSL content - are one or more closing curly braces missing?");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,6 @@ class StructurizrDslTokens {
static final String COMPONENT_FINDER_STRATEGY_FILTER_TOKEN = "filter";
static final String COMPONENT_FINDER_STRATEGY_SUPPORTING_TYPES_TOKEN = "supportingTypes";
static final String COMPONENT_FINDER_STRATEGY_NAMING_TOKEN = "naming";
static final String COMPONENT_FINDER_STRATEGY_FOREACH_TOKEN = "forEach";

}
28 changes: 22 additions & 6 deletions structurizr-dsl/src/test/java/com/structurizr/dsl/DslTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ void test_includeLocalFile() throws Exception {
" }\n" +
" }\n" +
"\n" +
"}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
"}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
}

@Test
Expand Down Expand Up @@ -380,7 +380,7 @@ void test_includeLocalDirectory() throws Exception {
" }\n" +
" }\n" +
"\n" +
"}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
"}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
}

@Test
Expand Down Expand Up @@ -415,7 +415,7 @@ void test_includeUrl() throws Exception {
" }\n" +
" }\n" +
"\n" +
"}\n", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
"}", new String(Base64.getDecoder().decode(workspace.getProperties().get("structurizr.dsl"))));
}

@Test
Expand Down Expand Up @@ -1150,10 +1150,16 @@ void springPetClinic() throws Exception {
System.out.println(springPetClinicHome);
if (!StringUtils.isNullOrEmpty(springPetClinicHome)) {
System.out.println("Running Spring PetClinic example...");

File workspaceFile = new File("src/test/resources/dsl/spring-petclinic/workspace.dsl");
StructurizrDslParser parser = new StructurizrDslParser();
parser.parse(new File("src/test/resources/dsl/spring-petclinic/workspace.dsl"));
parser.parse(workspaceFile);

Person clinicEmployee = (Person)parser.getIdentifiersRegister().getElement("clinicEmployee");

Container webApplication = (Container)parser.getIdentifiersRegister().getElement("springPetClinic.webApplication");
Container relationalDatabaseSchema = (Container)parser.getIdentifiersRegister().getElement("springPetClinic.relationalDatabaseSchema");

assertEquals(7, webApplication.getComponents().size());

Component welcomeController = webApplication.getComponentWithName("Welcome Controller");
Expand All @@ -1162,56 +1168,66 @@ void springPetClinic() throws Exception {
assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java").getAbsolutePath(), welcomeController.getProperties().get("component.src"));
assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/system/WelcomeController.java", welcomeController.getUrl());
assertSame(welcomeController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.welcomecontroller"));
assertTrue(clinicEmployee.hasEfferentRelationshipWith(welcomeController));

Component ownerController = webApplication.getComponentWithName("Owner Controller");
assertNotNull(ownerController);
assertEquals("org.springframework.samples.petclinic.owner.OwnerController", ownerController.getProperties().get("component.type"));
assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java").getAbsolutePath(), ownerController.getProperties().get("component.src"));
assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerController.java", ownerController.getUrl());
assertSame(ownerController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerController"));
assertTrue(clinicEmployee.hasEfferentRelationshipWith(ownerController));

Component petController = webApplication.getComponentWithName("Pet Controller");
assertNotNull(petController);
assertEquals("org.springframework.samples.petclinic.owner.PetController", petController.getProperties().get("component.type"));
assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/PetController.java").getAbsolutePath(), petController.getProperties().get("component.src"));
assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/PetController.java", petController.getUrl());
assertSame(petController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.petcontroller"));
assertTrue(clinicEmployee.hasEfferentRelationshipWith(petController));

Component vetController = webApplication.getComponentWithName("Vet Controller");
assertNotNull(vetController);
assertEquals("org.springframework.samples.petclinic.vet.VetController", vetController.getProperties().get("component.type"));
assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetController.java").getAbsolutePath(), vetController.getProperties().get("component.src"));
assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetController.java", vetController.getUrl());
assertSame(vetController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetcontroller"));
assertTrue(clinicEmployee.hasEfferentRelationshipWith(vetController));

Component visitController = webApplication.getComponentWithName("Visit Controller");
assertNotNull(visitController);
assertEquals("org.springframework.samples.petclinic.owner.VisitController", visitController.getProperties().get("component.type"));
assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/VisitController.java").getAbsolutePath(), visitController.getProperties().get("component.src"));
assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/VisitController.java", visitController.getUrl());
assertSame(visitController, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.visitcontroller"));
assertTrue(clinicEmployee.hasEfferentRelationshipWith(visitController));

Component ownerRepository = webApplication.getComponentWithName("Owner Repository");
assertNotNull(ownerRepository);
assertEquals("org.springframework.samples.petclinic.owner.OwnerRepository", ownerRepository.getProperties().get("component.type"));
assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java").getAbsolutePath(), ownerRepository.getProperties().get("component.src"));
assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java", ownerRepository.getUrl());
assertSame(ownerRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.ownerrepository"));
assertTrue(ownerRepository.hasEfferentRelationshipWith(relationalDatabaseSchema));

Component vetRepository = webApplication.getComponentWithName("Vet Repository");
assertNotNull(vetRepository);
assertEquals("org.springframework.samples.petclinic.vet.VetRepository", vetRepository.getProperties().get("component.type"));
assertEquals(new File(springPetClinicHome, "src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java").getAbsolutePath(), vetRepository.getProperties().get("component.src"));
assertEquals("https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/vet/VetRepository.java", vetRepository.getUrl());
assertSame(vetRepository, parser.getIdentifiersRegister().getElement("springPetClinic.webApplication.vetrepository"));
assertTrue(vetRepository.hasEfferentRelationshipWith(relationalDatabaseSchema));

assertTrue(welcomeController.getRelationships().isEmpty());

assertNotNull(petController.getEfferentRelationshipWith(ownerRepository));
assertNotNull(visitController.getEfferentRelationshipWith(ownerRepository));
assertNotNull(ownerController.getEfferentRelationshipWith(ownerRepository));

assertNotNull(vetController.getEfferentRelationshipWith(vetRepository));

// this checks that the component forEach { ... } lines don't get repeated in the outputted DSL source
String content = Files.readString(workspaceFile.toPath());
assertEquals(content, new String(Base64.getDecoder().decode(parser.getWorkspace().getProperties().get("structurizr.dsl"))));

} else {
System.out.println("Skipping Spring PetClinic example...");
}
Expand Down
Loading

0 comments on commit 8d57511

Please sign in to comment.