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 de.cuioss.tools.string.MoreStrings.isEmpty; 020import static java.util.Objects.requireNonNull; 021 022import de.cuioss.test.valueobjects.property.PropertyMetadata; 023import de.cuioss.test.valueobjects.property.util.PropertyAccessStrategy; 024import lombok.AccessLevel; 025import lombok.EqualsAndHashCode; 026import lombok.Getter; 027import lombok.RequiredArgsConstructor; 028import lombok.ToString; 029import lombok.experimental.Delegate; 030 031/** 032 * Extensions of {@link PropertyMetadata} that deals with builder-specific 033 * aspects 034 * 035 * @author Oliver Wolff 036 */ 037@EqualsAndHashCode 038@ToString 039@RequiredArgsConstructor(access = AccessLevel.MODULE) 040public class BuilderMetadata implements PropertyMetadata { 041 042 @Delegate 043 private final PropertyMetadata delegate; 044 045 /** 046 * Used for builder testing / verifying: In case builderMethodPrefix is null the 047 * corresponding build method to be accessed for setting the value is the name 048 * attribute: {@link PropertyMetadata#getName()}, in case it is a concrete 049 * value, e.g. 'with' it will taken into account: withPropertName(). 050 */ 051 @Getter 052 private final String builderAddMethodName; 053 054 /** 055 * Similar to {@link BuilderMetadataBuilder#builderMethodName(String)} but for 056 * special cases described within 057 * {@link PropertyAccessStrategy#BUILDER_COLLECTION_AND_SINGLE_ELEMENT} There 058 * are two different steps for deriving the concrete method-name: 059 * <ul> 060 * <li>Base-name: In case 061 * {@link BuilderMetadataBuilder#builderSingleAddMethodName(String)} is not set 062 * it used {@link PropertyMetadata#getName()} as base name -> Overloading</li> 063 * <li>The base-name will prefixed if there is a builderPerfixed configured, see 064 * {@link BuilderMetadataBuilder#builderMethodPrefix(String)}</li> 065 * </ul> 066 */ 067 @Getter 068 private final String builderSingleAddMethodName; 069 070 static String prefixBuilderMethod(final String nameToBePrefixed, final String builderMethodPrefix) { 071 requireNonNull(emptyToNull(nameToBePrefixed), "nameToBePrefixed"); 072 if (isEmpty(builderMethodPrefix)) { 073 return nameToBePrefixed; 074 } 075 final var builder = new StringBuilder(builderMethodPrefix); 076 builder.append(Character.toUpperCase(nameToBePrefixed.charAt(0))); 077 builder.append(nameToBePrefixed.substring(1)); 078 return builder.toString(); 079 } 080 081 /** 082 * Builder for instances of {@link BuilderMetadata} 083 * 084 * @author Oliver Wolff 085 */ 086 public static class BuilderMetadataBuilder { 087 088 private String tempBuilderMethodName; 089 private String tempBuilderMethodPrefix; 090 private String tempBuilderSingleAddMethodName; 091 private PropertyMetadata tempDelegate; 092 093 /** 094 * In case this methodName is set it will be used directly for deriving the 095 * write-method. {@link #builderMethodPrefix(String)} will then ignored 096 * 097 * @param builderMethodName to be set 098 * @return the builder for {@link BuilderMetadata} 099 */ 100 public BuilderMetadataBuilder builderMethodName(final String builderMethodName) { 101 tempBuilderMethodName = builderMethodName; 102 return this; 103 } 104 105 /** 106 * Defines a delegate {@link PropertyMetadata} used for all attributes that are 107 * not builder specific, required. 108 * 109 * @param delegate to be set 110 * @return the builder for {@link BuilderMetadata} 111 */ 112 public BuilderMetadataBuilder delegateMetadata(final PropertyMetadata delegate) { 113 tempDelegate = delegate; 114 return this; 115 } 116 117 /** 118 * see {@link BuilderMetadata#getBuilderAddMethodName()} for details 119 * 120 * @param builderMethodPrefix to be set 121 * @return the builder for {@link BuilderMetadata} 122 */ 123 public BuilderMetadataBuilder builderMethodPrefix(final String builderMethodPrefix) { 124 tempBuilderMethodPrefix = emptyToNull(builderMethodPrefix); 125 return this; 126 } 127 128 /** 129 * Only needed for builder that deal with {@link Iterable} and single elements, 130 * see {@link PropertyAccessStrategy#BUILDER_COLLECTION_AND_SINGLE_ELEMENT} for 131 * details 132 * 133 * @param builderSingleAddMethodName to be set 134 * @return the builder for {@link BuilderMetadata} 135 */ 136 public BuilderMetadataBuilder builderSingleAddMethodName(final String builderSingleAddMethodName) { 137 tempBuilderSingleAddMethodName = emptyToNull(builderSingleAddMethodName); 138 return this; 139 } 140 141 /** 142 * @return the built {@link BuilderMetadata} 143 */ 144 public BuilderMetadata build() { 145 requireNonNull(tempDelegate, "delegate"); 146 147 final var tempPropertyName = tempDelegate.getName(); 148 149 var addMethodName = tempBuilderMethodName; 150 if (isEmpty(addMethodName)) { 151 addMethodName = prefixBuilderMethod(tempPropertyName, tempBuilderMethodPrefix); 152 } 153 154 var singleAddMethodName = tempBuilderSingleAddMethodName; 155 if (isEmpty(singleAddMethodName)) { 156 singleAddMethodName = prefixBuilderMethod(tempPropertyName, tempBuilderMethodPrefix); 157 } 158 159 return new BuilderMetadata(tempDelegate, addMethodName, singleAddMethodName); 160 } 161 } 162 163 /** 164 * @return a new instance of {@link BuilderMetadataBuilder} 165 */ 166 public static BuilderMetadataBuilder builder() { 167 return new BuilderMetadataBuilder(); 168 } 169 170 /** 171 * Simple converter for creating a {@link BuilderMetadata} from any given 172 * {@link PropertyMetadata} with defaults for the methods. 173 * 174 * @param metadata must not be null 175 * @return a {@link BuilderMetadata} computed from the given 176 * {@link PropertyMetadata} without further configuration 177 */ 178 public static BuilderMetadata wrapFromMetadata(final PropertyMetadata metadata) { 179 return builder().delegateMetadata(metadata).build(); 180 } 181}