ANTLR4 Listener "different context" issue with labels - listener

I have a problem with my listener :
I am using a ParseTreeProperty to store "anything" (it is basically a Map< ParserRuleContext,MyObject >): private ParseTreeProperty< MyObject > parseTreeProperty;
I store the variable in the enterEveryRule method : parseTreeProperty.put(ctx, myObject);
I get it back on the exitEveryRule method : MyObject myObject = parseTreeProperty.get(ctx);
This works perfectly, except with labels.
Any grammar with labels :
general_rule
: rule_1 #label_1
| rule_2 #label_2
;
Problem :
when the listener enters the method enterEveryRule(ParserRuleContext ctx), the context ctx is General_ruleContext.
when the listener enters the method exitEveryRule(ParserRuleContext ctx), the context ctx is Label_1Context/Label_2Context.
Since the context is different between the enterEveryRule and exitEveryRule, the ParseTreeProperty doesn't work as intended with labels.
Why isn't the entering context the "label rule" or the exiting context the "main rule" ?
How can I solve that ?
Thanks.

In your case, in the generated listener, the results of the tree walk walker alternatives of "general_rule" will be in the same enterGeneral_rule() and exitGeneral_rule() methods.
But with the labels that you're using the methods will be like:
void enterLabel_1();
void exitLabel_1();
void enterLabel_2();
void exitLabel_2();

Related

Is it possible to take a void as argument in processing

I am making a button class and I would like it to take a void as an argument(in the constroctor) so that the void gets called when the user clicks it. So that I don't have to void mouseClicked() {if (button.mouseIsIn()) {doIt();} and it automatically gets called when the user clicks it. Like Button button = new Button(x,y,width,height,handleMouseClick());. Thanks in advance.
First of all, there's no such thing as "a void" - you can have a function with a void return type, but it doesn't make sense to call them voids.
That being said, this depends on whether you're using Java or JavaScript mode. Assuming you're using Java mode, then the answer is no, you can't pass functions as parameters. What you can do instead is pass an instance of a class, and then call the functions of that class. Something like this:
class MyAction{
void doIt(){
//whatever
}
}
Once you have this class defined, you can create an instance of it using the new keyword:
MyAction myAction = new MyAction();
Which you can then pass into your Button constructor:
Button button = new Button(x, y, width, height, myAction);
Then in your Button class, you can simply call myAction.doIt() whenever you want to call the function:
void mouseClicked() {
if (button.mouseIsIn()) {
myAction.doIt();
}
}
You can combine this approach with anonymous inner classes to allow you to create MyAction instances on the fly with their own implementation of doIt(). Something like this:
MyAction actionOne = new MyAction(){
void doIt(){
doThingOne();
}
}
Button buttonOne = new Button(1, 2, 3, 4, actionOne);
MyAction actionTwo = new MyAction(){
void doIt(){
doThingTwo();
}
}
Button buttonTwo = new Button(1, 2, 3, 4, actionTwo);
If you're using JavaScript mode, then you can just pass functions as arguments directly.
More info can be found by googling "Java pass function as parameter" or "JavaScript pass function as parameter".

How to do CreateBindingSet() on Windows Phone?

In the N+1 video #34 (Progress), there was an example of using CreateBindingSet() for the Android version, which is not typical. But the narrator also mentioned briefly that the same can be done on the Windows platform.
As much as I tried, however, I am unable to get a View's property to be bound to its ModelView on the Windows Phone. I always get a NullReferenceException.
The closest I came was the code below, including suggestions from ReSharper. Here's my FirstView.xaml.cs:
using Cirrious.MvvmCross.Binding.BindingContext;
using Whatever.ViewModels;
namespace Whatever {
// inheriting from IMvxBindingContextOwner was suggested by ReSharper also
public partial class FirstView : BaseView, IMvxBindingContextOwner {
public class MyBindableMediaElement
{
private string _theMediaSource = "whatever";
public string TheMediaSource
{
get
{
return _theMediaSource;
}
set
{
_theMediaSource = value;
}
}
}
public FirstView()
{
InitializeComponent();
_mediaElement = new MyBindableMediaElement(this.theMediaElement);
var set = this.CreateBindingSet<FirstView, FirstViewModel>();
// the corresponding view model has a .SongToPlay property with get/set defined
set.Bind(_mediaElement).For(v => v.TheMediaSource).To(vm => vm.SongToPlay);
set.Apply();
}
public IMvxBindingContext BindingContext { get; set; } // this was suggested by ReSharper
}
I get a NullReferenceException in MvxBaseFluentBindingDescription.cs as soon as the view is created. The exact location is below:
protected static string TargetPropertyName(Expression<Func<TTarget, object>> targetPropertyPath)
{
var parser = MvxBindingSingletonCache.Instance.PropertyExpressionParser; // <----- exception here**
var targetPropertyName = parser.Parse(targetPropertyPath).Print();
return targetPropertyName;
}
I have not seen a working example of creating a binding set on a Windows Phone emulator. Has anyone gotten this to work? Thanks.
I can confirm that the narrator said that remark a little too flippantly without actually thinking about how he might do it...
However, with a little effort, you definitely can get the CreateBindingSet to work in Windows if you want to.
Before you start, do consider some alternatives - in particular, I suspect most people will use either Windows DependencyProperty binding or some hand-crafted code-behind with a PropertyChanged event subscription.
If you do want to add CreateBindingSet code to a Windows project then:
Add the Binding and BindingEx assemblies to your Ui project - the easiest way to do this is using nuget to add the BindingEx package.
In your Setup class, override InitializeLastChance and use this opportunity to create a MvxWindowsBindingBuilder instance and to call DoRegistration on that builder. Both these first two steps are covered in the n=35 Tibet binding video - and it's this second step that will initialise the binding framework and help you get past your current 'NullReferenceException' (for the code, see BindMe.Store/Setup.cs)
In your view, you'll need to implement the IMvxBindingContextOwner interface and you'll need to ensure the binding context gets created. You should be able to do this as simply as BindingContext = new MvxBindingContext();
In your view, you'll need to make sure the binding context is given the same DataContext (view model) as the windows DataContext. For a Phone Page, the easiest way to do this is probably just to add BindingContext.DataContext = this.ViewModel; to the end of your phone page's OnNavigatedTo method. Both steps 3 and 4 could go in your BaseView if you intend to use Mvx Binding in other classes too.
With this done, you should be able to use the CreateBindingSet code - although do make sure that all binding is done after the new MvxBindingContext() has been created.
I've not got a windows machine with me right now so I'm afraid this answer code comes untested - please do post again if it does or doesn't work.
I can confirm it works almost perfectly; the only problem is, there are no defaults register, so one has to do the full binding like:
set.Bind(PageText).For(c => c.Text).To(vm => vm.Contents.PageText).OneTime();
to fix this, instead of registering MvxWindowsBindingBuilder, I am registering the following class. Note: I have just created this class, and needs testing.
public class UpdatedMvxWindowsBindingBuilder : MvxWindowsBindingBuilder
{
protected override void FillDefaultBindingNames(IMvxBindingNameRegistry registry)
{
base.FillDefaultBindingNames(registry);
registry.AddOrOverwrite(typeof(Button), "Command");
registry.AddOrOverwrite(typeof(HyperlinkButton), "Command");
//registry.AddOrOverwrite(typeof(UIBarButtonItem), "Clicked");
//registry.AddOrOverwrite(typeof(UISearchBar), "Text");
//registry.AddOrOverwrite(typeof(UITextField), "Text");
registry.AddOrOverwrite(typeof(TextBlock), "Text");
//registry.AddOrOverwrite(typeof(UILabel), "Text");
//registry.AddOrOverwrite(typeof(MvxCollectionViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxTableViewSource), "ItemsSource");
//registry.AddOrOverwrite(typeof(MvxImageView), "ImageUrl");
//registry.AddOrOverwrite(typeof(UIImageView), "Image");
//registry.AddOrOverwrite(typeof(UIDatePicker), "Date");
//registry.AddOrOverwrite(typeof(UISlider), "Value");
//registry.AddOrOverwrite(typeof(UISwitch), "On");
//registry.AddOrOverwrite(typeof(UIProgressView), "Progress");
//registry.AddOrOverwrite(typeof(IMvxImageHelper<UIImage>), "ImageUrl");
//registry.AddOrOverwrite(typeof(MvxImageViewLoader), "ImageUrl");
//if (_fillBindingNamesAction != null)
// _fillBindingNamesAction(registry);
}
}
This is a skeleton from Touch binding, and so far I have only updated three controls to test out (Button, HyperButton and TextBlock)

Is there a way to pass 'self' or 'this' when calling a function inline in Flex for current component?

I hope I have worded the question ok.
I know that you can pass the 'id' of the component but I'm just wondering if there is a more generic way. I have tried using 'this' (shown in example below) but the 'this' keyword refers to the application.
Example:
<mx:Button click="someFunc(this)"/>
And the following function call doesn't work (as it's the wrong type):
someFunc(comp : UIComponent) : void {
comp.label = 'Change label';
}
If I have 20 components I'd like to be able to call the same function without using 'id' for the different components.
Is there a way?
the 'this' keyword refers to the application.
In an MXML component, The this" keyword will refer to the "top level" tag; which will not be the application in many situations.
In your case, I would pass in the click event and use the target or currentTarget properties to figure out which component was clicked:
<mx:Button click="someFunc(event)"/>
someFunc(event:MouseEvent) : void {
trace(event.target);
trace(event.currentTarget);
(event.target as Button).label = "Change Label";
}
I'm pretty sure that you'll need target; as that will always be the component that dispatched the event.

AS3: How to know if dataprovider or its content(s) is changed

I'm implementing some kind of combobox control (by extending spark.components.supportClasses.DropDownListBase)
Now, inside this control; I need to know:
if the dataprovider is changed/assigned. (which I can do... the first approach below works);
if any item in the dataprovider collection has changed.
I tried 2 methods that did not do the trick...
1ST APPROACH:
[Bindable("collectionChange")]
override public function set dataProvider(value:IList):void
{
if (value) value.addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataChange);
super.dataProvider = value;
trace("DATA CHANGED"); //fires
}
protected function onDataChange(event:CollectionEvent):void
{
trace("COLLECTION ITEM(S) CHANGED"); //does not fire
}
2ND APPROACH:
Since this is based on DropDownListBase; it should dispatch the CollectionEvent.COLLECTION_CHANGE event already..?
public function myClass() //constructor
{
addEventListener(CollectionEvent.COLLECTION_CHANGE, onDataChange);
}
protected function onDataChange(event:CollectionEvent):void
{
trace("DATA CHANGED"); //does not fire
}
Any ideas?
UPDATE: Edited above.. The first approach lets me know if the dataprovider is changed but not if any item is updated in the dataprovider collection. The second approach does not work at all..
We'll be able to help significantly more if you show us a runnable sample to demonstrate the problem.
if the dataprovider is
changed/assigned.
Your first approach should work. Can you tell us what makes you think it didn't? ( No trace statement I assume? ). And tell us what you did to change the dataProvider.
The second approach won't work because myClass is not firing off the collectionChange event.
2 if any item in the dataprovider collection has changed.
There is not really a way to tell this. In most cases, a collection is just a list of pointers to other objects. If you change those pointers, then a collectionChange event is fired. IF you change the item that he pointer is pointed to, the collection has no way to know that something changed. Binding works very similarly if your an MXML fan.
If you have control over how items are changed, you can deal with it that way. Instead of:
(collection.getITemAt(x) as myObject).property = newValue;
Do something like this:
var myObject : MyObject = collection.getITemAt(x) as myObject
myObject.property = newValue;
collection.setItemAt(x, myObject);
I would expect that to fire a collectionChange event, but not the former.
That said, in the context of a dropDownListBase: As you scroll or open and close the drop down, the itemRenderers should be updated to reflect the most current dataProvider's data. But if you change something on the fly while the drop down is open, I would not expect it to update automatically [if you're not changing the dataProvider.

DependencyProperty PropertyChangedCallback causes NullReferenceException in XAML

I've got a subclassed UserControl that is the content for my main window. I added a DepedencyProperty to my usercontrol, of type ResizeMode, and the PropertyChanged callback sets the ResizeMode of the main window to the value correctly. It runs fine. I can set it from the code behind, or from the XAML, and it works correctly.
However, when I set it from XAML, the designer throws an Object reference not set to an instance of an object exception, on the code in the PropertyChanged callback that sets the window's resize.
<classes:MyUserControl ResizeMode="NoResize">
<...>
</classes:MyUserControl>
This is the callback. MainWindow is a reference to the parent window.
private static void OnResizeModeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as MyUserControl).MainWindow.ResizeMode = (ResizeMode) e.NewValue;
}
public ResizeMode ResizeMode
{
get { return (ResizeMode) GetValue(ResizeModeProperty); }
set { SetValue(ResizeModeProperty, value); }
}
public static readonly DependencyProperty ResizeModeProperty =
DependencyProperty.Register("SizeToFit", typeof(ResizeMode), typeof(MyUserControl),
new UIPropertyMetadata(ResizeMode.CanResize, new PropertyChangedCallback(OnResizeModeChanged)));
I could ignore it, or set it in the code behind, but I don't really understand the reason for this error, and I would prefer to set it in XAML.
Can anyone shed some light?
Do you know exactly where the NullReferenceExceptoin is being thrown? For example, if you try this instead:
var uc = o as MyUserControl;
var mw = uc.MainWindow;
mw.ResizeMode = (ResizeMode)e.NewValue;
... then is the exception raised on the second line or the third?
My feeling is that MainWindow has not been assigned by the time ResizeMode is first given a value, so accessing MainWindow.ResizeMode is causing the error.
If that's the case, it's safe to ignore:
var mw = (o as MyUserControl).MainWindow;
if (mw == null) return;
But you might want to cache the value somewhere, and then assign it to MainWindow.ResizeMode when MainWindow gets assigned later.
OK, I think I found the culprit.
The MainWindow is set by App.Current.MainWindow.
Now from what I've read, the Current.MainWindow doesn't exist in Design time, and then when the OnResizeModeChanged methods fire during designtime, MainWindow.ResizeMode, boom! Exception!
I added this line to my methods:
if ((bool) (DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)) return;
I encountered another problem where my XAML suddenly couldn't load my usercontrol, due to me setting some properties on MainWindow in the constructor, added this:
if (DesignerProperties.GetIsInDesignMode(this))
return;