diff --git a/bundles/mdpa.gdpr.analysis.validation/src/mdpa/gdpr/analysis/validation/MemoryGDPRResourceProvider.java b/bundles/mdpa.gdpr.analysis.validation/src/mdpa/gdpr/analysis/validation/MemoryGDPRResourceProvider.java index e35d447..7087051 100644 --- a/bundles/mdpa.gdpr.analysis.validation/src/mdpa/gdpr/analysis/validation/MemoryGDPRResourceProvider.java +++ b/bundles/mdpa.gdpr.analysis.validation/src/mdpa/gdpr/analysis/validation/MemoryGDPRResourceProvider.java @@ -14,7 +14,7 @@ public MemoryGDPRResourceProvider(GDPRModelBuilder modelBuilder) { } @Override - public LegalAssessmentFacts getModel() { + public LegalAssessmentFacts getGDPRModel() { return modelBuilder.getGdprModel(); } diff --git a/bundles/mdpa.gdpr.analysis/META-INF/MANIFEST.MF b/bundles/mdpa.gdpr.analysis/META-INF/MANIFEST.MF index 6f30d15..50cd97c 100644 --- a/bundles/mdpa.gdpr.analysis/META-INF/MANIFEST.MF +++ b/bundles/mdpa.gdpr.analysis/META-INF/MANIFEST.MF @@ -7,15 +7,17 @@ Export-Package: mdpa.gdpr.analysis, mdpa.gdpr.analysis.core, mdpa.gdpr.analysis.core.resource, mdpa.gdpr.analysis.dfd +Import-Package: org.antlr.runtime;version="3.2.0", + org.eclipse.xtext.parser.antlr Require-Bundle: mdpa.gdpr.dfdconverter;bundle-version="1.0.0", mdpa.gdpr.dfdconverter.tracemodel;bundle-version="0.1.0", - org.dataflowanalysis.analysis;bundle-version="2.0.0", + org.dataflowanalysis.analysis, mdpa.gdpr.metamodel.contextproperties;bundle-version="0.1.0", - org.dataflowanalysis.analysis.dfd;bundle-version="2.0.0", + org.dataflowanalysis.analysis.dfd, org.apache.log4j;bundle-version="1.2.24", - org.eclipse.core.runtime;bundle-version="3.26.100", - tools.mdsd.library.standalone.initialization;bundle-version="0.3.0", - tools.mdsd.library.standalone.initialization.log4j;bundle-version="0.3.0", - org.eclipse.emf.ecore.xmi;bundle-version="2.18.0" + org.eclipse.core.runtime, + tools.mdsd.library.standalone.initialization, + tools.mdsd.library.standalone.initialization.log4j, + org.eclipse.emf.ecore.xmi Automatic-Module-Name: mdpa.gdpr.analysis Bundle-RequiredExecutionEnvironment: JavaSE-17 diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysis.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysis.java index 88b493b..06d325a 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysis.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysis.java @@ -12,6 +12,14 @@ import tools.mdsd.library.standalone.initialization.StandaloneInitializationException; import tools.mdsd.library.standalone.initialization.StandaloneInitializerBuilder; +/** + * Extension of the {@link DataFlowConfidentialityAnalysis} for usage with the GDPR metamodel that is able to resolve + * uncertain context dependent attributes. + *

+ * Inputs to the analysis are a metamodel instance of the GDPR model and the context properties model + *

+ * Note: Do not create an instance of this class manually, use the {@link GDPRLegalAssessmentAnalysisBuilder} instead + */ public class GDPRLegalAssessmentAnalysis extends DataFlowConfidentialityAnalysis { public static final String PLUGIN_PATH = "mdpa.gdpr.analysis"; @@ -21,6 +29,16 @@ public class GDPRLegalAssessmentAnalysis extends DataFlowConfidentialityAnalysis private final Optional> modelProjectActivator; private final String modelProjectName; + /** + * Create a new {@link GDPRLegalAssessmentAnalysis} with the given resource provider and optionally a modelling project + * with a plugin activator + *

+ * Note: Do not create an instance of this class manually, use the {@link GDPRLegalAssessmentAnalysisBuilder} instead + * @param resourceProvider {@link GDPRResourceProvider} providing a metamodel instance of the GDPR and Context Property + * model + * @param modelProjectActivator Optional model project activator + * @param modelProjectName Optional model project name + */ public GDPRLegalAssessmentAnalysis(GDPRResourceProvider resourceProvider, Optional> modelProjectActivator, String modelProjectName) { this.resourceProvider = resourceProvider; diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysisBuilder.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysisBuilder.java index a1020ff..baae828 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysisBuilder.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/GDPRLegalAssessmentAnalysisBuilder.java @@ -8,6 +8,11 @@ import org.dataflowanalysis.analysis.utils.ResourceUtils; import org.eclipse.core.runtime.Plugin; +/** + * Extension of the {@link DataFlowAnalysisBuilder} responsible for creating a valid {@link GDPRLegalAssessmentAnalysis} + * from the following: - A valid path to a .gdpr metamodel instance - A valid path to a .contextproperties metamodel + * instance + */ public class GDPRLegalAssessmentAnalysisBuilder extends DataFlowAnalysisBuilder { private final Logger logger = Logger.getLogger(GDPRLegalAssessmentAnalysisBuilder.class); diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/UncertaintyUtils.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/UncertaintyUtils.java index d1bee40..b124348 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/UncertaintyUtils.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/UncertaintyUtils.java @@ -2,15 +2,17 @@ import java.util.ArrayList; import java.util.List; +import java.util.Optional; import mdpa.gdpr.analysis.core.ContextDependentAttributeScenario; import mdpa.gdpr.analysis.core.ContextDependentAttributeSource; import mdpa.gdpr.analysis.dfd.DFDGDPRVertex; import mdpa.gdpr.metamodel.GDPR.Data; import mdpa.gdpr.metamodel.GDPR.NaturalPerson; import mdpa.gdpr.metamodel.GDPR.PersonalData; -import mdpa.gdpr.metamodel.contextproperties.ContextDefinition; -import mdpa.gdpr.metamodel.contextproperties.GDPRContextElement; -import mdpa.gdpr.metamodel.contextproperties.PropertyValue; +import mdpa.gdpr.metamodel.contextproperties.Expression; +import mdpa.gdpr.metamodel.contextproperties.LAFScopeElement; +import mdpa.gdpr.metamodel.contextproperties.Scope; + import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.dataflowanalysis.dfd.datadictionary.Assignment; @@ -68,23 +70,7 @@ public static Behavior createBehavior(DFDGDPRVertex impactedElement, DataDiction .map(it -> EcoreUtil.copy(it)) .toList()); - LabelType type = dd.getLabelTypes() - .stream() - .filter(it -> it.getEntityName() - .equals(source.getPropertyType() - .getEntityName())) - .findAny() - .orElseThrow(); - List

+ * As a Context Dependent Attribute Scenario can occur in two scenarios we differentiate: + */ public class ContextDependentAttributeScenario { private final Logger logger = Logger.getLogger(ContextDependentAttributeScenario.class); private final String name; - private final TransformationManager transformationManager; - private final List propertyValues; - private final List context; + private final List expressions; + private final List scopes; private final List sources; private final ContextDependentAttributeSource contextDependentAttributeSource; private final boolean resolvedUncertainty; - public ContextDependentAttributeScenario(ContextAnnotation contextAnnotation, ContextDependentAttributeSource contextDependentAttributeSource) { - this.name = contextAnnotation.getEntityName(); - this.transformationManager = new TransformationManager(); - this.propertyValues = contextAnnotation.getPropertyvalue(); - this.context = contextAnnotation.getContextdefinition(); + /** + * Creates a new context dependent attribute scenario that matches a specific context. Therefore, it does not resolve an + * uncertain CDA + * @param scopeSet {@link ScopeSet} the Scenario requires + * @param contextDependentAttributeSource Corresponding {@link ContextDependentAttributeSource} + */ + public ContextDependentAttributeScenario(ScopeSet scopeSet, ContextDependentAttributeSource contextDependentAttributeSource) { + this.name = scopeSet.getEntityName(); + this.expressions = scopeSet.getExpression(); + this.scopes = scopeSet.getScope(); this.sources = List.of(); this.contextDependentAttributeSource = contextDependentAttributeSource; this.resolvedUncertainty = false; } - public ContextDependentAttributeScenario(PropertyValue propertyValue, ContextDependentAttributeSource contextDependentAttributeSource, + /** + * Creates a new {@link ContextDependentAttributeScenario} that is resolving an uncertainty. Therefore, it requires a + * expression that is applied, the corresponding {@link ContextDependentAttributeSource} and a list of other + * {@link ContextDependentAttributeSource} that contradict the uncertain CDA + * @param expression Expression that is applied, when this scenario is applied + * @param contextDependentAttributeSource Corresponding {@link ContextDependentAttributeSource} + * @param sources Other {@link ContextDependentAttributeSource} that must not be true + */ + public ContextDependentAttributeScenario(Expression expression, ContextDependentAttributeSource contextDependentAttributeSource, List sources) { - this.name = propertyValue.getEntityName() + "@" + contextDependentAttributeSource.getName(); - this.transformationManager = new TransformationManager(); - this.propertyValues = List.of(propertyValue); - this.context = List.of(); + this.name = expression.getEntityName() + "@" + contextDependentAttributeSource.getName(); + this.expressions = List.of(expression); + this.scopes = List.of(); this.sources = sources; this.contextDependentAttributeSource = contextDependentAttributeSource; this.resolvedUncertainty = true; } + /** + * Returns whether the {@link ContextDependentAttributeScenario} is applicable to the given vertex + * @param vertex {@link DFDGDPRVertex} that is checked + * @return Returns true, if the scenario should be applied to the vertex. Otherwise, the method returns false + */ public boolean applicable(DFDGDPRVertex vertex) { - logger.info("Determining whether " + this.name + " can be applied to " + vertex); + logger.trace("Determining whether " + this.name + " can be applied to " + vertex); if (this.resolvedUncertainty) { if (!this.contextDependentAttributeSource.applicable(vertex)) { return false; } - logger.info("Context Depdendent Attribute Scenario is resolved with uncertainties!"); + logger.trace("Context Dependent Attribute Scenario is resolved with uncertainties!"); return this.sources.stream() .noneMatch(it -> { - logger.info("Should not match: " + it.getContextDependentAttributeScenarios() + logger.trace("Should not match: " + it.getContextDependentAttributeScenarios() .get(0) .getName()); var scenario = it.getContextDependentAttributeScenarios() .get(0); - logger.info("Result: " + scenario.applicable(vertex)); + logger.trace("Result: " + scenario.applicable(vertex)); return scenario.applicable(vertex); }); } - return this.context.stream() + return this.scopes.stream() .anyMatch(it -> UncertaintyUtils.matchesContextDefinition(vertex, it)); } + /** + * Determines whether the {@link ContextDependentAttributeScenario} is applicable to any of the nodes in the given + * transpose flow graph + * @param transposeFlowGraph {@link DFDGDPRTransposeFlowGraph} that is checked + * @return Returns true, if the {@link ContextDependentAttributeScenario} can be applied to any of the vertices in the + * TFG. Otherwise, the method returns false + */ public boolean applicable(DFDGDPRTransposeFlowGraph transposeFlowGraph) { if (this.resolvedUncertainty) { return this.sources.stream() @@ -77,21 +105,29 @@ public boolean applicable(DFDGDPRTransposeFlowGraph transposeFlowGraph) { .stream() .filter(DFDGDPRVertex.class::isInstance) .map(DFDGDPRVertex.class::cast) - .anyMatch(it -> this.applicable(it)); + .anyMatch(this::applicable); } - public List getPropertyValues() { - return this.propertyValues; - } - - public boolean resolvedByUncertainty() { - return this.resolvedUncertainty; + /** + * Returns the property values that are applied, when this scenario is fulfilled + * @return Returns a list of {@link Expression} that are applied in the case the scenario is true + */ + public List getExpressions() { + return this.expressions; } + /** + * Retrieves the {@link ContextDependentAttributeSource} that this scenario is a part of + * @return Returns the parent {@link ContextDependentAttributeSource} + */ public ContextDependentAttributeSource getContextDependentAttributeSource() { return contextDependentAttributeSource; } + /** + * Returns the name of the {@link ContextDependentAttributeScenario} + * @return The name of the {@link ContextDependentAttributeScenario} + */ public String getName() { return name; } diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/ContextDependentAttributeSource.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/ContextDependentAttributeSource.java index aa818c1..c989f6b 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/ContextDependentAttributeSource.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/ContextDependentAttributeSource.java @@ -5,57 +5,92 @@ import mdpa.gdpr.analysis.UncertaintyUtils; import mdpa.gdpr.analysis.dfd.DFDGDPRVertex; import mdpa.gdpr.metamodel.GDPR.AbstractGDPRElement; -import mdpa.gdpr.metamodel.contextproperties.ContextAnnotation; -import mdpa.gdpr.metamodel.contextproperties.ContextDefinition; -import mdpa.gdpr.metamodel.contextproperties.Property; -import mdpa.gdpr.metamodel.contextproperties.PropertyAnnotation; -import mdpa.gdpr.metamodel.contextproperties.PropertyValue; +import mdpa.gdpr.metamodel.contextproperties.Expression; +import mdpa.gdpr.metamodel.contextproperties.SAFAnnotation; +import mdpa.gdpr.metamodel.contextproperties.Scope; +import mdpa.gdpr.metamodel.contextproperties.ScopeDependentAssessmentFact; +import mdpa.gdpr.metamodel.contextproperties.ScopeSet; +/** + * This class models an application of a context dependent attribute on an element in the GDPR model. The different + * values it can take are saved in one or multiple child {@link ContextDependentAttributeScenario}. + */ public class ContextDependentAttributeSource { private final String name; private final AbstractGDPRElement annotatedElement; - private final Property propertyType; + private final ScopeDependentAssessmentFact scopeDependentAssessmentFact; private final List contextDependentAttributeScenarios; - private final List context; + private final List scopes; private final List sources; private final boolean resolvedUncertainty; - public ContextDependentAttributeSource(PropertyAnnotation propertyAnnotation, ContextAnnotation contextAnnotation) { - this.name = contextAnnotation.getEntityName() + "@" + propertyAnnotation.getEntityName(); - this.annotatedElement = propertyAnnotation.getAnnotatedElement(); - this.propertyType = propertyAnnotation.getProperty(); - this.contextDependentAttributeScenarios = List.of(new ContextDependentAttributeScenario(contextAnnotation, this)); - this.context = contextAnnotation.getContextdefinition(); + /** + * Creates a new {@link ContextDependentAttributeSource} with the given property annotation containing the information + * about the annotated element and value and the context annotation describing the context of the + * {@link ContextDependentAttributeSource} + * @param safAnnotation {@link SAFAnnotation} describing where the CDA is applied + * @param scopeSet {@link ScopeSet} describing which scenarios the source has + */ + public ContextDependentAttributeSource(SAFAnnotation safAnnotation, ScopeSet scopeSet) { + this.name = scopeSet.getEntityName() + "@" + safAnnotation.getEntityName(); + this.annotatedElement = safAnnotation.getAnnotatedElement(); + this.scopeDependentAssessmentFact = safAnnotation.getScopeDependentAssessmentFact(); + this.contextDependentAttributeScenarios = List.of(new ContextDependentAttributeScenario(scopeSet, this)); + this.scopes = scopeSet.getScope(); this.sources = List.of(); this.resolvedUncertainty = false; } - public ContextDependentAttributeSource(PropertyAnnotation propertyAnnotation, List values, + /** + * Creates a new {@link ContextDependentAttributeSource} that needs to be resolved with uncertain CDAs. Resolves an + * uncertainty regarding the value of an {@link ContextDependentAttributeSource} by creating a scenario for each passed + * {@link Expression}. Additionally, the given list of other {@link ContextDependentAttributeSource} denotes where + * this source cannot apply + * @param safAnnotation {@link SAFAnnotation} containing information about the annotated element and value + * @param expressions Different {@link Expression} that are resolved by the uncertainty + * @param sources List of {@link ContextDependentAttributeSource} that cannot be applied at the same time + */ + public ContextDependentAttributeSource(SAFAnnotation safAnnotation, List expressions, List sources) { - this.name = "Unknown@" + propertyAnnotation.getEntityName(); - this.annotatedElement = propertyAnnotation.getAnnotatedElement(); - this.propertyType = propertyAnnotation.getProperty(); - this.contextDependentAttributeScenarios = values.stream() + this.name = "Unknown@" + safAnnotation.getEntityName(); + this.annotatedElement = safAnnotation.getAnnotatedElement(); + this.scopeDependentAssessmentFact = safAnnotation.getScopeDependentAssessmentFact(); + this.contextDependentAttributeScenarios = expressions.stream() .map(it -> new ContextDependentAttributeScenario(it, this, sources)) .toList(); - this.context = List.of(); + this.scopes = List.of(); this.sources = sources; this.resolvedUncertainty = true; } + /** + * Determines whether this {@link ContextDependentAttributeSource} is applicable to the given list of vertices + * @param vertices Given list of vertices + * @return Returns true, if this {@link ContextDependentAttributeSource} is applicable at least one of the vertices + * Otherwise, the method returns false. + */ public boolean applicable(Collection vertices) { if (!vertices.stream() - .map(it -> it.getRelatedElements()) + .map(DFDGDPRVertex::getRelatedElements) .flatMap(List::stream) .toList() .contains(this.annotatedElement)) { return false; } return vertices.stream() - .anyMatch(it -> this.applicable(it)); + .anyMatch(this::applicable); } + /** + * Determines whether the {@link ContextDependentAttributeSource} is applicable at the given vertex. + *

+ * This is the case, it the vertex has the annotated element in its context. If this + * {@link ContextDependentAttributeSource} is resolving an uncertainty, the other saved sources must not match. If this + * source is not resolving an uncertainty, it must match at least one context definition + * @param vertex Given {@link DFDGDPRVertex} that is checked + * @return Returns true, if the source is applicable to the vertex. Otherwise, the method returns false + */ public boolean applicable(DFDGDPRVertex vertex) { if (!vertex.getRelatedElements() .contains(this.annotatedElement)) { @@ -65,22 +100,38 @@ public boolean applicable(DFDGDPRVertex vertex) { return this.sources.stream() .noneMatch(it -> it.applicable(vertex)); } - return this.context.stream() + return this.scopes.stream() .anyMatch(it -> UncertaintyUtils.matchesContextDefinition(vertex, it)); } - public Property getPropertyType() { - return propertyType; + /** + * Returns the property type that will be applied if the source is applicable + * @return Returns the applied {@link ScopeDependentAssessmentFact} + */ + public ScopeDependentAssessmentFact getScopeDependentAssessmentFact() { + return scopeDependentAssessmentFact; } + /** + * Returns the different possible {@link ContextDependentAttributeScenario} of this source + * @return List of possible {@link ContextDependentAttributeScenario} + */ public List getContextDependentAttributeScenarios() { return contextDependentAttributeScenarios; } + /** + * Returns the {@link AbstractGDPRElement} the source is annotated to + * @return Annotated {@link AbstractGDPRElement} + */ public AbstractGDPRElement getAnnotatedElement() { return annotatedElement; } + /** + * Returns the name of the {@link ContextDependentAttributeSource} + * @return Returns the name of the source + */ public String getName() { return name; } diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/TransformationManager.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/TransformationManager.java index a958666..8c8aa28 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/TransformationManager.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/TransformationManager.java @@ -7,54 +7,60 @@ import java.util.Optional; import mdpa.gdpr.analysis.dfd.DataFlowDiagramAndDataDictionary; import mdpa.gdpr.dfdconverter.GDPR2DFD; -import mdpa.gdpr.metamodel.GDPR.AbstractGDPRElement; -import mdpa.gdpr.metamodel.GDPR.Collecting; import mdpa.gdpr.metamodel.GDPR.LegalAssessmentFacts; import mdpa.gdpr.metamodel.GDPR.Processing; -import mdpa.gdpr.metamodel.GDPR.Storing; -import mdpa.gdpr.metamodel.contextproperties.ContextDependentProperties; -import mdpa.gdpr.metamodel.contextproperties.Property; -import mdpa.gdpr.metamodel.contextproperties.PropertyAnnotation; -import mdpa.gdpr.metamodel.contextproperties.PropertyValue; +import mdpa.gdpr.metamodel.contextproperties.Expression; +import mdpa.gdpr.metamodel.contextproperties.SAFAnnotation; +import mdpa.gdpr.metamodel.contextproperties.ScopeDependentAssessmentFact; +import mdpa.gdpr.metamodel.contextproperties.ScopeDependentAssessmentFacts; import org.apache.log4j.Logger; import org.dataflowanalysis.dfd.datadictionary.DataDictionary; -import org.dataflowanalysis.dfd.datadictionary.ForwardingAssignment; import org.dataflowanalysis.dfd.datadictionary.Label; import org.dataflowanalysis.dfd.datadictionary.LabelType; import org.dataflowanalysis.dfd.datadictionary.datadictionaryFactory; import org.dataflowanalysis.dfd.dataflowdiagram.DataFlowDiagram; import org.dataflowanalysis.dfd.dataflowdiagram.Node; +/** + * Manages the transformation from GDPR to DFD that is required to find + * {@link org.dataflowanalysis.analysis.core.AbstractTransposeFlowGraph} for the analysis. + */ public class TransformationManager { private final Logger logger = Logger.getLogger(TransformationManager.class); - private final Map> relatedElementMapping; - private final Map gdprToDFDMapping; private final Map dfdToGDPRMapping; private final List contextDependentAttributes; + /** + * Creates a new empty {@link TransformationManager} + */ public TransformationManager() { - this.relatedElementMapping = new HashMap<>(); - this.gdprToDFDMapping = new HashMap<>(); this.dfdToGDPRMapping = new HashMap<>(); this.contextDependentAttributes = new ArrayList<>(); } /** * Converts model to DFD and saves tracemodel - * @param gdprModel - * @return + * @param gdprModel Input GDPR Model + * @param scopeDependentAssessmentFacts Input context property model + * @return Returns the data flow diagram and data dictionary of the converted model */ - public DataFlowDiagramAndDataDictionary transform(LegalAssessmentFacts gdprModel, ContextDependentProperties contextDependentProperties) { + public DataFlowDiagramAndDataDictionary transform(LegalAssessmentFacts gdprModel, ScopeDependentAssessmentFacts scopeDependentAssessmentFacts) { GDPR2DFD converter = new GDPR2DFD(gdprModel); converter.transform(); - processTransformation(converter.getDataFlowDiagram(), converter.getDataDictionary(), gdprModel); - processContextDependentAttributes(contextDependentProperties, converter.getDataDictionary()); + processTransformation(converter.getDataFlowDiagram(), gdprModel); + processContextDependentAttributes(scopeDependentAssessmentFacts, converter.getDataDictionary()); return new DataFlowDiagramAndDataDictionary(converter.getDataFlowDiagram(), converter.getDataDictionary()); } - private void processTransformation(DataFlowDiagram dfd, DataDictionary dd, LegalAssessmentFacts gdprModel) { - List nodes = dfd.getNodes(); + /** + * Runs some postprocessing on the resulting DFD model for keeping track of the trace between nodes and processing + * elements TODO: This can be replaced with the tracemodel + * @param dataFlowDiagram Data flow diagram of the Transformation + * @param gdprModel GDPR model of the transformation + */ + private void processTransformation(DataFlowDiagram dataFlowDiagram, LegalAssessmentFacts gdprModel) { + List nodes = dataFlowDiagram.getNodes(); for (Node node : nodes) { Processing gdprElement = gdprModel.getProcessing() .stream() @@ -66,78 +72,69 @@ private void processTransformation(DataFlowDiagram dfd, DataDictionary dd, Legal } } - private void processContextDependentAttributes(ContextDependentProperties propertyModel, DataDictionary dd) { - for (Property property : propertyModel.getProperty()) { + /** + * Creates the {@link ContextDependentAttributeSource}s and {@link ContextDependentAttributeScenario} for the context + * property model. Additionally, it creates the required labels in the data dictionary. + * @param scopeDependentAssessmentFacts Context Property Model of the transformation + * @param dataDictionary Data Dictionary of the transformation + */ + private void processContextDependentAttributes(ScopeDependentAssessmentFacts scopeDependentAssessmentFacts, DataDictionary dataDictionary) { + for (ScopeDependentAssessmentFact scopeDependentAssessmentFact : scopeDependentAssessmentFacts.getScopeDependentAssessmentFact()) { LabelType type = datadictionaryFactory.eINSTANCE.createLabelType(); - type.setEntityName(property.getEntityName()); - type.setId(property.getId()); - dd.getLabelTypes() + type.setEntityName(scopeDependentAssessmentFact.getEntityName()); + type.setId(scopeDependentAssessmentFact.getId()); + dataDictionary.getLabelTypes() .add(type); - for (PropertyValue propertyValue : property.getPropertyvalue()) { + for (Expression expression : scopeDependentAssessmentFact.getExpression()) { Label label = datadictionaryFactory.eINSTANCE.createLabel(); - label.setEntityName(propertyValue.getEntityName()); - label.setId(propertyValue.getId()); + label.setEntityName(expression.getEntityName()); + label.setId(expression.getId()); type.getLabel() .add(label); } } - for (PropertyAnnotation propertyAnnotation : propertyModel.getPropertyannotation()) { - if (propertyAnnotation.getContextannotation() + for (SAFAnnotation safAnnotation : scopeDependentAssessmentFacts.getSafAnnotation()) { + if (safAnnotation.getScopeSet() .isEmpty()) { - this.contextDependentAttributes.add(new ContextDependentAttributeSource(propertyAnnotation, propertyAnnotation.getProperty() - .getPropertyvalue(), List.of())); + this.contextDependentAttributes.add(new ContextDependentAttributeSource(safAnnotation, safAnnotation.getScopeDependentAssessmentFact() + .getExpression(), List.of())); } else { List sources = new ArrayList<>(); - propertyAnnotation.getContextannotation() - .stream() + safAnnotation.getScopeSet() .forEach(it -> { - var source = new ContextDependentAttributeSource(propertyAnnotation, it); + var source = new ContextDependentAttributeSource(safAnnotation, it); sources.add(source); this.contextDependentAttributes.add(source); }); - this.contextDependentAttributes.add(new ContextDependentAttributeSource(propertyAnnotation, propertyAnnotation.getProperty() - .getPropertyvalue(), sources)); + this.contextDependentAttributes.add(new ContextDependentAttributeSource(safAnnotation, safAnnotation.getScopeDependentAssessmentFact() + .getExpression(), sources)); } } logger.info("Parsed " + this.contextDependentAttributes.size() + " CDA!"); } - private void addAssignments(DataFlowDiagram dfd, DataDictionary dd) { - for (Node node : dfd.getNodes()) { - Processing gdprElement = this.getElement(node) - .orElseThrow(); - if (!(gdprElement instanceof Storing) && !(gdprElement instanceof Collecting)) { - ForwardingAssignment assignment = datadictionaryFactory.eINSTANCE.createForwardingAssignment(); - assignment.getInputPins() - .addAll(node.getBehavior() - .getInPin()); - if (!node.getBehavior() - .getOutPin() - .isEmpty()) { - assignment.setOutputPin(node.getBehavior() - .getOutPin() - .get(0)); - } - node.getBehavior() - .getAssignment() - .add(assignment); - } - } - } - + /** + * Adds a new mapping between the given GDPR {@link Processing} element an the DFD {@link Node} + * @param gdprElement Given {@link Processing} element + * @param dfdElement Given {@link Node} element + */ private void addMapping(Processing gdprElement, Node dfdElement) { - this.gdprToDFDMapping.put(gdprElement, dfdElement); this.dfdToGDPRMapping.put(dfdElement, gdprElement); } + /** + * Returns the GDPR {@link Processing} element that corresponds to the given node + * @param node Given DFD {@link Node} + * @return Returns the {@link Processing} element, if one exits + */ public Optional getElement(Node node) { return Optional.ofNullable(this.dfdToGDPRMapping.get(node)); } - public Optional getElement(Processing gdprElement) { - return Optional.ofNullable(this.gdprToDFDMapping.get(gdprElement)); - } - + /** + * Returns the list of {@link ContextDependentAttributeSource} that were parsed by the transformation + * @return Returns the list of parsed context dependent attributes from the metamodel instance + */ public List getContextDependentAttributes() { return this.contextDependentAttributes; } diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRResourceProvider.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRResourceProvider.java index 7da908e..e43ee15 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRResourceProvider.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRResourceProvider.java @@ -1,24 +1,53 @@ package mdpa.gdpr.analysis.core.resource; import mdpa.gdpr.analysis.core.TransformationManager; +import mdpa.gdpr.metamodel.GDPR.GDPRPackage; import mdpa.gdpr.metamodel.GDPR.LegalAssessmentFacts; -import mdpa.gdpr.metamodel.contextproperties.ContextDependentProperties; +import mdpa.gdpr.metamodel.contextproperties.ContextpropertiesPackage; +import mdpa.gdpr.metamodel.contextproperties.ScopeDependentAssessmentFacts; import org.dataflowanalysis.analysis.resource.ResourceProvider; +import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; +/** + * A {@link ResourceProvider} providing the necessary resources to run a + * {@link mdpa.gdpr.analysis.GDPRLegalAssessmentAnalysis} + */ public abstract class GDPRResourceProvider extends ResourceProvider { @Override public void setupResources() { - + this.resources.getPackageRegistry() + .put(GDPRPackage.eNS_URI, GDPRPackage.eINSTANCE); + this.resources.getResourceFactoryRegistry() + .getExtensionToFactoryMap() + .put(GDPRPackage.eNAME, new XMIResourceFactoryImpl()); + this.resources.getPackageRegistry() + .put(ContextpropertiesPackage.eNS_URI, ContextpropertiesPackage.eINSTANCE); + this.resources.getResourceFactoryRegistry() + .getExtensionToFactoryMap() + .put(ContextpropertiesPackage.eNAME, new XMIResourceFactoryImpl()); } - public abstract LegalAssessmentFacts getModel(); + /** + * Returns the loaded GDPR model + * @return Returns the GDPR model that is loaded by the resource provider + */ + public abstract LegalAssessmentFacts getGDPRModel(); - public abstract ContextDependentProperties getContextDependentProperties(); + /** + * Returns the {@link ScopeDependentAssessmentFacts} metamodel that is required to run a + * {@link mdpa.gdpr.analysis.GDPRLegalAssessmentAnalysis} + * @return Returns the loaded Context Property model + */ + public abstract ScopeDependentAssessmentFacts getScopeDependentAssessmentFacts(); + /** + * Returns the transformation manager that should be used for the transformation from gdpr to dfd + * @return Returns the {@link TransformationManager} of the running analysis + */ public abstract TransformationManager getTransformationManager(); @Override public boolean sufficientResourcesLoaded() { - return this.getModel() != null; + return this.getGDPRModel() != null; } } diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRURIResourceProvider.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRURIResourceProvider.java index 74b493c..23f8383 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRURIResourceProvider.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/core/resource/GDPRURIResourceProvider.java @@ -3,46 +3,41 @@ import java.util.ArrayList; import java.util.List; import mdpa.gdpr.analysis.core.TransformationManager; -import mdpa.gdpr.metamodel.GDPR.GDPRPackage; import mdpa.gdpr.metamodel.GDPR.LegalAssessmentFacts; -import mdpa.gdpr.metamodel.contextproperties.ContextDependentProperties; -import mdpa.gdpr.metamodel.contextproperties.ContextpropertiesPackage; +import mdpa.gdpr.metamodel.contextproperties.ScopeDependentAssessmentFacts; +import org.apache.log4j.Logger; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; -import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl; +/** + * Implementation of an {@link GDPRResourceProvider} using modelling project URIs to load the required models + */ public class GDPRURIResourceProvider extends GDPRResourceProvider { private final URI modelURI; private final URI propertyURI; private LegalAssessmentFacts model; - private ContextDependentProperties contextDependentProperties; + private ScopeDependentAssessmentFacts scopeDependentAssessmentFacts; private final TransformationManager transformationManager; + /** + * Creates a new {@link GDPRURIResourceProvider} using the provided URIs to the models + *

+ * Usually, the resource provider will be created automatically when using + * {@link mdpa.gdpr.analysis.GDPRLegalAssessmentAnalysisBuilder} + * @param modelURI URI path to the GDPR model + * @param propertyURI URI path to the context property model + */ public GDPRURIResourceProvider(URI modelURI, URI propertyURI) { this.modelURI = modelURI; this.propertyURI = propertyURI; this.transformationManager = new TransformationManager(); } - @Override - public void setupResources() { - this.resources.getPackageRegistry() - .put(GDPRPackage.eNS_URI, GDPRPackage.eINSTANCE); - this.resources.getResourceFactoryRegistry() - .getExtensionToFactoryMap() - .put(GDPRPackage.eNAME, new XMIResourceFactoryImpl()); - this.resources.getPackageRegistry() - .put(ContextpropertiesPackage.eNS_URI, ContextpropertiesPackage.eINSTANCE); - this.resources.getResourceFactoryRegistry() - .getExtensionToFactoryMap() - .put(ContextpropertiesPackage.eNAME, new XMIResourceFactoryImpl()); - } - @Override public void loadRequiredResources() { this.model = (LegalAssessmentFacts) this.loadModelContent(modelURI); - this.contextDependentProperties = (ContextDependentProperties) this.loadModelContent(propertyURI); + this.scopeDependentAssessmentFacts = (ScopeDependentAssessmentFacts) this.loadModelContent(propertyURI); List loadedResources; do { loadedResources = new ArrayList<>(this.resources.getResources()); @@ -52,13 +47,13 @@ public void loadRequiredResources() { } @Override - public LegalAssessmentFacts getModel() { + public LegalAssessmentFacts getGDPRModel() { return this.model; } @Override - public ContextDependentProperties getContextDependentProperties() { - return this.contextDependentProperties; + public ScopeDependentAssessmentFacts getScopeDependentAssessmentFacts() { + return this.scopeDependentAssessmentFacts; } @Override diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRFlowGraphCollection.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRFlowGraphCollection.java index add84be..2111e23 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRFlowGraphCollection.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRFlowGraphCollection.java @@ -9,12 +9,9 @@ import mdpa.gdpr.analysis.core.ContextDependentAttributeSource; import mdpa.gdpr.analysis.core.resource.GDPRResourceProvider; import mdpa.gdpr.metamodel.GDPR.AbstractGDPRElement; -import mdpa.gdpr.metamodel.GDPR.Collecting; import mdpa.gdpr.metamodel.GDPR.PersonalData; +import mdpa.gdpr.metamodel.GDPR.Processing; import mdpa.gdpr.metamodel.GDPR.Role; -import mdpa.gdpr.metamodel.GDPR.Storing; -import mdpa.gdpr.metamodel.GDPR.Transferring; -import mdpa.gdpr.metamodel.GDPR.Usage; import org.apache.log4j.Logger; import org.dataflowanalysis.analysis.core.AbstractTransposeFlowGraph; import org.dataflowanalysis.analysis.core.FlowGraphCollection; @@ -25,13 +22,28 @@ import org.dataflowanalysis.dfd.datadictionary.DataDictionary; import org.dataflowanalysis.dfd.datadictionary.Pin; +/** + * Models a collection of {@link DFDGDPRTransposeFlowGraph}s for use with the + * {@link mdpa.gdpr.analysis.GDPRLegalAssessmentAnalysis} + */ public class DFDGDPRFlowGraphCollection extends FlowGraphCollection { private final Logger logger = Logger.getLogger(DFDGDPRFlowGraphCollection.class); + private DataDictionary dataDictionary; + /** + * Creates a new {@link DFDGDPRFlowGraphCollection} using the provided resource provider + * @param resourceProvider {@link ResourceProvider} that provides the necessary models + */ public DFDGDPRFlowGraphCollection(ResourceProvider resourceProvider) { super(resourceProvider); } + /** + * Creates a new {@link DFDGDPRFlowGraphCollection} with the given list of transpose flow graphs and a given resource + * provider + * @param transposeFlowGraphs List of {@link DFDGDPRTransposeFlowGraph}s that are stored in the flow graph collection + * @param resourceProvider {@link ResourceProvider} that has the relevant model elements loaded + */ public DFDGDPRFlowGraphCollection(List transposeFlowGraphs, ResourceProvider resourceProvider) { super(transposeFlowGraphs, resourceProvider); } @@ -43,31 +55,58 @@ public List findTransposeFlowGraphs() { throw new IllegalArgumentException(); } DataFlowDiagramAndDataDictionary dfd = gdprResourceProvider.getTransformationManager() - .transform(gdprResourceProvider.getModel(), gdprResourceProvider.getContextDependentProperties()); + .transform(gdprResourceProvider.getGDPRModel(), gdprResourceProvider.getScopeDependentAssessmentFacts()); + this.dataDictionary = dfd.dataDictionary(); DFDTransposeFlowGraphFinder finder = new DFDTransposeFlowGraphFinder(dfd.dataDictionary(), dfd.dataFlowDiagram()); List completeFlowGraphs = finder.findTransposeFlowGraphs() .stream() .map(it -> this.transformFlowGraph((DFDTransposeFlowGraph) it, dfd.dataDictionary())) .toList(); - List result = new ArrayList<>(completeFlowGraphs); - /* - * result.addAll(completeFlowGraphs.stream() .map(it -> this.getPartialTransposeFlowGraphs(it, dfd.dataDictionary())) - * .flatMap(List::stream) .toList()); - */ - return result; + return new ArrayList<>(completeFlowGraphs); + } + + /** + * Finds the partial responsibility flow graphs for the contained flow graphs and creates a new + * {@link DFDGDPRFlowGraphCollection} containing the previous {@link DFDGDPRTransposeFlowGraph} with the additional + * partial responsibility flow graphs + * @return Returns a new {@link DFDGDPRFlowGraphCollection} containing additional partial responsibility flow graphs + */ + public DFDGDPRFlowGraphCollection findResponsibilityFlowGraphs() { + List completeFlowGraphs = this.getTransposeFlowGraphs() + .stream() + .filter(DFDGDPRTransposeFlowGraph.class::isInstance) + .map(DFDGDPRTransposeFlowGraph.class::cast) + .toList(); + List flowGraphs = new ArrayList<>(completeFlowGraphs); + flowGraphs.addAll(completeFlowGraphs.stream() + .map(this::getPartialTransposeFlowGraphs) + .flatMap(List::stream) + .toList()); + return new DFDGDPRFlowGraphCollection(flowGraphs, this.resourceProvider); } - private DFDGDPRTransposeFlowGraph transformFlowGraph(DFDTransposeFlowGraph transposeFlowGraph, DataDictionary dd) { + /** + * Transforms the given DFD-based {@link DFDTransposeFlowGraph} to an {@link DFDGDPRTransposeFlowGraph} + * @param transposeFlowGraph Given {@link DFDTransposeFlowGraph} + * @param dataDictionary Data dictionary containing the labels for CDAs + * @return Returns the corresponding {@link DFDGDPRTransposeFlowGraph} + */ + private DFDGDPRTransposeFlowGraph transformFlowGraph(DFDTransposeFlowGraph transposeFlowGraph, DataDictionary dataDictionary) { Map mapping = new IdentityHashMap<>(); transposeFlowGraph.getVertices() .stream() .map(DFDVertex.class::cast) .forEach(vertex -> mapping.put(vertex, this.getDFDGDPRVertex(vertex, new IdentityHashMap<>()))); return new DFDGDPRTransposeFlowGraph(mapping.get((DFDVertex) transposeFlowGraph.getSink()), - this.determineContextDependentAttributes(transposeFlowGraph, mapping.values()), dd); + this.determineContextDependentAttributes(mapping.values()), dataDictionary); } - private List getPartialTransposeFlowGraphs(DFDGDPRTransposeFlowGraph transposeFlowGraph, DataDictionary dd) { + /** + * Determines the list of partial responsibility flow graphs for the given {@link DFDGDPRTransposeFlowGraph} + * @param transposeFlowGraph Given {@link DFDGDPRTransposeFlowGraph} + * @return Returns the list of responsibility transpose flow graphs for the input {@link DFDGDPRTransposeFlowGraph} + */ + private List getPartialTransposeFlowGraphs(DFDGDPRTransposeFlowGraph transposeFlowGraph) { List result = new ArrayList<>(); Map> roleMap = new HashMap<>(); transposeFlowGraph.getVertices() @@ -96,27 +135,37 @@ private List getPartialTransposeFlowGraphs(DFDGDPRTra .filter(it -> !previousElements.contains(it)) .toList(); for (DFDGDPRVertex sink : sinks) { - Map mapping = new HashMap<>(); - result.add(new DFDGDPRTransposeFlowGraph(this.getMappingForSink(sink, roleVertices, mapping), - new ArrayList<>(transposeFlowGraph.getContextDependentAttributeSources()), dd)); + result.add(new DFDGDPRTransposeFlowGraph(this.getMappingForSink(sink, roleVertices), + new ArrayList<>(transposeFlowGraph.getContextDependentAttributeSources()), this.dataDictionary)); } } return result; } - private DFDGDPRVertex getMappingForSink(DFDGDPRVertex sink, List roleVertices, Map mapping) { + /** + * Determines the {@link DFDGDPRVertex} that denotes the sink of the partial responsibility transpose flow graph + * @param sink {@link DFDGDPRVertex} that is the current sink + * @param roleVertices List of {@link DFDGDPRVertex} that denote the beginnings of each responsibility segment + * @return Returns the {@link DFDGDPRVertex} sink of the partial responsibility TFG + */ + private DFDGDPRVertex getMappingForSink(DFDGDPRVertex sink, List roleVertices) { Map pinVertexMap = new HashMap<>(); sink.getPinDFDVertexMap() .entrySet() .stream() + .filter(it -> it.getValue() instanceof DFDGDPRVertex) .filter(it -> roleVertices.contains(it.getValue())) - .forEach(it -> { - pinVertexMap.put(it.getKey(), this.getMappingForSink((DFDGDPRVertex) it.getValue(), roleVertices, mapping)); - }); + .forEach(it -> pinVertexMap.put(it.getKey(), this.getMappingForSink((DFDGDPRVertex) it.getValue(), roleVertices))); return new DFDGDPRVertex(sink.getReferencedElement(), pinVertexMap, new HashMap<>(sink.getPinFlowMap()), new ArrayList<>(sink.getRelatedElements())); } + /** + * Creates the {@link DFDGDPRVertex} for a corresponding {@link DFDVertex} and a given mapping + * @param vertex Given {@link DFDVertex} + * @param mapping Mapping between {@link DFDVertex} + * @return Returns the corresponding {@link DFDGDPRVertex} of the {@link DFDVertex} + */ private DFDGDPRVertex getDFDGDPRVertex(DFDVertex vertex, Map mapping) { if (!(this.resourceProvider instanceof GDPRResourceProvider gdprResourceProvider)) { this.logger.error("Resource provider is not a GDPR resource provider!"); @@ -136,96 +185,45 @@ private DFDGDPRVertex getDFDGDPRVertex(DFDVertex vertex, Map(vertex.getPinFlowMap()), relatedElements); } + /** + * Determines the relevant elements for the given {@link AbstractGDPRElement} + * @param gdprElement Given {@link AbstractGDPRElement} + * @return Related elements for the given {@link AbstractGDPRElement} + */ private List determineRelatedElements(AbstractGDPRElement gdprElement) { List result = new ArrayList<>(); result.add(gdprElement); - if (gdprElement instanceof Collecting collecting) { - result.addAll(collecting.getInputData()); - result.addAll(collecting.getInputData() - .stream() - .filter(PersonalData.class::isInstance) - .map(PersonalData.class::cast) - .map(PersonalData::getDataReferences) - .flatMap(List::stream) - .toList()); - result.addAll(collecting.getOutputData()); - result.addAll(collecting.getOutputData() - .stream() - .filter(PersonalData.class::isInstance) - .map(PersonalData.class::cast) - .map(PersonalData::getDataReferences) - .flatMap(List::stream) - .toList()); - result.addAll(collecting.getPurpose()); - result.addAll(collecting.getOnTheBasisOf()); - result.add(collecting.getResponsible()); - } else if (gdprElement instanceof Usage usage) { - result.addAll(usage.getInputData()); - result.addAll(usage.getInputData() - .stream() - .filter(PersonalData.class::isInstance) - .map(PersonalData.class::cast) - .map(PersonalData::getDataReferences) - .flatMap(List::stream) - .toList()); - result.addAll(usage.getOutputData()); - result.addAll(usage.getOutputData() - .stream() - .filter(PersonalData.class::isInstance) - .map(PersonalData.class::cast) - .map(PersonalData::getDataReferences) - .flatMap(List::stream) - .toList()); - result.addAll(usage.getPurpose()); - result.addAll(usage.getOnTheBasisOf()); - result.add(usage.getResponsible()); - } else if (gdprElement instanceof Transferring transferring) { - result.addAll(transferring.getInputData()); - result.addAll(transferring.getInputData() - .stream() - .filter(PersonalData.class::isInstance) - .map(PersonalData.class::cast) - .map(PersonalData::getDataReferences) - .flatMap(List::stream) - .toList()); - result.addAll(transferring.getOutputData()); - result.addAll(transferring.getOutputData() - .stream() - .filter(PersonalData.class::isInstance) - .map(PersonalData.class::cast) - .map(PersonalData::getDataReferences) - .flatMap(List::stream) - .toList()); - result.addAll(transferring.getPurpose()); - result.addAll(transferring.getOnTheBasisOf()); - result.add(transferring.getResponsible()); - - } else if (gdprElement instanceof Storing storing) { - result.addAll(storing.getInputData()); - result.addAll(storing.getInputData() + if (gdprElement instanceof Processing processing) { + result.addAll(processing.getInputData()); + result.addAll(processing.getInputData() .stream() .filter(PersonalData.class::isInstance) .map(PersonalData.class::cast) .map(PersonalData::getDataReferences) .flatMap(List::stream) .toList()); - result.addAll(storing.getOutputData()); - result.addAll(storing.getOutputData() + result.addAll(processing.getOutputData()); + result.addAll(processing.getOutputData() .stream() .filter(PersonalData.class::isInstance) .map(PersonalData.class::cast) .map(PersonalData::getDataReferences) .flatMap(List::stream) .toList()); - result.addAll(storing.getPurpose()); - result.addAll(storing.getOnTheBasisOf()); - result.add(storing.getResponsible()); + result.addAll(processing.getPurpose()); + result.addAll(processing.getOnTheBasisOf()); + result.add(processing.getResponsible()); } return result; } - private List determineContextDependentAttributes(DFDTransposeFlowGraph transposeFlowGraph, - Collection vertices) { + /** + * Determines the context dependent attribute sources for the given collection of vertices using the + * {@link mdpa.gdpr.analysis.core.TransformationManager} + * @param vertices List of vertices of which the {@link ContextDependentAttributeSource}s should be determined + * @return List of {@link ContextDependentAttributeSource} that are applicable to the given list of vertices + */ + private List determineContextDependentAttributes(Collection vertices) { if (!(this.resourceProvider instanceof GDPRResourceProvider gdprResourceProvider)) { this.logger.error("Resource provider is not a GDPR resource provider!"); throw new IllegalArgumentException(); @@ -237,6 +235,12 @@ private List determineContextDependentAttribute .toList(); } + /** + * Returns the flow graph collection that contains the final flow graph with resolved context dependent attributes. + *

+ * Note: This will create a new instance of the {@link DFDGDPRFlowGraphCollection} + * @return Returns a new {@link DFDGDPRFlowGraphCollection} containing the resolved {@link DFDGDPRTransposeFlowGraph}s + */ public DFDGDPRFlowGraphCollection resolveContextDependentAttributes() { List resultingTransposeFlowGraphs = this.getTransposeFlowGraphs() .stream() diff --git a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRTransposeFlowGraph.java b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRTransposeFlowGraph.java index 40e6232..1e48b06 100644 --- a/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRTransposeFlowGraph.java +++ b/bundles/mdpa.gdpr.analysis/src/mdpa/gdpr/analysis/dfd/DFDGDPRTransposeFlowGraph.java @@ -14,7 +14,6 @@ import mdpa.gdpr.metamodel.GDPR.Data; import mdpa.gdpr.metamodel.GDPR.NaturalPerson; import mdpa.gdpr.metamodel.GDPR.PersonalData; -import mdpa.gdpr.metamodel.contextproperties.PropertyValue; import org.apache.log4j.Logger; import org.dataflowanalysis.analysis.core.AbstractTransposeFlowGraph; import org.dataflowanalysis.analysis.core.AbstractVertex; @@ -23,7 +22,6 @@ import org.dataflowanalysis.dfd.datadictionary.Behavior; import org.dataflowanalysis.dfd.datadictionary.DataDictionary; import org.dataflowanalysis.dfd.datadictionary.Label; -import org.dataflowanalysis.dfd.datadictionary.LabelType; import org.dataflowanalysis.dfd.datadictionary.Pin; import org.dataflowanalysis.dfd.dataflowdiagram.Node; import org.eclipse.emf.ecore.util.EcoreUtil; @@ -31,7 +29,7 @@ public class DFDGDPRTransposeFlowGraph extends DFDTransposeFlowGraph { private final Logger logger = Logger.getLogger(DFDGDPRTransposeFlowGraph.class); private final List relevantContextDependentAttributes; - private final DataDictionary dd; + private final DataDictionary dataDictionary; private final Optional contextAttributeState; @@ -39,11 +37,12 @@ public class DFDGDPRTransposeFlowGraph extends DFDTransposeFlowGraph { * Creates a new dfd transpose flow graph with the given sink that induces the transpose flow graph * @param sink Sink vertex that induces the transpose flow graph */ - public DFDGDPRTransposeFlowGraph(AbstractVertex sink, List contextDependentAttributes, DataDictionary dd) { + public DFDGDPRTransposeFlowGraph(AbstractVertex sink, List contextDependentAttributes, + DataDictionary dataDictionary) { super(sink); this.relevantContextDependentAttributes = contextDependentAttributes; this.contextAttributeState = Optional.empty(); - this.dd = dd; + this.dataDictionary = dataDictionary; } /** @@ -51,19 +50,18 @@ public DFDGDPRTransposeFlowGraph(AbstractVertex sink, List sink, List contextDependentAttributes, - ContextAttributeState contextAttributeState, DataDictionary dd) { + ContextAttributeState contextAttributeState, DataDictionary dataDictionary) { super(sink); this.relevantContextDependentAttributes = contextDependentAttributes; this.contextAttributeState = Optional.of(contextAttributeState); - this.dd = dd; + this.dataDictionary = dataDictionary; } public List determineAlternateFlowGraphs() { List result = new ArrayList<>(); List states = ContextAttributeState.createAllContextAttributeStates(this.relevantContextDependentAttributes); - Map, List> unmatchedStates = new HashMap<>(); for (ContextAttributeState state : states) { - if (state.getSelectedScenarios() + if (state.selectedScenarios() .stream() .noneMatch(it -> it.applicable(this))) { logger.warn("State not applicable to transpose flow graph, skipping"); @@ -71,183 +69,182 @@ public List determineAlternateFlowGraphs() { } DFDGDPRTransposeFlowGraph currentTransposeFlowGraph = (DFDGDPRTransposeFlowGraph) this.copy(new HashMap<>(), state); - for (ContextDependentAttributeScenario scenario : state.getSelectedScenarios()) { - ContextDependentAttributeSource source = scenario.getContextDependentAttributeSource(); - Optional matchingVertex = currentTransposeFlowGraph.getVertices() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(source::applicable) - .findFirst(); - if (matchingVertex.isEmpty()) { - logger.warn("Could not find matching vertex for context dependent attribute"); - continue; - } - if (!scenario.applicable(matchingVertex.get())) { - // Scenario must not be resolved by uncertainty - logger.warn("Scenario not applicable to vertex!"); - continue; - } + for (ContextDependentAttributeScenario scenario : state.selectedScenarios()) { + currentTransposeFlowGraph = this.handleScenario(scenario, currentTransposeFlowGraph, state) + .orElse(currentTransposeFlowGraph); + } + result.add(currentTransposeFlowGraph); + } + return result; + } - if (source.getAnnotatedElement() instanceof NaturalPerson person) { - // Insert Data Characteristic - List targetedVertices = currentTransposeFlowGraph.getVertices() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(scenario::applicable) - .toList(); - List finalTargetedVertices = targetedVertices; - targetedVertices = targetedVertices.stream() - .filter(it -> UncertaintyUtils.shouldReapply(finalTargetedVertices, it)) - .toList(); - logger.info("Applying state to vertices: " + targetedVertices.toString()); + private Optional handleScenario(ContextDependentAttributeScenario scenario, + DFDGDPRTransposeFlowGraph currentTransposeFlowGraph, ContextAttributeState state) { + ContextDependentAttributeSource source = scenario.getContextDependentAttributeSource(); + Optional matchingVertex = currentTransposeFlowGraph.getVertices() + .stream() + .filter(DFDGDPRVertex.class::isInstance) + .map(DFDGDPRVertex.class::cast) + .filter(source::applicable) + .findFirst(); + if (matchingVertex.isEmpty()) { + logger.warn("Could not find matching vertex for context dependent attribute"); + return Optional.empty(); + } + if (!scenario.applicable(matchingVertex.get())) { + // Scenario must not be resolved by uncertainty + logger.warn("Scenario not applicable to vertex!"); + return Optional.empty(); + } - for (DFDGDPRVertex targetVertex : targetedVertices) { - DFDGDPRVertex currentTargetVertex = currentTransposeFlowGraph.getVertices() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(it -> it.getReferencedElement() - .getId() - .equals(targetVertex.getReferencedElement() - .getId())) - .filter(it -> it.getReferencedElement() - .getEntityName() - .equals(targetVertex.getReferencedElement() - .getEntityName())) - .findAny() - .orElseThrow(); - DFDGDPRVertex impactedElement = currentTargetVertex.getPreviousElements() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(it -> { - return it.getOutgoingData() - .stream() - .filter(PersonalData.class::isInstance) - .map(PersonalData.class::cast) - .anyMatch(data -> data.getDataReferences() - .contains(person)); - }) - .findAny() - .orElse(currentTargetVertex); - Behavior replacingBehavior = UncertaintyUtils.createBehavior(impactedElement, dd, source, scenario, person); - Node replacingNode = EcoreUtil.copy(impactedElement.getReferencedElement()); - replacingNode.setBehavior(replacingBehavior); - DFDGDPRVertex replacingVertex = this.copyVertex(impactedElement, replacingNode); - List scenarios = new ArrayList<>(impactedElement.getContextDependentAttributes()); - scenarios.add(scenario); - replacingVertex.setContextDependentAttributes(scenarios); - Map mapping = new HashMap<>(); - mapping.put(impactedElement, replacingVertex); - currentTransposeFlowGraph = (DFDGDPRTransposeFlowGraph) currentTransposeFlowGraph.copy(mapping, state); - } + if (source.getAnnotatedElement() instanceof NaturalPerson person) { + // Insert Data Characteristic + List targetedVertices = this.determineTargetedVertices(currentTransposeFlowGraph, scenario); - } else if (source.getAnnotatedElement() instanceof Data data) { - // Insert Data Characteristic - List targetedVertices = currentTransposeFlowGraph.getVertices() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(scenario::applicable) - .toList(); - List finalTargetedVertices = targetedVertices; - targetedVertices = targetedVertices.stream() - .filter(it -> UncertaintyUtils.shouldReapply(finalTargetedVertices, it)) - .toList(); - logger.info("Applying state to vertices: " + targetedVertices.toString()); + for (DFDGDPRVertex targetVertex : targetedVertices) { + DFDGDPRVertex currentTargetVertex = currentTransposeFlowGraph.getVertices() + .stream() + .filter(DFDGDPRVertex.class::isInstance) + .map(DFDGDPRVertex.class::cast) + .filter(it -> it.getReferencedElement() + .getId() + .equals(targetVertex.getReferencedElement() + .getId())) + .filter(it -> it.getReferencedElement() + .getEntityName() + .equals(targetVertex.getReferencedElement() + .getEntityName())) + .findAny() + .orElseThrow(); + DFDGDPRVertex impactedElement = currentTargetVertex.getPreviousElements() + .stream() + .filter(DFDGDPRVertex.class::isInstance) + .map(DFDGDPRVertex.class::cast) + .filter(it -> { + return it.getOutgoingData() + .stream() + .filter(PersonalData.class::isInstance) + .map(PersonalData.class::cast) + .anyMatch(data -> data.getDataReferences() + .contains(person)); + }) + .findAny() + .orElse(currentTargetVertex); + Behavior replacingBehavior = UncertaintyUtils.createBehavior(impactedElement, dataDictionary, source, scenario, person); + Node replacingNode = EcoreUtil.copy(impactedElement.getReferencedElement()); + replacingNode.setBehavior(replacingBehavior); + DFDGDPRVertex replacingVertex = this.copyVertex(impactedElement, replacingNode); + List scenarios = new ArrayList<>(impactedElement.getContextDependentAttributes()); + scenarios.add(scenario); + replacingVertex.setContextDependentAttributes(scenarios); + Map mapping = new HashMap<>(); + mapping.put(impactedElement, replacingVertex); + currentTransposeFlowGraph = (DFDGDPRTransposeFlowGraph) currentTransposeFlowGraph.copy(mapping, state); + } + return Optional.of(currentTransposeFlowGraph); - for (DFDGDPRVertex targetVertex : targetedVertices) { - DFDGDPRVertex currentTargetVertex = currentTransposeFlowGraph.getVertices() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(it -> it.getReferencedElement() - .getId() - .equals(targetVertex.getReferencedElement() - .getId())) - .filter(it -> it.getReferencedElement() - .getEntityName() - .equals(targetVertex.getReferencedElement() - .getEntityName())) - .findAny() - .orElseThrow(); - DFDGDPRVertex impactedElement = currentTargetVertex.getPreviousElements() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(it -> it.getOutgoingData() - .contains(data)) - .findAny() - .orElse(currentTargetVertex); - Behavior replacingBehavior = UncertaintyUtils.createBehavior(impactedElement, dd, source, scenario, data); - Node replacingNode = EcoreUtil.copy(impactedElement.getReferencedElement()); - replacingNode.setBehavior(replacingBehavior); - DFDGDPRVertex replacingVertex = this.copyVertex(impactedElement, replacingNode); - List scenarios = new ArrayList<>(impactedElement.getContextDependentAttributes()); - scenarios.add(scenario); - replacingVertex.setContextDependentAttributes(scenarios); - Map mapping = new HashMap<>(); - mapping.put(impactedElement, replacingVertex); - currentTransposeFlowGraph = (DFDGDPRTransposeFlowGraph) currentTransposeFlowGraph.copy(mapping, state); - } - } else { - // Insert Node Characteristics at all matching vertices - List matchingVertices = currentTransposeFlowGraph.getVertices() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(it -> source.applicable(it)) - .filter(it -> scenario.applicable(it)) - .map(it -> it.getReferencedElement() - .getId()) - .toList(); - for (String targetVertexID : matchingVertices) { - DFDGDPRVertex targetVertex = currentTransposeFlowGraph.getVertices() - .stream() - .filter(DFDGDPRVertex.class::isInstance) - .map(DFDGDPRVertex.class::cast) - .filter(it -> it.getReferencedElement() - .getId() - .equals(targetVertexID)) - .findFirst() - .orElseThrow(); - Node replacingNode = EcoreUtil.copy(targetVertex.getReferencedElement()); + } else if (source.getAnnotatedElement() instanceof Data data) { + // Insert Data Characteristic + List targetedVertices = this.determineTargetedVertices(currentTransposeFlowGraph, scenario); + ; + for (DFDGDPRVertex targetVertex : targetedVertices) { + DFDGDPRVertex currentTargetVertex = currentTransposeFlowGraph.getVertices() + .stream() + .filter(DFDGDPRVertex.class::isInstance) + .map(DFDGDPRVertex.class::cast) + .filter(it -> it.getReferencedElement() + .getId() + .equals(targetVertex.getReferencedElement() + .getId())) + .filter(it -> it.getReferencedElement() + .getEntityName() + .equals(targetVertex.getReferencedElement() + .getEntityName())) + .findAny() + .orElseThrow(); + DFDGDPRVertex impactedElement = currentTargetVertex.getPreviousElements() + .stream() + .filter(DFDGDPRVertex.class::isInstance) + .map(DFDGDPRVertex.class::cast) + .filter(it -> it.getOutgoingData() + .contains(data)) + .findAny() + .orElse(currentTargetVertex); + Behavior replacingBehavior = UncertaintyUtils.createBehavior(impactedElement, dataDictionary, source, scenario, data); + Node replacingNode = EcoreUtil.copy(impactedElement.getReferencedElement()); + replacingNode.setBehavior(replacingBehavior); + DFDGDPRVertex replacingVertex = this.copyVertex(impactedElement, replacingNode); + List scenarios = new ArrayList<>(impactedElement.getContextDependentAttributes()); + scenarios.add(scenario); + replacingVertex.setContextDependentAttributes(scenarios); + Map mapping = new HashMap<>(); + mapping.put(impactedElement, replacingVertex); + currentTransposeFlowGraph = (DFDGDPRTransposeFlowGraph) currentTransposeFlowGraph.copy(mapping, state); + } + return Optional.of(currentTransposeFlowGraph); + } else { + // Insert Node Characteristics at all matching vertices + List matchingVertices = currentTransposeFlowGraph.getVertices() + .stream() + .filter(DFDGDPRVertex.class::isInstance) + .map(DFDGDPRVertex.class::cast) + .filter(source::applicable) + .filter(scenario::applicable) + .map(it -> it.getReferencedElement() + .getId()) + .toList(); + for (String targetVertexID : matchingVertices) { + DFDGDPRVertex targetVertex = currentTransposeFlowGraph.getVertices() + .stream() + .filter(DFDGDPRVertex.class::isInstance) + .map(DFDGDPRVertex.class::cast) + .filter(it -> it.getReferencedElement() + .getId() + .equals(targetVertexID)) + .findFirst() + .orElseThrow(); + Node replacingNode = EcoreUtil.copy(targetVertex.getReferencedElement()); - LabelType labelType = dd.getLabelTypes() - .stream() - .filter(it -> it.getEntityName() - .equals(source.getPropertyType() - .getEntityName())) - .findAny() - .orElseThrow(); - List