NSPredicateEditorRowTemplate, specifying of Key Path with spaces? - objective-c

As per a previous question, I have reluctantly given up on using IB/Xcode4 to edit an NSPredicateEditor and done it purely in code.
In the GUI way of editing the fields, key paths can be specified with spaces, like 'field name', and it makes them work as 'fieldName'-style key paths, while still displaying them in the UI with spaces. How do I do this in code? When I specify them with spaces, they don't work. When I specify them in camelCase, they work but display in camelCase. I'm just adding a bunch of NSExpressions like this:
[NSExpression expressionForKeyPath:#"original filename"]

The proper way to get human readable strings in the predicate editor's row views is to use the localization capabilities of NSRuleEditor and NSPredicateEditor.
If you follow the instructions in this blog post, you'll have everything you need to localize the editor.
As an example, let's say your key path is fileName, you support 2 operators (is and contains), and you want the user to enter a string. You'll end up with a strings file that looks like this:
"%[fileName]# %[is]# %#" = "%1$[fileName]# %2$[is]# %3$#";
"%[fileName]# %[contains]# %#" = "%1$[fileName]# %2$[contains]# %3$#";
You can use this file to put in human-readable stuff, and even reorder things:
"%[fileName]# %[is]# %#" = "%1$[original filename]# %2$[is]# %3$#";
"%[fileName]# %[contains]# %#" = "%3$# %2$[is contained in]# %1$[original filename]#";
Once you've localized the strings file, you hand that file back to the predicate editor, and it'll pull out the translated values, do its magic, and everything will show up correctly.

If you don't want to localize everything, just map the key paths consider overriding value(forKey:) in your evaluated object like this:
class Match: NSObject {
var date: Date?
var fileName: String?
override func value(forKey key: String) -> Any? {
// Alternatively use static dictionary for mapping key paths
super.value(forKey: camelCasedKeyPath(forKey: key))
}
private func camelCasedKeyPath(forKey key: String) -> String {
key.components(separatedBy: .whitespaces)
.enumerated()
.map { $0.offset > 0 ? $0.element.capitalized : $0.element.lowercased() }
.joined()
}
}

Related

can't find all field of a pdf (acroform)

Considering this pdf
With this code I except retrieve all field but I get half of them:
pdfOriginal.getDocumentCatalog().getAcroForm().getFields().forEach(field -> {
System.out.println(field.getValueAsString());
});
What is wrong here ? It seems all annotations are not in aocroform reference, what is the correct way to add form field annotation into acroform object?
Update 1
The wierd thing here if I tried to set field's value which is not referenced/found in getAcroForm.getFields() like this :
doc.getDocumentCatalog().getAcroForm().getField("fieldNotInGetFields").setValue("a");
This works
Update 2
It seems using doc.getDocumentCatalog().getAcroForm().getFieldTree() retrieve all fields. I don't understand why doc.getDocumentCatalog().getAcroForm().getFields() not ?
What is the correct way retrieve all fields of a pdf acroform.getFieldTree() or acroform.getFields() (I need retrieve them to set them partialValue)
From the java doc on method public List<PDField> getFields() we can read:
A field might have children that are fields (non-terminal field) or does not have children which are fields (terminal fields).
In my case some fields contain non-terminal field so to print them all we need check if we are in a PDNonTerminalField like :
document.getDocumentCatalog().getAcroForm().getFields().forEach(f -> {
listFields(f);
});
// loop over PDNonTerminalField otherwise print field value
public static void listFields(PDField f){
if(f instanceof PDNonTerminalField) {
((PDNonTerminalField) f).getChildren().forEach(ntf-> {
listFields(ntf);
});
}else {
System.out.println(f.getValueAsString());
}
}

Accessing Resources IDs using Kotlin & Anko

I am new to Android/Kotlin/Anko and I have a question regarding the way to access color (and probably other) resources from within Anko.
I know that there are helpers like textResource where you simply pass the R.string.my_color to simplify the process of setting resource strings but how about accessing colors using the Resources instance from the View class ?
Let’s say you have a subclass of Button and want to change the text color. If you use the textResource it will change the text string not the color, and if you use textColor then you must specify the real resource ID by using resources.getColor(R.color.my_color, null) which wouldn't be so annoying if you didn't have to pass the optional theme parameter (null here)
Is creating an extension on Resources useful here ?
fun Int.fromResources(resources: Resources): Int {
return resources.getColor(this, null)
}
What is the recommended way ?
EDIT
I changed the textColor value extension to do just that, which I found the cleanest thing to do except I have no idea if this is really Android friendly
var android.widget.TextView.textColor: Int
get() = throw AnkoException("'android.widget.TextView.textColor' property does not have a getter")
set(v) = setTextColor(resources.getColor(v, null))
I think you can use a property extension like this one instead of the one you suggested:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(resources.getColor(v, null))
Or use ContextCompat as suggested by Damian Petla:
var TextView.textColorRes: Int
get() = throw PropertyWithoutGetterException("textColorRes")
set(#ColorRes v) = setTextColor(ContextCompat.getColor(context, v))
You should keep Anko's textColor:
Allows you to set a color directly without taking it from XML, if needed at some point
Prevents you from importing the wrong textColor (Anko's one or yours), same property names with different behaviour is not a good idea.

Objective-C string wrapped in parentheses?

I'm getting this on console.log;
2014-08-13 11:55:11.877 Wevo[14264:1830541] artist name: (
"Vance Joy"
)
How do I unwrap it so its just the string?
The problem comes because I'm parsing json that looks like this:
output = {
contributor = {
"/music/recording/artist" = [
{
mid = "/m/026hdj4";
name = "Marie-Mai";
}
];
};
};
notice how the mid is wrapped in an array?
So it gets converted to an object literal somewhere
I'm setting the value using:
_artistName = [[attributes[#"output"][#"contributor"][#"/music/recording/artist"] valueForKeyPath:#"name"] copy];
Why are you using valueForKeyPath:? If you use
_artistName = attributes[#"output"][#"contributor"][#"/music/recording/artist"][0][#"name"];
it should come out correctly.
Edit: For future viewers, one off lines like this will work. However, for a more maintainable and debuggable app, I would recommend splitting up the lines to extract only one object per line. That way, if something breaks, the debugger will be a larger help.
For apps where you deal with more JSON than just a one off, I would recommend creating model objects and pulling your JSON into those. There are libraries on github that could also help you there with model objects.

An interesting Restlet Attribute behavior

Using Restlet 2.1 for Java EE, I am discovering an interesting problem with its ability to handle attributes.
Suppose you have code like the following:
cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
and on your browser you provide the following URL:
http://localhost:8100/testpath/command
then, of course, the attr attribute gets set to "command".
Unfortunately, suppose you want the attribute to be something like command/test, as in the following URL:
http://localhost:8100/testpath/command/test
or if you want to dynamically add things with different levels, like:
http://localhost:800/testpath/command/test/subsystems/network/security
in both cases the attr attribute is still set to "command"!
Is there some way in a restlet application to make an attribute that can retain the "slash", so that one can, for example, make the attr attribute be set to "command/test"? I would like to be able to just grab everything after testpath and have the entire string be the attribute.
Is this possible? Someone please advise.
For the same case I usually change the type of the variable :
Route route = cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
route.getTemplate().getVariables().get("attr") = new Variable(Variable.TYPE_URI_PATH);
You can do this by using url encoding.
I made the following attachment in my router:
router.attach("/test/{cmd}", TestResource.class);
My test resource class looks like this, with a little help from Apache Commons Codec URLCodec
#Override
protected Representation get() {
try {
String raw = ResourceWrapper.get(this, "cmd");
String decoded = new String(URLCodec.decodeUrl(raw.getBytes()));
return ResourceWrapper.wrap(raw + " " + decoded);
} catch(Exception e) { throw new RuntimeException(e); }
}
Note my resource wrapper class is simply utility methods. The get returns the string of the url param, and the wrap returns a StringRepresentation.
Now if I do something like this:
http://127.0.0.1/test/haha/awesome
I get a 404.
Instead, I do this:
http://127.0.0.1/test/haha%2fawesome
I have URLEncoded the folder path. This results in my browser saying:
haha%2fawesome haha/awesome
The first is the raw string, the second is the result. I don't know if this is suitable for your needs as it's a simplistic example, but as long as you URLEncode your attribute, you can decode it on the other end.

OpenNLP Name Finder

I am using the NameFinder API example doc of OpenNLP. After initializing the Name Finder the documentation uses the following code for the input text:
for (String document[][] : documents) {
for (String[] sentence : document) {
Span nameSpans[] = nameFinder.find(sentence);
// do something with the names
}
nameFinder.clearAdaptiveData()
}
However when I bring this into eclipse the 'documents' (not 'document') variable is giving me an error saying the variable documents cannot be resolved. What is the documentation referring to with the 'documents' array variable? Do I need to initialize an array called 'documents' which hold txt files for this error to go away?
Thank you for your help.
The OpenNLP documentation states that the input text should be segmented into documents, sentences and tokens. The piece of code you provided illustrates how to deal with several documents.
If you have only one document you don't need the first for, just the inner one with the array of sentences, which is composed by as an array of tokens.
To create an array of sentences from a document you can use the OpenNLP SentenceDetector, and for each sentence you can use OpenNLP Tokenizer to get the array of tokens.
Your code will look like this:
// somehow get the contents from the txt file
// and populate a string called documentStr
String sentences[] = sentenceDetector.sentDetect(documentStr);
for (String sentence : sentences) {
String tokens[] = tokenizer.tokenize(sentence);
Span nameSpans[] = nameFinder.find(tokens);
// do something with the names
System.out.println("Found entity: " + Arrays.toString(Span.spansToStrings(nameSpans, tokens)));
}
You can learn how to use the SentenceDetector and the Tokenizer from OpenNLP documentation documentation.