001/*
002 * Copyright 2015 SirWellington Tech.
003 *
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 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
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 ANYTIME KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package tech.sirwellington.alchemy.test.junit.runners;
018
019import java.lang.annotation.Retention;
020import java.lang.annotation.Target;
021import java.nio.ByteBuffer;
022import java.time.Instant;
023import java.util.Date;
024import java.util.List;
025import tech.sirwellington.alchemy.annotations.access.Internal;
026import tech.sirwellington.alchemy.annotations.access.NonInstantiable;
027import tech.sirwellington.alchemy.generator.AlchemyGenerator;
028import tech.sirwellington.alchemy.generator.BinaryGenerators;
029import tech.sirwellington.alchemy.generator.BooleanGenerators;
030import tech.sirwellington.alchemy.generator.CollectionGenerators;
031import tech.sirwellington.alchemy.generator.DateGenerators;
032import tech.sirwellington.alchemy.generator.NumberGenerators;
033import tech.sirwellington.alchemy.generator.TimeGenerators;
034
035import static java.lang.annotation.ElementType.FIELD;
036import static java.lang.annotation.RetentionPolicy.RUNTIME;
037import static tech.sirwellington.alchemy.generator.CollectionGenerators.listOf;
038import static tech.sirwellington.alchemy.generator.NumberGenerators.positiveIntegers;
039import static tech.sirwellington.alchemy.generator.NumberGenerators.positiveLongs;
040import static tech.sirwellington.alchemy.generator.ObjectGenerators.pojos;
041import static tech.sirwellington.alchemy.generator.StringGenerators.alphanumericString;
042import static tech.sirwellington.alchemy.test.Checks.Internal.checkNotNull;
043import static tech.sirwellington.alchemy.test.Checks.Internal.checkThat;
044
045/**
046 * Used in with the {@link AlchemyTestRunner}, this Annotations allows the Runtime Injection of {@link List} values, using
047 * {@link CollectionGenerators} from the {@link AlchemyGenerator} library.
048 *
049 * Example:
050 * <pre>
051 * {@code
052 * `@RunWith(AlchemyTestRunner.class)
053 *  public class ExampleTest
054 *  {
055 *
056 *    `@GenerateList(String.class)
057 *     private List<String> ids;
058 *
059 *    ...
060 *  }
061 * }
062 * </pre> Note, ticks (`) used to escape Javadocs.
063 *
064 * @see GenerateEnum
065 * @see GeneratePojo
066 *
067 * @author SirWellington
068 */
069@Target(FIELD)
070@Retention(RUNTIME)
071public @interface GenerateList
072{
073
074    /**
075     * Specify the Generic Type of the List. This is necessary since the type information is erased at Runtime.
076     *
077     * @return
078     */
079    Class<?> value();
080
081    /**
082     * The number of elements to include in the list. Defaults to 10. This number must be {@code > 0}.
083     *
084     * @return
085     */
086    int size() default 10;
087
088    @Internal
089    @NonInstantiable
090    static class Values
091    {
092
093        private Values() throws IllegalAccessException
094        {
095            throw new IllegalAccessException("cannot instantiate");
096        }
097
098        static AlchemyGenerator<List<?>> createGeneratorFor(GenerateList annotation) throws IllegalArgumentException
099        {
100            checkNotNull(annotation, "missing annotation");
101            int size = annotation.size();
102            checkThat(size > 0, "size must be > 0");
103
104            Class<?> genericType = annotation.value();
105            checkNotNull(genericType, "annotation is missing generic type information");
106
107            AlchemyGenerator<?> generator = determineGeneratorFor(genericType);
108
109            return () -> listOf(generator, size);
110        }
111
112        private static AlchemyGenerator<?> determineGeneratorFor(Class<?> genericType)
113        {
114            if (genericType == String.class)
115            {
116                return alphanumericString();
117            }
118
119            if (genericType == Integer.class)
120            {
121                return positiveIntegers();
122            }
123            
124            if (genericType == Long.class)
125            {
126                return positiveLongs();
127            }
128
129            if (genericType == Double.class)
130            {
131                return NumberGenerators.doubles(0, 10000);
132            }
133
134            if (Date.class.isAssignableFrom(genericType))
135            {
136                return DateGenerators.anyTime();
137            }
138
139            if (genericType == Instant.class)
140            {
141                return TimeGenerators.anytime();
142            }
143
144            if (genericType == Boolean.class)
145            {
146                return BooleanGenerators.booleans();
147            }
148
149            if (ByteBuffer.class.isAssignableFrom(genericType))
150            {
151                return BinaryGenerators.byteBuffers(1024);
152            }
153
154            return pojos(genericType);
155        }
156    }
157
158}