Undo Operation fails on Repeated Use - c#

I created an undo operation to set the current Value to the previous one that was within a list. It accomplishes this by removing the most recent index from the list and setting the value to the value that is behind it. Currently it works when there no value with in the list at initialization, as well as if there is more than one value within the list.
The code the way it is works when there is at least one index in the array, as previous unit tests pass this.
The problem is occurring where I try to have the InvalidOperationException to pass when the command is run twice, after the last item in the list has also been removed. so Add(5) -> Undo() -> Undo() to give an example. Also changing the if statement from valDict.Count > 0 to Value > 0, the same error occurs.
List<int> valDict = new List<int>();
public void Undo()
{
repOp1 = "Undo";
Console.WriteLine("1: " + valDict.Count);
if ( valDict.Count > 0)
{
int temp = valDict.Count - 1;
Console.WriteLine("2: " + temp);
valDict.RemoveAt(temp);
Console.WriteLine("3: " + valDict.Count);
valDict.TrimExcess();
//Below Line is flagged as the error
Value = valDict[valDict.Count-1];
}
else
{
throw new InvalidOperationException();
}
}
Can someone looking at this with fresh eyes, point out a possible solution, but not a fixed implementation, as I need to fix the implementation myself.

You can control that your List is not empty before getting the Value: list.Count==0, or !list.Any().

Related

Changing MultiAutoCompleteTextView Adapter Mid-phrase

When the user is typing something into my MultiAutoCompleteTextView, I re-check the list of available phrases after each space. Some of my suggested values contain spaces, and I expect that the user will be able to type a space and have the suggested values continue to list only strings that begin with the entered word, so long as my ITokenizer's FindTokenStart function returns the position representing the beginning of the word before the space. This is actually working in cases where I don't change the Adapter to a different value when typing space. But there's a problem if it changes when typing space.
For example, I have the phrase "Lunch Token" in my suggested value list, and as I begin to type it, everything looks fine:
It continues to look good up through the end of the word "Lunch". Then I add a space, and I have some logic that detects that because the full word lunch has been entered, a couple of the options are no longer valid. So it switches the Adapter property of the MultiAutoCompleteTextView to a different adapter that has fewer words in it. But doing this while the list is popped up appears to cause the list to get populated with all the possible values:
I have added code in my FindTokenStart function to verify that is is returning the right values, and it is. So it must have something to do with changing the adapter while the list is popped up. How do I work around this and force the list to continue to only display matching terms? I tried only updating the Adapter when the new list was not a subset of the prior list, but this causes other problems (I can't remember them at the moment).
Highest level code that changes the Adapter property:
private void Command_AfterTextChanged(object sender, Android.Text.AfterTextChangedEventArgs e)
{
try
{
if (!(command.Text.EndsWith(" ") || command.Text.Length == 0))
return;
var words = new SuggestionCache.WordList();
if (command.Text.Length > 0)
words.AddFromParserStates(parser.ParsePartial(command.Text, currentStory));
else
words.AddFromSyntaxes(Syntax.currentSyntaxes);
command.Adapter = suggestions.GetArrayAdapter(words);
}
catch (Exception ex)
{
ShowMessage(ex.Message);
}
}
FindTokenStart implementation:
public int FindTokenStart(ICharSequence text, int cursor)
{
System.Diagnostics.Debug.Write("FindTokenStart: ");
if ((currentWordList == null) || (currentWordList.tokenBounds.Count == 0))
{
System.Diagnostics.Debug.WriteLine("A 0");
return 0;
}
int idx = currentWordList.tokenBounds.BinarySearch(new Syntax.SyntaxMatch() { startPos = cursor }, SyntaxMatchComparer.EndComparer);
if (idx >= 0)
{
System.Diagnostics.Debug.WriteLine("B {0}", currentWordList.tokenBounds[idx].startPos);
return currentWordList.tokenBounds[idx].startPos;
}
if (~idx < currentWordList.tokenBounds.Count)
{
System.Diagnostics.Debug.WriteLine("C {0}", currentWordList.tokenBounds[~idx].startPos);
return currentWordList.tokenBounds[~idx].startPos;
}
var lastBound = currentWordList.tokenBounds.LastOrDefault((b) => b.matched == 2);
if (lastBound.NextPos <= cursor)
{
System.Diagnostics.Debug.WriteLine("D {0}", lastBound.NextPos);
return lastBound.NextPos;
}
else
{
System.Diagnostics.Debug.WriteLine("E {0}", lastBound.startPos);
return lastBound.startPos;
}
}
Debug output while pressing the space key:
[0:] FindTokenStart:
[0:] D 5
[0:] FindTokenStart:
[0:] D 5
[0:] FindTokenStart:
[0:] D 5
[0:] FindTokenStart:
[0:] B 5
01-11 19:08:10.743 E/Surface ( 3326): getSlotFromBufferLocked: unknown buffer: 0x9a5e7d60
01-11 19:08:10.759 E/EGL_emulation( 3326): tid 3369: eglSurfaceAttrib(1210): error 0x3009 (EGL_BAD_MATCH)
01-11 19:08:10.759 W/OpenGLRenderer( 3326): Failed to set EGL_SWAP_BEHAVIOR on surface 0x98422b20, error=EGL_BAD_MATCH
I'm using Visual Studio 2017 and Xamarin top develop this Android application, and testing it on the Android emulator if it matters.
It shouldn't be necessary to change the adapter for a MultiAutoCompleteTextView mid-phrase. The expectation is that all the options are given to the adapter, and the adapter filters that list as more characters are provided, determining which options are still relevant within the current token/phrase. The reason I was changing adapters so often was out of laziness about determining the appropriate times to determine what new adapter should be applied. It shouldn't be based only on a space separator if space is not always the terminator of a token.
My solution was to make the code smarter in trying to ensure that the adapter is only updated at the separation between two tokens. This was relatively easy to accomplish because my existing parser knew where the last token ended and where the next should begin. When these were the same, it means that the token was still incomplete. Once a complete token value was entered and terminated by whitespace, the end of the last token would be less than the beginning of the next token. So by adding this condition before updating the adapter, my problem is resolved:
if (words.tokenBounds.Count == 0 || words.tokenBounds.Last().NextPos > words.tokenBounds.Last().EndPos)
command.Adapter = suggestions.GetArrayAdapter(words);

calling an array index with a variable

I am trying to test a specific element from a list, and compare it to a string the user inputs. It is inside an if statement:
    if (textboxinput.Text.ToString() == main.steps[current])
In this context, current is an integer equal to 0, and after the if statement is done current++.
this is a new wpf form window that is opened by clicking a button on the MainWindow wpf form. main is instantiated from the MainWindow1 class containing our list:
public List<string> steps = new List<string>();
Whenever I run this code, I receive the argument out of range exception.
In short: I am trying substitute the index of the list with a variable, and it is saying it is out of range.
Still learning c#, and this is probably an easy fix, and it's probably a super obvious rule I'm forgetting, but any help would be appreciated, as the same exception happens on arrays as well.
Here is the event for the button being clicked
int current = 0;
private void buttonanswer_Click(object sender, RoutedEventArgs e)
{
if (textboxinput.Text.ToString() == main.steps[current])
{
textboxoutput.Foreground = Brushes.Green;
textboxoutput.AppendText(textboxinput + Environment.NewLine);
textboxinput.Clear();
}
else
{
textboxoutput.Foreground = Brushes.Red;
textboxoutput.AppendText(textboxinput + Environment.NewLine);
textboxinput.Clear();
}
current++;
}
Make sure that when you're looping current, it is always less than the length of the main.steps array. Remember, if an array contains 5 elements, they are numbered 0, 1, 2, 3, and 4 for example. Most likely you are allowing current to reach the number 5 (or whatever is the size of your array in your particular case). This type of mistake would be what causes an out of bounds exception when working with an array.

Out of bounds exception whilst using arrays

I have an array of textboxes in which they change dyanmically depending on what the user types in. Those textboxes contain a number which represents a score of an assignment. Those score are linked to a module object. So if the user has 3 modules; 2 assignments on the first and second module and 3 assignments on the third module; then in total there would be 7 textboxes created for the user to input all their assignment marks.
What I am trying to do is to create a keyup event handler in which it gets the number in typed in by the user, and then dynamically calls a method to display the average of the the module. This is what I have so far. The following method gets called whenever the user types in a character:
public void calculateLevel4Modules(int counter) {
//iterate through modules
//iterate through assignts in that module
//whilst iterating, check tb and set userscore
//after iterating, update overall label with regards to modulecounter
//int assignmentCounter = 0;
//Console.WriteLine("in If statement.. " + counter);
for (int moduleCounter = 0; moduleCounter < requiredLevelList().Count; moduleCounter++)
{
int totalNumberOfAssignmentsInCurrentModule = requiredLevelList().ElementAt(moduleCounter).Assignments.Count;
Console.WriteLine("total number of assignmetns: " + totalNumberOfAssignmentsInCurrentModule);
assignmentCounter = assignmentCounter + totalNumberOfAssignmentsInCurrentModule;
Console.WriteLine("assignment counter: " + totalNumberOfAssignmentsInCurrentModule);
if (counter < assignmentCounter)
{
Console.WriteLine("in If statement.. " + userMarksTBLvl4[moduleCounter].Text);
try
{
int userMark = int.Parse(userMarksTBLvl4[counter].Text);
requiredLevelList().ElementAt(moduleCounter).Assignments.ElementAt(counter).UsersScore = userMark;
double modAvg = requiredLevelList().ElementAt(moduleCounter).getModuleScoreOverall();
moduleOverallLvl4[moduleCounter].Text = modAvg.ToString();
break;
}
catch (FormatException) { break; }
}
else { }
}
it works fine if the user has one module but if the user has two or more, then I get an error in the following line:
requiredLevelList().ElementAt(moduleCounter).Assignments.ElementAt(counter).UsersScore = userMark;
I am getting an out of bounds exception. I know why; its because counter is basically the # of the textbox that was typed into but by me using counter, I am accessing something not within the assignments list. This is an example of when the problem occus:
The user has 2 modules. In each module there are 2 assignments thus 4 textboxes are been created with their index ranging from 0 - 3. If the user wants to type in their score of the first assignment on the second module, its basically trying to write to the third index in that element then it crashes since that module only consist of 2 assignments.
There are some strange things in your code that make it hard to answer. First, the code you posted doesn't compile, so we have no way to test it.
Several times you use code like:
requiredLevelList().ElementAt(moduleCounter)
I assume requiredLevelList is a method that returns a list of things. There is no reason to assume requiredLevelList returns the same list, or even lists with the same number of elements, each time you call it. Maybe it does in your particular case, but this is a dangerous thing to rely on. You should use a construct like:
foreach (var module in requiredLevelList())
{
int totalNumberOfAssignmentsInCurrentModule = module.Assignments.Count;
...
module.Assignments.ElementAt(counter).UsersScore = userMark;
...
}
Code like this:
Console.WriteLine("total number of assignmetns: " + totalNumberOfAssignmentsInCurrentModule);
is symptomatic of trying to debug something after it has crashed. That is extremely inefficient. Learn how to use a debugger; you will not become an effective programmer until you know how to do this.
requiredLevelList().ElementAt(moduleCounter).Assignments.ElementAt(counter).UsersScore = userMark;
You're probably getting an out-of-bounds exception here because counter is outside the indexes of Assignments. Since you never initialize or change counter, I have no way to know what it is or should be. A debugger will tell you this, use one.
the # of the textbox that was typed into but by me using counter, I am accessing something not within the assignments list.
OK, if you're typing something “not within the assignments list” then you have to test for that and decide what to do. Perhaps something like:
if (counter >= 0 && counter < module.Assignments.Count)
module.Assignments.ElementAt(counter).UsersScore = userMark;
else
throw new Exception("I really have no idea what you want to do here.");
This also looks wrong:
moduleOverallLvl4[moduleCounter].Text = modAvg.ToString();
You never tell us what moduleOverallLvl4 is, but here you're assuming it has the same size as what is returned by requiredLevelList(). Maybe they are in this particular case, but that is a dangerous assumption. If these values are related, moduleOverallLvl4 should be contained in whatever class implements requiredLevelList, and you should have a method that assigns getModuleScoreOverall() to the correct element of moduleOverallLvl4.

Selenium webdriver System Invalid Cast Exception

To collect some test data for a neural net in C# I want to use Selenium to scrape some dynamically generated data WSJ. There is an example implementation on the Selenium site that seems to do exactly what I need Finding all the input elements to the every label on a page. The example searches on TagName, I search on ClassName, but other than that, I think it's identical.
Yet, when I run this code, creating an IList with IWebElements works, but the following IJavaScriptExecutor throws an Invalid Cast exception:
Unable to cast object of type
System.Collections.ObjectModel.ReadOnlyCollection 1[System.Object]
to type
System.Collections.Generic.IList 1[OpenQA.Selenium.IWebElement]
Here's a bit of the code, this is for "text", I do the same for "num":
// Find elements by class name
IList<IWebElement> labels = driver.FindElements(By.ClassName("text"));
// get all input elements for every class label
IList<IWebElement> labVals = (IList<IWebElement>)((IJavaScriptExecutor)driver).ExecuteScript(
"var labels = arguments[0], labVals = []; for (var i=0; i < labels.length; i++){" +
"labVals.push(document.getElementById(labels[i].getAttribute('for'))); } return labVals;", labels);
I have looked at this question Selenium Web Driver C# InvalidCastException which may point to the same problem, but I don't see how the answers provided can help me.
An option could be to break up the IJavaScriptExecutor statement into "discrete" code with a work around, but I would not know how to do that.
Once I have the text labels and data values both in a List structure, I should be able to find the numbers I need.
This is not using javascript, but it will work.
I would use a CssSelector method that receives throught parameters which column/row you need, and then you would be calling this method with loop to get all info from the page.
Checking the css of the page this is what I get from the first column/row
table.mdcTable > tbody > tr:nth-of-type(3) > td:nth-of-type(1)
So, the number "3" is related to the first row, and "1" is the first column. So we can make a method to return the exact element you want:
public IWebElement test(int line, int row)
{
return driver.FindElement(By.CssSelector(string.Format("table.mdcTable > tbody > tr:nth-of-type({}) > td:nth-of-type({})", line + 2, row)));
}
Calling this method will return the element that has the text, so all you need to do is use 'element.Text' to the the value of the 'cell', or make the method return the text directly.
public String test(int line, int row)
{
return driver.FindElement(By.CssSelector(string.Format("table.mdcTable > tbody > tr:nth-of-type({}) > td:nth-of-type({})", line + 2, row))).Text;
}
The only problem would be with the columns "Latest", because they do not only contain the numbers, but a bar. You would have to create a method to take care only of these column.
This would end up with something like this:
try
{
int line = 1;
int column = 1;
while(column <= 7)
valueOfTheCell = test(line, column);
getLatestGreen(line); //string.Format("tbody > tr:nth-of-type({0}) > td:nth-of-type(9) > span.text", line)
getLatestRed(line); //string.Format("tbody > tr:nth-of-type({0}) > td:nth-of-type(8) > span.text > b", line)
}
catch (NoSuchElementException)
{
//Exception will be thrown when the code reaches the end of the list
}
I won't say this is optimal, but it is an option.
If you want to do this way I can help you with any question or problem about how to use the selector.
The cast error occurs because the IJavascriptExecutor outputs the general System.Object class MSDN which I then try to cast to an IWebElement. This may work in some cases, but in this case it does not. Changing the receiving IList to IList<Object> solves the cast exception. With this the code runs, and then I found out with the debugger that all the data is captured with the first part of the code in the Labels list. The IJavaScriptExecutor returns null items only. So the second step is not required in my case.

How to read a list when writing to a Byte Array

I am using C#, Google Play Game Services, and Unity. I need to be able to write from a list which contains floats, to a byte array, then read them again in another method. I can make it so it stores the floats in the list of course (I can see it in my output as well), but when I print the Count of the list when trying to write to the byte array it tells me my list Count is zero... Its odd because when I check the list anywhere else outside this method it gives me the correct size. What am I doing wrong?
I have added the portions of code from the two other classes where I am adding items to the list. Again, I already know it successfully is added to the list, it just wont give me the correction count when im in the ToByte method.
Match Data Class----
private List<float> mRuneTypes = new List<float>();
public byte[] ToBytes() { //here is where I am trying to write to the mem stream
MemoryStream memStream = new MemoryStream();
BinaryWriter w = new BinaryWriter(memStream);
w.Write(Header); //writes header ID to memory
w.Write(mRuneTypes.Count); //this shows up as zero but everywhere else as 4
for (int i =0; i<4; i++) {
w.Write(mRuneTypes[i]); //i get an index out of bounds since list is empty?
Debug.Log( "Writing the color val is: " + mRuneTypes[i]);
}
networkStatus = "has wrote to mem"; //just something for me to check
w.Close();
byte[] buf = memStream.GetBuffer();
memStream.Close();
return buf;
}
public void AddRuneTypes(float runeIntDesignation) { //this is where the values are passed and added to the list
mRuneTypes.Add(runeIntDesignation);
Debug.Log ("the before tobyte count is : " + mRuneTypes.Count); //gives the correct count of 4... why isnt it the same in ToByte[]??
}
/////from TurnedBasedGameOp Class
public void passRunes(float runeIntDesignation) {
//sent runes are passed from the block selector to this class to matchdatarunes
mMatchData.AddRuneTypes (runeIntDesignation);
print ("rune type of " + runeIntDesignation + " has been added");
}
/////from Block Selection Class
this.GetComponent<TurnedBasedGameOrganizer>().passRunes(selectedBlocks[i].gameObject.GetComponent<MeteorCode>().runeIntDesignation);//this sends the runes selected to the TurnedBasedGameOp Class above this code is contained in a larger For loop which doesnt need to be included
This right here is bad...
for (int i =0; i < 4; i++)
The reason it's bad is because right from the get-go, you're presuming that mRuneTypes contains a collection of items, but from the code that I can see, all that you've done is initialized a collection, you haven't populated it.
If you're attempting to iterate through every item within the list, either use the foreach statement or use the for statement that will only increment up to the total number of items within the collection:
int i = 0; i < mRuneTypes.Count; i++`.
Another tip that may help you is this little helper method whilst you're debugging...
public static class Extension
{
public static T Dump<T>(this T tObject)
{
tObject.GetType()
.GetProperties()
.Where(property => property.GetValue(tObject) != null)
.Select(property => string.Format("{0}: {1}", property.Name, property.GetValue(tObject)))
.ToList()
.ForEach(Console.WriteLine);
return tObject;
}
}
At the end of any object just add .Dump() and observe the output to see the properties of the object...
In this code snippet the mRuneTypes list was empty, I think you need paste the code which you fill the list data so that we could try to find out the reason why it doesn't work.
And for the for loop statement, don't use the magic number "4", please using the following code instead yours, so the out of index exception will be gone.
for(int i=0; i<mRuneTypes.count; i++){
// Do your logic code.
}
Wish it will be helpful.
Thanks.
I figured out the answer. It had something to do with the turnbasedgameorganizer class that i was using to send data to the match data runes class. I just ended up combining them.

Categories

Resources