|
- import logging
- import re
- from typing import Optional, TypeAlias
- from xml.etree import ElementTree
-
- from xmlutils import pom_namespace as ns, find_tag_text
-
- logger = logging.getLogger(__name__)
-
- Properties: TypeAlias = dict[str, Optional[str]]
-
-
- class PropertyMissing(Exception):
- def __init__(self, prop: str):
- self.prop = prop
-
-
- class PackageError(Exception):
- pass
-
-
- class PackagePOM:
- name: str
- is_bom: bool
- parent: Optional[str]
- group_id: Optional[str]
- artifact_id: Optional[str]
- version: Optional[str]
- properties: Properties
-
- _raw_root: ElementTree.Element
-
- def __init__(self, name: str, pom: str):
- logger.debug(f'{name}: Parsing POM')
- self.name = name
-
- self._raw_root = ElementTree.fromstring(pom)
-
- self.group_id, self.artifact_id, self.version = name.split(':')
-
- if self.group_id is None or self.artifact_id is None or self.version is None:
- logger.warning(
- f'{name}: One of groupId={self.group_id}, artifactId={self.artifact_id}, version={self.version} was '
- f'None. This can cause issues with dependency resolution'
- )
-
- self.parent = None
-
- if (parent_tag := self._raw_root.find('parent', ns)) is not None:
- parent_group = find_tag_text(parent_tag, 'groupId')
- parent_artifact = find_tag_text(parent_tag, 'artifactId')
- parent_version = find_tag_text(parent_tag, 'version')
-
- if parent_group is None or parent_artifact is None or parent_version is None:
- raise PackageError(f'Invalid parent {parent_group}:{parent_artifact}:{parent_version}')
- else:
- self.parent = f'{parent_group}:{parent_artifact}:{parent_version}'
-
- if (packaging := self._raw_root.find('packaging', ns)) is not None:
- self.is_bom = packaging == 'pom'
- else:
- self.is_bom = False
-
- self.set_properties({})
-
- @property
- def dependency_management(self) -> list[str]:
- return [
- self._package_from_xml_dep(dep)
- for dep in self._raw_root.find('dependencyManagement/dependencies', ns) or []
- ]
-
- def set_properties(self, parent_properties: Optional[Properties]):
- logger.debug(f'{self.name}: Parsing properties')
- props: Properties = parent_properties or {}
-
- for prop_tag in self._raw_root.findall('.//properties/*', ns):
- prop = prop_tag.tag.replace(f'{{{ns[""]}}}', '')
- value = prop_tag.text if prop_tag.text is not None else ''
- logger.debug(f'{self.name}: Setting prop {prop}={value}')
- props[prop] = value
-
- changed = True
- while changed:
- changed = False
-
- for prop, value in props.items():
- new_value = self._prop_replace(value, props, True)
-
- if new_value != value:
- changed = True
- logger.debug(f'{self.name}: Setting prop {prop}={new_value}')
- props[prop] = new_value
-
- self.properties = props
-
- def _prop_replace(self, text, props: Optional[Properties] = None, quiet: bool = False) -> str:
- def lookup_prop(match) -> str:
- prop = match.group(1)
-
- if prop == 'project.groupId':
- value = str(self.group_id)
- elif prop == 'project.artifactId':
- value = str(self.artifact_id)
- elif prop == 'project.version':
- value = str(self.version)
- elif prop.startswith('project.build') or prop.startswith('env.') or prop.startswith('maven.'):
- value = None
- elif prop in ['project.basedir', 'basedir', 'user.home', 'debug.port']:
- value = None
- else:
- try:
- value = props[prop] if props is not None else self.properties[prop]
- except KeyError:
- value = None
-
- if value is None and not quiet:
- raise PropertyMissing(prop)
- else:
- logger.debug(f'{self.name}: Replacing property {prop} with {value}')
- return value
-
- return re.sub(
- r'\$\{([^}]*)}',
- lookup_prop,
- text,
- )
-
- def _package_from_xml_dep(self, dep: ElementTree.Element) -> str:
- def prop_replace_tag(tag) -> str:
- return self._prop_replace(
- elem.text or '' if (elem := dep.find(tag, ns)) is not None else '',
- )
-
- return f"{prop_replace_tag('groupId')}:{prop_replace_tag('artifactId')}:{prop_replace_tag('version')}"
|