Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
A
assert-object-equals-module
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Jira
Jira
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Packages & Registries
Packages & Registries
Container Registry
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Danijel Dragicevic
assert-object-equals-module
Commits
20f20531
Commit
20f20531
authored
May 09, 2019
by
Danijel Dragicevic
Browse files
Options
Browse Files
Download
Plain Diff
Merge development branch
parents
162d5e57
7933dc0f
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1071 additions
and
33 deletions
+1071
-33
README.md
README.md
+7
-12
pom.xml
pom.xml
+24
-2
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/AssertObjectEqualsExtension.java
...ertobjectequals/internal/AssertObjectEqualsExtension.java
+30
-1
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/AssertObjectEqualsOperations.java
...rtobjectequals/internal/AssertObjectEqualsOperations.java
+167
-18
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/ObjectComparator.java
...modules/assertobjectequals/internal/ObjectComparator.java
+209
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/ObjectCompareOptionsFactory.java
...ertobjectequals/internal/ObjectCompareOptionsFactory.java
+15
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/Path.java
...entric/mule/modules/assertobjectequals/internal/Path.java
+115
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/PathOption.java
.../mule/modules/assertobjectequals/internal/PathOption.java
+20
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/PathPattern.java
...mule/modules/assertobjectequals/internal/PathPattern.java
+121
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/PathPatternParser.java
...odules/assertobjectequals/internal/PathPatternParser.java
+208
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/PatternBasedOptionsFactory.java
...sertobjectequals/internal/PatternBasedOptionsFactory.java
+29
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/PatternEntry.java
...ule/modules/assertobjectequals/internal/PatternEntry.java
+101
-0
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/XmlCompareOption.java
...modules/assertobjectequals/internal/XmlCompareOption.java
+25
-0
No files found.
README.md
View file @
20f20531
# Assert
Object
Equals Extension
# Assert
Object
Equals Extension
Add description ...
...
...
Add this dependency to your application pom.xml
To install this Module in the Anypoint Studio add this dependency to your application pom.xml
```
<groupId>de.codecentric.mule.modules</groupId>
<artifactId>assert-object-equals-module</artifactId>
<version>1.0.
0
-SNAPSHOT</version>
<version>1.0.
175
-SNAPSHOT</version>
<classifier>mule-plugin</classifier>
```
Since this is not yet published on Maven repository, you have to install this project to your local Maven repository.
Please use following command:
mvn clean install -DskipTests
\ No newline at end of file
pom.xml
View file @
20f20531
...
...
@@ -6,9 +6,9 @@
<modelVersion>
4.0.0
</modelVersion>
<groupId>
de.codecentric.mule.modules
</groupId>
<artifactId>
assert-object-equals-module
</artifactId>
<version>
1.0.
0
-SNAPSHOT
</version>
<version>
1.0.
175
-SNAPSHOT
</version>
<packaging>
mule-extension
</packaging>
<name>
Assert
Object
Equals Extension
</name>
<name>
Assert
Object
Equals Extension
</name>
<parent>
<groupId>
org.mule.extensions
</groupId>
...
...
@@ -16,4 +16,26 @@
<version>
1.1.3
</version>
</parent>
<dependencies>
<dependency>
<groupId>
com.fasterxml.jackson.core
</groupId>
<artifactId>
jackson-core
</artifactId>
<version>
2.9.8
</version>
</dependency>
<dependency>
<groupId>
com.fasterxml.jackson.core
</groupId>
<artifactId>
jackson-databind
</artifactId>
<version>
2.9.8
</version>
</dependency>
<dependency>
<groupId>
org.apache.commons
</groupId>
<artifactId>
commons-lang3
</artifactId>
<version>
3.0
</version>
</dependency>
<dependency>
<groupId>
org.xmlunit
</groupId>
<artifactId>
xmlunit-core
</artifactId>
<version>
2.3.0
</version>
</dependency>
</dependencies>
</project>
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/AssertObjectEqualsExtension.java
View file @
20f20531
package
de.codecentric.mule.modules.assertobjectequals.internal
;
import
org.mule.runtime.extension.api.annotation.Extension
;
import
org.mule.runtime.extension.api.annotation.Configurations
;
import
org.mule.runtime.extension.api.annotation.Extension
;
import
org.mule.runtime.extension.api.annotation.dsl.xml.Xml
;
import
java.io.BufferedInputStream
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* This is the main class of an extension, is the entry point from which configurations, connection providers, operations
...
...
@@ -14,4 +20,27 @@ import org.mule.runtime.extension.api.annotation.dsl.xml.Xml;
@Configurations
(
AssertObjectEqualsConfiguration
.
class
)
public
class
AssertObjectEqualsExtension
{
/**
* This method is used for calling implementation logic ONLY in testing/development purposes.
* Once we reach "production" stage this method should be completely removed.
*/
public
static
void
main
(
String
[]
args
)
throws
Exception
{
//This is the expected file from my local disk. I'm imitating sending of real file from srt/test/resources directory to the mule component.
File
expectedFile
=
new
File
(
"/Users/ddanijel/AnypointStudio/studio-workspace/test-aoe-module/src/test/resources/expected-file.xml"
);
FileInputStream
expectedFileInputStream
=
new
FileInputStream
(
expectedFile
);
BufferedInputStream
expectedFileBuffered
=
new
BufferedInputStream
(
expectedFileInputStream
);
//This is the actual file from my local disk. I'm imitating sending of real file from srt/test/resources directory to the mule component.
File
actualFile
=
new
File
(
"/Users/ddanijel/AnypointStudio/studio-workspace/test-aoe-module/src/test/resources/actual-file.xml"
);
FileInputStream
actualFileInputStream
=
new
FileInputStream
(
actualFile
);
BufferedInputStream
actualFileBuffered
=
new
BufferedInputStream
(
actualFileInputStream
);
List
<
String
>
additionalOptions
=
new
ArrayList
<>();
additionalOptions
.
add
(
"['addresses'][#]['timestamp'] ignore"
);
AssertObjectEqualsOperations
aoe
=
new
AssertObjectEqualsOperations
();
//aoe.compareObjects(expectedFileBuffered, actualFileBuffered, additionalOptions);
aoe
.
compareXml
(
expectedFileBuffered
,
actualFileBuffered
,
"IGNORE_COMMENTS"
);
}
}
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/AssertObjectEqualsOperations.java
View file @
20f20531
package
de.codecentric.mule.modules.assertobjectequals.internal
;
import
static
org
.
mule
.
runtime
.
extension
.
api
.
annotation
.
param
.
MediaType
.
ANY
;
import
org.mule.runtime.extension.api.annotation.param.MediaType
;
import
org.mule.runtime.extension.api.annotation.param.Config
;
import
org.mule.runtime.extension.api.annotation.param.Connection
;
import
com.fasterxml.jackson.core.JsonProcessingException
;
import
com.fasterxml.jackson.databind.ObjectMapper
;
import
org.mule.runtime.extension.api.annotation.param.Optional
;
import
org.mule.runtime.extension.api.annotation.param.display.DisplayName
;
import
org.mule.runtime.extension.api.annotation.param.display.Example
;
import
org.mule.runtime.extension.api.annotation.values.OfValues
;
import
org.xmlunit.builder.DiffBuilder
;
import
org.xmlunit.diff.DefaultComparisonFormatter
;
import
org.xmlunit.diff.Diff
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.util.*
;
/**
* This class is a container for operations, every public method in this class will be taken as an extension operation.
...
...
@@ -13,25 +20,167 @@ import org.mule.runtime.extension.api.annotation.param.Connection;
public
class
AssertObjectEqualsOperations
{
/**
* Example of an operation that uses the configuration and a connection instance to perform some action.
* Compare two objects. Drill down into {@link Map} and {@link List}, use {@link Object#equals(Object)} for all other
* classes.
*
* @param expected
* The expected value. Automatic conversions are provided:
* <ul>
* <li>InputStream is read/parsed as Json</li>
* <li>byte[] is parsed as Json</li>
* <li>String is parsed as Json when it starts with [ or { (after <code>trim()</code></li>
* </ul>
* Remember: Encoding for Json is always UTF8
*
* @param actual
* The actual value. Automatic conversions are provided:
* <ul>
* <li>InputStream is read/parsed as Json</li>
* <li>byte[] is parsed as Json</li>
* <li>String is parsed as Json when it starts with [ or { (after <code>trim()</code></li>
* </ul>
* Remember: Encoding for Json is always UTF8
*
* @param pathOptions
* Options for path patterns to control the comparison. Syntax of one List entry: Zero to <code>n</code> path
* parts. The parts can have the following syntax:
* <ul>
* <li><code>?</code>: Wildcard one, matches one element in a path</li>
* <li><code>*</code>: Wildcard any, matches zero to <code>n</code> elements in a path</li>
* <li><code>[#]</code>: List wildcard, matches a list entry with any index</li>
* <li><code>[0]</code>: Matches a list entry with the given number. 0 or positive numbers: Count from
* beginning, negative number: Cound from end (-1 is last element)</li>
* <li><code>['.*']</code>: Matches a map entry where the key must match the given regular expression. If you
* need a ' in the expression, just write ''. The example '.*' matches all keys.</li>
* </ul>
* A space as separator. One or more of the following options (case not relevant):
*
* CONTAINS_ONLY_ON_MAPS: The actual value entry set of maps can contain more values than the expected set. So you tests do not fail
* when there are more elements than expected in the result.
*
* CHECK_MAP_ORDER: The order of map entries is checked. The default is to ignore order of map entries.
*
* IGNORE: The actual node and its subtree is ignored completely.
*
* @return The original payload.
*
* @throws Exception
* When comparison fails or on technical problems (e.g. parsing)
*/
@MediaType
(
value
=
ANY
,
strict
=
false
)
public
String
retrieveInfo
(
@Config
AssertObjectEqualsConfiguration
configuration
,
@Connection
AssertObjectEqualsConnection
connection
){
return
"Using Configuration ["
+
configuration
.
getConfigId
()
+
"] with Connection id ["
+
connection
.
getId
()
+
"]"
;
public
void
compareObjects
(
@DisplayName
(
"Expected value"
)
@Example
(
"#[MunitTools::getResourceAsStream()]"
)
Object
expected
,
@DisplayName
(
"Actual value"
)
@Example
(
"#[payload]"
)
Object
actual
,
@DisplayName
(
"Contains only on maps"
)
@Optional
(
defaultValue
=
"false"
)
boolean
containsOnlyOnMaps
,
@DisplayName
(
"Check map order"
)
@Optional
(
defaultValue
=
"false"
)
boolean
checkMapOrder
,
@DisplayName
(
"Path patterns+options"
)
List
<
String
>
pathOptions
)
throws
Exception
{
Object
expectedObj
=
convert2Object
(
expected
);
Object
actualObj
=
convert2Object
(
actual
);
ObjectComparator
comparator
=
createComparator
(
containsOnlyOnMaps
,
checkMapOrder
,
pathOptions
==
null
?
new
ArrayList
<
String
>()
:
pathOptions
);
Collection
<
String
>
diff
=
comparator
.
compare
(
expectedObj
,
actualObj
);
if
(!
diff
.
isEmpty
())
{
StringBuilder
messageBuilder
=
new
StringBuilder
();
for
(
String
s
:
diff
)
{
if
(
messageBuilder
.
length
()
>
0
)
{
messageBuilder
.
append
(
System
.
lineSeparator
());
}
messageBuilder
.
append
(
s
);
}
throw
new
AssertionError
(
"\n"
+
messageBuilder
);
}
}
/**
* Example of a simple operation that receives a string parameter and returns a new string message that will be set on the payload.
* Compare two XML documents. See <a href="https://github.com/xmlunit/user-guide/wiki/">XMLUnit Wiki</a>} how this works
*
* @param expected
* The expected value, XML as String, InputStream, byte[] or DOM tree.
* @param actual
* The actual value, XML as String, InputStream, byte[] or DOM tree.
* @param xmlCompareOption
* How to compare the XML documents.
*
* IGNORE_COMMENTS: Will remove all comment-Tags "<!-- Comment -->" from test- and control-XML before
* comparing.
*
* IGNORE_WHITESPACE: Ignore whitespace by removing all empty text nodes and trimming the non-empty ones.
*
* NORMALIZE_WHITESPACE: Normalize Text-Elements by removing all empty text nodes and normalizing the
* non-empty ones.
*
* @return The original payload.
*
* @throws Exception
* When comparison fails or on technical problems (e.g. parsing)
*/
@MediaType
(
value
=
ANY
,
strict
=
false
)
public
String
sayHi
(
String
person
)
{
return
buildHelloMessage
(
person
);
public
void
compareXml
(
@DisplayName
(
"Expected value"
)
@Example
(
"#[MunitTools::getResourceAsStream()]"
)
Object
expected
,
@DisplayName
(
"Actual value"
)
@Example
(
"#[payload]"
)
Object
actual
,
@DisplayName
(
"XML compare option"
)
@OfValues
(
XmlCompareOption
.
class
)
String
xmlCompareOption
)
throws
Exception
{
DiffBuilder
diffBuilder
=
DiffBuilder
.
compare
(
expected
).
withTest
(
actual
);
switch
(
xmlCompareOption
)
{
case
"IGNORE_COMMENTS"
:
diffBuilder
=
diffBuilder
.
ignoreComments
();
break
;
case
"IGNORE_WHITESPACE"
:
diffBuilder
=
diffBuilder
.
ignoreWhitespace
();
break
;
case
"NORMALIZE_WHITESPACE"
:
diffBuilder
=
diffBuilder
.
normalizeWhitespace
();
break
;
default
:
throw
new
IllegalArgumentException
(
"I forgot to implement for a new enum constant."
);
}
Diff
diff
=
diffBuilder
.
build
();
if
(
diff
.
hasDifferences
())
{
throw
new
AssertionError
(
diff
.
toString
(
new
DefaultComparisonFormatter
()));
}
}
/**
* Private Methods are not exposed as operations
*/
private
String
buildHelloMessage
(
String
person
)
{
return
"Hello "
+
person
+
"!!!"
;
private
Object
convert2Object
(
Object
value
)
throws
JsonProcessingException
,
IOException
{
if
(
value
==
null
)
{
return
null
;
}
else
if
(
value
instanceof
InputStream
)
{
return
new
ObjectMapper
().
readerFor
(
Object
.
class
).
readValue
((
InputStream
)
value
);
}
else
if
(
value
instanceof
byte
[])
{
return
new
ObjectMapper
().
readerFor
(
Object
.
class
).
readValue
((
byte
[])
value
);
}
else
if
(
value
instanceof
CharSequence
)
{
String
trimmed
=
((
CharSequence
)
value
).
toString
().
trim
();
if
(
trimmed
.
startsWith
(
"["
)
||
trimmed
.
startsWith
(
"{"
))
{
return
new
ObjectMapper
().
readerFor
(
Object
.
class
).
readValue
(
trimmed
);
}
else
{
return
value
;
}
}
else
{
return
value
;
}
}
private
ObjectComparator
createComparator
(
boolean
containsOnlyOnMaps
,
boolean
checkMapOrder
,
List
<
String
>
pathOptionsStrings
)
{
PathPatternParser
ppp
=
new
PathPatternParser
();
Collection
<
PathPattern
>
patterns
=
new
ArrayList
<>();
for
(
String
pathOptionString
:
pathOptionsStrings
)
{
patterns
.
add
(
ppp
.
parse
(
pathOptionString
));
}
EnumSet
<
PathOption
>
rootOptions
=
EnumSet
.
noneOf
(
PathOption
.
class
);
if
(
containsOnlyOnMaps
)
{
rootOptions
.
add
(
PathOption
.
CONTAINS_ONLY_ON_MAPS
);
}
if
(
checkMapOrder
)
{
rootOptions
.
add
(
PathOption
.
CHECK_MAP_ORDER
);
}
ObjectCompareOptionsFactory
optionFactory
=
new
PatternBasedOptionsFactory
(
rootOptions
,
patterns
);
return
new
ObjectComparator
(
optionFactory
);
}
}
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/ObjectComparator.java
0 → 100644
View file @
20f20531
package
de.codecentric.mule.modules.assertobjectequals.internal
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.EnumSet
;
import
java.util.Iterator
;
import
java.util.LinkedHashSet
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
/**
* Compare two objects, drill down into List an
*/
public
class
ObjectComparator
{
private
ObjectCompareOptionsFactory
optionFactory
;
private
class
State
{
final
Path
path
;
final
Object
expected
;
final
Object
actual
;
final
EnumSet
<
PathOption
>
options
;
private
State
(
Path
path
,
Object
expected
,
Object
actual
,
EnumSet
<
PathOption
>
options
)
{
this
.
path
=
path
;
this
.
expected
=
expected
;
this
.
actual
=
actual
;
this
.
options
=
options
;
}
public
State
(
Object
expected
,
Object
actual
)
{
path
=
new
Path
();
this
.
expected
=
expected
;
this
.
actual
=
actual
;
options
=
optionFactory
.
createOptions
(
null
,
path
);
}
public
State
listEntry
(
int
listIndex
,
int
listSize
,
Object
expected
,
Object
actual
)
{
Path
next
=
path
.
listEntry
(
listIndex
,
listSize
);
return
new
State
(
next
,
expected
,
actual
,
optionFactory
.
createOptions
(
options
,
next
));
}
public
State
mapEntry
(
String
key
,
Object
expected
,
Object
actual
)
{
Path
next
=
path
.
mapEntry
(
key
);
return
new
State
(
next
,
expected
,
actual
,
optionFactory
.
createOptions
(
options
,
next
));
}
}
public
ObjectComparator
(
ObjectCompareOptionsFactory
optionFactory
)
{
this
.
optionFactory
=
optionFactory
;
}
/**
* Compare two objects. Drill down into {@link Map} and {@link List}, use {@link Object#equals(Object)} for all other
* classes.
*
* @param expected
* The expected value.
* @param actual
* The actual value.
* @return Textual description of the differences.
*/
public
Collection
<
String
>
compare
(
Object
expected
,
Object
actual
)
{
State
state
=
new
State
(
expected
,
actual
);
Collection
<
String
>
diffs
=
new
ArrayList
<
String
>();
compare
(
state
,
diffs
);
return
diffs
;
}
private
void
compare
(
State
state
,
Collection
<
String
>
diffs
)
{
if
(
state
.
options
.
contains
(
PathOption
.
IGNORE
))
{
return
;
}
if
(
state
.
expected
==
null
)
{
if
(
state
.
actual
==
null
)
{
// ok, null equals null
}
else
{
// actual != null
diffs
.
add
(
"at '"
+
state
.
path
+
"', expected is null, actual "
+
state
.
actual
);
}
}
else
{
// expected != null
if
(
state
.
actual
==
null
)
{
diffs
.
add
(
"at '"
+
state
.
path
+
"', expected "
+
state
.
expected
+
", actual is null"
);
}
else
{
// actual != null
compareNonNullObjects
(
state
,
diffs
);
}
}
}
private
void
compareNonNullObjects
(
State
state
,
Collection
<
String
>
diffs
)
{
if
(
state
.
expected
instanceof
List
)
{
if
(
state
.
actual
instanceof
List
)
{
compareLists
(
state
,
diffs
);
}
else
{
diffs
.
add
(
"at '"
+
state
.
path
+
"', expected List, but found "
+
state
.
actual
.
getClass
().
getName
());
}
}
else
if
(
state
.
expected
instanceof
Map
)
{
if
(
state
.
actual
instanceof
Map
)
{
compareMaps
(
state
,
diffs
);
}
else
{
diffs
.
add
(
"at '"
+
state
.
path
+
"', expected Map, but found "
+
state
.
actual
.
getClass
().
getName
());
}
}
else
{
if
(!
state
.
expected
.
equals
(
state
.
actual
))
{
diffs
.
add
(
"at '"
+
state
.
path
+
"', expected "
+
state
.
expected
+
", but found "
+
state
.
actual
);
}
}
}
private
void
compareLists
(
State
state
,
Collection
<
String
>
diffs
)
{
@SuppressWarnings
(
"unchecked"
)
List
<
Object
>
expected
=
(
List
<
Object
>)
state
.
expected
;
@SuppressWarnings
(
"unchecked"
)
List
<
Object
>
actual
=
(
List
<
Object
>)
state
.
actual
;
if
(
expected
.
size
()
!=
actual
.
size
())
{
diffs
.
add
(
"at '"
+
state
.
path
+
"', expected size "
+
expected
.
size
()
+
", actual "
+
actual
.
size
());
return
;
}
int
size
=
expected
.
size
();
Iterator
<
Object
>
eIter
=
expected
.
iterator
();
Iterator
<
Object
>
aIter
=
actual
.
iterator
();
for
(
int
i
=
0
;
i
<
size
&&
eIter
.
hasNext
()
&&
aIter
.
hasNext
();
i
++)
{
State
nextState
=
state
.
listEntry
(
i
,
size
,
eIter
.
next
(),
aIter
.
next
());
compare
(
nextState
,
diffs
);
}
}
private
void
compareMaps
(
State
state
,
Collection
<
String
>
diffs
)
{
if
(
compareMapKeysOnly
(
state
,
diffs
))
{
return
;
}
@SuppressWarnings
(
"unchecked"
)
Map
<
Object
,
Object
>
expected
=
(
Map
<
Object
,
Object
>)
state
.
expected
;
@SuppressWarnings
(
"unchecked"
)
Map
<
Object
,
Object
>
actual
=
(
Map
<
Object
,
Object
>)
state
.
actual
;
for
(
Map
.
Entry
<
Object
,
Object
>
entry
:
expected
.
entrySet
())
{
Object
expectedKey
=
entry
.
getKey
();
compare
(
state
.
mapEntry
(
expectedKey
.
toString
(),
entry
.
getValue
(),
actual
.
get
(
expectedKey
)),
diffs
);
}
}
private
boolean
compareMapKeysOnly
(
State
state
,
Collection
<
String
>
diffs
)
{
@SuppressWarnings
(
"unchecked"
)
Map
<
Object
,
Object
>
expected
=
(
Map
<
Object
,
Object
>)
state
.
expected
;
@SuppressWarnings
(
"unchecked"
)
Map
<
Object
,
Object
>
actual
=
(
Map
<
Object
,
Object
>)
state
.
actual
;
// In all cases, expected keys must be a sub set of actual keys
Set
<
Object
>
keys
=
new
LinkedHashSet
<
Object
>(
expected
.
keySet
());
keys
.
removeAll
(
actual
.
keySet
());
if
(!
keys
.
isEmpty
())
{
diffs
.
add
(
"at '"
+
state
.
path
+
"', objects missing in actual: "
+
collectionToString
(
keys
));
return
true
;
}
// The other way is only relevant when we *don't* have a contains only
if
(!
state
.
options
.
contains
(
PathOption
.
CONTAINS_ONLY_ON_MAPS
))
{
keys
=
new
LinkedHashSet
<
Object
>(
actual
.
keySet
());
keys
.
removeAll
(
expected
.
keySet
());
if
(!
keys
.
isEmpty
())
{
diffs
.
add
(
"at '"
+
state
.
path
+
"', objects missing in expected: "
+
collectionToString
(
keys
));
return
true
;
}
}
if
(
state
.
options
.
contains
(
PathOption
.
CHECK_MAP_ORDER
))
{
return
checkOrder
(
state
.
path
,
expected
.
keySet
(),
actual
.
keySet
(),
diffs
);
}
return
false
;
}
private
boolean
checkOrder
(
Path
path
,
Set
<
Object
>
expectedKeys
,
Set
<
Object
>
actualKeysOrig
,
Collection
<
String
>
diffs
)
{
Set
<
Object
>
actualKeys
=
new
LinkedHashSet
<
Object
>(
actualKeysOrig
);
// Remove all keys which are *not* in expected
Iterator
<
Object
>
actualIter
=
actualKeys
.
iterator
();
while
(
actualIter
.
hasNext
())
{
if
(!
expectedKeys
.
contains
(
actualIter
.
next
()))
{
actualIter
.
remove
();
}
}
// Now the two sets should be equal (and in same order)
if
(
expectedKeys
.
size
()
!=
actualKeys
.
size
())
{
throw
new
RuntimeException
(
"at "
+
path
+
" unexpected size mismatch"
);
}
Iterator
<
Object
>
expectedIter
=
expectedKeys
.
iterator
();
actualIter
=
actualKeys
.
iterator
();
int
size
=
expectedKeys
.
size
();
for
(
int
i
=
0
;
i
<
size
&&
expectedIter
.
hasNext
()
&&
actualIter
.
hasNext
();
i
++)
{
Object
eKey
=
expectedIter
.
next
();
Object
aKey
=
actualIter
.
next
();
if
(!
eKey
.
equals
(
aKey
))
{
diffs
.
add
(
"at '"
+
path
+
"', expect key "
+
eKey
+
", actual "
+
aKey
);
return
true
;
}
}
return
false
;
}
private
String
collectionToString
(
Set
<
Object
>
col
)
{
StringBuilder
sb
=
new
StringBuilder
();
for
(
Object
o
:
col
)
{
if
(
sb
.
length
()
>
0
)
{
sb
.
append
(
", "
);
}
sb
.
append
(
o
.
toString
());
}
return
sb
.
toString
();
}
}
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/ObjectCompareOptionsFactory.java
0 → 100644
View file @
20f20531
package
de.codecentric.mule.modules.assertobjectequals.internal
;
import
java.util.EnumSet
;
public
interface
ObjectCompareOptionsFactory
{
/**
* @param inherited
* Options of parent path
* @param path
* A path
* @return Options controlling the comparison for a path.
*/
public
EnumSet
<
PathOption
>
createOptions
(
EnumSet
<
PathOption
>
inherited
,
Path
path
);
}
src/main/java/de/codecentric/mule/modules/assertobjectequals/internal/Path.java
0 → 100644
View file @
20f20531
package
de.codecentric.mule.modules.assertobjectequals.internal
;
/**
* Representation of a path in an object tree.
*/
public
class
Path
{
private
Path
predecessor
;
private
String
key
;
private
int
index
;
private
int
listSize
;
/**
* Create root.
*/
public
Path
()
{
}
private
Path
(
Path
predecessor
)
{
this
.
predecessor
=
predecessor
;
}