blob: fb1261a1e1a5872195836a2336135179c8061de6 [file] [log] [blame]
using J2N.IO;
using J2N.Text;
using Lucene.Net.Benchmarks.ByTask.Tasks;
using Lucene.Net.Support;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using JCG = J2N.Collections.Generic;
namespace Lucene.Net.Benchmarks.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.
*/
/// <summary>
/// Test algorithm, as read from file
/// </summary>
public class Algorithm
{
private TaskSequence sequence;
private readonly string[] taskPackages;
/// <summary>
/// Read algorithm from file.
/// Property examined: alt.tasks.packages == comma separated list of
/// alternate Assembly names where tasks would be searched for, when not found
/// in the default Assembly (that of <see cref="PerfTask"/>).
/// If the same task class appears in more than one Assembly, the Assembly
/// indicated first in this list will be used.
/// <para/>
/// The Lucene.Net implementation differs from Lucene in that all
/// referenced assemblies are also scanned for the type. However,
/// alt.tasks.packages may be included for assemblies that are
/// not referenced in your project.
/// </summary>
/// <param name="runData">perf-run-data used at running the tasks.</param>
/// <exception cref="Exception">if errors while parsing the algorithm.</exception>
public Algorithm(PerfRunData runData)
{
Config config = runData.Config;
taskPackages = InitTasksPackages(config);
string algTxt = config.AlgorithmText;
sequence = new TaskSequence(runData, null, null, false);
TaskSequence currSequence = sequence;
PerfTask prevTask = null;
StreamTokenizer stok = new StreamTokenizer(new StringReader(algTxt));
stok.CommentChar('#');
stok.EndOfLineIsSignificant = false;
stok.QuoteChar('"');
stok.QuoteChar('\'');
stok.OrdinaryChar('/');
stok.OrdinaryChar('(');
stok.OrdinaryChar(')');
bool colonOk = false;
bool isDisableCountNextTask = false; // only for primitive tasks
currSequence.Depth = 0;
while (stok.NextToken() != StreamTokenizer.TokenType_EndOfStream)
{
switch (stok.TokenType)
{
case StreamTokenizer.TokenType_Word:
string s = stok.StringValue;
PerfTask task = (PerfTask)Activator.CreateInstance(TaskClass(config, s), runData);
task.AlgLineNum = stok.LineNumber;
task.DisableCounting = isDisableCountNextTask;
isDisableCountNextTask = false;
currSequence.AddTask(task);
if (task is RepSumByPrefTask)
{
stok.NextToken();
string prefix = stok.StringValue;
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.TokenType != '(')
{
stok.PushBack();
}
else
{
// get params, for tasks that supports them - allow recursive parenthetical expressions
stok.EndOfLineIsSignificant = true; // Allow params tokenizer to keep track of line number
StringBuilder @params = new StringBuilder();
stok.NextToken();
if (stok.TokenType != ')')
{
int count = 1;
while (true)
{
switch (stok.TokenType)
{
case StreamTokenizer.TokenType_Number:
{
@params.Append(stok.NumberValue.ToString(CultureInfo.InvariantCulture));
break;
}
case StreamTokenizer.TokenType_Word:
{
@params.Append(stok.StringValue);
break;
}
case StreamTokenizer.TokenType_EndOfStream:
{
throw new Exception("Unexpexted EOF: - " + stok.ToString());
}
case '"':
case '\'':
{
@params.Append((char)stok.TokenType);
// re-escape delimiters, if any
@params.Append(stok.StringValue.Replace("" + (char)stok.TokenType, @"\" + (char)stok.TokenType));
@params.Append((char)stok.TokenType);
break;
}
case '(':
{
@params.Append((char)stok.TokenType);
++count;
break;
}
case ')':
{
if (--count >= 1)
{ // exclude final closing parenthesis
@params.Append((char)stok.TokenType);
}
else
{
goto BALANCED_PARENS_BREAK;
}
break;
}
default:
{
@params.Append((char)stok.TokenType);
break;
}
}
stok.NextToken();
}
BALANCED_PARENS_BREAK: { }
}
stok.EndOfLineIsSignificant = false;
string prm = @params.ToString().Trim();
if (prm.Length > 0)
{
task.SetParams(prm);
}
}
// ---------------------------------------
colonOk = false; prevTask = task;
break;
default:
char c = (char)stok.TokenType;
switch (c)
{
case ':':
if (!colonOk) throw new Exception("colon unexpexted: - " + stok.ToString());
colonOk = false;
// get repetitions number
stok.NextToken();
if ((char)stok.TokenType == '*')
{
((TaskSequence)prevTask).SetRepetitions(TaskSequence.REPEAT_EXHAUST);
}
else
{
if (stok.TokenType != StreamTokenizer.TokenType_Number)
{
throw new Exception("expected repetitions number or XXXs: - " + stok.ToString());
}
else
{
double num = stok.NumberValue;
stok.NextToken();
if (stok.TokenType == StreamTokenizer.TokenType_Word && stok.StringValue.Equals("s", StringComparison.Ordinal))
{
((TaskSequence)prevTask).SetRunTime(num);
}
else
{
stok.PushBack();
((TaskSequence)prevTask).SetRepetitions((int)num);
}
}
}
// check for rate specification (ops/min)
stok.NextToken();
if (stok.TokenType != ':')
{
stok.PushBack();
}
else
{
// get rate number
stok.NextToken();
if (stok.TokenType != StreamTokenizer.TokenType_Number) throw new Exception("expected rate number: - " + stok.ToString());
// check for unit - min or sec, sec is default
stok.NextToken();
if (stok.TokenType != '/')
{
stok.PushBack();
((TaskSequence)prevTask).SetRate((int)stok.NumberValue, false); // set rate per sec
}
else
{
stok.NextToken();
if (stok.TokenType != StreamTokenizer.TokenType_Word) throw new Exception("expected rate unit: 'min' or 'sec' - " + stok.ToString());
string unit = stok.StringValue.ToLowerInvariant();
if ("min".Equals(unit, StringComparison.Ordinal))
{
((TaskSequence)prevTask).SetRate((int)stok.NumberValue, true); // set rate per min
}
else if ("sec".Equals(unit, StringComparison.Ordinal))
{
((TaskSequence)prevTask).SetRate((int)stok.NumberValue, 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.TokenType != '"')
{
stok.PushBack();
}
else
{
name = stok.StringValue;
if (stok.TokenType != '"' || 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();
int deltaPri;
if (stok.TokenType != StreamTokenizer.TokenType_Number)
{
stok.PushBack();
deltaPri = 0;
}
else
{
// priority
deltaPri = (int)stok.NumberValue;
}
if (prevTask == null)
{
throw new Exception("& was unexpected");
}
else if (prevTask.RunInBackground)
{
throw new Exception("double & was unexpected");
}
else
{
prevTask.SetRunInBackground(deltaPri);
}
break;
case '>':
currSequence.SetNoChildReport(); /* intentional fallthrough */
// end sequence
colonOk = true; prevTask = currSequence;
currSequence = currSequence.Parent;
break;
case '}':
case ']':
// end sequence
colonOk = true; prevTask = currSequence;
currSequence = currSequence.Parent;
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.Repetitions == 1 && sequence.GetRate() == 0)
{
IList<PerfTask> t = sequence.Tasks;
if (t != null && t.Count == 1)
{
PerfTask p = t[0];
if (p is TaskSequence)
{
sequence = (TaskSequence)p;
continue;
}
}
break;
}
}
private string[] InitTasksPackages(Config config)
{
// LUCENENET specific - changing the logic a bit
// to add all referenced assemblies by default.
// The alt.tasks.packages parameter still exists, but
// it is only necessary for assemblies that are not
// referenced by the host assembly.
ISet<string> result = new JCG.HashSet<string>();
string alts = config.Get("alt.tasks.packages", null);
string dfltPkg = typeof(PerfTask).GetTypeInfo().Assembly.GetName().Name;
string[] referencedAssemblies = AssemblyUtils.GetReferencedAssemblies().Select(a => a.GetName().Name).ToArray();
result.Add(dfltPkg);
if (alts == null)
{
result.UnionWith(referencedAssemblies);
return result.ToArray();
}
foreach (string alt in alts.Split(',').TrimEnd())
{
result.Add(alt);
}
result.UnionWith(referencedAssemblies);
return result.ToArray();
}
private Type TaskClass(Config config, string taskName)
{
foreach (string pkg in taskPackages)
{
Type result = LoadType(pkg, taskName + "Task");
if (result != null)
{
return result;
}
}
// can only get here if failed to instantiate
throw new TypeLoadException(taskName + " not found in packages " + Collections.ToString(taskPackages));
}
private Type LoadType(string assemblyName, string typeName)
{
return Assembly.Load(new AssemblyName(assemblyName)).GetTypes().FirstOrDefault(t => t.Name == typeName);
}
public override string ToString()
{
string newline = Environment.NewLine;
StringBuilder sb = new StringBuilder();
sb.Append(sequence.ToString());
sb.Append(newline);
return sb.ToString();
}
/// <summary>
/// Execute this algorithm.
/// </summary>
public virtual void Execute()
{
try
{
sequence.RunAndMaybeStats(true);
}
finally
{
sequence.Dispose();
}
}
/// <summary>
/// Expert: for test purposes, return all tasks participating in this algorithm.
/// </summary>
/// <returns>All tasks participating in this algorithm.</returns>
public virtual IList<PerfTask> ExtractTasks()
{
List<PerfTask> res = new List<PerfTask>();
ExtractTasks(res, sequence);
return res;
}
private void ExtractTasks(IList<PerfTask> extrct, TaskSequence seq)
{
if (seq == null)
return;
extrct.Add(seq);
IList<PerfTask> t = sequence.Tasks;
if (t == null)
return;
foreach (PerfTask p in t)
{
if (p is TaskSequence)
{
ExtractTasks(extrct, (TaskSequence)p);
}
else
{
extrct.Add(p);
}
}
}
}
}