package de.retest.recheck.ui.diff;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import de.retest.recheck.ignore.GloballyIgnoredAttributes;
import de.retest.recheck.ui.descriptors.AdditionalAttributeDifference;
import de.retest.recheck.ui.descriptors.Attribute;
import de.retest.recheck.ui.descriptors.IdentifyingAttributes;
import de.retest.recheck.util.ObjectUtil;

public class IdentifyingAttributesDifferenceFinder {

	public IdentifyingAttributesDifference differenceFor( final IdentifyingAttributes expected,
			final IdentifyingAttributes actual ) {
		return differenceFor( expected, actual, GloballyIgnoredAttributes.getInstance() );
	}

	IdentifyingAttributesDifference differenceFor( final IdentifyingAttributes expected,
			final IdentifyingAttributes actual, final GloballyIgnoredAttributes ignored ) {
		Objects.requireNonNull( expected, "Expected cannot be null!" );
		Objects.requireNonNull( actual, "Actual cannot be null!" );

		final List<AttributeDifference> attributeDifferences = new ArrayList<>();

		final List<Attribute> expectedAttributes = expected.getAttributes();
		for ( final Attribute expectedAttr : expectedAttributes ) {
			if ( expectedAttr.isNotVisible() ) {
				continue;
			}
			final String key = expectedAttr.getKey();
			final Serializable expectedValue = expectedAttr.getValue();
			final Serializable actualValue = actual.get( key );
			if ( ignored.shouldIgnoreAttribute( key ) ) {
				continue;
			}
			if ( key.equals( "path" ) ) {
				if ( pathDiffers( expected, actual ) ) {
					attributeDifferences.add( new AttributeDifference( key, expected.getPath(), actual.getPath() ) );
				}
				continue;
			}
			if ( differs( expectedValue, actualValue ) ) {
				attributeDifferences.add( new AttributeDifference( key, expectedValue, actualValue ) );
			}
		}

		actual.getAttributes().stream() //
				.filter( actualAttr -> expected.get( actualAttr.getKey() ) == null ) //
				.map( additionalAttr -> new AdditionalAttributeDifference( additionalAttr.getKey(), additionalAttr ) ) //
				.forEach( attributeDifferences::add );

		return attributeDifferences.isEmpty() ? null
				: new IdentifyingAttributesDifference( expected, attributeDifferences );
	}

	private static boolean pathDiffers( final IdentifyingAttributes expected, final IdentifyingAttributes actual ) {
		return !expected.getPathElement().toString().replace( expected.getParentPath(), actual.getParentPath() )
				.equals( actual.getPathElement().toString() );
	}

	// We treat null == "". This is OK for visible attributeDifferences but less suitable for invisible ones.
	private static boolean differs( final Object expected, final Object actual ) {
		return ObjectUtil.isNullOrEmptyString( expected ) ? !ObjectUtil.isNullOrEmptyString( actual )
				: !expected.equals( actual );
	}

}

@@ -29,16 +29,106 @@
 .diff;%0A%0A
+import static de.retest.recheck.ui.descriptors.IdentifyingAttributes.PATH_ATTRIBUTE_KEY;%0A%0A
 import j
@@ -1303,67 +1303,8 @@
 ) %7B%0A
-%09%09%09if ( expectedAttr.isNotVisible() ) %7B%0A%09%09%09%09continue;%0A%09%09%09%7D%0A
 %09%09%09f
@@ -1462,23 +1462,55 @@
  key );%0A
+%0A
 %09%09%09if (
+ expectedAttr.isNotVisible() %7C%7C
  ignored
@@ -1554,32 +1554,33 @@
 %09continue;%0A%09%09%09%7D%0A
+%0A
 %09%09%09if ( key.equa
@@ -1587,14 +1587,26 @@
 ls( 
-%22path%22
+PATH_ATTRIBUTE_KEY
  ) )
@@ -1765,32 +1765,26 @@
 %09%09%09%7D%0A%09%09%09
-%09continue;%0A%09%09%09%7D%0A
+%7D else %7B%0A%09
 %09%09%09if ( 
@@ -1821,24 +1821,25 @@
 Value ) ) %7B%0A
+%09
 %09%09%09%09attribut
@@ -1918,16 +1918,22 @@
 ue ) );%0A
+%09%09%09%09%7D%0A
 %09%09%09%7D%0A%09%09%7D

Simplify algo to address SonarCloud issues