FHAReport.java

/**
 * Copyright (c) 2004-2025 Carnegie Mellon University and others. (see Contributors file). 
 * All Rights Reserved.
 * 
 * NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
 * OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
 * MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 * 
 * This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0
 * which is available at https://www.eclipse.org/legal/epl-2.0/
 * SPDX-License-Identifier: EPL-2.0
 * 
 * Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
 * 
 * This program includes and/or can make use of certain third party source code, object code, documentation and other
 * files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
 * configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
 * conditions contained in any such Third Party Software or separate license file distributed with such Third Party
 * Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
 * aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
 * censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
 */
package org.osate.aadl2.errormodel.analysis.fha;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.eclipse.emf.common.util.EList;
import org.eclipse.xtext.EcoreUtil2;
import org.osate.aadl2.AbstractNamedValue;
import org.osate.aadl2.BasicPropertyAssociation;
import org.osate.aadl2.Element;
import org.osate.aadl2.EnumerationLiteral;
import org.osate.aadl2.IntegerLiteral;
import org.osate.aadl2.ListValue;
import org.osate.aadl2.ModalPropertyValue;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.NamedValue;
import org.osate.aadl2.PropertyAssociation;
import org.osate.aadl2.PropertyConstant;
import org.osate.aadl2.PropertyExpression;
import org.osate.aadl2.RecordValue;
import org.osate.aadl2.StringLiteral;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.InstanceObject;
import org.osate.aadl2.instance.SystemInstance;
import org.osate.aadl2.modelsupport.WriteToFile;
import org.osate.xtext.aadl2.errormodel.errorModel.ConditionElement;
import org.osate.xtext.aadl2.errormodel.errorModel.ConditionExpression;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2Path;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2PropertyAssociation;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorState;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorTransition;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorEvent;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorPropagation;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorSource;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorTypes;
import org.osate.xtext.aadl2.errormodel.errorModel.EventOrPropagation;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeSet;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeToken;
import org.osate.xtext.aadl2.errormodel.util.EMV2Properties;
import org.osate.xtext.aadl2.errormodel.util.EMV2TypeSetUtil;
import org.osate.xtext.aadl2.errormodel.util.EMV2Util;
import org.osate.xtext.aadl2.properties.util.GetProperties;

public final class FHAReport {

	public enum HazardFormat {
		EMV2, MILSTD882, ARP4761
	};

	private HazardFormat currentFormat = HazardFormat.EMV2;

	public FHAReport(HazardFormat emv2) {
		this.currentFormat = emv2;
	}

	public void doFHAReport(SystemInstance si) {
		WriteToFile report = new WriteToFile("FHA", si);
		if (currentFormat != HazardFormat.EMV2) {
			report.setSuffix("_" + currentFormat.name());
		}
		reportHeading(report);
		List<ComponentInstance> cilist = EcoreUtil2.getAllContentsOfType(si, ComponentInstance.class);
		processHazards(si, report);
		for (ConnectionInstance conni : si.getConnectionInstances()) {
			processHazards(conni, report);
		}
		for (ComponentInstance componentInstance : cilist) {
			processHazards(componentInstance, report);
			for (ConnectionInstance conni : componentInstance.getConnectionInstances()) {
				processHazards(conni, report);
			}
		}
		report.saveToFile();
	}

	protected void processHazards(ComponentInstance ci, WriteToFile report) {

		for (ErrorBehaviorTransition trans : EMV2Util.getAllErrorBehaviorTransitions(ci.getComponentClassifier())) {
			ConditionExpression cond = trans.getCondition();
			if (cond instanceof ConditionElement) {
				ConditionElement condElement = (ConditionElement) trans.getCondition();
				EventOrPropagation eop = EMV2Util.getErrorEventOrPropagation(condElement);
				if (eop instanceof ErrorEvent) {
					ErrorEvent errorEvent = (ErrorEvent) eop;
					List<EMV2PropertyAssociation> PA = getHazardsPropertyInCurrentFormat(ci, errorEvent,
							errorEvent.getTypeSet());
					List<EMV2PropertyAssociation> Sev = EMV2Properties.getSeverityProperty(ci, errorEvent,
							errorEvent.getTypeSet());
					List<EMV2PropertyAssociation> Like = EMV2Properties.getLikelihoodProperty(ci, errorEvent,
							errorEvent.getTypeSet());
					reportHazardProperty(ci, PA, Sev, Like, errorEvent, errorEvent.getTypeSet(), errorEvent, report);
				}
				// condElement.getIncoming()
			}

		}

		for (ErrorBehaviorState state : EMV2Util.getAllErrorBehaviorStates(ci)) {

			List<EMV2PropertyAssociation> PA = getHazardsPropertyInCurrentFormat(ci, state, state.getTypeSet());
			List<EMV2PropertyAssociation> Sev = EMV2Properties.getSeverityProperty(ci, state, state.getTypeSet());
			List<EMV2PropertyAssociation> Like = EMV2Properties.getLikelihoodProperty(ci, state, state.getTypeSet());
			reportHazardProperty(ci, PA, Sev, Like, state, state.getTypeSet(), state, report);
		}

		// report all error sources as hazards if they have the property
		Collection<ErrorSource> eslist = EMV2Util.getAllErrorSources(ci.getComponentClassifier());
		Collection<ErrorPropagation> oeplist = EMV2Util.getAllOutgoingErrorPropagations(ci.getComponentClassifier());

		for (ErrorSource errorSource : eslist) {
			NamedElement ne = errorSource.getSourceModelElement();
			ErrorBehaviorState failureMode = errorSource.getFailureModeReference();
			List<EMV2PropertyAssociation> HazardPA = Collections.emptyList();
			List<EMV2PropertyAssociation> Sev = Collections.emptyList();
			List<EMV2PropertyAssociation> Like = Collections.emptyList();
			TypeSet ts = null;
			NamedElement target = null;
			Element localContext = null;
			// not dealing with type set as failure mode
			if (failureMode != null) {
				// state is originating hazard, possibly with a type set
				ts = failureMode.getTypeSet();
				// error source a local context
				HazardPA = getHazardsPropertyInCurrentFormat(ci, failureMode, ts);
				Sev = EMV2Properties.getSeverityProperty(ci, failureMode, ts);
				Like = EMV2Properties.getLikelihoodProperty(ci, failureMode, ts);
				target = failureMode;
				localContext = errorSource;
			}
			if (HazardPA.isEmpty()) {
				// error source is originating hazard
				ts = errorSource.getTypeTokenConstraint();
				if (ts == null && ne instanceof ErrorPropagation) {
					ts = ((ErrorPropagation) ne).getTypeSet();
				}
				HazardPA = getHazardsPropertyInCurrentFormat(ci, errorSource, ts);
				Sev = EMV2Properties.getSeverityProperty(ci, errorSource, ts);
				Like = EMV2Properties.getLikelihoodProperty(ci, errorSource, ts);
				target = errorSource;
				localContext = null;
				if (HazardPA.isEmpty() && errorSource.getFailureModeType() != null) {
					ts = errorSource.getFailureModeType();
					HazardPA = getHazardsPropertyInCurrentFormat(ci, errorSource, ts);
					Sev = EMV2Properties.getSeverityProperty(ci, errorSource, ts);
					Like = EMV2Properties.getLikelihoodProperty(ci, errorSource, ts);
				}
			}
			if (!HazardPA.isEmpty()) {
				reportHazardProperty(ci, HazardPA, Sev, Like, target, ts, localContext, report);
			}
		}
		for (ErrorPropagation ep : oeplist) {
			TypeSet ts = null;
			NamedElement target = null;
			Element localContext = null;
			// error propagation is originating hazard
			ts = ep.getTypeSet();
			List<EMV2PropertyAssociation> HazardPA = getHazardsPropertyInCurrentFormat(ci, ep, ts);
			List<EMV2PropertyAssociation> Sev = EMV2Properties.getSeverityProperty(ci, ep, ts);
			List<EMV2PropertyAssociation> Like = EMV2Properties.getLikelihoodProperty(ci, ep, ts);
			target = ep;
			localContext = null;
			// XXX we may have more than one matching hazard
			if (!HazardPA.isEmpty()) {
				reportHazardProperty(ci, HazardPA, Sev, Like, target, ts, localContext, report);
			}
		}
	}

	protected void processHazards(ConnectionInstance conni, WriteToFile report) {
		ErrorSource ces = EMV2Util.findConnectionErrorSourceForConnection(conni);
		if (ces == null) {
			return;
		}
		Element localContext = null;
		// error propagation is originating hazard
		TypeSet ts = ces.getTypeTokenConstraint();
		List<EMV2PropertyAssociation> HazardPA = getHazardsPropertyInCurrentFormat(conni, ces, ts);
		List<EMV2PropertyAssociation> Sev = EMV2Properties.getSeverityProperty(conni, ces, ts);
		List<EMV2PropertyAssociation> Like = EMV2Properties.getLikelihoodProperty(conni, ces, ts);
		NamedElement target = ces;
		// XXX we may have more than one matching hazard
		if (!HazardPA.isEmpty()) {
			reportHazardProperty(conni, HazardPA, Sev, Like, target, ts, localContext, report);
		}
	}

	protected String getEnumerationorIntegerPropertyValue(PropertyAssociation pa) {
		if (pa == null) {
			return "";
		}
		for (ModalPropertyValue modalPropertyValue : pa.getOwnedValues()) {
			PropertyExpression val = modalPropertyValue.getOwnedValue();
			if (val instanceof NamedValue) {
				AbstractNamedValue eval = ((NamedValue) val).getNamedValue();
				if (eval instanceof EnumerationLiteral) {
					return ((EnumerationLiteral) eval).getName();

				} else if (eval instanceof PropertyConstant) {
					return ((PropertyConstant) eval).getName();
				}
			} else if (val instanceof IntegerLiteral) {
				// empty string to force integer conversion to string
				return "" + ((IntegerLiteral) val).getValue();
			}
		}

		return "";
	}

	protected void reportHazardProperty(InstanceObject ci, List<EMV2PropertyAssociation> PAList,
			List<EMV2PropertyAssociation> SevList, List<EMV2PropertyAssociation> LikeList, NamedElement target,
			TypeSet ts,
			Element localContext, WriteToFile report) {

		String targetName;
		if (PAList.isEmpty()) {
			return;
		}
		if (target == null) {
			targetName = "";
		} else {
			targetName = EMV2Util.getPrintName(target);
			if (target instanceof ErrorEvent) {
				targetName = "event " + targetName;
			}

			if (target instanceof ErrorBehaviorState) {
				targetName = "state " + targetName;
			}
		}
		for (EMV2PropertyAssociation PA : PAList) {

			for (ModalPropertyValue modalPropertyValue : PA.getOwnedValues()) {
				PropertyExpression peVal = modalPropertyValue.getOwnedValue();
				ListValue lv = (ListValue) peVal;
				for (PropertyExpression pe : lv.getOwnedListElements()) {
					EList<BasicPropertyAssociation> fields = ((RecordValue) pe).getOwnedFieldValues();
					// for all error types/aliases in type set or the element identified in the containment clause

					if (ts != null) {
						// do smaller of ts or hazard type set.
						EList<EMV2Path> epathlist = PA.getEmv2Path();
						for (EMV2Path ep : epathlist) {
							ErrorTypes et = EMV2Util.getErrorType(ep);
							ErrorTypes targettype = ts;
							if (et != null && EMV2TypeSetUtil.contains(ts, et)) {
								targettype = et;
							}
							List<EMV2PropertyAssociation> Sevs = EMV2Properties.getSeverityProperty(ci, target, et);
							List<EMV2PropertyAssociation> Likes = EMV2Properties.getLikelihoodProperty(ci, target, et);
							EMV2PropertyAssociation Sev = Sevs.isEmpty() ? null : Sevs.get(0);
							EMV2PropertyAssociation Like = Likes.isEmpty() ? null : Likes.get(0);
							PropertyExpression severityValue = EMV2Properties.getPropertyValue(Sev);
							PropertyExpression likelihoodValue = EMV2Properties.getPropertyValue(Like);
							if (targettype instanceof TypeSet) {
								for (TypeToken token : ((TypeSet) targettype).getTypeTokens()) {
									reportFHAEntry(report, fields, severityValue, likelihoodValue, ci, targetName,
											EMV2Util.getName(token));
								}
							} else {
								reportFHAEntry(report, fields, severityValue, likelihoodValue, ci, targetName,
										EMV2Util.getName(targettype));
							}
						}
					} else {
						// did not have a type set. Let's use fmr (state of type set as failure mode.
						EMV2PropertyAssociation Sev = SevList.isEmpty() ? null : SevList.get(0);
						EMV2PropertyAssociation Like = LikeList.isEmpty() ? null : LikeList.get(0);
						PropertyExpression severityValue = EMV2Properties.getPropertyValue(Sev);
						PropertyExpression likelihoodValue = EMV2Properties.getPropertyValue(Like);
						if (localContext == null) {
							reportFHAEntry(report, fields, severityValue, likelihoodValue, ci, targetName, "");
						} else {
							reportFHAEntry(report, fields, severityValue, likelihoodValue, ci,
									EMV2Util.getPrintName(localContext), EMV2Util.getPrintName(target));
						}
					}
				}
			}
		}
	}

	protected String makeCSVText(String text) {
		return text.replaceAll(",", ";");
	}

	protected void reportHeading(WriteToFile report) {

		switch (currentFormat) {
		case EMV2:
			report.addOutputNewline("Component, Error Model Element," + " Hazard Title, Description, Crossreference, "
					+ "Failure, " + "Failure Effect, " + "Operational Phases, Environment," + "Risk,"
					+ "Mishap, Failure Condition," + " Severity, Likelihood, Probability,"
					+ "Target Severity, Target Likelihood, Development Assurance Level, " + "Verification Method, "
					+ "Safety Report, " + "Comment");
			break;
		case ARP4761:
			report.addOutputNewline("Component, Error Model Element," + " Hazard Title, Description, Crossreference, "
					+ "Failure, " + "Failure Effect, " + "Operational Phases, Environment,"
					+ "  Risk, Mishap, Failure Condition,Failure Condition Classification, "
					+ "Qualitative Probability, Quantitative Probability, Qualitative Probability Objective, Quantitative Probability Objective, Development Assurance Level, "
					+ "Verification Method, " + "Safety Report, " + "Comment");
			break;
		case MILSTD882:
			report.addOutputNewline("Component, Error Model Element," + " Hazard Title, Description, Crossreference, "
					+ "Failure, " + "Failure Effect, " + "Operational Phases, Environment," + " Mishap, Risk,"
					+ " Severity Level, Severity Category, Qualitative Probability,Probability Level,Quantitative Probability, "
					+ "Target Severity Level, Target Probability Level, " + "Verification Method, " + "Safety Report, "
					+ "Comment");
			break;
		}
	}

	protected void reportFHAEntry(WriteToFile report, EList<BasicPropertyAssociation> fields,
			PropertyExpression Severity, PropertyExpression Likelihood, InstanceObject ci, String failureModeName,
			String typetext) {

		switch (currentFormat) {
		case EMV2:
			reportFHAEntryEMV2(report, fields, Severity, Likelihood, ci, failureModeName, typetext);
			break;
		case ARP4761:
			reportFHAEntryARP4761(report, fields, Severity, Likelihood, ci, failureModeName, typetext);
			break;
		case MILSTD882:
			reportFHAEntryMILSTD882(report, fields, Severity, Likelihood, ci, failureModeName, typetext);
			break;
		}
	}

	protected void reportFHAEntryEMV2(WriteToFile report, EList<BasicPropertyAssociation> fields,
			PropertyExpression Severity, PropertyExpression Likelihood, InstanceObject ci, String failureModeName,
			String typetext) {
		String componentName = ci.getName();
		/*
		 * We include the parent component name if not null and if this is not the root system
		 * instance.
		 */
		if ((ci.getContainingComponentInstance() != null)
				&& (ci.getContainingComponentInstance() != ci.getSystemInstance())) {
			componentName = ci.getContainingComponentInstance().getName() + "/" + componentName;
		}
		if (ci instanceof SystemInstance) {
			componentName = "Root system";
		}
		// component name & error propagation name/type
		report.addOutput(componentName + ", \"" + (typetext.isEmpty() ? "" : typetext)
				+ (failureModeName.isEmpty() ? "" : " on " + failureModeName) + "\"");
		// description (Effect)
		addComma(report);
		reportStringProperty(fields, "hazardtitle", report);
		addComma(report);
		reportStringProperty(fields, "description", report);
		// crossreference
		addComma(report);
		reportStringProperty(fields, "crossreference", report);
		// failure
		addComma(report);
		reportStringProperty(fields, "failure", report);
		// failure effect
		addComma(report);
		reportStringProperty(fields, "failureeffect", report);
		// phase
		addComma(report);
		reportStringProperty(fields, "phases", report);
		// phase
		addComma(report);
		reportStringProperty(fields, "environment", report);
		// phase
		addComma(report);
		reportStringProperty(fields, "risk", report);
		// mishap/failure condition
		addComma(report);
		reportStringProperty(fields, "mishap", report);
		addComma(report);
		reportStringProperty(fields, "failurecondition", report);
		// severity
		addComma(report);
		if (hasFieldValue(fields, "Severity")) {
			reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "Severity", report, Severity);
		} else if (hasFieldValue(fields, "FailureConditionClassification")) {
			reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "FailureConditionClassification", report,
					Severity);
		} else {
			reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "SeverityLevel", report, Severity);
		}
		// criticality
		addComma(report);
		if (hasFieldValue(fields, "Likelihood")) {
			reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "Likelihood", report, Likelihood);
		} else {
			reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "QualitativeProbability", report,
					Likelihood);
		}
		// probability
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "probability", report, null);
		// target severity
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "targetseverity", report, null);
		// target criticality
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "targetlikelihood", report, null);
		// Development assurance level
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "developmentassurancelevel", report, null);
		// verification method
		addComma(report);
		reportStringProperty(fields, "verificationmethod", report);
		// safety report
		addComma(report);
		reportStringProperty(fields, "safetyreport", report);
		// comment
		addComma(report);
		reportStringProperty(fields, "comment", report);
		report.addOutputNewline("");
	}

	protected void reportFHAEntryARP4761(WriteToFile report, EList<BasicPropertyAssociation> fields,
			PropertyExpression Severity, PropertyExpression Likelihood, InstanceObject ci, String failureModeName,
			String typetext) {
		String componentName = ci.getName();
		/*
		 * We include the parent component name if not null and if this is not the root system
		 * instance.
		 */
		if ((ci.getContainingComponentInstance() != null)
				&& (ci.getContainingComponentInstance() != ci.getSystemInstance())) {
			componentName = ci.getContainingComponentInstance().getName() + "/" + componentName;
		}
		if (ci instanceof SystemInstance) {
			componentName = "Root system";
		}
		// component name & error propagation name/type
		report.addOutput(componentName + ", \"" + (typetext.isEmpty() ? "" : typetext)
				+ (failureModeName.isEmpty() ? "" : " on " + failureModeName) + "\"");
		// description (Effect)
		addComma(report);
		reportStringProperty(fields, "hazardtitle", report);
		addComma(report);
		reportStringProperty(fields, "description", report);
		// crossreference
		addComma(report);
		reportStringProperty(fields, "crossreference", report);
		// failure
		addComma(report);
		reportStringProperty(fields, "failure", report);
		// failure effect
		addComma(report);
		reportStringProperty(fields, "failureeffect", report);
		addComma(report);
		reportStringProperty(fields, "phases", report);
		addComma(report);
		reportStringProperty(fields, "environment", report);
		addComma(report);
		reportStringProperty(fields, "risk", report);
		addComma(report);
		reportStringProperty(fields, "mishap", report);
		// mishap/failure condition
		addComma(report);
		reportStringProperty(fields, "failurecondition", report);
		// severity
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "FailureConditionClassification", report,
				Severity);
		// criticality
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "QualitativeProbability", report, Likelihood);
		// probability
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "QuantitativeProbability", report, null);
		// criticality
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "QualitativeProbabilityObjective", report,
				Likelihood);
		// probability
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "QuantitativeProbabilityObjective", report,
				null);
		// Development assurance level
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "developmentassurancelevel", report, null);
		// verification method
		addComma(report);
		reportStringProperty(fields, "verificationmethod", report);
		// safety report
		addComma(report);
		reportStringProperty(fields, "safetyreport", report);
		// comment
		addComma(report);
		reportStringProperty(fields, "comment", report);
		report.addOutputNewline("");
	}

	protected void reportFHAEntryMILSTD882(WriteToFile report, EList<BasicPropertyAssociation> fields,
			PropertyExpression Severity, PropertyExpression Likelihood, InstanceObject ci, String failureModeName,
			String typetext) {
		String componentName = ci.getName();
		/*
		 * We include the parent component name if not null and if this is not the root system
		 * instance.
		 */
		if ((ci.getContainingComponentInstance() != null)
				&& (ci.getContainingComponentInstance() != ci.getSystemInstance())) {
			componentName = ci.getContainingComponentInstance().getName() + "/" + componentName;
		}
		if (ci instanceof SystemInstance) {
			componentName = "Root system";
		}
		// component name & error propagation name/type
		report.addOutput(componentName + ", \"" + (typetext.isEmpty() ? "" : typetext)
				+ (failureModeName.isEmpty() ? "" : " on " + failureModeName) + "\"");
		// description (Effect)
		addComma(report);
		reportStringProperty(fields, "hazardtitle", report);
		addComma(report);
		reportStringProperty(fields, "description", report);
		// crossreference
		addComma(report);
		reportStringProperty(fields, "crossreference", report);
		// failure
		addComma(report);
		reportStringProperty(fields, "failure", report);
		// failure effect
		addComma(report);
		reportStringProperty(fields, "failureeffect", report);
		// phase
		addComma(report);
		reportStringProperty(fields, "phases", report);
		// phase
		addComma(report);
		reportStringProperty(fields, "environment", report);
		// mishap/failure condition
		addComma(report);
		if (!reportStringProperty(fields, "mishap", report)) {
			reportStringProperty(fields, "failurecondition", report);
		}
		// phase
		addComma(report);
		reportStringProperty(fields, "risk", report);
		// severity
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "SeverityLevel", report, Severity);
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "SeverityCategory", report, Severity);
		// criticality
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "QualitativeProbability", report, Likelihood);
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "ProbabilityLevel", report, Likelihood);
		// probability
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "QuantitativeProbability", report, null);
		// criticality
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "TargetSeverityLevel", report, Likelihood);
		// probability
		addComma(report);
		reportEnumerationOrIntegerPropertyConstantPropertyValue(fields, "TargetProbabilityLevel", report, null);
		// verification method
		addComma(report);
		reportStringProperty(fields, "verificationmethod", report);
		// safety report
		addComma(report);
		reportStringProperty(fields, "safetyreport", report);
		// comment
		addComma(report);
		reportStringProperty(fields, "comment", report);
		report.addOutputNewline("");
	}

	protected void addComma(WriteToFile report) {
		report.addOutput(", ");
	}

	protected void addString(WriteToFile report, String str) {
		report.addOutput(str);
	}

	/**
	 * report String based property values. Can be list of string values (for handling Phases)
	 * @param fields
	 * @param fieldName
	 * @param report
	 */
	protected Boolean reportStringProperty(EList<BasicPropertyAssociation> fields, String fieldName,
			WriteToFile report) {
		BasicPropertyAssociation xref = GetProperties.getRecordField(fields, fieldName);
		String text = null;
		if (xref != null) {
			PropertyExpression val = xref.getOwnedValue();
			if (val instanceof StringLiteral) {
				text = ((StringLiteral) val).getValue();
			}
			if (val instanceof ListValue) {
				ListValue lv = (ListValue) val;
				text = "";
				for (PropertyExpression pe : lv.getOwnedListElements()) {
					if (text.length() > 0) {
						text += " or ";
					}
					text += stripQuotes(((StringLiteral) pe).getValue());
				}
			}
		}
		if (text != null) {
			text = makeCSVText(stripQuotes(text));
			text = text.replaceAll(System.getProperty("line.separator"), " ");
			report.addOutput("\"" + text + "\"");
			return true;
		}
		return false;
	}

	/**
	 * Handle enumeration literals or integer values possibly assigned as property constant
	 * @param fields
	 * @param fieldName
	 * @param report
	 */
	protected void reportEnumerationOrIntegerPropertyConstantPropertyValue(EList<BasicPropertyAssociation> fields,
			String fieldName, WriteToFile report, PropertyExpression alternativeValue) {
		// added code to handle integer value and use of property constant instead of enumeration literal
		PropertyExpression val = alternativeValue;
		BasicPropertyAssociation xref = GetProperties.getRecordField(fields, fieldName);
		if (xref != null) {
			val = xref.getOwnedValue();
		}
		report.addOutput(EMV2Properties.getEnumerationOrIntegerPropertyConstantPropertyValue(val));
	}

	protected boolean hasFieldValue(EList<BasicPropertyAssociation> fields, String fieldName) {
		return GetProperties.getRecordField(fields, fieldName) != null;
	}

	protected String stripQuotes(String text) {
		if (text.startsWith("\"") && text.endsWith("\"")) {
			return text.substring(1, text.length() - 1);
		}
		return text;
	}

	protected List<EMV2PropertyAssociation> getHazardsPropertyInCurrentFormat(NamedElement ci, NamedElement target,
			TypeSet ts) {
		switch (currentFormat) {
		case EMV2:
			return EMV2Properties.getHazardsProperty(ci, target, ts);
		case ARP4761:
			return EMV2Properties.getARP4761HazardsProperty(ci, target, ts);
		case MILSTD882:
			return EMV2Properties.getMILSTD882HazardsProperty(ci, target, ts);
		}
		return null;
	}

}