View Javadoc
1   /*
2    * (c) Copyright 2006-2020 by rapiddweller GmbH & Volker Bergmann. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, is permitted under the terms of the
6    * GNU General Public License.
7    *
8    * For redistributing this software or a derivative work under a license other
9    * than the GPL-compatible Free Software License as defined by the Free
10   * Software Foundation or approved by OSI, you must first obtain a commercial
11   * license to this software product from rapiddweller GmbH & Volker Bergmann.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
15   * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
17   * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   */
26  
27  package com.rapiddweller.benerator.factory;
28  
29  import com.rapiddweller.benerator.Generator;
30  import com.rapiddweller.benerator.GeneratorProvider;
31  import com.rapiddweller.benerator.NonNullGenerator;
32  import com.rapiddweller.benerator.sample.ConstantGenerator;
33  import com.rapiddweller.benerator.wrapper.AlternativeGenerator;
34  import com.rapiddweller.benerator.wrapper.ConcatenatingGenerator;
35  import com.rapiddweller.benerator.wrapper.WrapperFactory;
36  import com.rapiddweller.common.CharSet;
37  import com.rapiddweller.common.CollectionUtil;
38  import com.rapiddweller.common.ConfigurationError;
39  import com.rapiddweller.format.regex.Choice;
40  import com.rapiddweller.format.regex.Factor;
41  import com.rapiddweller.format.regex.Group;
42  import com.rapiddweller.format.regex.Quantifier;
43  import com.rapiddweller.format.regex.RegexChar;
44  import com.rapiddweller.format.regex.RegexCharClass;
45  import com.rapiddweller.format.regex.RegexParser;
46  import com.rapiddweller.format.regex.RegexPart;
47  import com.rapiddweller.format.regex.RegexString;
48  import com.rapiddweller.format.regex.Sequence;
49  import com.rapiddweller.model.data.Uniqueness;
50  
51  import java.util.Objects;
52  
53  /**
54   * Creates generators for regular expressions and their sub parts.<br/>
55   * <br/>
56   * Created: 17.11.2007 16:30:09
57   *
58   * @author Volker Bergmann
59   */
60  public class RegexGeneratorFactory {
61  
62    /**
63     * Create non null generator.
64     *
65     * @param pattern the pattern
66     * @param factory the factory
67     * @return the non null generator
68     */
69    public static NonNullGenerator<String> create(String pattern, GeneratorFactory factory) {
70      return create(pattern, 0, null, Uniqueness.NONE, factory);
71    }
72  
73    /**
74     * Create non null generator.
75     *
76     * @param pattern    the pattern
77     * @param minLength  the min length
78     * @param maxLength  the max length
79     * @param uniqueness the uniqueness
80     * @param factory    the factory
81     * @return the non null generator
82     */
83    public static NonNullGenerator<String> create(String pattern, int minLength, Integer maxLength,
84                                                  Uniqueness uniqueness, GeneratorFactory factory) {
85      if (pattern == null) {
86        throw new IllegalArgumentException("Not a regular expression: null");
87      }
88      RegexPart regex = new RegexParser().parseRegex(pattern);
89      return createFromObject(regex, minLength, maxLength, uniqueness, factory);
90    }
91  
92    // private helpers -------------------------------------------------------------------------------------------------
93  
94    /**
95     * Create from object non null generator.
96     *
97     * @param part       the part
98     * @param minLength  the min length
99     * @param maxLength  the max length
100    * @param uniqueness the uniqueness
101    * @param factory    the factory
102    * @return the non null generator
103    */
104   static NonNullGenerator<String> createFromObject(RegexPart part, int minLength, Integer maxLength,
105                                                    Uniqueness uniqueness, GeneratorFactory factory) {
106     if (part instanceof Factor) {
107       return createFromFactor((Factor) part, minLength, maxLength, uniqueness, factory);
108     } else {
109       return createFromObject(part, 1, 1, minLength, maxLength, uniqueness, factory);
110     }
111   }
112 
113   private static NonNullGenerator<String> createFromFactor(Factor part, int minLength, Integer maxLength,
114                                                            Uniqueness uniqueness, GeneratorFactory factory) {
115     Quantifier quantifier = part.getQuantifier();
116     int minQuant = quantifier.getMin();
117     Integer maxQuant = quantifier.getMax();
118     RegexPart atom = part.getAtom();
119     return createFromObject(atom, minQuant, maxQuant, minLength, maxLength, uniqueness, factory);
120   }
121 
122   private static NonNullGenerator<String> createFromObject(RegexPart object, int minQuant, Integer maxQuant,
123                                                            int minLength, Integer maxLength, Uniqueness uniqueness, GeneratorFactory factory) {
124     if (object instanceof Factor) {
125       return createFromFactor((Factor) object, minLength, maxLength, uniqueness, factory);
126     } else if (object instanceof RegexChar) {
127       return createFromCharacter(((RegexChar) object).getChar(), minQuant, maxQuant, minLength, maxLength,
128           uniqueness.isUnique(), factory);
129     } else if (object instanceof RegexCharClass) {
130       return createCharSetGenerator(((RegexCharClass) object).getCharSet(), minQuant, maxQuant, minLength, maxLength,
131           uniqueness, factory);
132     } else if (object instanceof Sequence) {
133       return createFromSequence((Sequence) object, minQuant, maxQuant, minLength, maxLength, uniqueness, factory);
134     } else if (object instanceof Group) {
135       return createFromGroup((Group) object, minQuant, maxQuant, minLength, maxLength, uniqueness, factory);
136     } else if (object instanceof Choice) {
137       return createFromChoice((Choice) object, minQuant, maxQuant, minLength, maxLength, uniqueness, factory);
138     } else if (object instanceof RegexString) {
139       return WrapperFactory.asNonNullGenerator(factory.createSingleValueGenerator(
140           ((RegexString) object).getString(), uniqueness.isUnique()));
141     } else if (object == null) {
142       return WrapperFactory.asNonNullGenerator(new ConstantGenerator<>(null, String.class));
143     } else {
144       throw new UnsupportedOperationException("Unsupported regex part type: " + object.getClass().getName());
145     }
146   }
147 
148   @SuppressWarnings("unchecked")
149   private static NonNullGenerator<String> createFromSequence(Sequence sequence, int minCount, Integer maxCount,
150                                                              int minLength, Integer maxLength, Uniqueness uniqueness, GeneratorFactory factory) {
151     RegexPart[] parts = sequence.getFactors();
152     Generator<String>[] componentGenerators = createComponentGenerators(
153         parts, maxLength, maxLength, uniqueness, factory);
154     Generator<String[]> partGenerator = factory.createCompositeArrayGenerator(
155         String.class, componentGenerators, uniqueness);
156     return WrapperFactory.asNonNullGenerator(new ConcatenatingGenerator(partGenerator));
157   }
158 
159   /**
160    * Create component generators non null generator [ ].
161    *
162    * @param parts              the parts
163    * @param maxComponentLength the max component length
164    * @param maxTotalLength     the max total length
165    * @param uniqueness         the uniqueness
166    * @param factory            the factory
167    * @return the non null generator [ ]
168    */
169   @SuppressWarnings("rawtypes")
170   static NonNullGenerator[] createComponentGenerators(RegexPart[] parts, Integer maxComponentLength,
171                                                       Integer maxTotalLength, Uniqueness uniqueness, GeneratorFactory factory) {
172     NonNullGenerator<?>[] components = new NonNullGenerator<?>[parts.length];
173     Integer remainingLength = maxTotalLength;
174     for (int i = 0; i < parts.length; i++) {
175       RegexPart part = parts[i];
176       Integer componentLength = part.maxLength();
177       if (componentLength != null && maxComponentLength != null) {
178         componentLength = Math.min(componentLength, maxComponentLength);
179       }
180       if (componentLength != null && remainingLength != null) {
181         componentLength = Math.min(componentLength, remainingLength);
182       }
183       components[i] = createFromObject(part, part.minLength(), componentLength, uniqueness, factory);
184       if (remainingLength != null) {
185         remainingLength -= part.minLength();
186         if (remainingLength < 0) {
187           throw new ConfigurationError("Remaining length is negative: " + remainingLength);
188         }
189       }
190     }
191     return components;
192   }
193 
194   @SuppressWarnings({"unchecked", "rawtypes"})
195   private static NonNullGenerator<String> createFromChoice(
196       final Choice choice, final int minCount, final int maxCount, final int minLength, final Integer maxLength,
197       final Uniqueness uniqueness, final GeneratorFactory factory) {
198     final RegexPart[] alternatives = choice.getAlternatives();
199     GeneratorProvider<String> generatorProvider = () -> {
200       final Generator[] altGens = createComponentGenerators(
201           alternatives, maxLength, null, uniqueness, factory);
202       return new AlternativeGenerator<String>(String.class, altGens);
203     };
204     return factory.createCompositeStringGenerator(generatorProvider, minCount, maxCount, uniqueness);
205   }
206 
207   private static NonNullGenerator<String> createFromGroup(
208       final Group group, final int minCount, final Integer maxCount,
209       final int minLength, final Integer maxLength,
210       final Uniqueness uniqueness, final GeneratorFactory factory) {
211     GeneratorProvider<String> partGeneratorProvider = () -> createFromObject(group.getRegex(), minLength, maxLength, uniqueness, factory);
212     return factory.createCompositeStringGenerator(partGeneratorProvider, minCount, maxCount, uniqueness);
213   }
214 
215   private static NonNullGenerator<String> createFromCharacter(char c, int minCount, Integer maxCount,
216                                                               int minLength, Integer maxLength, boolean unique, GeneratorFactory factory) {
217     DefaultsProvider defaultsProvider = factory.getDefaultsProvider();
218     int minReps = max(minLength, minCount, defaultsProvider.defaultMinLength());
219     int maxReps = min(maxLength, maxCount, defaultsProvider.defaultMaxLength());
220     return factory.createStringGenerator(CollectionUtil.toSet(c), minReps, maxReps, 1, null, (unique ? Uniqueness.ORDERED : Uniqueness.NONE));
221   }
222 
223   private static NonNullGenerator<String> createCharSetGenerator(
224       CharSet charSet, int minCount, Integer maxCount, int minLength, Integer maxLength,
225       Uniqueness uniqueness, GeneratorFactory factory) {
226     int min = Math.max(minCount, minLength);
227     Integer max = maxCount;
228     if (max == null) {
229       max = maxLength;
230     } else if (maxLength != null) {
231       max = Math.min(max, maxLength);
232     }
233     if (max == null) {
234       max = factory.getDefaultsProvider().defaultMaxLength();
235     }
236     return factory.createStringGenerator(charSet.getSet(), min, max, 1, null, uniqueness);
237   }
238 
239   private static int min(Integer v1, Integer v2, int defaultValue) {
240     if (v1 != null) {
241       if (v2 != null) {
242         return Math.min(v1, v2);
243       } else {
244         return v1;
245       }
246     } else {
247       return Objects.requireNonNullElse(v2, defaultValue);
248     }
249   }
250 
251   private static int max(Integer v1, Integer v2, int defaultValue) {
252     if (v1 != null) {
253       if (v2 != null) {
254         return Math.max(v1, v2);
255       } else {
256         return v1;
257       }
258     } else {
259       return Objects.requireNonNullElse(v2, defaultValue);
260     }
261   }
262 
263 }