From c1187ae0d787b39ca4019b2a9d5a16d031bef08f Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Tue, 31 May 2016 20:12:27 +0200 Subject: [PATCH 1/8] #355 init abstract-document module --- abstract-document/README.md | 26 +++++++++++++++++++++++ abstract-document/pom.xml | 42 +++++++++++++++++++++++++++++++++++++ pom.xml | 3 ++- 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 abstract-document/README.md create mode 100644 abstract-document/pom.xml diff --git a/abstract-document/README.md b/abstract-document/README.md new file mode 100644 index 000000000..d9538c0bd --- /dev/null +++ b/abstract-document/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Abstract Document +folder: abstract-document +permalink: /patterns/abstract-document/ +categories: Structural +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Achieve flexibility of untyped languages and keep the type-safety + + + +## Applicability +Use the Abstract Document Pattern when + +* there is a need for dynamic properties +* you want a better way to organize domain +* you want loosely coupled system with flexibility of untyped languages + +## Real world examples + +* [Speedment](https://github.com/speedment/speedment) \ No newline at end of file diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml new file mode 100644 index 000000000..e7fc58047 --- /dev/null +++ b/abstract-document/pom.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + The MIT License + Copyright (c) 2014 Ilkka Seppälä + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <artifactId>java-design-patterns</artifactId> + <groupId>com.iluwatar</groupId> + <version>1.12.0-SNAPSHOT</version> + </parent> + <artifactId>abstract-document</artifactId> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/pom.xml b/pom.xml index af807b8ba..362519090 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,8 @@ <module>mutex</module> <module>semaphore</module> <module>hexagonal</module> - </modules> + <module>abstract-document</module> + </modules> <dependencyManagement> <dependencies> From 913411773f708a5d3d73cc7953a75c596f9bd7cd Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Wed, 1 Jun 2016 22:20:55 +0200 Subject: [PATCH 2/8] #355 document, abstract base, traits and example domain --- .../abstractdocument/AbstractDocument.java | 38 +++++++++++++++++++ .../iluwatar/abstractdocument/Document.java | 34 +++++++++++++++++ .../iluwatar/abstractdocument/domain/Car.java | 18 +++++++++ .../abstractdocument/domain/HasModel.java | 13 +++++++ .../abstractdocument/domain/HasParts.java | 13 +++++++ .../abstractdocument/domain/HasPrice.java | 13 +++++++ .../abstractdocument/domain/Part.java | 18 +++++++++ 7 files changed, 147 insertions(+) create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java new file mode 100644 index 000000000..d4aa8ed5b --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -0,0 +1,38 @@ +package com.iluwatar.abstractdocument; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Stream; + +public abstract class AbstractDocument implements Document { + + private final Map<String, Object> properties; + + protected AbstractDocument(Map<String, Object> properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.get(key); + } + + @Override + public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) { + return Stream.of(get(key)) + .filter(el -> el != null) + .map(el -> (List<Map<String, Object>>) el) + .findFirst().get().stream() + .map(constructor); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java new file mode 100644 index 000000000..8d4db30b9 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java @@ -0,0 +1,34 @@ +package com.iluwatar.abstractdocument; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +public interface Document { + + /** + * Puts the value related to the key + * + * @param key + * @param value + * @return Void + */ + Void put(String key, Object value); + + /** + * Gets the value for the key + * + * @param key + * @return value or null + */ + Object get(String key); + + /** + * Gets the stream of child documents + * + * @param key + * @param constructor + * @return child documents + */ + <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor); +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java new file mode 100644 index 000000000..70e31e70e --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java @@ -0,0 +1,18 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.HashMap; +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { + + protected Car() { + super(new HashMap<String, Object>()); + } + + protected Car(Map<String,Object> properties) { + super(properties); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java new file mode 100644 index 000000000..635f80abf --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java @@ -0,0 +1,13 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +public interface HasModel extends Document { + + default Optional<String> getModel() { + return Optional.ofNullable((String) get("model")); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java new file mode 100644 index 000000000..92c7dc7d9 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java @@ -0,0 +1,13 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.stream.Stream; + +import com.iluwatar.abstractdocument.Document; + +public interface HasParts extends Document { + + default Stream<Part> getParts() { + return children("parts", Part::new); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java new file mode 100644 index 000000000..7c9e8e6a1 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java @@ -0,0 +1,13 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +public interface HasPrice extends Document { + + default Optional<Number> getPartner() { + return Optional.ofNullable((Number) get("price")); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java new file mode 100644 index 000000000..fa252a7bc --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java @@ -0,0 +1,18 @@ +package com.iluwatar.abstractdocument.domain; + +import java.util.HashMap; +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +public class Part extends AbstractDocument implements HasModel, HasPrice { + + protected Part() { + super(new HashMap<String, Object>()); + } + + protected Part(Map<String, Object> properties) { + super(properties); + } + +} From 32b647323c845733fd7b8bfc93752a376b0543ae Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Wed, 1 Jun 2016 22:21:31 +0200 Subject: [PATCH 3/8] #355 class diagrams --- .../etc/abstract-document-base.png | Bin 0 -> 14973 bytes .../etc/abstract-document-base.ucls | 58 +++++++++++ abstract-document/etc/abstract-document.png | Bin 0 -> 16712 bytes abstract-document/etc/abstract-document.ucls | 96 ++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 abstract-document/etc/abstract-document-base.png create mode 100644 abstract-document/etc/abstract-document-base.ucls create mode 100644 abstract-document/etc/abstract-document.png create mode 100644 abstract-document/etc/abstract-document.ucls diff --git a/abstract-document/etc/abstract-document-base.png b/abstract-document/etc/abstract-document-base.png new file mode 100644 index 0000000000000000000000000000000000000000..13345dbb862c12463c352af53dafb7f3ee42bf37 GIT binary patch literal 14973 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 T^}xTC-jjMQFIFP*#_#_C2aR!W literal 0 HcmV?d00001 diff --git a/abstract-document/etc/abstract-document-base.ucls b/abstract-document/etc/abstract-document-base.ucls new file mode 100644 index 000000000..bfe927ed9 --- /dev/null +++ b/abstract-document/etc/abstract-document-base.ucls @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<class-diagram version="1.1.9" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true" + realizations="true" associations="true" dependencies="false" nesting-relationships="true" router="FAN"> + <interface id="1" language="java" name="com.iluwatar.abstractdocument.Document" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/Document.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="249" y="405"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <class id="2" language="java" name="com.iluwatar.abstractdocument.AbstractDocument" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/AbstractDocument.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="250" y="237"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <class id="3" language="java" name="com.iluwatar.abstractdocument.domain.Car" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/domain/Car.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="108" y="75"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <class id="4" language="java" name="com.iluwatar.abstractdocument.domain.Part" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/domain/Part.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="400" y="76"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <generalization id="5"> + <end type="SOURCE" refId="4"/> + <end type="TARGET" refId="2"/> + </generalization> + <realization id="6"> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="1"/> + </realization> + <generalization id="7"> + <end type="SOURCE" refId="3"/> + <end type="TARGET" refId="2"/> + </generalization> + <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </classifier-display> + <association-display labels="true" multiplicity="true"/> +</class-diagram> \ No newline at end of file diff --git a/abstract-document/etc/abstract-document.png b/abstract-document/etc/abstract-document.png new file mode 100644 index 0000000000000000000000000000000000000000..98d186f7e7aa8de23be6656c58332c826db77aa7 GIT binary patch literal 16712 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 literal 0 HcmV?d00001 diff --git a/abstract-document/etc/abstract-document.ucls b/abstract-document/etc/abstract-document.ucls new file mode 100644 index 000000000..ad97457fd --- /dev/null +++ b/abstract-document/etc/abstract-document.ucls @@ -0,0 +1,96 @@ +<?xml version="1.0" encoding="UTF-8"?> +<class-diagram version="1.1.9" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true" + realizations="true" associations="true" dependencies="false" nesting-relationships="true" router="FAN"> + <interface id="1" language="java" name="com.iluwatar.abstractdocument.Document" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/Document.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="-1" width="-1" x="341" y="376"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <interface id="2" language="java" name="com.iluwatar.abstractdocument.domain.HasModel" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/domain/HasModel.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="81" width="173" x="41" y="194"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <interface id="3" language="java" name="com.iluwatar.abstractdocument.domain.HasPrice" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/domain/HasPrice.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="81" width="175" x="254" y="194"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <interface id="4" language="java" name="com.iluwatar.abstractdocument.domain.HasParts" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/domain/HasParts.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="81" width="173" x="469" y="194"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </interface> + <class id="5" language="java" name="com.iluwatar.abstractdocument.domain.Car" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/domain/Car.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="99" width="173" x="254" y="37"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <class id="6" language="java" name="com.iluwatar.abstractdocument.domain.Part" project="design-patterns" + file="/design-patterns/src/com/iluwatar/abstractdocument/domain/Part.java" binary="false" corner="BOTTOM_RIGHT"> + <position height="99" width="173" x="41" y="37"/> + <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </display> + </class> + <realization id="7"> + <end type="SOURCE" refId="5"/> + <end type="TARGET" refId="2"/> + </realization> + <realization id="8"> + <end type="SOURCE" refId="6"/> + <end type="TARGET" refId="2"/> + </realization> + <generalization id="9"> + <end type="SOURCE" refId="2"/> + <end type="TARGET" refId="1"/> + </generalization> + <realization id="10"> + <end type="SOURCE" refId="5"/> + <end type="TARGET" refId="3"/> + </realization> + <realization id="11"> + <end type="SOURCE" refId="5"/> + <end type="TARGET" refId="4"/> + </realization> + <generalization id="12"> + <end type="SOURCE" refId="3"/> + <end type="TARGET" refId="1"/> + </generalization> + <generalization id="13"> + <end type="SOURCE" refId="4"/> + <end type="TARGET" refId="1"/> + </generalization> + <realization id="14"> + <end type="SOURCE" refId="6"/> + <end type="TARGET" refId="3"/> + </realization> + <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" + sort-features="false" accessors="true" visibility="true"> + <attributes public="true" package="true" protected="true" private="true" static="true"/> + <operations public="true" package="true" protected="true" private="true" static="true"/> + </classifier-display> + <association-display labels="true" multiplicity="true"/> +</class-diagram> \ No newline at end of file From a372f05e41910ed86b5d9d2d6cfd43ad1f964a24 Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Thu, 2 Jun 2016 07:27:35 +0200 Subject: [PATCH 4/8] #355 override toString to log properties --- .../abstractdocument/AbstractDocument.java | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java index d4aa8ed5b..c3f8e2a4b 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -8,31 +8,42 @@ import java.util.stream.Stream; public abstract class AbstractDocument implements Document { - private final Map<String, Object> properties; + private final Map<String, Object> properties; - protected AbstractDocument(Map<String, Object> properties) { - Objects.requireNonNull(properties, "properties map is required"); - this.properties = properties; - } + protected AbstractDocument(Map<String, Object> properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } - @Override - public Void put(String key, Object value) { - properties.put(key, value); - return null; - } + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } - @Override - public Object get(String key) { - return properties.get(key); - } + @Override + public Object get(String key) { + return properties.get(key); + } - @Override - public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) { - return Stream.of(get(key)) - .filter(el -> el != null) - .map(el -> (List<Map<String, Object>>) el) - .findFirst().get().stream() - .map(constructor); - } + @Override + public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) { + return Stream.of(get(key)) + .filter(el -> el != null) + .map(el -> (List<Map<String, Object>>) el) + .findAny().get().stream() + .map(constructor); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()).append("["); + properties.entrySet().forEach(e -> + builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]") + ); + builder.append("]"); + return builder.toString(); + } } From 43f90ead48709c44d5253864d3d31c6bc24b0601 Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Thu, 2 Jun 2016 07:29:37 +0200 Subject: [PATCH 5/8] #355 abstract document test --- .../AbstractDocumentTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java new file mode 100644 index 000000000..43585d853 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -0,0 +1,48 @@ +package com.iluwatar.abstractdocument; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; + +public class AbstractDocumentTest { + + private static final String KEY = "key"; + private static final Object VALUE = "value"; + + private class DocumentImplementation extends AbstractDocument { + + DocumentImplementation(Map<String, Object> properties) { + super(properties); + } + } + + private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); + + @Test + public void shouldPutAndGetValue() { + document.put(KEY, VALUE); + assertEquals(VALUE, document.get(KEY)); + System.out.println(document); + } + + @Test + public void shouldRetrieveChildren() { + Map<String,Object> child1 = new HashMap<>(); + Map<String,Object> child2 = new HashMap<>(); + List<Map<String, Object>> children = Arrays.asList(child1, child2); + + document.put(KEY, children); + + Stream<DocumentImplementation> childrenStream = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(2, childrenStream.count()); + } + +} From f3110de1306659b6cd70a318a1070a28fefc680e Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Thu, 2 Jun 2016 07:32:48 +0200 Subject: [PATCH 6/8] #355 handle case when there are no child elements for the given key --- .../com/iluwatar/abstractdocument/AbstractDocument.java | 7 ++++--- .../iluwatar/abstractdocument/AbstractDocumentTest.java | 8 +++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java index c3f8e2a4b..f5ef69e3c 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -3,6 +3,7 @@ package com.iluwatar.abstractdocument; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; @@ -28,11 +29,11 @@ public abstract class AbstractDocument implements Document { @Override public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) { - return Stream.of(get(key)) + Optional<List<Map<String, Object>>> any = Stream.of(get(key)) .filter(el -> el != null) .map(el -> (List<Map<String, Object>>) el) - .findAny().get().stream() - .map(constructor); + .findAny(); + return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty(); } @Override diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java index 43585d853..baf872e4f 100644 --- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -29,7 +29,6 @@ public class AbstractDocumentTest { public void shouldPutAndGetValue() { document.put(KEY, VALUE); assertEquals(VALUE, document.get(KEY)); - System.out.println(document); } @Test @@ -45,4 +44,11 @@ public class AbstractDocumentTest { assertEquals(2, childrenStream.count()); } + @Test + public void shouldRetrieveEmptyStreamForNonExistinChildren() { + Stream<DocumentImplementation> children = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(0, children.count()); + } + } From c229ec23b3f9d40a5f91f6865e299307a6117727 Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Thu, 2 Jun 2016 07:41:32 +0200 Subject: [PATCH 7/8] #355 clean up --- .../java/com/iluwatar/abstractdocument/AbstractDocument.java | 2 +- .../src/main/java/com/iluwatar/abstractdocument/domain/Car.java | 2 +- .../main/java/com/iluwatar/abstractdocument/domain/Part.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java index f5ef69e3c..054b6ebfb 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -38,7 +38,7 @@ public abstract class AbstractDocument implements Document { @Override public String toString() { - final StringBuilder builder = new StringBuilder(); + StringBuilder builder = new StringBuilder(); builder.append(getClass().getName()).append("["); properties.entrySet().forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]") diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java index 70e31e70e..b3dcaa77f 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java @@ -8,7 +8,7 @@ import com.iluwatar.abstractdocument.AbstractDocument; public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { protected Car() { - super(new HashMap<String, Object>()); + super(new HashMap<>()); } protected Car(Map<String,Object> properties) { diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java index fa252a7bc..c6e1a3908 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java @@ -8,7 +8,7 @@ import com.iluwatar.abstractdocument.AbstractDocument; public class Part extends AbstractDocument implements HasModel, HasPrice { protected Part() { - super(new HashMap<String, Object>()); + super(new HashMap<>()); } protected Part(Map<String, Object> properties) { From afdeba4f9abbc18592585f98c0809517b7f80f33 Mon Sep 17 00:00:00 2001 From: qza <kokovic.zoran@gmail.com> Date: Sat, 4 Jun 2016 20:06:32 +0200 Subject: [PATCH 8/8] #355 finalize example --- abstract-document/README.md | 17 ++-- .../abstractdocument/AbstractDocument.java | 86 +++++++++++------- .../com/iluwatar/abstractdocument/App.java | 88 ++++++++++++++++++ .../iluwatar/abstractdocument/Document.java | 71 ++++++++++----- .../iluwatar/abstractdocument/domain/Car.java | 36 ++++++-- .../abstractdocument/domain/HasModel.java | 35 +++++++- .../abstractdocument/domain/HasParts.java | 35 +++++++- .../abstractdocument/domain/HasPrice.java | 35 +++++++- .../abstractdocument/domain/HasType.java | 40 +++++++++ .../abstractdocument/domain/Part.java | 38 ++++++-- .../AbstractDocumentTest.java | 90 +++++++++++++------ .../iluwatar/abstractdocument/AppTest.java | 37 ++++++++ .../iluwatar/abstractdocument/DomainTest.java | 77 ++++++++++++++++ pom.xml | 2 +- 14 files changed, 568 insertions(+), 119 deletions(-) create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java create mode 100644 abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java create mode 100644 abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java create mode 100644 abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java diff --git a/abstract-document/README.md b/abstract-document/README.md index d9538c0bd..bf28ff999 100644 --- a/abstract-document/README.md +++ b/abstract-document/README.md @@ -12,15 +12,20 @@ tags: ## Intent Achieve flexibility of untyped languages and keep the type-safety - + + + + ## Applicability Use the Abstract Document Pattern when -* there is a need for dynamic properties -* you want a better way to organize domain -* you want loosely coupled system with flexibility of untyped languages +* there is a need to add new properties on the fly +* you want a flexible way to organize domain in tree like structure +* you want more loosely coupled system -## Real world examples -* [Speedment](https://github.com/speedment/speedment) \ No newline at end of file +## Credits + +* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern) +* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf) \ No newline at end of file diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java index 054b6ebfb..4bf8f0d14 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument; import java.util.List; @@ -7,44 +29,44 @@ import java.util.Optional; import java.util.function.Function; import java.util.stream.Stream; +/** + * Abstract implementation of Document interface + */ public abstract class AbstractDocument implements Document { - private final Map<String, Object> properties; + private final Map<String, Object> properties; - protected AbstractDocument(Map<String, Object> properties) { - Objects.requireNonNull(properties, "properties map is required"); - this.properties = properties; - } + protected AbstractDocument(Map<String, Object> properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } - @Override - public Void put(String key, Object value) { - properties.put(key, value); - return null; - } + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } - @Override - public Object get(String key) { - return properties.get(key); - } + @Override + public Object get(String key) { + return properties.get(key); + } - @Override - public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) { - Optional<List<Map<String, Object>>> any = Stream.of(get(key)) - .filter(el -> el != null) - .map(el -> (List<Map<String, Object>>) el) - .findAny(); - return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty(); - } + @Override + public <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor) { + Optional<List<Map<String, Object>>> any = Stream.of(get(key)).filter(el -> el != null) + .map(el -> (List<Map<String, Object>>) el).findAny(); + return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty(); + } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(getClass().getName()).append("["); - properties.entrySet().forEach(e -> - builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]") - ); - builder.append("]"); - return builder.toString(); - } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()).append("["); + properties.entrySet() + .forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]")); + builder.append("]"); + return builder.toString(); + } } diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java new file mode 100644 index 000000000..d7758b6f7 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import com.iluwatar.abstractdocument.domain.Car; +import com.iluwatar.abstractdocument.domain.HasModel; +import com.iluwatar.abstractdocument.domain.HasParts; +import com.iluwatar.abstractdocument.domain.HasPrice; +import com.iluwatar.abstractdocument.domain.HasType; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * The Abstract Document pattern enables handling additional, non-static + * properties. This pattern uses concept of traits to enable type safety and + * separate properties of different classes into set of interfaces. + * <p> + * <p> + * In Abstract Document pattern,({@link AbstractDocument}) fully implements + * {@link Document}) interface. Traits are then defined to enable access to + * properties in usual, static way. + */ +public class App { + + /** + * Executes the App + */ + public App() { + System.out.println("Constructing parts and car"); + + Map<String, Object> carProperties = new HashMap<>(); + carProperties.put(HasModel.PROPERTY, "300SL"); + carProperties.put(HasPrice.PROPERTY, 10000L); + + Map<String, Object> wheelProperties = new HashMap<>(); + wheelProperties.put(HasType.PROPERTY, "wheel"); + wheelProperties.put(HasModel.PROPERTY, "15C"); + wheelProperties.put(HasPrice.PROPERTY, 100L); + + Map<String, Object> doorProperties = new HashMap<>(); + doorProperties.put(HasType.PROPERTY, "door"); + doorProperties.put(HasModel.PROPERTY, "Lambo"); + doorProperties.put(HasPrice.PROPERTY, 300L); + + carProperties.put(HasParts.PROPERTY, Arrays.asList(wheelProperties, doorProperties)); + + Car car = new Car(carProperties); + + System.out.println("Here is our car:"); + System.out.println("-> model: " + car.getModel().get()); + System.out.println("-> price: " + car.getPrice().get()); + System.out.println("-> parts: "); + car.getParts().forEach(p -> System.out + .println("\t" + p.getType().get() + "/" + p.getModel().get() + "/" + p.getPrice().get())); + } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + new App(); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java index 8d4db30b9..7705f37eb 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java @@ -1,34 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument; import java.util.Map; import java.util.function.Function; import java.util.stream.Stream; +/** + * Document interface + */ public interface Document { - /** - * Puts the value related to the key - * - * @param key - * @param value - * @return Void - */ - Void put(String key, Object value); + /** + * Puts the value related to the key + * + * @param key element key + * @param value element value + * @return Void + */ + Void put(String key, Object value); - /** - * Gets the value for the key - * - * @param key - * @return value or null - */ - Object get(String key); + /** + * Gets the value for the key + * + * @param key element key + * @return value or null + */ + Object get(String key); - /** - * Gets the stream of child documents - * - * @param key - * @param constructor - * @return child documents - */ - <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor); + /** + * Gets the stream of child documents + * + * @param key element key + * @param constructor constructor of child class + * @return child documents + */ + <T> Stream<T> children(String key, Function<Map<String, Object>, T> constructor); } diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java index b3dcaa77f..e29ee63da 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java @@ -1,18 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument.domain; -import java.util.HashMap; import java.util.Map; import com.iluwatar.abstractdocument.AbstractDocument; +/** + * Car entity + */ public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { - protected Car() { - super(new HashMap<>()); - } - - protected Car(Map<String,Object> properties) { - super(properties); - } + public Car(Map<String, Object> properties) { + super(properties); + } } diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java index 635f80abf..fbd8c0d7f 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java @@ -1,13 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument.domain; import java.util.Optional; import com.iluwatar.abstractdocument.Document; +/** + * HasModel trait for static access to 'model' property + */ public interface HasModel extends Document { - - default Optional<String> getModel() { - return Optional.ofNullable((String) get("model")); - } + + String PROPERTY = "model"; + + default Optional<String> getModel() { + return Optional.ofNullable((String) get(PROPERTY)); + } } diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java index 92c7dc7d9..581702cc9 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java @@ -1,13 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument.domain; import java.util.stream.Stream; import com.iluwatar.abstractdocument.Document; +/** + * HasParts trait for static access to 'parts' property + */ public interface HasParts extends Document { - - default Stream<Part> getParts() { - return children("parts", Part::new); - } + + String PROPERTY = "parts"; + + default Stream<Part> getParts() { + return children(PROPERTY, Part::new); + } } diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java index 7c9e8e6a1..3d1d0e3e7 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java @@ -1,13 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument.domain; import java.util.Optional; import com.iluwatar.abstractdocument.Document; +/** + * HasPrice trait for static access to 'price' property + */ public interface HasPrice extends Document { - - default Optional<Number> getPartner() { - return Optional.ofNullable((Number) get("price")); - } + + String PROPERTY = "price"; + + default Optional<Number> getPrice() { + return Optional.ofNullable((Number) get(PROPERTY)); + } } diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java new file mode 100644 index 000000000..b0f292bb6 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument.domain; + +import com.iluwatar.abstractdocument.Document; + +import java.util.Optional; + +/** + * HasType trait for static access to 'type' property + */ +public interface HasType extends Document { + + String PROPERTY = "type"; + + default Optional<String> getType() { + return Optional.ofNullable((String) get(PROPERTY)); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java index c6e1a3908..e42f099d9 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java @@ -1,18 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument.domain; -import java.util.HashMap; import java.util.Map; import com.iluwatar.abstractdocument.AbstractDocument; -public class Part extends AbstractDocument implements HasModel, HasPrice { +/** + * Part entity + */ +public class Part extends AbstractDocument implements HasType, HasModel, HasPrice { - protected Part() { - super(new HashMap<>()); - } - - protected Part(Map<String, Object> properties) { - super(properties); - } + public Part(Map<String, Object> properties) { + super(properties); + } } diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java index baf872e4f..b6467e232 100644 --- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.abstractdocument; import org.junit.Test; @@ -11,44 +33,56 @@ import java.util.stream.Stream; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotNull; +/** + * AbstractDocument test class + */ public class AbstractDocumentTest { - private static final String KEY = "key"; - private static final Object VALUE = "value"; + private static final String KEY = "key"; + private static final String VALUE = "value"; - private class DocumentImplementation extends AbstractDocument { + private class DocumentImplementation extends AbstractDocument { - DocumentImplementation(Map<String, Object> properties) { - super(properties); - } + DocumentImplementation(Map<String, Object> properties) { + super(properties); } + } - private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); + private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); - @Test - public void shouldPutAndGetValue() { - document.put(KEY, VALUE); - assertEquals(VALUE, document.get(KEY)); - } + @Test + public void shouldPutAndGetValue() { + document.put(KEY, VALUE); + assertEquals(VALUE, document.get(KEY)); + } - @Test - public void shouldRetrieveChildren() { - Map<String,Object> child1 = new HashMap<>(); - Map<String,Object> child2 = new HashMap<>(); - List<Map<String, Object>> children = Arrays.asList(child1, child2); + @Test + public void shouldRetrieveChildren() { + Map<String, Object> child1 = new HashMap<>(); + Map<String, Object> child2 = new HashMap<>(); + List<Map<String, Object>> children = Arrays.asList(child1, child2); - document.put(KEY, children); + document.put(KEY, children); - Stream<DocumentImplementation> childrenStream = document.children(KEY, DocumentImplementation::new); - assertNotNull(children); - assertEquals(2, childrenStream.count()); - } + Stream<DocumentImplementation> childrenStream = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(2, childrenStream.count()); + } - @Test - public void shouldRetrieveEmptyStreamForNonExistinChildren() { - Stream<DocumentImplementation> children = document.children(KEY, DocumentImplementation::new); - assertNotNull(children); - assertEquals(0, children.count()); - } + @Test + public void shouldRetrieveEmptyStreamForNonExistingChildren() { + Stream<DocumentImplementation> children = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(0, children.count()); + } + + @Test + public void shouldIncludePropsInToString() { + Map<String, Object> props = new HashMap<>(); + props.put(KEY, VALUE); + DocumentImplementation document = new DocumentImplementation(props); + assertNotNull(document.toString().contains(KEY)); + assertNotNull(document.toString().contains(VALUE)); + } } diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java new file mode 100644 index 000000000..787ae3aa6 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import org.junit.Test; + +/** + * Simple App test + */ +public class AppTest { + + @Test + public void shouldExecuteAppWithoutException() { + App.main(null); + } + +} diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java new file mode 100644 index 000000000..437244a3d --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * <p> + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * <p> + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * <p> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.abstractdocument; + +import com.iluwatar.abstractdocument.domain.Car; +import com.iluwatar.abstractdocument.domain.HasModel; +import com.iluwatar.abstractdocument.domain.HasParts; +import com.iluwatar.abstractdocument.domain.HasPrice; +import com.iluwatar.abstractdocument.domain.HasType; +import com.iluwatar.abstractdocument.domain.Part; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static junit.framework.TestCase.assertEquals; + +/** + * Test for Part and Car + */ +public class DomainTest { + + private static final String TEST_PART_TYPE = "test-part-type"; + private static final String TEST_PART_MODEL = "test-part-model"; + private static final long TEST_PART_PRICE = 0L; + + private static final String TEST_CAR_MODEL = "test-car-model"; + private static final long TEST_CAR_PRICE = 1L; + + @Test + public void shouldConstructPart() { + Map<String, Object> partProperties = new HashMap<>(); + partProperties.put(HasType.PROPERTY, TEST_PART_TYPE); + partProperties.put(HasModel.PROPERTY, TEST_PART_MODEL); + partProperties.put(HasPrice.PROPERTY, TEST_PART_PRICE); + Part part = new Part(partProperties); + + assertEquals(TEST_PART_TYPE, part.getType().get()); + assertEquals(TEST_PART_MODEL, part.getModel().get()); + assertEquals(TEST_PART_PRICE, part.getPrice().get()); + } + + @Test + public void shouldConstructCar() { + Map<String, Object> carProperties = new HashMap<>(); + carProperties.put(HasModel.PROPERTY, TEST_CAR_MODEL); + carProperties.put(HasPrice.PROPERTY, TEST_CAR_PRICE); + carProperties.put(HasParts.PROPERTY, Arrays.asList(new HashMap<>(), new HashMap<>())); + Car car = new Car(carProperties); + + assertEquals(TEST_CAR_MODEL, car.getModel().get()); + assertEquals(TEST_CAR_PRICE, car.getPrice().get()); + assertEquals(2, car.getParts().count()); + } + +} diff --git a/pom.xml b/pom.xml index 362519090..0e3318fb6 100644 --- a/pom.xml +++ b/pom.xml @@ -124,7 +124,7 @@ <module>mute-idiom</module> <module>mutex</module> <module>semaphore</module> - <module>hexagonal</module> + <module>hexagonal</module> <module>abstract-document</module> </modules>