package org.apache.cassandra.stress.settings; import java.io.File; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.cassandra.stress.generatedata.*; /** * For selecting a data generator */ class OptionDataGen extends Option { private static final Pattern FULL = Pattern.compile("([A-Z]+)\\(([^)]+)\\)", Pattern.CASE_INSENSITIVE); private static final Pattern ARGS = Pattern.compile("[^,]+"); final String prefix; private DataGenFactory factory; private final DataGenFactory defaultFactory; public OptionDataGen(String prefix, String defaultSpec) { this.prefix = prefix; this.defaultFactory = defaultSpec == null ? null : get(defaultSpec); } @Override public boolean accept(String param) { if (!param.toLowerCase().startsWith(prefix)) return false; factory = get(param.substring(prefix.length())); return true; } private static DataGenFactory get(String spec) { Matcher m = FULL.matcher(spec); if (!m.matches()) throw new IllegalArgumentException("Illegal data generator specification: " + spec); String name = m.group(1); Impl impl = LOOKUP.get(name.toLowerCase()); if (impl == null) throw new IllegalArgumentException("Illegal data generator type: " + name); List<String> params = new ArrayList<>(); m = ARGS.matcher(m.group(2)); while (m.find()) params.add(m.group()); return impl.getFactory(params); } public DataGenFactory get() { return factory != null ? factory : defaultFactory; } @Override public boolean happy() { return factory != null || defaultFactory != null; } @Override public String shortDisplay() { return prefix + "ALG()"; } public String longDisplay() { return shortDisplay() + ": Specify a data generator from:"; } @Override public List<String> multiLineDisplay() { return Arrays.asList( GroupedOptions.formatMultiLine("RANDOM()", "Completely random byte generation"), GroupedOptions.formatMultiLine("REPEAT(<freq>)", "An MD5 hash of (opIndex % freq) combined with the column index"), GroupedOptions.formatMultiLine("DICT(<file>)","Random words from a dictionary; the file should be in the format \"<freq> <word>\"") ); } private static final Map<String, Impl> LOOKUP; static { final Map<String, Impl> lookup = new HashMap<>(); lookup.put("random", new RandomImpl()); lookup.put("rand", new RandomImpl()); lookup.put("rnd", new RandomImpl()); lookup.put("repeat", new RepeatImpl()); lookup.put("dict", new DictionaryImpl()); lookup.put("dictionary", new DictionaryImpl()); LOOKUP = lookup; } private static interface Impl { public DataGenFactory getFactory(List<String> params); } private static final class RandomImpl implements Impl { @Override public DataGenFactory getFactory(List<String> params) { if (params.size() != 0) throw new IllegalArgumentException("Invalid parameter list for random generator: " + params); return new RandomFactory(); } } private static final class RepeatImpl implements Impl { @Override public DataGenFactory getFactory(List<String> params) { if (params.size() != 1) throw new IllegalArgumentException("Invalid parameter list for repeating generator: " + params); try { int repeatFrequency = Integer.parseInt(params.get(0)); return new RepeatsFactory(repeatFrequency); } catch (Exception _) { throw new IllegalArgumentException("Invalid parameter list for repeating generator: " + params); } } } private static final class DictionaryImpl implements Impl { @Override public DataGenFactory getFactory(List<String> params) { if (params.size() != 1) throw new IllegalArgumentException("Invalid parameter list for dictionary generator: " + params); try { final File file = new File(params.get(0)); return DataGenStringDictionary.getFactory(file); } catch (Exception e) { throw new IllegalArgumentException("Invalid parameter list for dictionary generator: " + params, e); } } } private static final class RandomFactory implements DataGenFactory { @Override public DataGen get() { return new DataGenBytesRandom(); } } private static final class RepeatsFactory implements DataGenFactory { final int frequency; private RepeatsFactory(int frequency) { this.frequency = frequency; } @Override public DataGen get() { return new DataGenStringRepeats(frequency); } } }