Skip to content

Commit 00f8ee5

Browse files
committed
Add parent POM property inheritance support
- Recursively load properties from parent POMs via <relativePath> - Support multi-level inheritance (grandparent -> parent -> child) - Child properties override parent properties as expected - Handles both explicit and default relativePath (../pom.xml) - Gracefully handles missing parent POMs with warnings - Update README to document parent POM support This enables deptrast to handle most Maven multi-module projects where dependencies use properties defined in parent POMs.
1 parent ac9ed4f commit 00f8ee5

File tree

2 files changed

+81
-4
lines changed

2 files changed

+81
-4
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ Lines starting with `#` are treated as comments and ignored.
118118
Standard Maven pom.xml files. Deptrast extracts dependencies from `<dependencies>` blocks with smart handling:
119119
- **Test-scoped dependencies** - Automatically skipped
120120
- **Property variables** - Resolved from `<properties>` section (e.g., `${spring.version}`)
121+
- **Parent POM properties** - Recursively loads properties from parent POMs via `<relativePath>`
121122
- **Nested properties** - Supports properties that reference other properties
122-
- **Unresolvable variables** - Skipped with warning (e.g., parent POM properties)
123+
- **Unresolvable variables** - Skipped with warning (e.g., properties from remote artifacts)
123124

124125
#### Gradle build.gradle / build.gradle.kts
125126
Gradle build files in Groovy or Kotlin syntax. Supported formats:

src/main/java/com/contrastsecurity/deptrast/util/FileParser.java

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,18 @@ public static List<Package> parsePomFile(String filePath) {
181181
Document doc = builder.parse(new File(filePath));
182182
doc.getDocumentElement().normalize();
183183

184-
// First, parse all properties into a map
185-
Map<String, String> properties = parseProperties(doc);
186-
logger.info("Loaded {} properties from pom.xml", properties.size());
184+
// First, try to load parent POM properties if there's a parent reference
185+
Map<String, String> properties = new HashMap<>();
186+
Map<String, String> parentProperties = parseParentPomProperties(doc, filePath, factory, builder);
187+
if (parentProperties != null && !parentProperties.isEmpty()) {
188+
properties.putAll(parentProperties);
189+
logger.info("Loaded {} properties from parent pom.xml", parentProperties.size());
190+
}
191+
192+
// Then parse properties from current POM (these override parent properties)
193+
Map<String, String> currentProperties = parseProperties(doc);
194+
properties.putAll(currentProperties);
195+
logger.info("Loaded {} properties total ({} from current pom.xml)", properties.size(), currentProperties.size());
187196

188197
// Find all <dependency> elements
189198
NodeList dependencyNodes = doc.getElementsByTagName("dependency");
@@ -483,6 +492,73 @@ private static Map<String, String> parseProperties(Document doc) {
483492
return properties;
484493
}
485494

495+
/**
496+
* Parse properties from parent POM if it exists
497+
*
498+
* @param doc current document
499+
* @param currentFilePath path to current pom.xml
500+
* @param factory DocumentBuilderFactory to reuse
501+
* @param builder DocumentBuilder to reuse
502+
* @return map of parent properties or empty map
503+
*/
504+
private static Map<String, String> parseParentPomProperties(
505+
Document doc,
506+
String currentFilePath,
507+
DocumentBuilderFactory factory,
508+
DocumentBuilder builder) {
509+
510+
Map<String, String> parentProperties = new HashMap<>();
511+
512+
try {
513+
// Look for <parent> element
514+
NodeList parentNodes = doc.getElementsByTagName("parent");
515+
if (parentNodes.getLength() == 0) {
516+
return parentProperties;
517+
}
518+
519+
Element parentElement = (Element) parentNodes.item(0);
520+
521+
// Get relativePath (defaults to ../pom.xml if not specified)
522+
String relativePath = getElementText(parentElement, "relativePath");
523+
if (relativePath == null || relativePath.isEmpty()) {
524+
relativePath = "../pom.xml";
525+
}
526+
527+
// Resolve parent POM path relative to current POM
528+
Path currentPath = Paths.get(currentFilePath).toAbsolutePath().getParent();
529+
Path parentPath = currentPath.resolve(relativePath).normalize();
530+
531+
// Check if parent POM exists
532+
File parentPomFile = parentPath.toFile();
533+
if (!parentPomFile.exists()) {
534+
logger.warn("Parent POM not found at: {}", parentPath);
535+
return parentProperties;
536+
}
537+
538+
logger.info("Found parent POM at: {}", parentPath);
539+
540+
// Parse parent POM
541+
Document parentDoc = builder.parse(parentPomFile);
542+
parentDoc.getDocumentElement().normalize();
543+
544+
// Recursively get parent's parent properties first (grandparent)
545+
Map<String, String> grandparentProperties = parseParentPomProperties(
546+
parentDoc, parentPath.toString(), factory, builder);
547+
if (!grandparentProperties.isEmpty()) {
548+
parentProperties.putAll(grandparentProperties);
549+
}
550+
551+
// Then get parent's own properties (these override grandparent)
552+
Map<String, String> parentOwnProperties = parseProperties(parentDoc);
553+
parentProperties.putAll(parentOwnProperties);
554+
555+
} catch (Exception e) {
556+
logger.warn("Error parsing parent POM: {}", e.getMessage());
557+
}
558+
559+
return parentProperties;
560+
}
561+
486562
/**
487563
* Resolve a property reference like ${spring.version} using the properties map
488564
* Supports nested properties (properties that reference other properties)

0 commit comments

Comments
 (0)