/*
* Copyright 2008-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.springframework.data.repository.query.parser;
import static java.util.Arrays.*;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.repository.query.parser.Part.Type.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PropertyPath;
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree.OrPart;
/**
* Unit tests for {@link PartTree}.
*
* @author Oliver Gierke
* @author Phillip Webb
* @author Thomas Darimont
* @author Martin Baumgartner
* @author Christoph Strobl
* @author Mark Paluch
* @author Michael Cramer
*/
public class PartTreeUnitTests {
private String[] PREFIXES = { "find", "read", "get", "query", "stream", "count", "delete", "remove", "exists" };
@Test(expected = IllegalArgumentException.class)
public void rejectsNullSource() throws Exception {
new PartTree(null, getClass());
}
@Test(expected = IllegalArgumentException.class)
public void rejectsNullDomainClass() throws Exception {
new PartTree("test", null);
}
@Test(expected = IllegalArgumentException.class)
public void rejectsMultipleOrderBy() throws Exception {
partTree("firstnameOrderByLastnameOrderByFirstname");
}
@Test
public void parsesSimplePropertyCorrectly() throws Exception {
PartTree partTree = partTree("firstname");
assertPart(partTree, parts("firstname"));
}
@Test
public void parsesAndPropertiesCorrectly() throws Exception {
PartTree partTree = partTree("firstnameAndLastname");
assertPart(partTree, parts("firstname", "lastname"));
assertThat(partTree.getSort().isSorted()).isFalse();
}
@Test
public void parsesOrPropertiesCorrectly() throws Exception {
PartTree partTree = partTree("firstnameOrLastname");
assertPart(partTree, parts("firstname"), parts("lastname"));
assertThat(partTree.getSort().isSorted()).isFalse();
}
@Test
public void parsesCombinedAndAndOrPropertiesCorrectly() throws Exception {
PartTree tree = partTree("firstnameAndLastnameOrLastname");
assertPart(tree, parts("firstname", "lastname"), parts("lastname"));
}
@Test
public void hasSortIfOrderByIsGiven() throws Exception {
assertThat(partTree("firstnameOrderByLastnameDesc").getSort()).isEqualTo(Sort.by("lastname").descending());
}
@Test
public void hasSortIfOrderByIsGivenWithAllIgnoreCase() throws Exception {
hasSortIfOrderByIsGivenWithAllIgnoreCase("firstnameOrderByLastnameDescAllIgnoreCase");
hasSortIfOrderByIsGivenWithAllIgnoreCase("firstnameOrderByLastnameDescAllIgnoringCase");
hasSortIfOrderByIsGivenWithAllIgnoreCase("firstnameAllIgnoreCaseOrderByLastnameDesc");
}
private void hasSortIfOrderByIsGivenWithAllIgnoreCase(String source) throws Exception {
PartTree partTree = partTree(source);
assertThat(partTree.getSort()).isEqualTo(Sort.by("lastname").descending());
}
@Test
public void detectsDistinctCorrectly() throws Exception {
for (String prefix : PREFIXES) {
detectsDistinctCorrectly(prefix + "DistinctByLastname", true);
detectsDistinctCorrectly(prefix + "UsersDistinctByLastname", true);
detectsDistinctCorrectly(prefix + "DistinctUsersByLastname", true);
detectsDistinctCorrectly(prefix + "UsersByLastname", false);
detectsDistinctCorrectly(prefix + "ByLastname", false);
// Check it's non-greedy (would strip everything until Order*By*
// otherwise)
PartTree tree = detectsDistinctCorrectly(prefix + "ByLastnameOrderByFirstnameDesc", false);
assertThat(tree.getSort()).isEqualTo(Sort.by("firstname").descending());
}
}
private PartTree detectsDistinctCorrectly(String source, boolean expected) {
PartTree tree = partTree(source);
assertThat(tree.isDistinct()).isEqualTo(expected);
return tree;
}
@Test
public void parsesWithinCorrectly() {
PartTree tree = partTree("findByLocationWithin");
for (Part part : tree.getParts()) {
assertThat(part.getType()).isEqualTo(Type.WITHIN);
assertThat(part.getProperty()).isEqualTo(newProperty("location"));
}
}
@Test
public void parsesNearCorrectly() {
assertType(Collections.singletonList("locationNear"), NEAR, "location");
}
@Test
public void supportToStringWithoutSortOrder() throws Exception {
assertType(Collections.singletonList("firstname"), SIMPLE_PROPERTY, "firstname");
}
@Test
public void supportToStringWithSortOrder() throws Exception {
PartTree tree = partTree("firstnameOrderByLastnameDesc");
assertThat(tree.toString()).isEqualTo("firstname SIMPLE_PROPERTY (1): [Is, Equals] NEVER Order By lastname: DESC");
}
@Test
public void detectsIgnoreAllCase() throws Exception {
detectsIgnoreAllCase("firstnameOrderByLastnameDescAllIgnoreCase", IgnoreCaseType.WHEN_POSSIBLE);
detectsIgnoreAllCase("firstnameOrderByLastnameDescAllIgnoringCase", IgnoreCaseType.WHEN_POSSIBLE);
detectsIgnoreAllCase("firstnameAllIgnoreCaseOrderByLastnameDesc", IgnoreCaseType.WHEN_POSSIBLE);
detectsIgnoreAllCase("getByFirstnameAllIgnoreCase", IgnoreCaseType.WHEN_POSSIBLE);
detectsIgnoreAllCase("getByFirstname", IgnoreCaseType.NEVER);
detectsIgnoreAllCase("firstnameOrderByLastnameDesc", IgnoreCaseType.NEVER);
}
private void detectsIgnoreAllCase(String source, IgnoreCaseType expected) throws Exception {
PartTree tree = partTree(source);
for (Part part : tree.getParts()) {
assertThat(part.shouldIgnoreCase()).isEqualTo(expected);
}
}
@Test
public void detectsSpecificIgnoreCase() throws Exception {
PartTree tree = partTree("findByFirstnameIgnoreCaseAndLastname");
assertPart(tree, parts("firstnameIgnoreCase", "lastname"));
Iterator<Part> parts = tree.getParts().iterator();
assertThat(parts.next().shouldIgnoreCase()).isEqualTo(IgnoreCaseType.ALWAYS);
assertThat(parts.next().shouldIgnoreCase()).isEqualTo(IgnoreCaseType.NEVER);
}
@Test
public void detectsSpecificIgnoringCase() throws Exception {
PartTree tree = partTree("findByFirstnameIgnoringCaseAndLastname");
assertPart(tree, parts("firstnameIgnoreCase", "lastname"));
Iterator<Part> parts = tree.getParts().iterator();
assertThat(parts.next().shouldIgnoreCase()).isEqualTo(IgnoreCaseType.ALWAYS);
assertThat(parts.next().shouldIgnoreCase()).isEqualTo(IgnoreCaseType.NEVER);
}
@Test // DATACMNS-78
public void parsesLessThanEqualCorrectly() {
assertType(Arrays.asList("lastnameLessThanEqual", "lastnameIsLessThanEqual"), LESS_THAN_EQUAL, "lastname");
}
@Test // DATACMNS-78
public void parsesGreaterThanEqualCorrectly() {
assertType(Arrays.asList("lastnameGreaterThanEqual", "lastnameIsGreaterThanEqual"), GREATER_THAN_EQUAL, "lastname");
}
@Test
public void returnsAllParts() {
PartTree tree = partTree("findByLastnameAndFirstname");
assertPart(tree, parts("lastname", "firstname"));
}
@Test
public void returnsAllPartsOfType() {
PartTree tree = partTree("findByLastnameAndFirstnameGreaterThan");
Collection<Part> parts = toCollection(tree.getParts(Type.SIMPLE_PROPERTY));
assertThat(parts).containsExactly(part("lastname"));
parts = toCollection(tree.getParts(Type.GREATER_THAN));
assertThat(parts).containsExactly(new Part("FirstnameGreaterThan", User.class));
}
@Test // DATACMNS-94
public void parsesExistsKeywordCorrectly() {
assertType(Collections.singletonList("lastnameExists"), EXISTS, "lastname", 0, false);
}
@Test // DATACMNS-94
public void parsesRegexKeywordCorrectly() {
assertType(asList("lastnameRegex", "lastnameMatchesRegex", "lastnameMatches"), REGEX, "lastname");
}
@Test // DATACMNS-107
public void parsesTrueKeywordCorrectly() {
assertType(asList("activeTrue", "activeIsTrue"), TRUE, "active", 0, false);
}
@Test // DATACMNS-107
public void parsesFalseKeywordCorrectly() {
assertType(asList("activeFalse", "activeIsFalse"), FALSE, "active", 0, false);
}
@Test // DATACMNS-111
public void parsesStartingWithKeywordCorrectly() {
assertType(asList("firstnameStartsWith", "firstnameStartingWith", "firstnameIsStartingWith"), STARTING_WITH,
"firstname");
}
@Test // DATACMNS-111
public void parsesEndingWithKeywordCorrectly() {
assertType(asList("firstnameEndsWith", "firstnameEndingWith", "firstnameIsEndingWith"), ENDING_WITH, "firstname");
}
@Test // DATACMNS-111
public void parsesContainingKeywordCorrectly() {
assertType(asList("firstnameIsContaining", "firstnameContains", "firstnameContaining"), CONTAINING, "firstname");
}
@Test // DATACMNS-141
public void parsesAfterKeywordCorrectly() {
assertType(asList("birthdayAfter", "birthdayIsAfter"), Type.AFTER, "birthday");
}
@Test // DATACMNS-141
public void parsesBeforeKeywordCorrectly() {
assertType(Arrays.asList("birthdayBefore", "birthdayIsBefore"), Type.BEFORE, "birthday");
}
@Test // DATACMNS-433
public void parsesLikeKeywordCorrectly() {
assertType(asList("activeLike", "activeIsLike"), LIKE, "active");
}
@Test // DATACMNS-433
public void parsesNotLikeKeywordCorrectly() {
assertType(asList("activeNotLike", "activeIsNotLike"), NOT_LIKE, "active");
}
@Test // DATACMNS-182
public void parsesContainingCorrectly() {
PartTree tree = new PartTree("findAllByLegalNameContainingOrCommonNameContainingAllIgnoringCase",
Organization.class);
assertPart(tree, new Part[] { new Part("legalNameContaining", Organization.class, true) },
new Part[] { new Part("commonNameContaining", Organization.class, true) });
}
@Test // DATACMNS-221
public void parsesSpecialCharactersCorrectly() {
PartTree tree = new PartTree("findByØreAndÅrOrderByÅrAsc", DomainObjectWithSpecialChars.class);
assertPart(tree, new Part[] { new Part("øre", DomainObjectWithSpecialChars.class),
new Part("år", DomainObjectWithSpecialChars.class) });
assertThat(tree.getSort().getOrderFor("år").isAscending()).isTrue();
}
@Test // DATACMNS-363
public void parsesSpecialCharactersOnlyCorrectly_Korean() {
PartTree tree = new PartTree("findBy이름And생일OrderBy생일Asc", DomainObjectWithSpecialChars.class);
assertPart(tree, new Part[] { new Part("이름", DomainObjectWithSpecialChars.class),
new Part("생일", DomainObjectWithSpecialChars.class) });
assertThat(tree.getSort().getOrderFor("생일").isAscending()).isTrue();
}
@Test // DATACMNS-363
public void parsesSpecialUnicodeCharactersMixedWithRegularCharactersCorrectly_Korean() {
PartTree tree = new PartTree("findBy이름AndOrderIdOrderBy생일Asc", DomainObjectWithSpecialChars.class);
assertPart(tree, new Part[] { new Part("이름", DomainObjectWithSpecialChars.class),
new Part("order.id", DomainObjectWithSpecialChars.class) });
assertThat(tree.getSort().getOrderFor("생일").isAscending()).isTrue();
}
@Test // DATACMNS-363
public void parsesNestedSpecialUnicodeCharactersMixedWithRegularCharactersCorrectly_Korean() {
PartTree tree = new PartTree( //
"findBy" + "이름" //
+ "And" + "OrderId" //
+ "And" + "Nested_이름" // we use _ here to mark the beginning of a new property reference "이름"
+ "Or" + "NestedOrderId" //
+ "OrderBy" + "생일" + "Asc",
DomainObjectWithSpecialChars.class);
Iterator<OrPart> parts = tree.iterator();
assertPartsIn(parts.next(),
new Part[] { //
new Part("이름", DomainObjectWithSpecialChars.class), //
new Part("order.id", DomainObjectWithSpecialChars.class), //
new Part("nested.이름", DomainObjectWithSpecialChars.class) //
});
assertPartsIn(parts.next(), new Part[] { //
new Part("nested.order.id", DomainObjectWithSpecialChars.class) //
});
assertThat(tree.getSort().getOrderFor("생일").isAscending()).isTrue();
}
@Test // DATACMNS-363
public void parsesNestedSpecialUnicodeCharactersMixedWithRegularCharactersCorrectly_KoreanNumbersSymbols() {
PartTree tree = new PartTree( //
"findBy" + "이름" //
+ "And" + "OrderId" //
+ "And" + "Anders" //
+ "And" + "Property1" //
+ "And" + "Øre" //
+ "And" + "År" //
+ "Or" + "NestedOrderId" //
+ "And" + "Nested_property1" // we use _ here to mark the beginning of a new property reference "이름"
+ "And" + "Property1" //
+ "OrderBy" + "생일" + "Asc",
DomainObjectWithSpecialChars.class);
Iterator<OrPart> parts = tree.iterator();
assertPartsIn(parts.next(),
new Part[] { //
new Part("이름", DomainObjectWithSpecialChars.class), //
new Part("order.id", DomainObjectWithSpecialChars.class), //
new Part("anders", DomainObjectWithSpecialChars.class), //
new Part("property1", DomainObjectWithSpecialChars.class), //
new Part("øre", DomainObjectWithSpecialChars.class), //
new Part("år", DomainObjectWithSpecialChars.class) //
});
assertPartsIn(parts.next(),
new Part[] { //
new Part("nested.order.id", DomainObjectWithSpecialChars.class), //
new Part("nested.property1", DomainObjectWithSpecialChars.class), //
new Part("property1", DomainObjectWithSpecialChars.class) //
});
assertThat(tree.getSort().getOrderFor("생일").isAscending()).isTrue();
}
@Test // DATACMNS-303
public void identifiesSimpleCountByCorrectly() {
PartTree tree = new PartTree("countByLastname", User.class);
assertThat(tree.isCountProjection()).isTrue();
}
@Test // DATACMNS-875
public void identifiesSimpleExistsByCorrectly() {
PartTree tree = new PartTree("existsByLastname", User.class);
assertThat(tree.isExistsProjection()).isEqualTo(true);
}
@Test // DATACMNS-399
public void queryPrefixShouldBeSupportedInRepositoryQueryMethods() {
PartTree tree = new PartTree("queryByFirstnameAndLastname", User.class);
Iterable<Part> parts = tree.getParts();
assertThat(parts).containsExactly(part("firstname"), part("lastname"));
}
@Test // DATACMNS-303
public void identifiesExtendedCountByCorrectly() {
PartTree tree = new PartTree("countUserByLastname", User.class);
assertThat(tree.isCountProjection()).isTrue();
}
@Test // DATACMNS-303
public void identifiesCountAndDistinctByCorrectly() {
PartTree tree = new PartTree("countDistinctUserByLastname", User.class);
assertThat(tree.isCountProjection()).isTrue();
assertThat(tree.isDistinct()).isTrue();
}
@Test // DATAJPA-324
public void resolvesPropertyPathFromGettersOnInterfaces() {
assertThat(new PartTree("findByCategoryId", Product.class)).isNotNull();
}
@Test // DATACMNS-368
public void detectPropertyWithOrKeywordPart() {
assertThat(new PartTree("findByOrder", Product.class)).isNotNull();
}
@Test // DATACMNS-368
public void detectPropertyWithAndKeywordPart() {
assertThat(new PartTree("findByAnders", Product.class)).isNotNull();
}
@Test // DATACMNS-368
public void detectPropertyPathWithOrKeywordPart() {
assertThat(new PartTree("findByOrderId", Product.class)).isNotNull();
}
@Test // DATACMNS-387
public void buildsPartTreeFromEmptyPredicateCorrectly() {
PartTree tree = new PartTree("findAllByOrderByLastnameAsc", User.class);
assertThat(tree.getParts()).isEmpty();
assertThat(tree.getSort()).isEqualTo(Sort.by("lastname").ascending());
}
@Test // DATACMNS-448
public void identifiesSimpleDeleteByCorrectly() {
PartTree tree = new PartTree("deleteByLastname", User.class);
assertThat(tree.isDelete()).isTrue();
}
@Test // DATACMNS-448
public void identifiesExtendedDeleteByCorrectly() {
PartTree tree = new PartTree("deleteUserByLastname", User.class);
assertThat(tree.isDelete()).isTrue();
}
@Test // DATACMNS-448
public void identifiesSimpleRemoveByCorrectly() {
PartTree tree = new PartTree("removeByLastname", User.class);
assertThat(tree.isDelete()).isTrue();
}
@Test // DATACMNS-448
public void identifiesExtendedRemoveByCorrectly() {
PartTree tree = new PartTree("removeUserByLastname", User.class);
assertThat(tree.isDelete()).isTrue();
}
@Test // DATACMNS-516
public void disablesFindFirstKImplicitIfNotPresent() {
assertLimiting("findByLastname", User.class, false, null);
}
@Test // DATACMNS-516
public void identifiesFindFirstImplicit() {
assertLimiting("findFirstByLastname", User.class, true, 1);
}
@Test // DATACMNS-516
public void identifiesFindFirst1Explicit() {
assertLimiting("findFirstByLastname", User.class, true, 1);
}
@Test // DATACMNS-516
public void identifiesFindFirstKExplicit() {
assertLimiting("findFirst10ByLastname", User.class, true, 10);
}
@Test // DATACMNS-516
public void identifiesFindFirstKUsersExplicit() {
assertLimiting("findFirst10UsersByLastname", User.class, true, 10);
}
@Test // DATACMNS-516
public void identifiesFindFirstKDistinctUsersExplicit() {
assertLimiting("findFirst10DistinctUsersByLastname", User.class, true, 10, true);
assertLimiting("findDistinctFirst10UsersByLastname", User.class, true, 10, true);
assertLimiting("findFirst10UsersDistinctByLastname", User.class, true, 10, true);
}
@Test // DATACMNS-516
public void identifiesFindTopImplicit() {
assertLimiting("findTopByLastname", User.class, true, 1);
}
@Test // DATACMNS-516
public void identifiesFindTop1Explicit() {
assertLimiting("findTop1ByLastname", User.class, true, 1);
}
@Test // DATACMNS-516
public void identifiesFindTopKExplicit() {
assertLimiting("findTop10ByLastname", User.class, true, 10);
}
@Test // DATACMNS-516
public void identifiesFindTopKUsersExplicit() {
assertLimiting("findTop10UsersByLastname", User.class, true, 10);
}
@Test // DATACMNS-516
public void identifiesFindTopKDistinctUsersExplicit() {
assertLimiting("findTop10DistinctUsersByLastname", User.class, true, 10, true);
assertLimiting("findDistinctTop10UsersByLastname", User.class, true, 10, true);
assertLimiting("findTop10UsersDistinctByLastname", User.class, true, 10, true);
}
@Test
public void shouldNotSupportLimitingCountQueries() {
assertLimiting("countFirst10DistinctUsersByLastname", User.class, false, null, true);
assertLimiting("countTop10DistinctUsersByLastname", User.class, false, null, true);
}
@Test // DATACMNS-875
public void shouldNotSupportLimitingExistQueries() {
assertLimiting("existsFirst10DistinctUsersByLastname", User.class, false, null, true);
assertLimiting("existsTop10DistinctUsersByLastname", User.class, false, null, true);
}
@Test // DATACMNS-581
public void parsesIsNotContainingCorrectly() throws Exception {
assertType(asList("firstnameIsNotContaining", "firstnameNotContaining", "firstnameNotContains"), NOT_CONTAINING,
"firstname");
}
@Test // DATACMNS-581
public void buildsPartTreeForNotContainingCorrectly() throws Exception {
PartTree tree = new PartTree("findAllByLegalNameNotContaining", Organization.class);
assertPart(tree, new Part[] { new Part("legalNameNotContaining", Organization.class) });
}
@Test // DATACMNS-750
public void doesNotFailOnPropertiesContainingAKeyword() {
PartTree partTree = new PartTree("findBySomeInfoIn", Category.class);
Iterable<Part> parts = partTree.getParts();
assertThat(parts).hasSize(1);
Part part = parts.iterator().next();
assertThat(part.getType()).isEqualTo(Type.IN);
assertThat(part.getProperty()).isEqualTo(PropertyPath.from("someInfo", Category.class));
}
@Test // DATACMNS-1007
public void parsesEmptyKeywordCorrectly() {
assertType(asList("friendsIsEmpty", "friendsEmpty"), IS_EMPTY, "friends", 0, false);
}
@Test // DATACMNS-1007
public void parsesNotEmptyKeywordCorrectly() {
assertType(asList("friendsIsNotEmpty", "friendsNotEmpty"), IS_NOT_EMPTY, "friends", 0, false);
}
@Test // DATACMNS-1007
public void parsesEmptyAsPropertyIfDifferentKeywordIsUsed() {
assertType(asList("emptyIsTrue"), TRUE, "empty", 0, false);
assertType(asList("emptyIs"), SIMPLE_PROPERTY, "empty", 1, true);
}
private static void assertLimiting(String methodName, Class<?> entityType, boolean limiting, Integer maxResults) {
assertLimiting(methodName, entityType, limiting, maxResults, false);
}
private static void assertLimiting(String methodName, Class<?> entityType, boolean limiting, Integer maxResults,
boolean distinct) {
PartTree tree = new PartTree(methodName, entityType);
assertThat(tree.isLimiting()).isEqualTo(limiting);
assertThat(tree.getMaxResults()).isEqualTo(maxResults);
assertThat(tree.isDistinct()).isEqualTo(distinct);
}
private static void assertType(Iterable<String> sources, Type type, String property) {
assertType(sources, type, property, 1, true);
}
private static void assertType(Iterable<String> sources, Type type, String property, int numberOfArguments,
boolean parameterRequired) {
for (String source : sources) {
Part part = part(source);
assertThat(part.getType()).isEqualTo(type);
assertThat(part.getProperty()).isEqualTo(newProperty(property));
assertThat(part.getNumberOfArguments()).isEqualTo(numberOfArguments);
assertThat(part.isParameterRequired()).isEqualTo(parameterRequired);
}
}
private static PartTree partTree(String source) {
return new PartTree(source, User.class);
}
private static Part part(String part) {
return new Part(part, User.class);
}
private static Part[] parts(String... part) {
Part[] parts = new Part[part.length];
for (int i = 0; i < parts.length; i++) {
parts[i] = part(part[i]);
}
return parts;
}
private static PropertyPath newProperty(String name) {
return PropertyPath.from(name, User.class);
}
private void assertPart(PartTree tree, Part[]... parts) {
Iterator<OrPart> orParts = tree.iterator();
for (Part[] part : parts) {
assertThat(orParts.hasNext()).isTrue();
assertPartsIn(orParts.next(), part);
}
assertThat(orParts.hasNext()).isFalse();
}
private void assertPartsIn(OrPart orPart, Part[] part) {
Iterator<Part> partIterator = orPart.iterator();
for (int k = 0; k < part.length; k++) {
assertThat(partIterator.hasNext()).as("Expected %d parts but have %d", part.length, k).isTrue();
Part next = partIterator.next();
assertThat(part[k]).as("Expected %s but got %s!", part[k], next).isEqualTo(next);
}
assertThat(partIterator.hasNext()).as("Too many parts!").isFalse();
}
private static <T> Collection<T> toCollection(Iterable<T> iterable) {
List<T> result = new ArrayList<>();
for (T element : iterable) {
result.add(element);
}
return result;
}
class User {
String firstname;
String lastname;
double[] location;
boolean active;
Date birthday;
List<User> friends;
boolean empty;
}
class Organization {
String commonName;
String legalName;
}
class DomainObjectWithSpecialChars {
String øre;
String år;
String 생일; // Birthday
String 이름; // Name
int property1;
Order order;
Anders anders;
DomainObjectWithSpecialChars nested;
}
interface Product {
Order getOrder(); // contains Or keyword
Anders getAnders(); // constains And keyword
Category getCategory();
}
interface Category {
Long getId();
String getSomeInfo();
}
interface Order {
Long getId();
}
interface Anders {
Long getId();
}
}