blob: 091180e41198fed9f39e5dd0b4f65521a595ced4 [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*/
package org.eclipse.osbp.utils.fillertext;
import java.sql.Date;
import java.sql.Time;
import java.util.HashMap;
import java.util.Locale;
import org.eclipse.osbp.utils.fillertext.provider.BaseProvider;
import org.eclipse.osbp.utils.fillertext.provider.DateProvider;
import org.eclipse.osbp.utils.fillertext.provider.TextProvider;
import org.joda.time.DateTime;
/**
* Provides filler texts (german: Blindtext) for different data types,
* depending on the locale set.
* <br><br>
* <b><u><i>Filler text</i> vs. <i>Mock object</i></u></b><br>
* There is an essential difference between <i>Filler text</i> and <i>Mock object</i>!
* <ul>
* <li><b><a href="https://en.wikipedia.org/wiki/Filler_text">Filler text</a> (german: <a href="https://de.wikipedia.org/wiki/Blindtext">Blindtext</a>)</b><br>
* [...] (also placeholder text or dummy text) is text that shares some characteristics of a real written text, but <b>is random or otherwise generated</b>. It may be used to display a sample of fonts, generate text for testing, or to spoof an e-mail spam filter. The process of using filler text is sometimes called Greeking, although <b>the text itself may be nonsense</b>, or largely Latin, as in Lorem ipsum.[...]<br>
* [...] Mit Hilfe des Blindtextes kann die Verteilung des Textes auf der Seite (Layout oder Satzspiegel) sowie Lesbarkeit und Platzbedarf der verwendeten Schriftarten (Typografie) beurteilt werden. Er <b>besteht aus einer mehr oder minder sinnlosen Folge von Wörtern, oft auch nur aus wortähnlichen Silbenfolgen</b>. Ein bekanntes Beispiel dafür ist das „lateinische“ Lorem ipsum.[...]
* </li>
* <li><b><a href="https://en.wikipedia.org/wiki/Mock_object">Mock object</a> (german: <a href="https://de.wikipedia.org/wiki/Mock-Objekt">Mock-Objekt</a>)</b><br>
* [...] are <b>simulated objects that mimic the behavior of real objects in controlled ways</b>. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.[...]<br>
* [...] implementieren die Schnittstellen, über die das zu testende Objekt auf seine Umgebung zugreift. Sie <b>stellen sicher, dass die erwarteten Methodenaufrufe vollständig, mit den korrekten Parametern und in der erwarteten Reihenfolge durchgeführt werden</b>. Das Mock-Objekt liefert keine Echtdaten zurück, sondern <b>vorher zum Testfall passend festgelegte Werte</b>. Das Mock-Objekt kann somit dazu verwendet werden, <b>ein bestimmtes Verhalten nachzustellen</b>.[...]
* </li>
* </ul>
*/
public class FillerTextProvider {
private static HashMap<Locale, FillerTextProvider> sProviders = new HashMap<>();
private final BaseProvider fBaseProducer;
private final DateProvider fDateProducer;
private final TextProvider fTextProducer;
private final Locale fLocale;
/**
* Get the provider for the given locale. The provider will be cached!
* @see #destroy()
* @param locale
* @return the provider
*/
public static FillerTextProvider get(Locale locale) {
FillerTextProvider provider = sProviders.get(locale);
if (provider == null) {
provider = new FillerTextProvider(locale);
sProviders.put(locale, provider);
}
return provider;
}
private FillerTextProvider(Locale locale) {
fLocale = locale;
fBaseProducer = new BaseProvider();
fDateProducer = new DateProvider(fBaseProducer);
fTextProducer = new TextProvider(fBaseProducer, fLocale);
}
/**
* The provider is removed from the cache
* @see #get(Locale)
*/
public void destroy() {
sProviders.remove(this);
}
/**
* The provider uses some internal objects to return consistent filled text,
* for example first, last, full name and email.<br>
* To force the provider to generate new internal objects, call {@link #reset()}!<br>
* This could be necessary, if more than one data row has to be generated.
*/
public void reset() {
// fPerson = null;
}
/**
* generate a filled text for the given {@link javax.sql.Types}
* @see #generate(FillerTextType, Object...)
* @param type any supported java.sql.Types
* @param params
* @return the generated filler text object
*/
public Object generate(int type, Object...params) {
return generate(FillerTextType.typeFor(type), params);
}
/**
* generate a filled text for the given type string
* @see #generate(FillerTextType, Object...)
* @param type string, can be any <u>case insensitive</u> name of {@link FillerTextType}
* @param params
* @return the generated filler text object
*/
public Object generate(String type, Object...params) {
return generate(FillerTextType.typeFor(type), params);
}
/**
* generate a filled text for the given {@link FillerTextType}
* @param type
* @param params, see source code and the corresponding methods for supported paramaters
* @return the generated filler text object
*/
public Object generate(FillerTextType type, Object...params) {
if ((params.length == 1) && (params[0] instanceof Object[])) {
params = (Object[]) params[0];
}
switch(type) {
case TIMESTAMP:
return timestamp(params);
case DATE:
return date(params);
case TIME:
return time();
case SIGNEDINTEGER:
return signedinteger(params);
case UNSIGNEDINTEGER:
return unsignedinteger(params);
case SIGNEDDOUBLE:
case SIGNEDDECIMAL:
return signeddouble(params);
case UNSIGNEDDOUBLE:
case UNSIGNEDDECIMAL:
return unsigneddouble(params);
case TRUEORFALSE:
return trueOrFalse();
case BLOB:
case PARAGRAPH:
if ((params.length == 1) && (params[0] instanceof Integer)) {
return paragraphs((int) params[0]);
}
else {
return paragraph();
}
case OTHER:
case SENTENCE:
if ((params.length == 1) && (params[0] instanceof Integer)) {
return sentences((int) params[0]);
}
else {
return sentence();
}
case WORD:
case TEXT:
default:
if ((params.length == 1) && (params[0] instanceof Integer)) {
return words((int) params[0]);
}
else {
return word();
}
}
}
/**
* @see FillerTextType#UNSIGNEDINTEGER
* @param params may be<ul>
* <li>none - generate an unsigned (positive) integer in the range 0..100</li>
* <li>(int)max - generate an unsigned (positive) integer in the range 0..max; same as {@link #unsignedinteger(int)}</li>
* <li>(int)min,(int)max - generate an unsigned (positive) integer in the range min..max; same as {@link #unsignedinteger(int, int)}</li>
* <li>(int)min,(int)max,(int)round - generate an unsigned (positive) integer in the range min..max rounding to round; same as {@link #unsignedinteger(int, int, int)}</li>
* </ul>
* @return the generated filler text object
*/
public int unsignedinteger(Object...params) {
if ((params.length == 1) && (params[0] instanceof Object[])) {
params = (Object[]) params[0];
}
int length = 0;
while ((params.length > length) && (params[length] instanceof Integer)) {
length++;
}
switch (length) {
case 0:
return unsignedinteger(100);
case 1:
return unsignedinteger((int)params[0]);
case 2:
return unsignedinteger((int)params[0], (int)params[1]);
case 3:
default:
return unsignedinteger((int)params[0], (int)params[1], (int)params[2]);
}
}
/**
* @see FillerTextType#UNSIGNEDINTEGER
* @param max - generate an unsigned (positive) integer in the range 0..max
* @return the generated filler text object
*/
public int unsignedinteger(int max) {
return unsignedinteger(0, Math.max(0, max));
}
/**
* generate an unsigned (positive) integer in the range min..max
* @see FillerTextType#UNSIGNEDINTEGER
* @param min
* @param max
* @return the generated filler text object
*/
public int unsignedinteger(int min, int max) {
return unsignedinteger(min, max, 1);
}
/**
* generate an unsigned (positive) integer in the range min..max
* @see FillerTextType#UNSIGNEDINTEGER
* @param min
* @param max
* @param round
* @return the generated filler text object
*/
public int unsignedinteger(int min, int max, int round) {
int result = fBaseProducer.randomBetween(Math.min(min, max), Math.max(min, max));
if (round > 1) {
result = ((result + round/2) / round) * round;
}
return result;
}
/**
* @see FillerTextType#SIGNEDINTEGER
* @param params may be<ul>
* <li>none - generate a signed (positive or negative) integer in the range -100..100</li>
* <li>(int)max - generate a signed (positive or negative) integer in the range -max..max; same as {@link #signedinteger(int)}</li>
* <li>(int)min,(int)max - generate a signed (positive or negative) integer in the range min..max; same as {@link #signedinteger(int, int)}</li>
* <li>(int)min,(int)max,(int)round - generate an unsigned (positive) integer in the range min..max rounding to round; same as {@link #signedinteger(int, int, int)}</li>
* </ul>
* @return the generated filler text object
*/
public int signedinteger(Object...params) {
if ((params.length == 1) && (params[0] instanceof Object[])) {
params = (Object[]) params[0];
}
int length = 0;
while ((params.length > length) && (params[length] instanceof Integer)) {
length++;
}
switch (length) {
case 0:
return signedinteger(100);
case 1:
return signedinteger((int)params[0]);
case 2:
return signedinteger((int)params[0], (int)params[1]);
case 3:
default:
return signedinteger((int)params[0], (int)params[1], (int)params[2]);
}
}
/**
* @see FillerTextType#SIGNEDINTEGER
* @param max - generate a signed (positive or negative) integer in the range -max..max
* @return the generated filler text object
*/
public int signedinteger(int range) {
return signedinteger(-Math.abs(range), Math.abs(range));
}
/**
* generate a signed (positive or negative) integer in the range min..max
* @see FillerTextType#SIGNEDINTEGER
* @param min
* @param max
* @return the generated filler text object
*/
public int signedinteger(int min, int max) {
return signedinteger(min, max, 1);
}
/**
* generate a signed (positive or negative) integer in the range min..max
* @see FillerTextType#SIGNEDINTEGER
* @param min
* @param max
* @param round, must be positive
* @return the generated filler text object
*/
public int signedinteger(int min, int max, int round) {
int result = fBaseProducer.randomBetween(Math.min(min, max), Math.max(min, max));
if (round > 1) {
result = ((result + round/2) / round) * round;
}
return result;
}
/**
* @see FillerTextType#UNSIGNEDDOUBLE
* @param params may be<ul>
* <li>none - generate an unsigned (positive) double in the range 0.00..100.00 with two decimal digits</li>
* <li>(int)decimals - generate an unsigned (positive) double in the range 0.00..100.00 with (decimal) decimal digits; same as {@link #unsigneddouble(int)}</li>
* <li>(int)decimals,(double)max - generate an unsigned (positive) double in the range 0..max with (decimal) decimal digits; same as {@link #unsigneddouble(int,double)}</li>
* <li>(int)decimals,(double)min,(double)max - generate an unsigned (positive) double in the range min..max with (decimal) decimal digits; same as {@link #unsigneddouble(int,double,double)}</li>
* <li>(int)decimals,(double)min,(double)max,(double)round - generate an unsigned (positive) double in the range min..max with (decimal) decimal digits rounding; same as {@link #unsigneddouble(int,double,double,double)}</li>
* </ul>
* @return the generated filler text object
*/
public double unsigneddouble(Object...params) {
if ((params.length == 1) && (params[0] instanceof Object[])) {
params = (Object[]) params[0];
}
int length = 0;
int decimals = 2;
if ((params.length > 0) && (params[0] instanceof Integer)) {
decimals = (int)params[0];
while ((params.length > length+1) && (params[length+1] instanceof Double)) {
length++;
}
}
switch (length) {
case 0:
return unsigneddouble(decimals);
case 1:
return unsigneddouble(decimals, (double)params[1]);
case 2:
return unsigneddouble(decimals, (double)params[1], (double)params[2]);
case 3:
default:
return unsigneddouble(decimals, (double)params[1], (double)params[2], (double)params[3]);
}
}
/**
* @see FillerTextType#UNSIGNEDDOUBLE
* @param decimals - generate an unsigned (positive) double in the range 0.00..100.00 with (decimal) decimal digits
* @return the generated filler text object
*/
public double unsigneddouble(int decimals) {
return unsigneddouble(decimals, 100);
}
/**
* generate an unsigned (positive) double in the range 0..max with (decimal) decimal digits
* @see FillerTextType#UNSIGNEDDOUBLE
* @param decimals
* @param max
* @return the generated filler text object
*/
public double unsigneddouble(int decimals, double max) {
return unsigneddouble(decimals, 0, Math.max(0, max));
}
/**
* generate an unsigned (positive) double in the range min..max with (decimal) decimal digits
* @see FillerTextType#UNSIGNEDDOUBLE
* @param decimals
* @param min
* @param max
* @return the generated filler text object
*/
public double unsigneddouble(int decimals, double min, double max) {
return unsigneddouble(decimals, min, max, 0.0);
}
/**
* generate an unsigned (positive) double in the range min..max with (decimal) decimal digits
* @see FillerTextType#UNSIGNEDDOUBLE
* @param decimals
* @param min
* @param max
* @param round; must be > 0.0
* @return the generated filler text object
*/
public double unsigneddouble(int decimals, double min, double max, double round) {
double factor = Math.pow(10.0, Math.max(0, decimals));
double result = fBaseProducer.randomBetween(Math.min(min, max), Math.max(min, max));
if (round > 0.0) {
result = Math.round((result + round/2) / round) * round;
}
return Math.rint (result * factor) / factor;
}
/**
* @see FillerTextType#SIGNEDDOUBLE
* @param params may be<ul>
* <li>none - generate a signed (positive or negative) double in the range 0.00..100.00 with two decimal digits</li>
* <li>(int)decimals - generate a signed (positive or negative) double in the range 0.00..100.00 with (decimal) decimal digits; same as {@link #signeddouble(int)}</li>
* <li>(int)decimals,(double)max - generate signed (positive or negative) double in the range 0..max with (decimal) decimal digits; same as {@link #signeddouble(int,double)}</li>
* <li>(int)decimals,(double)min,(double)max - generate a signed (positive or negative) double in the range min..max with (decimal) decimal digits; same as {@link #signeddouble(int,double,double)}</li>
* <li>(int)decimals,(double)min,(double)max,(double)round - generate an signed (positive or negative) double in the range min..max with (decimal) decimal digits rounding; same as {@link #signeddouble(int,double,double,double)}</li>
* </ul>
* @return the generated filler text object
*/
public double signeddouble(Object...params) {
if ((params.length == 1) && (params[0] instanceof Object[])) {
params = (Object[]) params[0];
}
int length = 0;
int decimals = 2;
if ((params.length > 0) && (params[0] instanceof Integer)) {
decimals = (int)params[0];
while ((params.length > length+1) && (params[length+1] instanceof Double)) {
length++;
}
}
switch (length) {
case 0:
return signeddouble(decimals);
case 1:
return signeddouble(decimals, (double)params[1]);
case 2:
return signeddouble(decimals, (double)params[1], (double)params[2]);
case 3:
default:
return signeddouble(decimals, (double)params[1], (double)params[2], (double)params[3]);
}
}
/**
* @see FillerTextType#SIGNEDDOUBLE
* @param decimals - generate a signed (positive or negative) double in the range 0.00..100.00 with (decimal) decimal digits
* @return the generated filler text object
*/
public double signeddouble(int decimals) {
return signeddouble(decimals, 100);
}
/**
* generate signed (positive or negative) double in the range 0..max with (decimal) decimal digits
* @see FillerTextType#SIGNEDDOUBLE
* @param decimals
* @param max
* @return the generated filler text object
*/
public double signeddouble(int decimals, double range) {
return signeddouble(decimals, -Math.abs(range), Math.abs(range));
}
/**
* generate a signed (positive or negative) double in the range min..max with (decimal) decimal digits
* @see FillerTextType#SIGNEDDOUBLE
* @param decimals
* @param max
* @param min
* @return the generated filler text object
*/
public double signeddouble(int decimals, double min, double max) {
return signeddouble(decimals, min, max, 0.0);
}
/**
* generate a signed (positive or negative) double in the range min..max with (decimal) decimal digits
* @see FillerTextType#SIGNEDDOUBLE
* @param decimals
* @param max
* @param min
* @param round; must be > 0.0
* @return the generated filler text object
*/
public double signeddouble(int decimals, double min, double max, double round) {
double factor = Math.pow(10.0, Math.max(0, decimals));
double result = fBaseProducer.randomBetween(Math.min(min, max), Math.max(min, max));
if (round > 0.0) {
result = Math.round((result + round/2) / round) * round;
}
return Math.rint (result * factor) / factor;
}
/**
* @see FillerTextType#TRUEORFALSE
* @return the generated filler text object
*/
public Boolean trueOrFalse() {
return fBaseProducer.trueOrFalse();
}
/**
* @see FillerTextType#TIMESTAMP
* @param params may be<ul>
* <li>none - same as {@link #timestamp(int)} with parameter 0</li>
* <li>(int)yearsToPastOrInFuture - generate a timestamp with a range of years from now; same as {@link #timestamp(int)}</li>
* <li>(int)from,(int)to - generate a timestamp between two years including both years; same as {@link #timestamp(int, int)}</li>
* </ul>
* @return the generated filler text object
*/
public DateTime timestamp(Object...params) {
if ((params.length == 1) && (params[0] instanceof Object[])) {
params = (Object[]) params[0];
}
int length = 0;
while ((params.length > length) && (params[length] instanceof Integer)) {
length++;
}
switch (length) {
case 0:
return timestamp(0);
case 1:
return timestamp((int) params[0]);
case 2:
default:
return timestamp((int) params[0], (int) params[1]);
}
}
/**
* generate a timestamp between two years including both years
* @see FillerTextType#TIMESTAMP
* @param from may be<ul>
* <li>either a year after 1900</li>
* <li>otherwise it will be interpreted as an offset to the actual year</li>
* </ul>
* @param to may be<ul>
* <li>either a year after 1900</li>
* <li>otherwise it will be interpreted as an offset to the actual year</li>
* </ul>
* @return the generated filler text object
*/
public DateTime timestamp(int from, int to) {
if (from < 1900) {
from += DateTime.now().getYear();
}
if (to < 1900) {
to += DateTime.now().getYear();
}
return fDateProducer.randomDateBetweenYears(Math.min(from, to), Math.max(from, to));
}
/**
* generate a timestamp with a range of years from now
* @see FillerTextType#TIMESTAMP
* @param yearsToPastOrInFuture may be<ul>
* <li>0, then a timestamp inside the actual year will be generated
* <li>negative, then a timestamp in the yearsToPastOrInFuture years before now = past will be generated
* <li>postive, then a timestamp in the yearsToPastOrInFuture years before now = will be generated
* </ul>
* @return the generated filler text object
*/
public DateTime timestamp(int yearsToPastOrInFuture) {
if (yearsToPastOrInFuture == 0) {
return timestamp(0, 0);
}
else if (yearsToPastOrInFuture < 0) {
return fDateProducer.randomDateInThePast(-yearsToPastOrInFuture);
}
else {
return fDateProducer.randomDateInTheFuture(yearsToPastOrInFuture);
}
}
/**
* @see FillerTextType#DATE
* @param params may be<ul>
* <li>none - same as {@link #date(int)} with parameter 0</li>
* <li>(int)yearsToPastOrInFuture - generate a date with a range of years from now; same as {@link #date(int)}</li>
* <li>(int)from,(int)to - generate a date between two years including both years; same as {@link #date(int, int)}</li>
* </ul>
* @return the generated filler text object
*/
@SuppressWarnings("deprecation")
public Date date(Object...params) {
DateTime dt = timestamp(params);
return (new Date(dt.getYear()-1900, dt.getMonthOfYear()-1, dt.getDayOfMonth()));
}
/**
* generate a date (without time) between two years including both years
* @see FillerTextType#DATE
* @param from may be<ul>
* <li>either a year after 1900</li>
* <li>otherwise it will be interpreted as an offset to the actual year</li>
* </ul>
* @param to may be<ul>
* <li>either a year after 1900</li>
* <li>otherwise it will be interpreted as an offset to the actual year</li>
* </ul>
* @return the generated filler text object
*/
@SuppressWarnings("deprecation")
public Date date(int from, int to) {
DateTime dt = timestamp(from, to);
return (new Date(dt.getYear()-1900, dt.getMonthOfYear()-1, dt.getDayOfMonth()));
}
/**
* generate a date (without time) with a range of years from now
* @see FillerTextType#DATE
* @param yearsToPastOrInFuture may be<ul>
* <li>0, then a date inside the actual year will be generated
* <li>negative, then a date in the yearsToPastOrInFuture years before now = past will be generated
* <li>postive, then a date in the yearsToPastOrInFuture years before now = will be generated
* </ul>
* @return the generated filler text object
*/
@SuppressWarnings("deprecation")
public Date date(int yearsToPastOrInFuture) {
DateTime dt = timestamp(yearsToPastOrInFuture);
return (new Date(dt.getYear()-1900, dt.getMonthOfYear()-1, dt.getDayOfMonth()));
}
/**
* generate a time (without date)
* @see FillerTextType#TIME
* @return the generated filler text object
*/
@SuppressWarnings("deprecation")
public Time time() {
DateTime dt = fDateProducer.randomDateInThePast(1);
return (new Time(dt.getHourOfDay(), dt.getMinuteOfHour(), dt.getSecondOfMinute()));
}
/**
* @see FillerTextType#PARAGRAPH
* @return the generated filler text object, containing one paragraph
*/
public String paragraph() {
return fTextProducer.paragraph(1);
}
/**
* @see FillerTextType#PARAGRAPH
* @param count 0 uses the internal default count value
* @return the generated filler text object, containing count paragraph(s)
*/
public String paragraphs(int count) {
if (count > 0) {
return fTextProducer.paragraph(count);
}
else {
return paragraph();
}
}
/**
* @see FillerTextType#SENTENCE
* @return the generated filler text object, containing one sentence
*/
public String sentence() {
return fTextProducer.sentence(1);
}
/**
* @see FillerTextType#SENTENCE
* @param count 0 uses the internal default count value
* @return the generated filler text object, containing count sentence(s)
*/
public String sentences(int count) {
if (count > 0) {
return fTextProducer.sentence(count);
}
else {
return sentence();
}
}
/**
* @see FillerTextType#WORD
* @return the generated filler text object, containing one word
*/
public String word() {
return fTextProducer.word(1);
}
/**
* @see FillerTextType#WORD
* @param count 0 uses the internal default count value
* @return the generated filler text object, containing count word(s)
*/
public String words(int count) {
if (count > 0) {
return fTextProducer.word(count);
}
else {
return word();
}
}
public String numerify(String numberString) {
return fBaseProducer.numerify(numberString);
}
}