# nxpy.maven package ---------------------------------------------------------
# Copyright Nicola Musatti 2010 - 2017
# Use, modification, and distribution are subject to the Boost Software
# License, Version 1.0. (See accompanying file LICENSE.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
# See http://nxpy.sourceforge.net for library home page. ---------------------
r"""
Manipulation of Maven POM files.
"""
from __future__ import absolute_import
import collections
import os.path
import lxml.etree
import nxpy.xml.util
import nxpy.maven.artifact
_ns = nxpy.xml.util.Namespace("http://maven.apache.org/POM/4.0.0")
[docs]class BadPomFileError(EnvironmentError):
pass
[docs]class ArtifactAlreadyPresentError(ValueError):
pass
[docs]class MissingArtifactError(ValueError):
pass
[docs]class ModuleAlreadyPresentError(ValueError):
pass
[docs]class MissingModuleError(ValueError):
pass
[docs]class DependencyIterator(collections.Iterator):
[docs] def __init__(self, deps):
self.iter = iter(deps.dependencies)
[docs] def __next__(self):
return next(self.iter)[1]
[docs]class Dependencies(object):
[docs] def __init__(self, element):
self.element = element
self._modified = False
self.dependencies = []
if self.element is not None:
i = 0
for d in element.getchildren():
if d.tag is not lxml.etree.Comment:
a = nxpy.maven.artifact.Artifact(d)
self.dependencies.append([i, a])
i += 1
def _saved(self):
self._modified = False
for a in self.dependencies:
a[1]._modified = False
@property
def modified(self):
return self._modified or any([ a[1]._modified for a in self.dependencies ])
[docs] def contains(self, artifact):
if isinstance(artifact, nxpy.maven.artifact.Artifact):
group_id = artifact.groupId
artifact_id = artifact.artifactId
else:
group_id, artifact_id = artifact.split(":")[0:2]
for a in self.dependencies:
if a[1].groupId == group_id and a[1].artifactId == artifact_id:
return a[1]
return None
[docs] def add(self, artifact):
if self.contains(artifact):
raise ArtifactAlreadyPresentError()
d = _ns.Element("dependency")
d.text = self.element.text + " "
d.tail = self.element.tail + " "
gi = _ns.SubElement(d,"groupId")
gi.text = artifact.groupId
gi.tail = d.text
ai = _ns.SubElement(d,"artifactId")
ai.text = artifact.artifactId
ai.tail = d.text
v = _ns.SubElement(d,"version")
v.text = artifact.version
if artifact.packaging != "jar":
v.tail = d.text
p = _ns.SubElement(d,"type")
p.text = artifact.packaging
p.tail = d.tail + " "
else:
v.tail = d.tail + " "
art = nxpy.maven.artifact.Artifact(d)
art._modified = True
index = 0
for i in range(0, len(self.dependencies) -1):
if index == 0:
if self.dependencies[i][1] > art:
index = i
else:
self.dependencies[i][0] += 1
if index < len(self.dependencies):
d.tail += " "
else:
self.element[self.dependencies[index-1][0]].tail += " "
self.element.insert(self.dependencies[index][0], d)
self.dependencies.insert(index, [self.dependencies[index][0], art])
[docs] def remove(self, artifact):
if not self.contains(artifact):
raise MissingArtifactError()
index = -1
for i in range(0, len(self.dependencies) -1):
if index == -1:
if ( self.dependencies[i][1].artifactId == artifact.artifactId and
self.dependencies[i][1].groupId == artifact.groupId ):
index = i
else:
self.dependencies[i][0] -= 1
if index == -1:
raise MissingArtifactError()
if self.dependencies[index][0] == len(self.element) - 1:
self.element[self.dependencies[index][0] -1].tail = self.element[self.dependencies[index][1]].tail
del self.element[self.dependencies[index][0]]
del self.dependencies[index]
self._modified = True
[docs] def __str__(self):
return "\n".join([ str(a) for a in self.dependencies ])
[docs] def __iter__(self):
return DependencyIterator(self)
[docs]class Modules(nxpy.xml.util.SequenceElement):
"""
Map a POM's modules element to a mutable sequence.
Supports addition and removal of modules by name.
"""
[docs] def __init__(self, root):
super(Modules, self).__init__(root, "modules", "module", _ns.url)
def _saved(self):
self.modified = False
[docs] def contains(self, module):
return module in self
[docs] def add(self, module):
if self.contains(module):
raise ModuleAlreadyPresentError()
i = 0
for m in self:
if m > module:
break
i += 1
self.insert(i, module)
[docs] def remove(self, module):
i = 0
for m in self:
if m == module:
break
i += 1
if i == len(self):
raise MissingModuleError()
del self[i]
[docs]class Scm(object):
[docs] def __init__(self, element):
self._modified = False
self.element = element
self._connection = _ns.find(element, "connection")
self._developerConnection = _ns.find(element, "developerConnection")
self._url = _ns.find(element, "url")
connection = nxpy.xml.util.make_property("_connection")
developerConnection = nxpy.xml.util.make_property("_developerConnection")
url = nxpy.xml.util.make_property("_url")
@property
def modified(self):
return self._modified
[docs]class Repository(object):
[docs] def __init__(self, element):
self._modified = False
self.element = element
self._id = _ns.find(element, "id")
self._name = _ns.find(element, "name")
self._url = _ns.find(element, "url")
id = nxpy.xml.util.make_property("_id")
name = nxpy.xml.util.make_property("_name")
url = nxpy.xml.util.make_property("_url")
@property
def modified(self):
return self._modified
[docs]class DistributionManagement(object):
[docs] def __init__(self, element):
self.element = element
self.repository = Repository(_ns.find(element, "repository"))
self.snapshotRepository = Repository(_ns.find(element, "snapshotRepository"))
def _saved(self):
self.repository._modified = False
self.snapshotRepository._modified = False
@property
def modified(self):
return self.repository.modified or self.snapshotRepository.modified
[docs]class Properties(nxpy.xml.util.MappingElement):
[docs] def __init__(self, parent):
super(Properties, self).__init__(parent, "properties", _ns.url)
[docs]class Pom(object):
_root = ( r'<project xmlns="http://maven.apache.org/POM/4.0.0"'
r' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n'
r' xsi:schemaLocation="http://maven.apache.org/POM/4.0.0'
r' http://maven.apache.org/maven-v4_0_0.xsd">' )
[docs] def __init__(self, path):
self.path = os.path.realpath(path)
self.dir = os.path.split(self.path)[0]
try:
self.tree = nxpy.xml.util.parse(path)
except IOError:
raise BadPomFileError(path)
self.root = self.tree.getroot()
self.artifact = nxpy.maven.artifact.Artifact(self.root)
self.dependencies = Dependencies(_ns.find(self.root, "dependencies"))
parent = _ns.find(self.root, "parent")
try:
self.parent = nxpy.maven.artifact.Artifact(parent)
except ( AttributeError, TypeError ):
self.parent = None
self.scm = None
scm = _ns.find(self.root, "scm")
if scm is not None:
self.scm = Scm(scm)
self.modules = Modules(self.root)
depmgmt = None
if self.artifact.packaging == "pom":
deps = _ns.find(self.root, "dependencyManagement")
if deps is not None:
depmgmt = _ns.find(deps, "dependencies")
self.dependencyManagement = Dependencies(depmgmt)
self.properties = Properties(self.root)
self.assembly_descriptor = None
build = _ns.find(self.root, "build")
if build is not None:
plugins = _ns.find(build, "plugins")
if plugins is not None:
for p in plugins.getchildren():
if _ns.find(p, "artifactId").text == "maven-assembly-plugin":
ad = _ns.find(_ns.find(p, "configuration"), "descriptors")[0]
self.assembly_descriptor = os.path.normpath(os.path.join(self.dir,
ad.text))
break
self.distributionManagement = None
dm = _ns.find(self.root, "distributionManagement")
if dm is not None:
self.distributionManagement = DistributionManagement(dm)
self._writer = nxpy.xml.util.Writer(self._root, None, 4)
[docs] def qualified_name(self, full=False):
return self.artifact.qualified_name(full)
@property
def modified(self):
return ( self.artifact.modified or ( self.parent and self.parent.modified ) or
self.modules.modified or self.dependencies.modified or
self.dependencyManagement.modified or ( self.scm and self.scm.modified ) or
self.properties.modified or
( self.distributionManagement and self.distributionManagement.modified ) )
[docs] def write(self, where):
if not where:
where = self.path
self._writer.write(self.root, where)
[docs] def save(self):
if self.modified:
self.write(None)
self.artifact._modified = False
if self.parent:
self.parent._modified = False
self.scm._modified = False
self.dependencies._saved()
self.dependencyManagement._saved()
self.distributionManagement._saved()
self.modules._saved()
self.properties.modified = False
return True
return False