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