ANTLR4 parser not moving past first rule - antlr4

I am completely new to ANTLR and I am working on a programming language with my group at university for our semesterproject. We are using visitors for our parser but when I attempt to visit the tree, it doesn't move past the first rule and I have no idea why.
This is the code where I implement it:
public class Implementation {
public static void main(String[] args) throws Exception {
SyntaxAnalysisLexer lexer = new SyntaxAnalysisLexer(CharStreams.fromFileName("<filepath>"));
CommonTokenStream tokens = new CommonTokenStream(lexer);
SyntaxAnalysisParser parser = new SyntaxAnalysisParser(tokens);
FuncCollect funcCollect = new FuncCollect();
funcCollect.visit(parser.prog());
EvalVisitor evalVisitor = new EvalVisitor(funcCollect.getEnvironment());
evalVisitor.visit(parser.prog());
List<String> errors = evalVisitor.getErrors();
File file = new File("<filepath>");
file.createNewFile();
FileWriter writer = new FileWriter(file);
for(String error : errors) {
writer.append(error);
writer.close();
}
}
}
This is a part of the grammar:
grammar SyntaxAnalysis;
//PARSER PART
prog: (func | eventHand)* EOF #program
;
func: 'function' ftype ID '(' fparam ')' block 'endFunction' #function
;
eventHand: 'when' ID '(' fparam ')' block 'endWhen' #when
;
block: stmt* #blk
;
And this is the two first visit functions:
#Override public SyntaxAnalysisType visitFunction(SyntaxAnalysisParser.FunctionContext ctx) {
varEnv = new HashMap<>();
visit(ctx.fparam());
visit(ctx.block());
return new SyntaxAnalysisVoid();
}
#Override public SyntaxAnalysisType visitWhen(SyntaxAnalysisParser.WhenContext ctx) {
varEnv = new HashMap<>();
visit(ctx.fparam());
visit(ctx.block());
return new SyntaxAnalysisVoid();
}
Can anyone tell me what I am doing wrong?

So I figured out what went wrong. I had to reset the parser before attempting to visit it a second time. So it went through it correctly the first time, but the second time it no longer knew where to go. I did this simply by writing this before the second visit:
parser.reset();

Related

What's the difference between ANTLR4's errorListener and errorHandler?

I want to get the specific error message of ANTLR4's parser.
And I found that there are two way to handle error: errorListener and errorHandler.
// set error handler
parser.removeErrorListeners();
parser.addErrorListener(new QueryErrorListener());
parser.setErrorHandler(new BailErrorStrategy());
But I'm confused about the difference between them.
I found that, errorListener can get the specific error message, but it can only print it or log it, can't throw a exception.
The implemention of errorListener as bellow:
public class QueryErrorListener extends BaseErrorListener {
private static final Logger LOGGER = LoggerFactory.getLogger(QueryDispatcher.class);
#Override
public void syntaxError(Recognizer<?, ?> recognizer, Object offendingSymbol,
int line, int charPositionInLine, String msg,
RecognitionException e)
{
List<String> stack = ((Parser)recognizer).getRuleInvocationStack(); Collections.reverse(stack);
String errorMessage = "line "+line+":"+charPositionInLine+" at "+
offendingSymbol+": "+msg;
LOGGER.error("rule stack: "+stack);
LOGGER.error(errorMessage);
QueryParseErrorStrategy queryParseErrorStrategy = new QueryParseErrorStrategy();
}
}
At the same time, the errorHandler can only throw a exception ParseCancellationException without any specific message.
public class BailErrorStrategy extends DefaultErrorStrategy {
/** Instead of recovering from exception {#code e}, re-throw it wrapped
* in a {#link ParseCancellationException} so it is not caught by the
* rule function catches. Use {#link Exception#getCause()} to get the
* original {#link RecognitionException}.
*/
#Override
public void recover(Parser recognizer, RecognitionException e) {
for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) {
context.exception = e;
}
throw new ParseCancellationException(e);
}
/** Make sure we don't attempt to recover inline; if the parser
* successfully recovers, it won't throw an exception.
*/
#Override
public Token recoverInline(Parser recognizer)
throws RecognitionException
{
InputMismatchException e = new InputMismatchException(recognizer);
for (ParserRuleContext context = recognizer.getContext(); context != null; context = context.getParent()) {
context.exception = e;
}
throw new ParseCancellationException(e);
}
/** Make sure we don't attempt to recover from problems in subrules. */
#Override
public void sync(Parser recognizer) { }
}
I've try to find a solution, add a transfer method to get detail message from ParseCancellationException, as bellow.
I found that I can get some message from a Token object of RecognitionException, but I can only find the line/charPositionInLine/offendingSymbol message, I don't know where is the detail message, like "missing 'xxx', expect 'yyy'"
public class ANTLRExceptionTransfer {
public static SemanticException transfer(RecognitionException re) {
String errorMsg = "";
Recognizer<?, ?> recognizer = re.getRecognizer();
Token offendingSymbol = re.getOffendingToken();
int line = offendingSymbol.getLine();
int charPositionInLine = offendingSymbol.getCharPositionInLine();
// ????????
String msg = "";
List<String> stack = ((Parser)recognizer).getRuleInvocationStack();
Collections.reverse(stack);
String errorMessage = "rule stack: "+stack;
errorMessage = "\nline "+line+":"+charPositionInLine+" at "+
offendingSymbol+": "+msg;
return new SemanticException(errorMessage);
}
}
Is it the right way to use errorHandler?
How can I get a exception with specific error message?
I find the setErrorHandler name a bit confusing. It should be consistent with what you can set there. It's for setting an error strategy (which is of course also some kind of handling...).
Both error listener and error strategy are means for the application to deal with parse errors. The error listener gets called for each encountered error and allows the application to collect them (e.g. to show them in a GUI). You'll get a pre-generated error message or can create an own one from the passed in parameters.
The error strategy is a class that determines how to continue after an error was found. The default stategy is to try to sync to the input stream and continue with parsing. Sometimes however you want the parser to stop immediately and avoid lengthy operations after an error was found. This so-called bail-out strategy is another class in ANTLR4, usually used for SLL parsing. See one of my projects for how that's used.
The thrown ParseCancellationException in the bail-out error strategy is an exception without any additional info. It's not meant for error handling (in the sense of sending it to the application/user, you have the error handler for that), but instead to throw an exception that's not one of the usual parser exceptions, in order to bypass all error catching and find a way out of the ongoing parse run as quick as possible. You have to catch this exception in your own code or it will bubble up to the root context of your application (and might cause the application to quit, depending on the target language).

Google Custom Search API - Search Results

I have somewhat lost touch with custom search engines ever since Google switched from its more legacy search engine api in favor of the google custom search api. I'm hoping someone might be able to tell me whether a (pretty simple) goal can be accomplished with the new framework, and potentially any starting help would be great.
Specifically, I am looking to write a program which will read in text from a text file, then use five words from said document in a google search - the point being to figure out how many results accrue from said search.
An example input/output would be:
Input: "This is my search term" -- quotations included in the search!
Output: there were 7 total results
Thanks so much, all, for your time/help
First you need to create a Google Custom Search project inside you google account.
From this project you must obtain a Custom Search Engine ID , known as cx parameter. You must also obtain a API key parameter. Both of these are available from your Google Custom Search API project inside your google account.
Then, if you prefer Java , here's a working example:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class GoogleCustonSearchAPI {
public static void main(String[] args) throws Exception {
String key="your_key";
String qry="your_query";
String cx = "your_cx";
//Fetch urls
URL url = new URL(
"https://www.googleapis.com/customsearch/v1?key="+key+"&cx="+cx+"&q="+ qry +"&alt=json&queriefields=queries(request(totalResults))");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
BufferedReader br = new BufferedReader(new InputStreamReader(
(conn.getInputStream())));
//Remove comments if you need to output in JSON format
/*String output;
System.out.println("Output from Server .... \n");
while ((output = br.readLine()) != null) {
System.out.println(output);
}*/
//Print the urls and domains from Google Custom Search String searchResult;
while ((searchResult = output.readLine()) != null) {
int startPos=searchResult.indexOf("\"link\": \"")+("\"link\": \"").length();
int endPos=searchResult.indexOf("\",");
if(searchResult.contains("\"link\": \"") && (endPos>startPos)){
String link=searchResult.substring(startPos,endPos);
if(link.contains(",")){
String tempLink = "\"";
tempLink+=link;
tempLink+="\"";
System.out.println(tempLink);
}
else{
System.out.println(link);
}
System.out.println(getDomainName(link));
}
}
conn.disconnect();
}
public static String getDomainName(String url) throws URISyntaxException {
URI uri = new URI(url);
String domain = uri.getHost();
return domain.startsWith("www.") ? domain.substring(4) : domain;
}
The "&queriefields=queries(request(totalResults))" is what makes the difference and gives sou what you need. But keep in mind that you can perform only 100 queries per day for free and that the results of Custom Search API are sometimes quite different from the those returned from Google.com search
If anybody would still need some example of CSE (Google Custom Search Engine) API, this is working method
public static List<Result> search(String keyword){
Customsearch customsearch= null;
try {
customsearch = new Customsearch(new NetHttpTransport(),new JacksonFactory(), new HttpRequestInitializer() {
public void initialize(HttpRequest httpRequest) {
try {
// set connect and read timeouts
httpRequest.setConnectTimeout(HTTP_REQUEST_TIMEOUT);
httpRequest.setReadTimeout(HTTP_REQUEST_TIMEOUT);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
List<Result> resultList=null;
try {
Customsearch.Cse.List list=customsearch.cse().list(keyword);
list.setKey(GOOGLE_API_KEY);
list.setCx(SEARCH_ENGINE_ID);
Search results=list.execute();
resultList=results.getItems();
}
catch ( Exception e) {
e.printStackTrace();
}
return resultList;
}
This method returns List of Result Objects, so you can iterate through it
List<Result> results = new ArrayList<>();
try {
results = search(QUERY);
} catch (Exception e) {
e.printStackTrace();
}
for(Result result : results){
System.out.println(result.getDisplayLink());
System.out.println(result.getTitle());
// all attributes
System.out.println(result.toString());
}
I use gradle dependencies
dependencies {
compile 'com.google.apis:google-api-services-customsearch:v1-rev57-1.23.0'
}
Don't forget to define your own GOOGLE_API_KEY, SEARCH_ENGINE_ID (cx), QUERY and HTTP_REQUEST_TIMEOUT (ie private static final int HTTP_REQUEST_TIMEOUT = 3 * 600000;)

error: cannot find symbol variable MyOpenWeatherMapApiKey Android

I am new to Android.I need help to solve the error below.
Got stuck here.
public class ForecastFragment extends Fragment {
private ArrayAdapter<String> mForecastAdapter;
public ForecastFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Add this line in order for this fragment to handle menu events.
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_refresh) {
FetchWeatherTask weatherTask = new FetchWeatherTask();
weatherTask.execute("94043");
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Create some dummy data for the ListView. Here's a sample weekly forecast
String[] data = {
"Mon 6/23 - Sunny - 31/17",
"Tue 6/24 - Foggy - 21/8",
"Wed 6/25 - Cloudy - 22/17",
"Thurs 6/26 - Rainy - 18/11",
"Fri 6/27 - Foggy - 21/10",
"Sat 6/28 - TRAPPED IN WEATHERSTATION - 23/18",
"Sun 6/29 - Sunny - 20/7"
};
List<String> weekForecast = new ArrayList<String>(Arrays.asList(data));
// Now that we have some dummy forecast data, create an ArrayAdapter.
// The ArrayAdapter will take data from a source (like our dummy forecast) and
// use it to populate the ListView it's attached to.
mForecastAdapter =
new ArrayAdapter<String>(
getActivity(), // The current context (this activity)
R.layout.list_item_forecast, // The name of the layout ID.
R.id.list_item_forecast_textview, // The ID of the textview to populate.
weekForecast);
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Get a reference to the ListView, and attach this adapter to it.
ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
listView.setAdapter(mForecastAdapter);
return rootView;
}
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();
/* The date/time conversion code is going to be moved outside the asynctask later,
* so for convenience we're breaking it out into its own method now.
*/
private String getReadableDateString(long time){
// Because the API returns a unix timestamp (measured in seconds),
// it must be converted to milliseconds in order to be converted to valid date.
SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd");
return shortenedDateFormat.format(time);
}
/**
* Prepare the weather high/lows for presentation.
*/
private String formatHighLows(double high, double low) {
// For presentation, assume the user doesn't care about tenths of a degree.
long roundedHigh = Math.round(high);
long roundedLow = Math.round(low);
String highLowStr = roundedHigh + "/" + roundedLow;
return highLowStr;
}
/**
* Take the String representing the complete forecast in JSON Format and
* pull out the data we need to construct the Strings needed for the wireframes.
*
* Fortunately parsing is easy: constructor takes the JSON string and converts it
* into an Object hierarchy for us.
*/
private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays)
throws JSONException {
// These are the names of the JSON objects that need to be extracted.
final String OWM_LIST = "list";
final String OWM_WEATHER = "weather";
final String OWM_TEMPERATURE = "temp";
final String OWM_MAX = "max";
final String OWM_MIN = "min";
final String OWM_DESCRIPTION = "main";
JSONObject forecastJson = new JSONObject(forecastJsonStr);
JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);
// OWM returns daily forecasts based upon the local time of the city that is being
// asked for, which means that we need to know the GMT offset to translate this data
// properly.
// Since this data is also sent in-order and the first day is always the
// current day, we're going to take advantage of that to get a nice
// normalized UTC date for all of our weather.
Time dayTime = new Time();
dayTime.setToNow();
// we start at the day returned by local time. Otherwise this is a mess.
int julianStartDay = Time.getJulianDay(System.currentTimeMillis(), dayTime.gmtoff);
// now we work exclusively in UTC
dayTime = new Time();
String[] resultStrs = new String[numDays];
for(int i = 0; i < weatherArray.length(); i++) {
// For now, using the format "Day, description, hi/low"
String day;
String description;
String highAndLow;
// Get the JSON object representing the day
JSONObject dayForecast = weatherArray.getJSONObject(i);
// The date/time is returned as a long. We need to convert that
// into something human-readable, since most people won't read "1400356800" as
// "this saturday".
long dateTime;
// Cheating to convert this to UTC time, which is what we want anyhow
dateTime = dayTime.setJulianDay(julianStartDay+i);
day = getReadableDateString(dateTime);
// description is in a child array called "weather", which is 1 element long.
JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0);
description = weatherObject.getString(OWM_DESCRIPTION);
// Temperatures are in a child object called "temp". Try not to name variables
// "temp" when working with temperature. It confuses everybody.
JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE);
double high = temperatureObject.getDouble(OWM_MAX);
double low = temperatureObject.getDouble(OWM_MIN);
highAndLow = formatHighLows(high, low);
resultStrs[i] = day + " - " + description + " - " + highAndLow;
}
for (String s : resultStrs) {
Log.v(LOG_TAG, "Forecast entry: " + s);
}
return resultStrs;
}
#Override
protected String[] doInBackground(String... params) {
// If there's no zip code, there's nothing to look up. Verify size of params.
if (params.length == 0) {
return null;
}
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String forecastJsonStr = null;
String format = "json";
String units = "metric";
int numDays = 7;
try {
// Construct the URL for the OpenWeatherMap query
// Possible parameters are avaiable at OWM's forecast API page, at
// http://openweathermap.org/API#forecast
final String FORECAST_BASE_URL =
"http://api.openweathermap.org/data/2.5/forecast/daily?";
final String QUERY_PARAM = "q";
final String FORMAT_PARAM = "mode";
final String UNITS_PARAM = "units";
final String DAYS_PARAM = "cnt";
final String APPID_PARAM = "02867cfd75153da1eda43a17f213ffc5";
Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon()
.appendQueryParameter(QUERY_PARAM, params[0])
.appendQueryParameter(FORMAT_PARAM, format)
.appendQueryParameter(UNITS_PARAM, units)
.appendQueryParameter(DAYS_PARAM, Integer.toString(numDays))
.appendQueryParameter(APPID_PARAM, BuildConfig.OPEN_WEATHER_MAP_API_KEY)
.build();
URL url = new URL(builtUri.toString());
Log.v(LOG_TAG, "Built URI " + builtUri.toString());
// Create the request to OpenWeatherMap, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
forecastJsonStr = buffer.toString();
Log.v(LOG_TAG, "Forecast string: " + forecastJsonStr);
} catch (IOException e) {
Log.e(LOG_TAG, "Error ", e);
// If the code didn't successfully get the weather data, there's no point in attemping
// to parse it.
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
try {
return getWeatherDataFromJson(forecastJsonStr, numDays);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
e.printStackTrace();
}
// This will only happen if there was an error getting or parsing the forecast.
return null;
}
}
}
}
}
[{
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.example.patels.sunshine";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from build type: debug
public static final String OPEN_WEATHER_MAP_API_KEY = MyOpenWeatherMapApiKey;
}
I have written this code....I got stuck here..Unable to solve the error.
Help me out....Thank you
Under the app folder you can find build.gradle file, in this make this below changes.
Since you are using a String you have to use this syntax:
it.buildConfigField "String" , "OPEN_WEATHER_MAP_API_KEY" , "\"MyOpenWeatherMapApiKey\""
The last parameter has to be a String.
You should create an account here http://openweathermap.org/ and when you register with your email, you get an APIKey for your account. Use this api key and replace the MyOpenWeatherMapApiKey in the following line:
public static final String OPEN_WEATHER_MAP_API_KEY = MyOpenWeatherMapApiKey;
Another alternative would be to write the APIKey in grandle file as proposed already.

How to get the Gherkin feature description runtime in java

I need to report the feature description for the scenario that is being executes to report to other system.
Was able to get the scenario name from cucumber.api.Scenario; how I can the feature description ?
Is there any interface that I can use?
Using cucumber-Jvm, get the feature description runtime; as each scenario being executed might be from different feature files.
You can get the description of a feature by retrieving the Gherkin feature from CucumberFeature:
List<CucumberFeature> cucumberFeatures = new ArrayList<>();
FeatureBuilder featureBuilder = new FeatureBuilder(cucumberFeatures);
featureBuilder.parse(new FileResource(featureFile.getParentFile(), featureFile), new ArrayList());
for (CucumberFeature feature: cucumberFeatures) {
// Here we retrieve the Gherkin model
Feature f = feature.getGherkinFeature();
// Here we get name and description of the feature.
System.out.format("%s: %s%n", f.getName(), f.getDescription());
}
Another solution is to implement your own formatter, and do the parsing with Gherkin directly:
public class MyFormatter implements Formatter {
private List<Feature> features = new ArrayList<>();
public static void main(String... args) throws Exception {
OutputStreamWriter out = new OutputStreamWriter(System.out, "UTF-8");
// Read the feature file into a string.
File f = new File("/path/to/file.feature");
String input = FixJava.readReader(new FileReader(f));
// Parse the gherkin string with our own formatter.
MyFormatter formatter = new MyFormatter();
Parser parser = new Parser(formatter);
parser.parse(input, f.getPath(), 0);
for (Feature feature: formatter.features) {
System.out.format("%s: %s%n", feature.getName(), feature.getDescription());
}
}
#Override
public void feature(Feature feature) {
features.add(feature);
}
// ...
// follow all the Formatter methods to implement.
}

How to pass parameters to a CodeActivity in a NativeActivity code sequence

I'm trying to get windows workflows working, and I've become a little stumped.
I've gotten a single workflow working, but now I am trying to do something a little more complex: start a workflow, where each activity itself contains a workflow. (Picture something like the main program starts the activities "Input, logic, and output", and then each of those have additional activities like "prompt user, get input, etc.")
I've had it working fine, with the example from here (http://msdn.microsoft.com/en-us/magazine/gg535667.aspx), when I am not passing any parameters from the main program to the activites. My question is, how exactly does the 'Variables' and 'metadata.SetVariablesCollection' work in the NativeActivity, and how to I get the parameters to the low level activities?
This is what I am currently trying:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Collections.ObjectModel;
using System.Activities.Statements;
namespace Project1
{
internal class MainProgram
{
internal static void Main(string[] args)
{
try
{
var act = new SimpleSequence();
act.Activities.Add((Activity)(new WriteSomeText()));
act.Activities.Add((Activity)(new WriteSomeText()));
act.Activities.Add((Activity)(new WriteSomeText()));
act.Variables.Add(new Variable<string> ("stringArg", "TEXT"));
WorkflowInvoker.Invoke(act);
}
catch (Exception ex)
{
System.Console.WriteLine("EXCEPTION: {0}", ex);
}
}
public class WriteSomeText : CodeActivity
{
[RequiredArgument]
public InArgument<string> stringArg { get; set; }
protected override void Execute(CodeActivityContext context)
{
string output = context.GetValue(stringArg);
System.Console.WriteLine(output);
}
}
public class SimpleSequence : NativeActivity
{
Collection<Activity> activities;
Collection<Variable> variables;
Variable<int> current = new Variable<int> { Default = 0 };
public Collection<Activity> Activities
{
get
{
if (this.activities == null)
this.activities = new Collection<Activity>();
return this.activities;
}
set
{
this.activities = value;
}
}
public Collection<Variable> Variables
{
get
{
if (this.variables == null)
this.variables = new Collection<Variable>();
return this.variables;
}
set
{
this.variables = value;
}
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.SetChildrenCollection(this.activities);
metadata.SetVariablesCollection(this.variables);
metadata.AddImplementationVariable(this.current);
}
protected override void Execute(NativeActivityContext context)
{
if (this.Activities.Count > 0)
context.ScheduleActivity(this.Activities[0], onChildComplete);
}
void onChildComplete(NativeActivityContext context, ActivityInstance completed)
{
int currentExecutingActivity = this.current.Get(context);
int next = currentExecutingActivity + 1;
if (next < this.Activities.Count)
{
context.ScheduleActivity(this.Activities[next], this.onChildComplete);
this.current.Set(context, next);
}
}
}
}
}
This ends up throwing the following exception:
EXCEPTION: System.Activities.InvalidWorkflowException: The following errors were encountered while processing the workflow tree:
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
'WriteSomeText': Value for a required activity argument 'stringArg' was not supplied.
at System.Activities.Validation.ActivityValidationServices.ThrowIfViolationsExist(IList`1 validationErrors)
at System.Activities.Hosting.WorkflowInstance.ValidateWorkflow(WorkflowInstanceExtensionManager extensionManager)
at System.Activities.Hosting.WorkflowInstance.RegisterExtensionManager(WorkflowInstanceExtensionManager extensionManager)
at System.Activities.WorkflowApplication.EnsureInitialized()
at System.Activities.WorkflowApplication.RunInstance(WorkflowApplication instance)
at System.Activities.WorkflowApplication.Invoke(Activity activity, IDictionary`2 inputs, WorkflowInstanceExtensionManager extensions, TimeSpan timeout)
at System.Activities.WorkflowInvoker.Invoke(Activity workflow, TimeSpan timeout, WorkflowInstanceExtensionManager extensions)
at System.Activities.WorkflowInvoker.Invoke(Activity workflow)
at Project1.MainProgram.Main(String[] args) in c:\users\user\documents\visual studio 2010\Projects\ModelingProject1\Project1\MainProgram.cs:line 25
I know, I only pass 1 parameter, but the exception still says that I am missing 3 parameters. I am missing something as to how to do this properly.
You're correctly declaring stringArg as an InArgument but you're not passing any value to it when calling it inside SimpleSequence.
You can pass something using the constructor, while constructing the all activity itself, like this:
public class WriteSomeText : CodeActivity
{
[RequiredArgument]
public InArgument<string> stringArg { get; set; }
public WriteSomeText(string stringArg)
{
this.stringArg = stringArg;
}
protected override void Execute(CodeActivityContext context
{
string output = context.GetValue(stringArg);
System.Console.WriteLine(output);
}
}
// Calling the activity like this:
internal static void Main(string[] args)
{
var act = new SimpleSequence()
{
Activities =
{
new WriteSomeText("hello"),
new WriteSomeText("world"),
new WriteSomeText("!")
}
};
WorkflowInvoker.Invoke(act);
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
Also notice that is a best practice to use the constructor to initialize collections:
public SimpleSequence()
{
activities = new Collection<Activity>();
variables = new Collection<Variable>();
}
This way is even more intuitive to initialize the activity:
var act = new SimpleSequence()
{
Activities =
{
new WriteSomeText("hello"),
new WriteSomeText("world"),
new WriteSomeText("!")
},
Variables =
{
new Variable<int>("myNewIntVar", 10),
// ....
}
};
EDIT:
There are a couple of other ways to approach the problem. This is your best friend while starting in the WF4 world.
Check WF\Basic\CustomActivities\Code-Bodied for a little push with this particular case.

Resources