How to implement ShouldChangeCharacters in Monotouch? - xamarin.ios

I have a UITextField where I want to change the characters using ShouldChangeCharacters delegate method. But when I use the textField obj, it says that does not match with UITextFieldChange...
How should I do it in Monotouch?

Here is an example of forcing all caps:
textField.ShouldChangeCharacters = (textField, range, replacementString) =>
{
using (NSString original = new NSString(textField.Text), replace = new NSString(replacementString.ToUpper()))
{
textField.Text = original.Replace (range, replace);
}
return false;
};
I think it should be what you need. I tend to use Lambda expressions always, that way you don't need to even know the delegate type, parameters types, etc. I let C# type inference do the work.

Related

Passing a std::unique_ptr to CListBox::GetSelItems

I have seen a great answer here which has helped me to a great extent (Proper way to create unique_ptr that holds an allocated array) but I still have an issue.
Code:
void CSelectedBroHighlight::BuildSelectedArray()
{
CString strText;
// empty current array
m_aryStrSelectedBro.RemoveAll();
// get selected count
const auto iSize = m_lbBrothers.GetSelCount();
if(iSize > 0)
{
//auto pIndex = std::make_unique<int[]>(iSize);
auto pIndex = new int[iSize];
m_lbBrothers.GetSelItems(iSize, pIndex);
for(auto i = 0; i < iSize; i++)
{
m_lbBrothers.GetText(pIndex[i], strText);
m_aryStrSelectedBro.Add(strText);
}
delete[] pIndex;
}
}
If I turn pIndex into a smart pointer:
auto pIndex = std::make_unique<int[]>(iSize);
So that I don't need the delete[] pIndex; call. Then I can't pass pIndex to GetSelItems. I can pass pIndex.release() here but then we have a problem for deleting again.
I have looked at this discussion (Issue passing std::unique_ptr's) but we don't want to pass ownership.
If I simplify this and declar my variable: auto pIndex = std::make_unique<int[]>(iSize).release(); then I can pass it, but now have the issue of calling delete[] pIndex;.
Whats correct?
If you need access to the pointer to an object managed by a std::unique_ptr without transferring ownership, you can call its get() method. This is useful for interop with a C interface such as here (GetSelItems() is really just wrapping a call to SendMessage with the LB_GETSELITEMS message).
That'd work, though in this case I'd probably use a std::vector<int> instead. It provides the same properties as a std::unique_ptr with respect to automatic cleanup, but also has other features that come in handy (specifically range adapters). It also feels more natural to use a container here, but that's a matter of personal preference.
The following implements the proposed changes:
void CSelectedBroHighlight::BuildSelectedArray() {
// empty current array
m_aryStrSelectedBro.RemoveAll();
// get selected count
auto const sel_item_count{ m_lbBrothers.GetSelCount() };
if(sel_item_count > 0) {
// get selected indices
std::vector<int> sel_items(sel_item_count);
m_lbBrothers.GetSelItems(sel_items.size(), sel_items.data());
// iterate over all selected item indices
for(auto const index : sel_items) {
CString strText;
m_lbBrothers.GetText(index, strText);
m_aryStrSelectedBro.Add(strText);
}
}
}
This provides the same automatic cleanup as an implementation based on std::unique_ptr, but also enables use of a range-based for loop further down.

Is it possible in Mono.Cecil to determine the actual type of an object on which a method is called?

For example, consider the following C# code:
interface IBase { void f(int); }
interface IDerived : IBase { /* inherits f from IBase */ }
...
void SomeFunction()
{
IDerived o = ...;
o.f(5);
}
I know how to get a MethodDefinition object corresponding to SomeFunction.
I can then loop through MethodDefinition.Instructions:
var methodDef = GetMethodDefinitionOfSomeFunction();
foreach (var instruction in methodDef.Body.Instructions)
{
switch (instruction.Operand)
{
case MethodReference mr:
...
break;
}
yield return memberRef;
}
And this way I can find out that the method SomeFunction calls the function IBase.f
Now I would like to know the declared type of the object on which the function f is called, i.e. the declared type of o.
Inspecting mr.DeclaringType does not help, because it returns IBase.
This is what I have so far:
TypeReference typeRef = null;
if (instruction.OpCode == OpCodes.Callvirt)
{
// Identify the type of the object on which the call is being made.
var objInstruction = instruction;
if (instruction.Previous.OpCode == OpCodes.Tail)
{
objInstruction = instruction.Previous;
}
for (int i = mr.Parameters.Count; i >= 0; --i)
{
objInstruction = objInstruction.Previous;
}
if (objInstruction.OpCode == OpCodes.Ldloc_0 ||
objInstruction.OpCode == OpCodes.Ldloc_1 ||
objInstruction.OpCode == OpCodes.Ldloc_2 ||
objInstruction.OpCode == OpCodes.Ldloc_3)
{
var localIndex = objInstruction.OpCode.Op2 - OpCodes.Ldloc_0.Op2;
typeRef = locals[localIndex].VariableType;
}
else
{
switch (objInstruction.Operand)
{
case FieldDefinition fd:
typeRef = fd.DeclaringType;
break;
case VariableDefinition vd:
typeRef = vd.VariableType;
break;
}
}
}
where locals is methodDef.Body.Variables
But this is, of course, not enough, because the arguments to a function can be calls to other functions, like in f(g("hello")). It looks like the case above where I inspect previous instructions must repeat the actions of the virtual machine when it actually executes the code. I do not execute it, of course, but I need to recognize function calls and replace them and their arguments with their respective returns (even if placeholders). It looks like a major pain.
Is there a simpler way? Maybe there is something built-in already?
I am not aware of an easy way to achieve this.
The "easiest" way I can think of is to walk the stack and find where the reference used as the target of the call is pushed.
Basically, starting from the call instruction go back one instruction at a time taking into account how each one affects the stack; this way you can find the exact instruction that pushes the reference used as the target of the call (a long time ago I wrote something like that; you can use the code at https://github.com/lytico/db4o/blob/master/db4o.net/Db4oTool/Db4oTool/Core/StackAnalyzer.cs as inspiration).
You'll need also to consider scenarios in which the pushed reference is produced through a method/property; for example, SomeFunction().f(5). In this case you may need to evaluate that method to find out the actual type returned.
Keep in mind that you'll need to handle a lot of different cases; for example, imagine the code bellow:
class Utils
{
public static T Instantiate<T>() where T : new() => new T();
}
class SomeType
{
public void F(int i) {}
}
class Usage
{
static void Main()
{
var o = Utils.Instantiate<SomeType>();
o.F(1);
}
}
while walking the stack you'll find that o is the target of the method call; then you'll evaluate Instantiate<T>() method and will find that it returns new T() and knowing that T is SomeType in this case, that is the type you're looking for.
So the answer of Vagaus helped me come up with a working implementation.
I published it on github - https://github.com/MarkKharitonov/MonoCecilExtensions
Included many unit tests, but I am sure I missed some cases.

How to define custom synonyms list in lucene synonymmap

I want to define synonym words related to a particular domain in Lucene 8*. I have a list of synonyms in CSV format. I didn't see any sample code of example for this. I only saw example for older version which doesn't work now.
Here is a simple example of using synonyms in Lucene 8 (tested using 8.7.0).
Here is an example analyzer:
boolean ignoreSynonymCase = Boolean.TRUE;
Analyzer analyzer = new Analyzer() {
#Override
protected Analyzer.TokenStreamComponents createComponents(String fieldName) {
Tokenizer source = new StandardTokenizer();
TokenStream tokenStream = source;
tokenStream = new LowerCaseFilter(tokenStream);
tokenStream = new ASCIIFoldingFilter(tokenStream);
tokenStream = new SynonymGraphFilter(tokenStream, getSynonyms(), ignoreSynonymCase);
tokenStream = new FlattenGraphFilter(tokenStream);
return new Analyzer.TokenStreamComponents(source, tokenStream);
}
};
It uses a SynonymGraphFilter to handle your synonyms, which need to be added to a SynonymMap (see below for that).
Note the use of FlattenGraphFilter in the above example - which is needed during indexing as described in the synonym filter javadoc:
However, if you use this during indexing, you must follow it with FlattenGraphFilter to squash tokens on top of one another like SynonymFilter, because the indexer can't directly consume a graph.
My getSynonyms() method is as follows:
private static SynonymMap getSynonyms() {
// de-duplicate rules when loading:
boolean dedup = Boolean.TRUE;
// include original word in index:
boolean includeOrig = Boolean.TRUE;
SynonymMap.Builder builder = new SynonymMap.Builder(dedup);
// examples of single synonyms:
builder.add(new CharsRef("can't"), new CharsRef("cannot"), includeOrig);
builder.add(new CharsRef("what's"), new CharsRef("what is"), includeOrig);
// example with multiple synonyms:
CharsRefBuilder multiWordCharsRef = new CharsRefBuilder();
SynonymMap.Builder.join(new String[]{"do not", "does not"}, multiWordCharsRef);
builder.add(new CharsRef("don't"), multiWordCharsRef.get(), includeOrig);
SynonymMap synonymMap = null;
try {
synonymMap = builder.build();
} catch (IOException ex) {
System.err.print(ex);
}
return synonymMap;
}
So, for example, it treats cannot as a synonym for can't. And you can therefore search for cannot successfully in a phrase such as This can't be done!.
How you load your synonyms from your source CSV file is up to you - for example, you can call builder.add() in a loop.

Dialog RunBase custom lookup: Alt + Down key combination doesn't work

MS Dynamics AX 4.0
I have a class with a dialog that extends RunBase, a dialogField of Range type and a custom lookup for it. It works as planned but one thing upsets me.
Normal lookup opens on Alt + Down key combination, but it doesn't work in my dialog. I assume this is because "Range" EDT is not related to any TableField.
But I have my own lookup, can I force it somehow to drop down on Alt + Down?
Here is my dialog method:
protected Object dialog(DialogRunBase dialog, boolean forceOnClient)
{
Object ret;
;
ret = super(dialog, forceOnClient);
dialogFld = new DialogField(ret, typeid(Range), 100);
dialogFld.init(ret);
dialogFld.lookupButton(FormLookupButton::Always);
dialogFld.fieldControl().replaceOnLookup(false);
return ret;
}
Here is my lookup, as you can see, it's based on ItemId EDT:
protected void Fld100_1_Lookup()
{
TableLookup_RU sysTableLookup = new TableLookup_RU();
Query query = new Query();
FormRun lookupForm;
QueryBuildDataSource qbds = query.addDataSource(tablenum(InventTable));
;
sysTableLookup.parmTableId(tablenum(InventTable));
sysTableLookup.parmCallingControl(dialogFld.fieldControl());
sysTableLookup.addLookupfield(fieldnum(InventTable, ItemId));
sysTableLookup.addLookupfield(fieldnum(InventTable, ItemName));
findOrCreateRange_W(qbds, fieldnum(InventTable, ItemType), SysQuery::valueNot(ItemType::Service));
sysTableLookup.parmQuery(query);
lookupForm = sysTableLookup.formRun();
dialogFld.fieldControl().performFormLookup(lookupForm);
}
And dialogPostRun:
public void dialogPostRun(DialogRunbase dialog)
{
;
dialog.formRun().controlMethodOverload(true);
dialog.formRun().controlMethodOverloadObject(this);
super(dialog);
}
This problem is not that critical, but it bothers me. If someone could help, I'd be really grateful.
P.S.: I could use ItemId typeId, but I need to append many items, and ItemId is only 20 chars long..
I've discovered that I don't have to use Range typeid for the dialogField. dialogField.limitText(int) works just fine, it overrides the length of EDT. So I changed dialog method like this:
protected Object dialog(DialogRunBase dialog, boolean forceOnClient)
{
Object ret;
;
ret = super(dialog, forceOnClient);
dialogFld = new DialogField(ret, typeid(ItemId), 100); //if typeId doesn't have relations Alt + Down doesn't work
dialogFld.init(ret);
dialogFld.label("#SYS72708");
dialogFld.lookupButton(FormLookupButton::Always);
dialogFld.limitText(200);
dialogFld.fieldControl().replaceOnLookup(false);
return ret;
}
Create a new extended data type ItemIdRange, extend from Range.
Be sure to set the relation on the new type to relate to InventTable.ItemId to get automatic lookup.
Also the form control must have property ReplaceOnLookup set to no, to allow the user to add more criteria. For a DialogRunbase field this may be done this way:
FormStringControl fsc = dialogField.control();
fsc.replaceOnLookup(false);
The code posted in the question is then not needed.

Get JSON from multi-value field in Java custom rest control

I have a custom rest control written in Java. Everything works fine for string fields, but not for multi-value string fields. My code returns
"[Value1, Value2, Value3...]". Note there are no commas around the values. On the web page the output looks like this:
If I can get commas around the values, the front-end framework can easily parse it.
I have tried to parse the value from the field and to get it formatted correctly, but cannot seem to get it right.
The first set of code works for a string. The second is an attempt to work for a multi-value field.
Any help would be greatly appreciated.
writer.startProperty("chgTitle");
writer.outStringLiteral(String.valueOf(columnValues.get(0)));
writer.endProperty();
writer.startProperty("plpAffected");
Object tmpObj = columnValues.get(5);
if (tmpObj instanceof Vector) {
String[] copyArr = new String[((Vector) tmpObj).size()];
((Vector) tmpObj).copyInto(copyArr);
writer.outStringLiteral(String.valueOf(copyArr));
} else {
}
writer.endProperty();
I would recommend a slightly different approach. I like to force my JSON structures to use an array anywhere a multiple value situation is possible. It also looks like you're using Philippe Riand's specialized JSON writer, so I'll assume that.
Here's about how I would attack it:
writer.startProperty("chgTitle");
writer.outStringLiteral(String.valueOf(columnValues.get(0)));
writer.endProperty();
writer.startProperty("plpAffected");
Vector<?> tmpVec = Util.getValueAsVector(columnValues.get(5));
writer.startArray();
for ( Object ob : tmpVec ) {
writer.startArrayItem();
// assuming String contents
writer.outStringLiteral(String.valueOf(ob));
writer.endArrayItem();
}
writer.endArray();
writer.endProperty();
To wrap the returning columnValues, I'm using a Java equivalent of my SSJS getValueAsVector helper function. It checks for Vector or ArrayList, which I happen to use almost exclusively; if it's not, it shoves it into a new Vector. Here's what that method looks like,
public static Vector<?> getValueAsVector(Object obj) {
if(obj instanceof java.util.Vector){
return (Vector<?>) obj;
}else if( obj instanceof ArrayList ) {
List<?> o = (List<?>) obj;
Vector<Object> tmpVec = new Vector<Object>();
for(int i=0;i<o.size();i++){
tmpVec.add(o.get(i));
}
return tmpVec;
}else {
Vector<Object> tmpVec = new Vector<Object>();
tmpVec.add(obj);
return tmpVec;
}
}

Resources