--- /dev/null
+package org.apache.lucene.benchmark.byTask.utils;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import org.apache.lucene.benchmark.byTask.PerfRunData;
+import org.apache.lucene.benchmark.byTask.tasks.PerfTask;
+import org.apache.lucene.benchmark.byTask.tasks.RepSumByPrefTask;
+import org.apache.lucene.benchmark.byTask.tasks.TaskSequence;
+
+/**
+ * Test algorithm, as read from file
+ */
+public class Algorithm {
+
+ private TaskSequence sequence;
+ private final String[] taskPackages;
+
+ /**
+ * Read algorithm from file
+ * Property examined: alt.tasks.packages == comma separated list of
+ * alternate package names where tasks would be searched for, when not found
+ * in the default package (that of {@link PerfTask}{@link #getClass()}).
+ * If the same task class appears in more than one package, the package
+ * indicated first in this list will be used.
+ * @param runData perf-run-data used at running the tasks.
+ * @throws Exception if errors while parsing the algorithm
+ */
+ @SuppressWarnings("fallthrough")
+ public Algorithm (PerfRunData runData) throws Exception {
+ Config config = runData.getConfig();
+ taskPackages = initTasksPackages(config);
+ String algTxt = config.getAlgorithmText();
+ sequence = new TaskSequence(runData,null,null,false);
+ TaskSequence currSequence = sequence;
+ PerfTask prevTask = null;
+ StreamTokenizer stok = new StreamTokenizer(new StringReader(algTxt));
+ stok.commentChar('#');
+ stok.eolIsSignificant(false);
+ stok.ordinaryChar('"');
+ stok.ordinaryChar('/');
+ stok.ordinaryChar('(');
+ stok.ordinaryChar(')');
+ boolean colonOk = false;
+ boolean isDisableCountNextTask = false; // only for primitive tasks
+ currSequence.setDepth(0);
+
+ while (stok.nextToken() != StreamTokenizer.TT_EOF) {
+ switch(stok.ttype) {
+
+ case StreamTokenizer.TT_WORD:
+ String s = stok.sval;
+ Constructor<? extends PerfTask> cnstr = taskClass(config,s)
+ .asSubclass(PerfTask.class).getConstructor(PerfRunData.class);
+ PerfTask task = cnstr.newInstance(runData);
+ task.setDisableCounting(isDisableCountNextTask);
+ isDisableCountNextTask = false;
+ currSequence.addTask(task);
+ if (task instanceof RepSumByPrefTask) {
+ stok.nextToken();
+ String prefix = stok.sval;
+ if (prefix==null || prefix.length()==0) {
+ throw new Exception("named report prefix problem - "+stok.toString());
+ }
+ ((RepSumByPrefTask) task).setPrefix(prefix);
+ }
+ // check for task param: '(' someParam ')'
+ stok.nextToken();
+ if (stok.ttype!='(') {
+ stok.pushBack();
+ } else {
+ // get params, for tasks that supports them, - anything until next ')'
+ StringBuilder params = new StringBuilder();
+ stok.nextToken();
+ while (stok.ttype!=')') {
+ switch (stok.ttype) {
+ case StreamTokenizer.TT_NUMBER:
+ params.append(stok.nval);
+ break;
+ case StreamTokenizer.TT_WORD:
+ params.append(stok.sval);
+ break;
+ case StreamTokenizer.TT_EOF:
+ throw new Exception("unexpexted EOF: - "+stok.toString());
+ default:
+ params.append((char)stok.ttype);
+ }
+ stok.nextToken();
+ }
+ String prm = params.toString().trim();
+ if (prm.length()>0) {
+ task.setParams(prm);
+ }
+ }
+
+ // ---------------------------------------
+ colonOk = false; prevTask = task;
+ break;
+
+ default:
+ char c = (char)stok.ttype;
+
+ switch(c) {
+
+ case ':' :
+ if (!colonOk) throw new Exception("colon unexpexted: - "+stok.toString());
+ colonOk = false;
+ // get repetitions number
+ stok.nextToken();
+ if ((char)stok.ttype == '*') {
+ ((TaskSequence)prevTask).setRepetitions(TaskSequence.REPEAT_EXHAUST);
+ } else {
+ if (stok.ttype!=StreamTokenizer.TT_NUMBER) {
+ throw new Exception("expected repetitions number or XXXs: - "+stok.toString());
+ } else {
+ double num = stok.nval;
+ stok.nextToken();
+ if (stok.ttype == StreamTokenizer.TT_WORD && stok.sval.equals("s")) {
+ ((TaskSequence) prevTask).setRunTime(num);
+ } else {
+ stok.pushBack();
+ ((TaskSequence) prevTask).setRepetitions((int) num);
+ }
+ }
+ }
+ // check for rate specification (ops/min)
+ stok.nextToken();
+ if (stok.ttype!=':') {
+ stok.pushBack();
+ } else {
+ // get rate number
+ stok.nextToken();
+ if (stok.ttype!=StreamTokenizer.TT_NUMBER) throw new Exception("expected rate number: - "+stok.toString());
+ // check for unit - min or sec, sec is default
+ stok.nextToken();
+ if (stok.ttype!='/') {
+ stok.pushBack();
+ ((TaskSequence)prevTask).setRate((int)stok.nval,false); // set rate per sec
+ } else {
+ stok.nextToken();
+ if (stok.ttype!=StreamTokenizer.TT_WORD) throw new Exception("expected rate unit: 'min' or 'sec' - "+stok.toString());
+ String unit = stok.sval.toLowerCase();
+ if ("min".equals(unit)) {
+ ((TaskSequence)prevTask).setRate((int)stok.nval,true); // set rate per min
+ } else if ("sec".equals(unit)) {
+ ((TaskSequence)prevTask).setRate((int)stok.nval,false); // set rate per sec
+ } else {
+ throw new Exception("expected rate unit: 'min' or 'sec' - "+stok.toString());
+ }
+ }
+ }
+ colonOk = false;
+ break;
+
+ case '{' :
+ case '[' :
+ // a sequence
+ // check for sequence name
+ String name = null;
+ stok.nextToken();
+ if (stok.ttype!='"') {
+ stok.pushBack();
+ } else {
+ stok.nextToken();
+ name = stok.sval;
+ stok.nextToken();
+ if (stok.ttype!='"' || name==null || name.length()==0) {
+ throw new Exception("sequence name problem - "+stok.toString());
+ }
+ }
+ // start the sequence
+ TaskSequence seq2 = new TaskSequence(runData, name, currSequence, c=='[');
+ currSequence.addTask(seq2);
+ currSequence = seq2;
+ colonOk = false;
+ break;
+
+ case '&' :
+ if (currSequence.isParallel()) {
+ throw new Exception("Can only create background tasks within a serial task");
+ }
+ stok.nextToken();
+ final int deltaPri;
+ if (stok.ttype != StreamTokenizer.TT_NUMBER) {
+ stok.pushBack();
+ deltaPri = 0;
+ } else {
+ // priority
+ deltaPri = (int) stok.nval;
+ }
+
+ if (prevTask == null) {
+ throw new Exception("& was unexpected");
+ } else if (prevTask.getRunInBackground()) {
+ throw new Exception("double & was unexpected");
+ } else {
+ prevTask.setRunInBackground(deltaPri);
+ }
+ break;
+
+ case '>' :
+ currSequence.setNoChildReport(); /* intentional fallthrough */
+ case '}' :
+ case ']' :
+ // end sequence
+ colonOk = true; prevTask = currSequence;
+ currSequence = currSequence.getParent();
+ break;
+
+ case '-' :
+ isDisableCountNextTask = true;
+ break;
+
+ } //switch(c)
+ break;
+
+ } //switch(stok.ttype)
+
+ }
+
+ if (sequence != currSequence) {
+ throw new Exception("Unmatched sequences");
+ }
+
+ // remove redundant top level enclosing sequences
+ while (sequence.isCollapsable() && sequence.getRepetitions()==1 && sequence.getRate()==0) {
+ ArrayList<PerfTask> t = sequence.getTasks();
+ if (t!=null && t.size()==1) {
+ PerfTask p = t.get(0);
+ if (p instanceof TaskSequence) {
+ sequence = (TaskSequence) p;
+ continue;
+ }
+ }
+ break;
+ }
+ }
+
+ private String[] initTasksPackages(Config config) {
+ String alts = config.get("alt.tasks.packages", null);
+ String dfltPkg = PerfTask.class.getPackage().getName();
+ if (alts==null) {
+ return new String[]{ dfltPkg };
+ }
+ ArrayList<String> pkgs = new ArrayList<String>();
+ pkgs.add(dfltPkg);
+ for (String alt : alts.split(",")) {
+ pkgs.add(alt);
+ }
+ return pkgs.toArray(new String[0]);
+ }
+
+ private Class<?> taskClass(Config config, String taskName)
+ throws ClassNotFoundException {
+ for (String pkg : taskPackages) {
+ try {
+ return Class.forName(pkg+'.'+taskName+"Task");
+ } catch (ClassNotFoundException e) {
+ // failed in this package, might succeed in the next one...
+ }
+ }
+ // can only get here if failed to instantiate
+ throw new ClassNotFoundException(taskName+" not found in packages "+Arrays.toString(taskPackages));
+ }
+
+ @Override
+ public String toString() {
+ String newline = System.getProperty("line.separator");
+ StringBuilder sb = new StringBuilder();
+ sb.append(sequence.toString());
+ sb.append(newline);
+ return sb.toString();
+ }
+
+ /**
+ * Execute this algorithm
+ * @throws Exception
+ */
+ public void execute() throws Exception {
+ try {
+ sequence.runAndMaybeStats(true);
+ } finally {
+ sequence.close();
+ }
+ }
+
+ /**
+ * Expert: for test purposes, return all tasks participating in this algorithm.
+ * @return all tasks participating in this algorithm.
+ */
+ public ArrayList<PerfTask> extractTasks() {
+ ArrayList<PerfTask> res = new ArrayList<PerfTask>();
+ extractTasks(res, sequence);
+ return res;
+ }
+ private void extractTasks (ArrayList<PerfTask> extrct, TaskSequence seq) {
+ if (seq==null)
+ return;
+ extrct.add(seq);
+ ArrayList<PerfTask> t = sequence.getTasks();
+ if (t==null)
+ return;
+ for (final PerfTask p : t) {
+ if (p instanceof TaskSequence) {
+ extractTasks(extrct, (TaskSequence)p);
+ } else {
+ extrct.add(p);
+ }
+ }
+ }
+
+}
+