001/* 002 * Copyright 2023 the original author or authors. 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package de.cuioss.test.valueobjects.property.impl; 017 018import static de.cuioss.tools.string.MoreStrings.emptyToNull; 019import static java.util.Objects.requireNonNull; 020 021import java.lang.reflect.Array; 022import java.util.ArrayList; 023import java.util.List; 024 025import de.cuioss.test.generator.TypedGenerator; 026import de.cuioss.test.generator.impl.CollectionGenerator; 027import de.cuioss.test.generator.impl.PrimitiveArrayGenerators; 028import de.cuioss.test.valueobjects.property.PropertyMetadata; 029import de.cuioss.test.valueobjects.property.util.AssertionStrategy; 030import de.cuioss.test.valueobjects.property.util.CollectionType; 031import de.cuioss.test.valueobjects.property.util.PropertyAccessStrategy; 032import de.cuioss.tools.property.PropertyMemberInfo; 033import de.cuioss.tools.property.PropertyReadWrite; 034import de.cuioss.tools.string.Joiner; 035import lombok.AccessLevel; 036import lombok.EqualsAndHashCode; 037import lombok.Getter; 038import lombok.RequiredArgsConstructor; 039 040/** 041 * Gathers all information needed for dynamically creating / asserting 042 * properties. 043 * 044 * @author Oliver Wolff 045 */ 046@RequiredArgsConstructor(access = AccessLevel.MODULE) 047@EqualsAndHashCode(exclude = "generator", doNotUseGetters = true) 048public class PropertyMetadataImpl implements PropertyMetadata { 049 050 @Getter 051 private final String name; 052 053 @Getter 054 private final TypedGenerator<?> generator; 055 056 @Getter 057 private final boolean defaultValue; 058 059 @Getter 060 private final Class<?> propertyClass; 061 062 private final Class<?> actualClass; 063 064 @Getter 065 private final boolean required; 066 067 @Getter 068 private final PropertyAccessStrategy propertyAccessStrategy; 069 070 @Getter 071 private final CollectionType collectionType; 072 073 @Getter 074 private final PropertyMemberInfo propertyMemberInfo; 075 076 @Getter 077 private final PropertyReadWrite propertyReadWrite; 078 079 @Getter 080 private final AssertionStrategy assertionStrategy; 081 082 @Override 083 public Object next() { 084 switch (collectionType) { 085 case NO_ITERABLE: 086 return generator.next(); 087 case ARRAY_MARKER: 088 if (!propertyClass.isPrimitive()) { 089 return resolveCollectionGenerator().list().toArray(); 090 } 091 return PrimitiveArrayGenerators.resolveForType(getPropertyClass()).next(); 092 default: 093 return collectionType.nextIterable(resolveCollectionGenerator()); 094 } 095 } 096 097 @Override 098 public CollectionGenerator<?> resolveCollectionGenerator() { 099 return new CollectionGenerator<>(generator); 100 } 101 102 @Override 103 public Class<?> resolveActualClass() { 104 return actualClass; 105 } 106 107 /** 108 * @author Oliver Wolff 109 */ 110 public static class PropertyMetadataBuilder { 111 112 private TypedGenerator<?> tempGenerator; 113 private String tempName; 114 private boolean tempDefaultValue = false; 115 private Class<?> tempPropertyClass; 116 private boolean tempRequired = false; 117 private PropertyMemberInfo tempPropertyMemberInfo = PropertyMemberInfo.DEFAULT; 118 private CollectionType tempCollectionType = CollectionType.NO_ITERABLE; 119 private PropertyAccessStrategy tempPropertyAccessStrategy = PropertyAccessStrategy.BEAN_PROPERTY; 120 private PropertyReadWrite tempPropertyReadWrite = PropertyReadWrite.READ_WRITE; 121 private AssertionStrategy tempAssertionStrategy = AssertionStrategy.DEFAULT; 122 123 /** 124 * @param propertyAccessStrategy to be set 125 * @return the builder for {@link PropertyMetadataImpl} 126 */ 127 public PropertyMetadataBuilder propertyAccessStrategy(final PropertyAccessStrategy propertyAccessStrategy) { 128 tempPropertyAccessStrategy = propertyAccessStrategy; 129 return this; 130 } 131 132 /** 133 * @param propertyReadWrite to be set 134 * @return the builder for {@link PropertyMetadataImpl} 135 */ 136 public PropertyMetadataBuilder propertyReadWrite(final PropertyReadWrite propertyReadWrite) { 137 tempPropertyReadWrite = propertyReadWrite; 138 return this; 139 } 140 141 /** 142 * @param assertionStrategy to be set 143 * @return the builder for {@link PropertyMetadataImpl} 144 */ 145 public PropertyMetadataBuilder assertionStrategy(final AssertionStrategy assertionStrategy) { 146 tempAssertionStrategy = assertionStrategy; 147 return this; 148 } 149 150 /** 151 * In case you have a {@link TypedGenerator} it will be implicitly set as 152 * {@link PropertyMetadataImpl#getGenerator()} and 153 * {@link PropertyMetadataImpl#getPropertyClass()} 154 * 155 * @param typedGenerator to be set 156 * @return the builder for {@link PropertyMetadataImpl} 157 */ 158 public PropertyMetadataBuilder generator(final TypedGenerator<?> typedGenerator) { 159 tempGenerator = typedGenerator; 160 tempPropertyClass = typedGenerator.getType(); 161 return this; 162 } 163 164 /** 165 * @see PropertyMetadata#getName() 166 * @param name to be set. must not be null nor empty 167 * @return the builder for {@link PropertyMetadataImpl} 168 */ 169 public PropertyMetadataBuilder name(final String name) { 170 tempName = emptyToNull(name); 171 return this; 172 } 173 174 /** 175 * @param defaultValue to be set 176 * @return the builder for {@link PropertyMetadataImpl} 177 */ 178 public PropertyMetadataBuilder defaultValue(final boolean defaultValue) { 179 tempDefaultValue = defaultValue; 180 return this; 181 } 182 183 /** 184 * @see PropertyMetadata#getCollectionType() 185 * @param collectionType 186 * @return the builder for {@link PropertyMetadataImpl} 187 */ 188 public PropertyMetadataBuilder collectionType(final CollectionType collectionType) { 189 tempCollectionType = collectionType; 190 return this; 191 } 192 193 /** 194 * @param propertyClass to be set 195 * @return the builder for {@link PropertyMetadataImpl} 196 */ 197 public PropertyMetadataBuilder propertyClass(final Class<?> propertyClass) { 198 tempPropertyClass = propertyClass; 199 return this; 200 } 201 202 /** 203 * @param required to be set 204 * @return the builder for {@link PropertyMetadataImpl} 205 */ 206 public PropertyMetadataBuilder required(final boolean required) { 207 tempRequired = required; 208 return this; 209 } 210 211 /** 212 * @see PropertyMetadata#getPropertyMemberInfo() 213 * @param propertyMemberInfo to be set. must not be null 214 * @return the builder for {@link PropertyMetadataImpl} 215 */ 216 public PropertyMetadataBuilder propertyMemberInfo(final PropertyMemberInfo propertyMemberInfo) { 217 requireNonNull(propertyMemberInfo, "objectMemberInfo"); 218 tempPropertyMemberInfo = propertyMemberInfo; 219 return this; 220 } 221 222 /** 223 * @return the built {@link PropertyMetadataImpl} 224 */ 225 public PropertyMetadataImpl build() { 226 requireNonNull(tempName, "name"); 227 requireNonNull(tempGenerator, "generator"); 228 requireNonNull(tempPropertyClass, "propertyClass"); 229 230 Class<?> actualClass = tempPropertyClass; 231 if (!CollectionType.NO_ITERABLE.equals(tempCollectionType)) { 232 if (CollectionType.ARRAY_MARKER.equals(tempCollectionType)) { 233 actualClass = Array.newInstance(tempPropertyClass, 0).getClass(); 234 } else { 235 actualClass = tempCollectionType.getIterableType(); 236 } 237 238 } 239 240 return new PropertyMetadataImpl(tempName, tempGenerator, tempDefaultValue, tempPropertyClass, actualClass, 241 tempRequired, tempPropertyAccessStrategy, tempCollectionType, tempPropertyMemberInfo, 242 tempPropertyReadWrite, tempAssertionStrategy); 243 } 244 245 } 246 247 /** 248 * @return a new instance of {@link PropertyMetadataBuilder} 249 */ 250 public static PropertyMetadataBuilder builder() { 251 return new PropertyMetadataBuilder(); 252 } 253 254 /** 255 * @param copyFrom to be use as template, must not be null 256 * @return a new instance of {@link PropertyMetadataBuilder} with the content of 257 * copyFrom being already copied into 258 */ 259 public static PropertyMetadataBuilder builder(final PropertyMetadata copyFrom) { 260 final var builder = builder(); 261 requireNonNull(copyFrom); 262 builder.collectionType(copyFrom.getCollectionType()).defaultValue(copyFrom.isDefaultValue()); 263 builder.generator(copyFrom.getGenerator()).name(copyFrom.getName()); 264 builder.propertyAccessStrategy(copyFrom.getPropertyAccessStrategy()); 265 builder.propertyClass(copyFrom.getPropertyClass()); 266 builder.propertyMemberInfo(copyFrom.getPropertyMemberInfo()); 267 builder.propertyReadWrite(copyFrom.getPropertyReadWrite()); 268 builder.required(copyFrom.isRequired()); 269 builder.assertionStrategy(copyFrom.getAssertionStrategy()); 270 return builder; 271 } 272 273 @Override 274 public int compareTo(final PropertyMetadata other) { 275 return name.compareTo(other.getName()); 276 } 277 278 @Override 279 public String toString() { 280 final List<Object> elements = new ArrayList<>(); 281 282 elements.add("'" + getName() + "' (" + getPropertyClass() + ")"); 283 284 if (isRequired()) { 285 elements.add("required"); 286 } 287 if (isDefaultValue()) { 288 elements.add("defaultValued"); 289 } 290 elements.add(getGenerator()); 291 if (!CollectionType.NO_ITERABLE.equals(getCollectionType())) { 292 elements.add(getCollectionType()); 293 elements.add("actualClass: " + actualClass); 294 } 295 elements.add(getPropertyReadWrite()); 296 elements.add(getPropertyAccessStrategy()); 297 if (!AssertionStrategy.DEFAULT.equals(getAssertionStrategy())) { 298 elements.add(getAssertionStrategy()); 299 } 300 301 return Joiner.on(", ").join(elements); 302 } 303 304}