Java, project panama and how to deal with Hunspell 'suggest' result - java

I'm experimenting with Hunspell and how to interact with it using Java Project Panama (Build 19-panama+1-13 (2022/1/18)). I was able to get some initial testing done, as in creating a handle to Hunspell and subsequently using that to perform a spell check. I'm now trying something more elaborate, letting Hunspell give me suggestions for a word not present in the dictionary. This is the code that I have for that now:
public class HelloHun {
public static void main(String[] args) {
MemoryAddress hunspellHandle = null;
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
var allocator = SegmentAllocator.nativeAllocator(scope);
// Point it to US english dictionary and (so called) affix file
// Note #1: it is possible to add words to the dictionary if you like
// Note #2: it is possible to have separate/individual dictionaries and affix files (e.g. per user/doc type)
var en_US_aff = allocator.allocateUtf8String("/usr/share/hunspell/en_US.aff");
var en_US_dic = allocator.allocateUtf8String("/usr/share/hunspell/en_US.dic");
// Get a handle to the Hunspell shared library and load up the dictionary and affix
hunspellHandle = Hunspell_create(en_US_aff, en_US_dic);
// Feed it a wrong word
var javaWord = "koing";
// Do a simple spell check of the word
var word = allocator.allocateUtf8String(javaWord);
var spellingResult = Hunspell_spell(hunspellHandle, word);
System.out.println(String.format("%s is spelled %s", javaWord, (spellingResult == 0 ? "incorrect" : "correct")));
// Hunspell also supports giving suggestions for a word - which is what we do next
// Note #3: by testing this `koing` word in isolation - we know that there are 4 alternatives for this word
// Note #4: I'm still investigating how to access individual suggestions
var suggestions = allocator.allocate(10);
var suggestionCount = Hunspell_suggest(hunspellHandle, suggestions, word);
System.out.println(String.format("There are %d suggestions for %s", suggestionCount, javaWord));
// `suggestions` - according to the hunspell API - is a `pointer to an array of strings pointer`
// we know how many `strings` pointer there are, as that is the returned value from `suggest`
// Question: how to process `suggestions` to get individual suggestions
} finally {
if (hunspellHandle != null) {
Hunspell_destroy(hunspellHandle);
}
}
}
}
What I'm seeing is that a call to Hunspell_suggest (created from jextract) succeeds and gives me back (4) suggestions (which I verified using Hunspell from the commandline) - so no problem there.
What is more challenging for me now is how do I unpack the suggestions element that comes back from this call? I've been looking at various examples, but none of them seem to go into this level of detail (and even if I find examples, they seem to be using outdated panama APIs).
So in essence, here is my question:
How do I unpack a structure that reportedly consists of a pointer to an array of strings pointer using panama JDK19 APIs to their respective collection of strings?

Looking at the header here: https://github.com/hunspell/hunspell/blob/master/src/hunspell/hunspell.h#L80
/* suggest(suggestions, word) - search suggestions
* input: pointer to an array of strings pointer and the (bad) word
* array of strings pointer (here *slst) may not be initialized
* output: number of suggestions in string array, and suggestions in
* a newly allocated array of strings (*slts will be NULL when number
* of suggestion equals 0.)
*/
LIBHUNSPELL_DLL_EXPORTED int Hunspell_suggest(Hunhandle* pHunspell,
char*** slst,
const char* word);
The slst is a classic 'out' parameter. i.e. we pass a pointer to some value (in this case a char** i.e. an array of strings), and the function will set this pointer for us, as a way to return multiple results. (the first result being the number of suggestions)
In panama you use 'out' parameters by allocating a segment with the layout of the type the parameter is a pointer of. In this case char*** is a pointer to char**, so the layout is ADDRESS. We then pass the created segment to the function, and finally retrieve/use the value from that segment after the function call, which will have filled in the segment contents:
// char***
var suggestionsRef = allocator.allocate(ValueLayout.ADDRESS); // allocate space for an address
var suggestionCount = Hunspell_suggest(hunspellHandle, suggestionsRef, word);
// char** (the value set by the function)
MemoryAddress suggestions = suggestionsRef.get(ValueLayout.ADDRESS, 0);
After that, you can iterate over the array of strings:
for (int i = 0; i < suggestionCount; i++) {
// char* (an element in the array)
MemoryAddress suggestion = suggestions.getAtIndex(ValueLayout.ADDRESS, i);
// read the string
String javaSuggestion = suggestion.getUtf8String(suggestion, 0);
}

Related

How to compare two datasets to check for missing rows which don't exist in the other dataset?

I have built a method which takes two datasets: dataset1 and retirementSimpleData.
It matches the two datasets based on a primary key/number, defined as cnum, in the code below.
I wanted to return the value of the difference between the getAssets value and the getSums value, and this is working, except for one little problem.
Some of the cnums that exist in dataset1 don't exist in retirementSimpleData. Similarly, some cnums which may exist in retirementSimpleData may not exist in
dataset1. This is resulting in no data being returned for that cnum.
I would like to implement two passes at the end which check in one direction to see if I missed anything. The second pass would check in the opposite direction.
However, not sure how I would go about implementing this.
public void getReportData(int index) {
String date1 = Util.dateTimeToShortString(reportDate);
String date2 = reportDao.getPreviousRetirementDate(date1);
List<SurveyCompareAssetsCheckData> dataset1 = reportDao.getSurveyCheckCompareAssetsData(date1);
List<RetSurveyAssets> retirementSimpleData = reportDao.findRetSurveyByDate(date1);
for (SurveyCompareAssetsCheckData surveyCompareAssetsCheckData : dataset1) {
for (RetSurveyAssets surveyCompareAssetsCheckData2 : retirementSimpleData) {
if (surveyCompareAssetsCheckData.getCnum() == surveyCompareAssetsCheckData2.getCnum()) {
surveyCompareAssetsCheckData.setRetirementsimple(surveyCompareAssetsCheckData2.getSums());
surveyCompareAssetsCheckData.setDifference(surveyCompareAssetsCheckData.getAssets() - surveyCompareAssetsCheckData2.getSums());
Caveat: dataset1 and retirementSimpledata both use existing SQL pulls which I am not allowed to touch, otherwise I would have simply defined new SQL for these methods in my "DAOImpl." Therefore, I have to work with the data I am getting, and programmatically check for this.
Below, is the report which is being generated with my code. As you can see, I am ending up with zeros, which is showing the difference (incorrectly) as zeros, because Cnum #45, in this example simply doesn't exist in the second dataset (retirementSimpleData)
What is the datatype of Cnum, if it is int then default value is Zero.
You have to add else-if condition to check for example:
else if (surveyCompareAssetsCheckData2.getCnum()== 0){
-------- logic here --------------------
}
else if (surveyCompareAssetsCheckData.getCnum() ==0){
----------logic here -----------
}

reference array using variable in Java

I please bear with me, I have been using Java for 2 days and i've hit a bit of a hurdle.
I am using Talend to perform a count using the tMemorize and tJava components but this may be a question for a Java developer. I have previously posted an issue with using this method within a joblet by my new issue is more Java related which can be viewed here:
using Joblets in talend with tMemorize and tJavaFlex
I need to reference an array generated by the java code talend. I cannot reference this element directly because of an issue with using tJavaFlex within multiple joblets: Java renames joblets each time they are used.
It may be useful to understand how my code works in normal circumstances (excluding the use of joblets).
int counter = 1;
if (EnquiryID_mem_1_tMemorizeRows_1[0].equals(EnquiryID_mem_1_tMemorizeRows_1[1]))
{
counter++;
}
row3.counter = counter;
The EnquiryID_mem_1_tMemorizeRows_1[0] and EnquiryID_mem_1_tMemorizeRows_1[1] is what I need to reference.
To overcome this I have written the following code.
String string = currentComponent;
String[] parts = string.split("_");
String part1 = parts[0];
String part2 = parts[1];
String joblet = part1+'_'+part2;
String newrow = "EnquiryID_"+joblet+"_tMemorizeRows_1"
if (newrow[0].equals(newrow[1]))
{
counter++;
}
row3.counter = counter;
However I get the following error:
The type of the expression must be an array type but it resolved to String
I understand that the newrow variable is a string and I am using it to reference an array. I have searched far and wide online for a resolve but I cannot fine one. Can someone help me please?
Thank you
Here is the talend code that my code should reference. I have taken it from the currentComponent that I am using to when it changes to one not in use directly.
currentComponent = "mem_1_tMemorizeRows_1";
// row1
// row1
if (execStat) {
runStat.updateStatOnConnection("row1" + iterateId,
1, 1);
}
for (int i_mem_1_tMemorizeRows_1 = iRows_mem_1_tMemorizeRows_1 - 1; i_mem_1_tMemorizeRows_1 > 0; i_mem_1_tMemorizeRows_1--) {
EnquiryID_mem_1_tMemorizeRows_1[i_mem_1_tMemorizeRows_1] = EnquiryID_mem_1_tMemorizeRows_1[i_mem_1_tMemorizeRows_1 - 1];
}
EnquiryID_mem_1_tMemorizeRows_1[0] = row1.EnquiryID;
mem_1_row2 = row1;
tos_count_mem_1_tMemorizeRows_1++;
/**
* [mem_1_tMemorizeRows_1 main ] stop
*/
/**
* [mem_1_tJavaFlex_1 main ] start
*/
currentComponent = "mem_1_tJavaFlex_1";
// mem_1_row2
// mem_1_row2
if (execStat) {
runStat.updateStatOnConnection("mem_1_row2"
+ iterateId, 1, 1);
}
mem_1_row3.QuoteID = mem_1_row2.QuoteID;
mem_1_row3.EnquiryID = mem_1_row2.EnquiryID;
if (EnquiryID_mem_1_tMemorizeRows_1[0]
.equals(EnquiryID_mem_1_tMemorizeRows_1[1])) {
rower++;
}
mem_1_row3.rower = rower;
tos_count_mem_1_tJavaFlex_1++;
/**
* [mem_1_tJavaFlex_1 main ] stop
*/
/**
* [mem_1_tMap_1 main ] start
*/
currentComponent = "mem_1_tMap_1";
Thank you to everyone who has helped so far.
This
if (newrow[0].equals(newrow[1]))
Tries to pick the first and second element of the array newrow. Unfortunately you declare newrow as
String newrow = "EnquiryID_"+joblet+"_tMemorizeRows_1"
which is not an array but a String. That syntax in the if will not work with a String. I am not sure what you are trying to do but that if check will not work.
EDIT:
If you are trying to pick up char from a string you need to use charAt(index).
If you want to treat newrow as an array you have to declare it as such and pass appropriate elements to it.
EDIT 2: I think you are trying to pass the actual data in joblet to newrow in this:
String newrow = "EnquiryID_"+joblet+"_tMemorizeRows_1"
But what happens here is that everything is concatenated in one String so you need to figure out where the data you are looking for (part[0] and part[1] I assume) is present in that String so you can pull them out (basically what indices contain the values you are looking for).
An example of how newrow will look after that assignment:
"EnquiryID_part1_part2_tMemorizeRows_1"
So "part1" will start at index 10 and will end at index 14. I am just using "part1" here, but it would have whatever value is stored in part1 variable.
If you can show us what you expect it to look like that would help.
I'm not super familiar with talend (understand: not at all). But it sounds like you have some sort of attribute of a generated class (say myGeneratedObject) and you want to access it by name.
In that case, you could do something like:
String newrow = "EnquiryID_"+joblet+"_tMemorizeRows_1"
Field field = myGeneratedObject.getClass().getField(newrow);
if (field.getClass().isArray()) {
if(Array.get(field, 0).equals(Array.get(field, 1)) {
counter++;
}
}
It all depends how you access that field really and where it's declared. But if it's an attribute of an object, then the code above should work, +/- contextual adjustments due to my lack of knowledge of the exact problem.

Deleting and Inserting Elements in Array [duplicate]

This question already has answers here:
How to remove specific element from an array [duplicate]
(6 answers)
Closed 8 years ago.
public class Example {
public static void main(String [] args) {
String [] wombology;
wombology = new String[3];
wombology[0] = "History of Wombology";
wombology[1] = "Why Wombology";
wombology[2] = "Wombology and Me";
Random book = new Random(System.currentTimeMillis());
Scanner choice = new Scanner(System.in);
String yourChoice;
do {
System.out.println("Press enter for random book");
yourChoice = choice.nextLine();
if(yourChoice.equals("")) {
System.out.println(wombology[randomizer.nextInt(3)]);
System.out.println();
}
if(yourChoice == "EXIT") {
break;
}
} while(!yourChoice.equals("EXIT"));
}
}
How could I take out a "book" from the array once chosen randomly?
How could I put back in said "book" later back into the array?
Example: "History of Wombology" is randomly chosen and is taken out.
The next random selection should NOT include "History of Wombology" until it is put back in.
Meaning only "Why Wombology" and "Wombology and Me" should be the only possible choices.
I'm assuming that this is homework and an ArrayList is not possible. And I don't want to give a full, detailed answer.
One option might be to create a parallel array, boolean isCheckedOut[], and track your books there.
You need to manage the array yourself. That means you need to know the real size of the array and the filled size. This is because once the array is created the size cannot change.
If you delete an object from the array you need to shift the adjacent elements towards that position.
For example, your array looks like this:
[A|B|C|D|E|F]
allocatedArraySize = 6
currentSize = 6
If you delete C which is at position 2 then you must shift D, E, F to the left. You could also make the last position null.
[A|B|D|E|F|null]
allocatedArraySize = 6
currentSize = 5
To insert, simply use this:
// Check Array is not full.
if(currentSize != allocatedArraySize)
{
// Then add your object to the last position in the array.
array[currentSize] = obj;
// Increment the index.
currentSize++;
}
else
{
// Don't allow insertion.
// Or create a new-bigger-array;
// then copy all elements of the full array into it.
}
You have to "define" an action for "taking out a book" on the technical level. I can image two possibilities for this
setting the array content at the specific position to null
setting the array content at the specific position to an empty string
As the title of most books consists of one or more letters, the empty string-proposal seems also to be valid.
The second task (putting a book back into the array) can be handled in a similar way. Here you have to find an empty place (an array position with an empty string/null as content) and assign the name of the book to it.
Concerning the randomizer and not allowing already removed books: you can use the aforementioned condition to rerun the randomizer, i.e until an non-empty string/not-null element is found in the array. If you found one, it is a valid choice. But beware, if you removed all books, the randomizer would never stop running (because it finds only invalid choices and hence never returns). Here you can use an additional check condition: if the array only consists of empty strings/ null values, it is not required to run the randomizer.
Hope that helps...

Breadth-First Search Algorithm JAVA 8-puzzle

Im trying to implement the Breadth-First algorithm for 8 puzzle game. I know that it is not a new case and theres a bunch of solutions on web, but I want to make it on my way of thinking.
This code already finds the node result, which is
123
456
780
But it takes 350,000 steps to do it!
Any thoughts would be appreciated!
=)
//This method receives a Collection of `Nodo` objects, each one will be checked and compare with the finalGoal
public void percorreNodos(Collection<Nodo> nodosBase)
{
//In this class a have an array that has all the IDs os nodes that already has been checked
//The ID of a Node, is the representation: 123456780, 354126870 , etc..
System.out.println("idsPercorrido.size() = " + idsPercorridos.size());
//Check if the collection nodosBase contains the finalGoal
Iterator<Nodo> iterator = nodosBase.iterator();
while( iterator.hasNext() )
{
Nodo nodoBase = (Nodo) iterator.next();
//If this current node has already been checked, we dont have to check it again
idsPercorridos.add( nodoBase.getId() );
//Just print the node (sysout)
nodoBase.print();
contPassos++;
System.out.println( "\n" + contPassos + " STEPS(number of nodes checked)..." );
//Check if this node is the final goal
if( nodoBase.isObjetivoFinal() )
{
//set the variable indicating that the result has been found
encontrouObjetivo = true;
System.out.println( "Resultado alcancado EM " + contPassos + " PASSOS..." );
nodoBase.print();
break;
}
}
// Now that we know that no one Node of nosoBase collection is the final goal, we are going to generate the next children to be checked, and call this function recursively
//Just confirm that we didnt find the goal
if(encontrouObjetivo == false)
{
//Creates the next frontier
Collection<Nodo> novaFronteira = new HashSet<Nodo>();
for(Nodo nodoPai : nodosBase)
{
//Generate each Node its childrens and add to a collection called "novaFronteira"
Collection<Nodo> filhos = nodoPai.gerarFilhos();
for(Nodo filho : filhos)
{
//idsPercorridos is a collection<String> which contains all the nodes ids that we checked, we dont want to check a node more than one time !
if( idsPercorridos.contains( filho.getId() ) == false )
{
novaFronteira.add( filho );
}
}
}
this.percorreNodos( novaFronteira );
}
}
You could make sure you don't add duplicate elements to novaFronteira.
There's nothing preventing this code:
for(Nodo nodoPai : nodosBase)
{
Collection<Nodo> filhos = nodoPai.gerarFilhos();
for(Nodo filho : filhos)
{
if( idsPercorridos.contains( filho.getId() ) == false )
{
novaFronteira.add( filho );
}
}
}
From adding many duplicate nodes to novaFronteira.
If you were to add to idsPercorridos inside the if-statement, that would prevent this from happening, and result in less steps, although, depending on exactly what your data and data structures looks like, the added running time of this call may actually make it take longer than it did originally.
If the problem is running time, you should make sure that idsPercorridos is a TreeSet or HashSet, as these allow for efficient contains calls, as opposed to ArrayList or LinkedList, which don't.
If this doesn't help, you could try using the A* algorithm instead, which involves adding a heuristic function to each node, which is the distance to the target - this allows us to explore the nodes closer to the target first, often resulting in less steps to get there.
A good heuristic function might be the sum of Manhattan distances between each tile and its target location.
Note that this would involve quite a few changes to your current code.
According to Wikipedia there are 9!/2 = 181440 possible solvable combinations to this puzzle. If you check each node for each of these combinations (which you don't, but it makes the calculation easier), it makes about (9!/2) * 9 = 1,632,960 steps. Therefore, there I don't see an issue if it takes your algorithm 350,000 steps because a computer can do those steps really fast.

why is this method returning null? (calling a set of strings from an array)

I'm working on (what is to a beginner) a rather complex assignment. I think I've got the jist of it down, but I'm having trouble with it printing "null" after Monday-Sunday are entered into the dialog box. Also, the goal of the assignment is, later, for the user to enter a number 0-6 and then the corresponding weekday (from the String[] weekArray set in the method) is printed. I'm not really sure how to do this and my book doesn't seem to be showing me the way, but am I even going to be able to do that the way my code is set up? Thanks and best regards. Here's the code.
EDIT - there's an example in my book under returning an array from a method. I'm not sure if it's applicable to my assignment as they seem to have different goals, but here it is..
EDIT#2 - instructions for reference to what I'm doing.
EDIT#3 - my interpretation of the example for passing methods to arrays.
EDIT #4 - solved the issue. I was going about it the wrong way for much. The book's example wasn't much help. Thanks to those of you who replied. I deleted the homework assignment description in consideration of the professor (who probably wouldn't like his assignments on the internet next semester)
Excerpt of the code below... I was making this way more complicated than it was
public static String[] getWeek() {
String[] weekArray = new String[7];
for (int i = 0; i < weekArray.length; i++) {
weekArray[0] = JOptionPane.showInputDialog("Enter 'Monday'. ");
if (weekArray[0].compareTo("Monday") > 0) {
weekArray[0] = JOptionPane.showInputDialog("Enter 'Monday'. ");
Your branches have unreachable statements -
weekArray[5] = ...
while(weekArray[5].equalsIgnoreCase(null)) {
// Cannot reach this point as equalsIgnoreCase(null) always returns false.
}
From String#equalsIgnoreCase:
true if the argument is not null and the Strings are equal, ignoring case; false otherwise.
Emphasis mine.
EDIT: From your edit above you appear to be having trouble with populating an array. The rough idea is:
public static void main(String[] args) {
// Just an example
int[] values = new int[5];
System.out.println(Arrays.toString(values));
populateArray(values);
System.out.println(Arrays.toString(values));
}
private static void populateArray(int[] toBePopulated) {
toBePopulated[0] = 42;
toBePopulated[1] = 11;
// Etc. Again, just an example.
}
This will print:
[0, 0, 0, 0, 0]
[42, 11, 0, 0, 0]
The idea is that what is passed to the method populateArray is a reference to the location of the array in memory. That means that you can manipulate the contents of the array, and the change will be reflected in values afterwards.
I don't know if i got it right , but from a cursory look , seems you're mistaking the sunday part , guess it should be sth like :
if (weekArray[5].equalsIgnoreCase("Sunday")) // Note the "5"
Your cheek for string being null is probably not what you want. If you indent the code it would be easier to see.
After formatting this is what you have
while (weekArray[0].equalsIgnoreCase(null)) {
if (weekArray[0].equalsIgnoreCase("Monday"))
return weekArray[0];
}
So, if weekArray[0] is null, then you check if null equalsIgnoreCase("Monday"). This logic is not what you want.
After asking and checking all days you return null (at the end of the method). This si the null being printed.

Categories