Skip to content

Commit

Permalink
first release of the redesigned Python version
Browse files Browse the repository at this point in the history
  • Loading branch information
uzdun committed Sep 17, 2018
1 parent 67599ae commit cf4bdb0
Show file tree
Hide file tree
Showing 109 changed files with 13,170 additions and 12,955 deletions.
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.idea/
out/
**/.pytest_cache
**/.vscode
**/__pycache__
*.noseids
21 changes: 0 additions & 21 deletions CodeableModels.iml

This file was deleted.

23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,28 @@ clear from the interfaces.

### Prerequisites / Installing

The project - by purpose - only uses plain old Java objects. So there are no requirements
to get the project up and running, other than placing it in your classpath.
The project - by purpose - only uses plain Python. So there are no requirements
to get the project up and running.

JUnit is required in the classpath for executing the test cases.
Nosetest is required for executing the test cases. Find installation instructions under:
[Nosetest](https://nose.readthedocs.io/en/latest/)

## Running the tests

```
codedableModels/tests/AllTests.java
```

contains the test suite. Run it as a JUnit test suite. All other files in the tests
directory contain individual JUnit tests.
Execute `nosetests` either in the main directory of the project or in `./tests`. The test files contained in
this directory comprise the test suite.

## Deployment

No specific instructions so far; simply build
No specific instructions so far; simply import from the `codeableModels` module like:

```
from codeableModels import CMetaclass, CClass, CObject, CAttribute, CException, CEnum, CStereotype
```

## Built With

* [JUnit](https://junit.org) - The test framework used
* [Nosetest](https://nose.readthedocs.io/en/latest/) - The test framework used

## Contributing

Expand Down
22 changes: 22 additions & 0 deletions codeableModels/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#from os.path import dirname, basename, isfile
#import glob
#import importlib

#modules = glob.glob(dirname(__file__)+"/*.py")
#__all__ = [ basename(f)[:-3] for f in modules if isfile(f) and not f.endswith('__init__.py')]

#from .impl import *

from codeableModels.cexception import CException
from codeableModels.cnamedelement import CNamedElement
from codeableModels.cbundlable import CBundlable
from codeableModels.cattribute import CAttribute
from codeableModels.cclassifier import CClassifier
from codeableModels.cmetaclass import CMetaclass
from codeableModels.cstereotype import CStereotype
from codeableModels.cclass import CClass
from codeableModels.cobject import CObject
from codeableModels.cenum import CEnum
from codeableModels.cbundle import CBundle, CPackage, CLayer
from codeableModels.cassociation import CAssociation
from codeableModels.clink import CLink, setLinks, addLinks, deleteLinks
241 changes: 241 additions & 0 deletions codeableModels/cassociation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
from codeableModels.internal.commons import setKeywordArgs, isCClassifier, isCMetaclass, isCStereotype
from codeableModels.cexception import CException
from codeableModels.cnamedelement import CNamedElement
from codeableModels.internal.stereotype_holders import CStereotypesHolder
import re

class CAssociation(CNamedElement):
STAR_MULTIPLICITY = -1

def __init__(self, source, target, descriptor = None, **kwargs):
self.source = source
self.target = target
self.roleName = None
self.sourceRoleName = None
self._sourceMultiplicityString = "1"
self._sourceLowerMultiplicity = 1
self._sourceUpperMultiplicity = 1
self._multiplicityString = "*"
self._lowerMultiplicity = 0
self._upperMultiplicity = self.STAR_MULTIPLICITY
self._aggregation = False
self._composition = False
self._stereotypesHolder = CStereotypesHolder(self)
name = kwargs.pop("name", None)
self.ends = None
super().__init__(name, **kwargs)
if descriptor != None:
self._evalDescriptor(descriptor)

def _initKeywordArgs(self, legalKeywordArgs = None, **kwargs):
if legalKeywordArgs == None:
legalKeywordArgs = []
legalKeywordArgs.extend(["multiplicity", "roleName", "sourceMultiplicity",
"sourceRoleName", "aggregation", "composition", "stereotypes"])
super()._initKeywordArgs(legalKeywordArgs, **kwargs)

def __str__(self):
return super(CAssociation, self).__str__()
def __repr__(self):
name = ""
if self.name != None:
name = self.name
return f"CAssociation name = {name!s}, source = {self.source!s} -> target = {self.target!s}"

def _getOppositeClass(self, cl):
if cl == self.source:
return self.target
else:
return self.source

def _matches(self, classifier, roleName, associationClassifier, associationRoleName):
if classifier == None and roleName == None:
return False
matches = True
if roleName != None:
if roleName != associationRoleName:
matches = False
if matches and classifier != None:
if not classifier.conformsToType(associationClassifier):
matches = False
if matches:
return True
return False

def _matchesTarget(self, classifier, roleName):
return self._matches(classifier, roleName, self.target, self.roleName)

def _matchesSource(self, classifier, roleName):
return self._matches(classifier, roleName, self.source, self.sourceRoleName)

@property
def aggregation(self):
return self._aggregation
@aggregation.setter
def aggregation(self, aggregation):
if aggregation:
self._composition = False
self._aggregation = aggregation

def _setMultiplicity(self, multiplicity, isTargetMultiplicity):
if not isinstance(multiplicity, str):
raise CException("multiplicity must be provided as a string")
lower = -2
upper = -2
try:
dotsPos = multiplicity.find("..")
if dotsPos != -1:
lowerMatch = multiplicity[:dotsPos]
upperMatch = multiplicity[dotsPos+2:]
lower = int(lowerMatch)
if lower < 0:
raise CException(f"negative multiplicity in '{multiplicity!s}'")
if upperMatch.strip() == "*":
upper = self.STAR_MULTIPLICITY
else:
upper = int(upperMatch)
if lower < 0 or upper < 0:
raise CException(f"negative multiplicity in '{multiplicity!s}'")
elif multiplicity.strip() == "*":
lower = 0
upper = self.STAR_MULTIPLICITY
else:
lower = int(multiplicity)
if lower < 0:
raise CException(f"negative multiplicity in '{multiplicity!s}'")
upper = lower
except Exception as e:
if isinstance(e, CException):
raise e
raise CException(f"malformed multiplicity: '{multiplicity!s}'")

if isTargetMultiplicity:
self._upperMultiplicity = upper
self._lowerMultiplicity = lower
else:
self._sourceUpperMultiplicity = upper
self._sourceLowerMultiplicity = lower

@property
def multiplicity(self):
return self._multiplicityString
@multiplicity.setter
def multiplicity(self, multiplicity):
self._multiplicityString = multiplicity
self._setMultiplicity(multiplicity, True)

@property
def sourceMultiplicity(self):
return self._sourceMultiplicityString
@sourceMultiplicity.setter
def sourceMultiplicity(self, multiplicity):
self._sourceMultiplicityString = multiplicity
self._setMultiplicity(multiplicity, False)

@property
def composition(self):
return self._composition
@composition.setter
def composition(self, composition):
if composition:
self._aggregation = False
self._composition = composition

@property
def stereotypes(self):
return self._stereotypesHolder.stereotypes

@stereotypes.setter
def stereotypes(self, elements):
self._stereotypesHolder.stereotypes = elements

def delete(self):
if self._isDeleted == True:
return
if isCMetaclass(self.source):
allInstances = self.source.allClasses
elif isCStereotype(self.source):
allInstances = self.source.allExtendedInstances
else:
allInstances = self.source.allObjects
for instance in allInstances:
for link in instance.linkObjects:
link.delete()
self.source._associations.remove(self)
if self.source != self.target:
self.target._associations.remove(self)
for s in self._stereotypesHolder._stereotypes:
s._extended.remove(self)
self._stereotypesHolder._stereotypes = []
super().delete()

def _checkMultiplicity(self, object, actualLength, actualOppositeLength, checkTargetMultiplicity):
if checkTargetMultiplicity:
upper = self._upperMultiplicity
lower = self._lowerMultiplicity
otherSideLower = self._sourceLowerMultiplicity
multiplicityString = self._multiplicityString
else:
upper = self._sourceUpperMultiplicity
lower = self._sourceLowerMultiplicity
otherSideLower = self._lowerMultiplicity
multiplicityString = self._sourceMultiplicityString

if (upper != CAssociation.STAR_MULTIPLICITY and actualLength > upper) or actualLength < lower:
# if there is actually no link as actualOppositeLength is zero, this is ok, if the otherLower including zero:
if not (actualOppositeLength == 0 and otherSideLower == 0):
raise CException(f"links of object '{object}' have wrong multiplicity '{actualLength!s}': should be '{multiplicityString!s}'")

def _evalDescriptor(self, descriptor):
# handle name only if a ':' is found in the descriptor
index = descriptor.find(":")
if index != -1:
name = descriptor[0:index]
descriptor = descriptor[index+1:]
self.name = name.strip()

# handle type of relation
aggregation = False
composition = False
index = descriptor.find("->")
length = 2
if index == -1:
index = descriptor.find("<>-")
if index != -1:
length = 3
aggregation = True
else:
index = descriptor.find("<*>-")
length = 4
composition = True
if index == -1:
raise CException("association descriptor malformed: '" + descriptor + "'")

# handle role names and multiplicities
sourceStr = descriptor[0:index]
targetStr = descriptor[index+length:]
regexpWithRoleName = '\s*\[([^\]]+)\]\s*(\S*)\s*'
regexpOnlyMultiplicity = '\s*(\S*)\s*'

m = re.search(regexpWithRoleName, sourceStr)
if m != None:
self.sourceRoleName = m.group(1)
if m.group(2) != '':
self.sourceMultiplicity = m.group(2)
else:
m = re.search(regexpOnlyMultiplicity, sourceStr)
self.sourceMultiplicity = m.group(1)

m = re.search(regexpWithRoleName, targetStr)
if m != None:
self.roleName = m.group(1)
if m.group(2) != '':
self.multiplicity = m.group(2)
else:
m = re.search(regexpOnlyMultiplicity, targetStr)
self.multiplicity = m.group(1)

if aggregation:
self.aggregation = True
elif composition:
self.composition = True
Loading

0 comments on commit cf4bdb0

Please sign in to comment.