Mini Shell
#
# Copyright (c) 2015, Red Hat, Inc.
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the
# distribution.
# 3. Neither the name of the Red Hat nor the names of its
# contributors may be used to endorse or promote products derived
# from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# Authors: Stanislav Ochotnicky <sochotnicky@redhat.com>
# Michal Srb <msrb@redhat.com>
import re
import six
import javapackages.maven.pomreader as POMReader
import javapackages.common.strutils as Printer
from javapackages.common.exception import JavaPackagesToolsException
from lxml.etree import Element, SubElement, tostring
class ArtifactException(JavaPackagesToolsException):
pass
class ArtifactFormatException(JavaPackagesToolsException):
pass
class ArtifactValidationException(JavaPackagesToolsException):
pass
class AbstractArtifact(object):
def get_mvn_str(self):
m = self.__get_members()
return Printer.get_mvn_str(m['groupId'],
m['artifactId'],
ext=m['extension'],
cla=m['classifier'],
ver=m['version'])
def get_rpm_str(self, namespace=None, compat_ver=None, pkg_ver=None):
m = self.__get_members()
return Printer.get_rpm_str(m['groupId'],
m['artifactId'],
ext=m['extension'],
cla=m['classifier'],
namespace=namespace,
compat_ver=compat_ver,
pkg_ver=pkg_ver)
@staticmethod
def get_parts_from_mvn_str(mvnstr):
tup = mvnstr.split(":")
# groupId and artifactId are always present
if len(tup) < 2:
raise ArtifactFormatException("Artifact string '{mvnstr}' does not "
"contain ':' character. Can not parse"
.format(mvnstr=mvnstr))
if len(tup) > 5:
raise ArtifactFormatException("Artifact string '{mvnstr}' contains "
"too many colons. Can not parse"
.format(mvnstr=mvnstr))
parts = {'groupId': '',
'artifactId': '',
'extension': '',
'classifier': '',
'version': ''}
parts['groupId'] = tup[0]
parts['artifactId'] = tup[1]
parts['extension'] = tup[2] if len(tup) >= 4 else ""
parts['classifier'] = tup[3] if len(tup) >= 5 else ""
parts['version'] = tup[-1] if len(tup) >= 3 else ""
return parts
def get_xml_element(self, root_element_name=None):
"""
Return XML Element node representation of the Artifact
"""
if root_element_name is None:
clsname = self.__class__.__name__
root_element_name = clsname[0].lower() + clsname[1:]
root = Element(root_element_name)
members = self.__get_members()
for key in members:
if (key == "extension" and members[key] == "jar" and
hasattr(self, "_default_extension") and
getattr(self, "_default_extension") is True):
continue
if members[key]:
item = SubElement(root, key)
item.text = members[key]
return root
def get_xml_str(self, root_element_name=None):
"""
Return XML formatted string representation of the Artifact
"""
if root_element_name is None:
clsname = self.__class__.__name__
root_element_name = clsname[0].lower() + clsname[1:]
root = self.get_xml_element(root_element_name)
return tostring(root, pretty_print=True)
def get_artifact(self, extension="", classifier="", version=""):
m = self.__get_members()
return Artifact(m['groupId'], m['artifactId'],
extension or m['extension'],
classifier or m['classifier'],
version or m['version'])
def merge_with(self, artifact):
if type(self) != type(artifact):
raise Exception("Unable to merge artifacts of different types. "
"{this} cannot be merged with {other}"
.format(this=type(self), other=type(artifact)))
if self.compare_to(artifact):
for member in self.__dict__:
if not member.startswith('_'):
# for "scope":
# copy value from parent artifact only if this object
# contains default/implicit value
if (member == "scope" and
hasattr(self, "_default_" + member) and
getattr(self, "_default_" + member)):
setattr(self, member, getattr(artifact, member))
# if the copied value was not a default value in parent,
# then we have a definitive value and we should never
# override it again
if not getattr(artifact, "_default_" + member):
setattr(self, "_default_" + member, False)
# for fields other than "scope":
# copy value from parent artifact, if current value is empty
elif not getattr(self, member):
setattr(self, member, getattr(artifact, member))
def update_from(self, artifact):
if self.compare_to(artifact):
for member in self.__dict__:
if not member.startswith('_') and not getattr(self, member):
setattr(self, member, getattr(artifact, member))
return True
return False
def compare_to(self, artifact):
if type(self) != type(artifact):
return False
this = self.__get_significant_members(ignore_version=True)
other = artifact.__get_significant_members(ignore_version=True)
if this == other:
return True
return False
def interpolate(self, properties):
leftovers = []
for member in self.__dict__:
if (not member.startswith('_') and
getattr(self, member) and
isinstance(getattr(self, member), six.string_types)):
curr_value = getattr(self, member)
prog = re.compile("\$\{([^}]+)\}")
props = prog.findall(curr_value)
for key in props:
try:
prop_value = properties[key]
curr_value = curr_value.replace("${{{prop}}}"
.format(prop=key), prop_value)
setattr(self, member, curr_value)
except KeyError:
leftovers.append(key)
return leftovers
def __unicode__(self):
return six.text_type(self.get_mvn_str())
def __str__(self):
return self.__unicode__()
def __eq__(self, other):
if type(other) is type(self):
return self.__get_significant_members() == other.__get_significant_members()
return False
def __ne__(self, other):
return not self.__eq__(other)
def __hash__(self):
result = 0
for key in self.__get_significant_members():
var = getattr(self, key)
result += var.__hash__()
return result
def __get_significant_members(self, ignore_version=False):
m = {}
for mname in ["groupId", "artifactId", "extension", "classifier"]:
if hasattr(self, mname):
m[mname] = getattr(self, mname)
if not ignore_version and hasattr(self, "version"):
m["version"] = getattr(self, "version")
return m
def __get_members(self):
m = {'groupId': '',
'artifactId': '',
'extension': '',
'classifier': '',
'version': ''}
for key in m:
if hasattr(self, key):
m[key] = getattr(self, key)
return m
class Artifact(AbstractArtifact):
"""
Simplified representation of Maven artifact for purpose of packaging
Package consists mostly of simple properties and string formatting and
loading functions to prevent code duplication elsewhere
"""
def __init__(self, groupId, artifactId, extension="",
classifier="", version=""):
self.groupId = groupId.strip()
self.artifactId = artifactId.strip()
self.extension = extension.strip() or "jar"
self.classifier = classifier.strip()
self.version = version.strip()
self._default_extension = True
if extension:
self._default_extension = False
@classmethod
def merge_artifacts(cls, dominant, recessive):
"""
Merge two artifacts into one. Information missing in dominant artifact
will be copied from recessive artifact. Returns new merged artifact.
"""
ret = cls(dominant.groupId, dominant.artifactId, dominant.extension,
dominant.classifier, dominant.version)
for key in ("artifactId", "groupId", "extension", "version",
"classifier"):
if not getattr(ret, key):
setattr(ret, key, getattr(recessive, key))
return ret
def validate(self, allow_empty=True, allow_wildcards=True,
allow_backref=True):
"""
Function to validate current state of artifact with regards to
wildcards, empty parts and backreference usage
"""
all_empty = True
wildcard_used = False
backref_used = False
backref_re = re.compile(r'@\d+')
for key in ("artifactId", "groupId", "extension", "version",
"classifier"):
val = getattr(self, key)
if not val:
continue
if val and key == "extension" and self._default_extension:
continue
else:
all_empty = False
if val.find('*') != -1:
wildcard_used = True
if backref_re.match(val):
backref_used = True
if not allow_empty and all_empty:
raise ArtifactValidationException("All parts of artifact are empty")
if not allow_wildcards and wildcard_used:
raise ArtifactValidationException("Wildcard used in artifact")
if not allow_backref and backref_used:
raise ArtifactValidationException("Backreference used in artifact")
return True
@classmethod
def from_xml_element(cls, xmlnode):
"""
Create Artifact from xml.etree.ElementTree.Element as contained
within pom.xml or a dependency map.
"""
parts = {'groupId': '',
'artifactId': '',
'extension': '',
'classifier': '',
'version': ''}
parts = POMReader.find_parts(xmlnode, parts)
if not parts['groupId'] or not parts['artifactId']:
raise ArtifactFormatException(
"Empty groupId or artifactId encountered. "
"This is a bug, please report it!")
return cls(parts['groupId'], parts['artifactId'], parts['extension'],
parts['classifier'], parts['version'])
@classmethod
def from_mvn_str(cls, mvnstr):
"""
Create Artifact from Maven-style definition
The string should be in the format of:
groupId:artifactId[:extension[:classifier]][:version]
Where last part is always considered to be version unless empty
"""
p = Artifact.get_parts_from_mvn_str(mvnstr)
return cls(p['groupId'], p['artifactId'], p['extension'],
p['classifier'], p['version'])
Zerion Mini Shell 1.0