| /* |
| * 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. |
| */ |
| package org.apache.lucene.benchmark.byTask.utils; |
| |
| |
| import java.io.StreamTokenizer; |
| import java.io.StringReader; |
| import java.lang.reflect.Constructor; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Locale; |
| |
| 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 |
| */ |
| @SuppressWarnings("try") |
| public class Algorithm implements AutoCloseable { |
| |
| 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.quoteChar('"'); |
| stok.quoteChar('\''); |
| 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.setAlgLineNum(stok.lineno()); |
| 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 - allow recursive parenthetical expressions |
| stok.eolIsSignificant(true); // Allow params tokenizer to keep track of line number |
| StringBuilder params = new StringBuilder(); |
| stok.nextToken(); |
| if (stok.ttype != ')') { |
| int count = 1; |
| BALANCED_PARENS: while (true) { |
| 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 RuntimeException("Unexpexted EOF: - "+stok.toString()); |
| } |
| case '"': |
| case '\'': { |
| params.append((char)stok.ttype); |
| // re-escape delimiters, if any |
| params.append(stok.sval.replaceAll("" + (char)stok.ttype, "\\\\" + (char)stok.ttype)); |
| params.append((char)stok.ttype); |
| break; |
| } |
| case '(': { |
| params.append((char)stok.ttype); |
| ++count; |
| break; |
| } |
| case ')': { |
| if (--count >= 1) { // exclude final closing parenthesis |
| params.append((char)stok.ttype); |
| } else { |
| break BALANCED_PARENS; |
| } |
| break; |
| } |
| default: { |
| params.append((char)stok.ttype); |
| } |
| } |
| stok.nextToken(); |
| } |
| } |
| stok.eolIsSignificant(false); |
| 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(Locale.ROOT); |
| 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 { |
| name = stok.sval; |
| 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<>(); |
| 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 |
| */ |
| 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<>(); |
| 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); |
| } |
| } |
| } |
| |
| @Override |
| public void close() throws Exception { |
| sequence.close(); |
| } |
| |
| } |
| |