Where should I put my setOnAction method in my Server/Client Chat?

I am writing a program for a simple Server/Client text chat. The original code I used was example code from my textbook’s powerpoint. The example program was to have the client enter the radius of a circle and the server would receive the radius and calculate the area of a circle, sending the area back to the client. In the original code, the Server had no TextField to input chat text or anything since it was automatically receiving a radius from the client and calculating it. I have changed up my code to suit the program I am supposed to write. The Client code looks fine, but where does my “setOnAction” code get placed in the code for the Server when the Server hits enter to send the text? Also, I referred to the Client class for that setOnAction method, but my Server class is so different that I am not sure how and where to write setOnAction code for the Server’s TextField.

Server

import java.io.*;
import java.net.*;
import java.util.Date;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;

public class Server extends Application {
  @Override // Override the start method in the Application class
  public void start(Stage primaryStage) {
    // Text area for displaying contents
    BorderPane paneForTextField = new BorderPane();
    paneForTextField.setPadding(new Insets(5, 5, 5, 5)); 
    paneForTextField.setStyle("-fx-border-color: green");
    paneForTextField.setLeft(new Label("Type here: "));

    TextField tf = new TextField();
    tf.setAlignment(Pos.BOTTOM_RIGHT);
    paneForTextField.setCenter(tf);

    BorderPane mainPane = new BorderPane();
    // Text area to display contents
    TextArea ta = new TextArea();
    mainPane.setCenter(new ScrollPane(ta));
    mainPane.setTop(paneForTextField);

    // Create a scene and place it in the stage
    Scene scene = new Scene(mainPane, 450, 200);
    primaryStage.setTitle("Server"); // Set the stage title
    primaryStage.setScene(scene); // Place the scene in the stage
    primaryStage.show(); // Display the stage

    new Thread( () -> {
      try {
        // Create a server socket
        ServerSocket serverSocket = new ServerSocket(8000);
        Platform.runLater(() ->
          ta.appendText("Server started at " + new Date() + 'n'));

        // Listen for a connection request
        Socket socket = serverSocket.accept();

        // Create data input and output streams
        DataInputStream inputFromClient = new DataInputStream(
          socket.getInputStream());
        DataOutputStream outputToClient = new DataOutputStream(
          socket.getOutputStream());

        while (true) {
          // Receive radius from the client
          //double radius = inputFromClient.readDouble();
          String clientText = inputFromClient.readLine();
          // Compute area
          //double area = radius * radius * Math.PI;
          String serverText = tf.getText().trim(); 
          // Send area back to the client
          outputToClient.writeBytes(serverText);

          Platform.runLater(() -> {
            ta.appendText("Client: " + clientText + 'n');
            ta.appendText("Server: " + serverText + 'n'); 
          });
        }
      }
      catch(IOException ex) {
        ex.printStackTrace();
      }
    }).start();
  }

  /**
   * The main method is only needed for the IDE with limited
   * JavaFX support. Not needed for running from the command line.
   */
  public static void main(String[] args) {
    launch(args);
  }
}

Client

import java.io.*;
import java.net.*;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Client extends Application {
  // IO streams
  DataOutputStream toServer = null;
  DataInputStream fromServer = null;

  @Override // Override the start method in the Application class
  public void start(Stage primaryStage) {
    // Panel p to hold the label and text field
    BorderPane paneForTextField = new BorderPane();
    paneForTextField.setPadding(new Insets(5, 5, 5, 5)); 
    paneForTextField.setStyle("-fx-border-color: green");
    paneForTextField.setLeft(new Label("Type here: "));

    TextField tf = new TextField();
    tf.setAlignment(Pos.BOTTOM_RIGHT);
    paneForTextField.setCenter(tf);

    BorderPane mainPane = new BorderPane();
    // Text area to display contents
    TextArea ta = new TextArea();
    mainPane.setCenter(new ScrollPane(ta));
    mainPane.setTop(paneForTextField);

    // Create a scene and place it in the stage
    Scene scene = new Scene(mainPane, 450, 200);
    primaryStage.setTitle("Client"); // Set the stage title
    primaryStage.setScene(scene); // Place the scene in the stage
    primaryStage.show(); // Display the stage

    tf.setOnAction(e -> {
      try {
        // Get the radius from the text field
        //double radius = Double.parseDouble(tf.getText().trim());
        String clientText = tf.getText().trim();
        String serverText = fromServer.readLine();
        // Send the radius to the server
        toServer.writeBytes(clientText);
        toServer.flush();

        // Get area from the server
        //double area = fromServer.readDouble();

        // Display to the text area
        ta.appendText("Client: " + clientText + "n");
        ta.appendText("Server: " + serverText + 'n');
      }
      catch (IOException ex) {
        System.err.println(ex);
      }
    });

    try {
      // Create a socket to connect to the server
      Socket socket = new Socket("localhost", 8000);
      // Socket socket = new Socket("130.254.204.36", 8000);
      // Socket socket = new Socket("drake.Armstrong.edu", 8000);

      // Create an input stream to receive data from the server
      fromServer = new DataInputStream(socket.getInputStream());

      // Create an output stream to send data to the server
      toServer = new DataOutputStream(socket.getOutputStream());
    }
    catch (IOException ex) {
      ta.appendText(ex.toString() + 'n');
    }
  }

  /**
   * The main method is only needed for the IDE with limited
   * JavaFX support. Not needed for running from the command line.
   */
  public static void main(String[] args) {
    launch(args);
  }
}

Firebase OrderbyChild Query returns the parent key

I am having a problem retrieve and delete the correct level of node in Firebase database using the orderByChild().equalTo() method.

Searched over the web for quite a while and didn’t really solve my problem. Please help.

My database structure:

enter image description here

My Firebase methods to add and delete photo

private void addPhotoToDatabase(String url){
    Log.d(TAG, "addPhotoToDatabase: adding photo to database");

    String newPhotoKey = myRef.child(mContext.getString(R.string.dbname_user_photos)).push().getKey();
    Photo photo = new Photo();
    photo.setDate_created(getTimeStamped());
    photo.setImage_path(url);
    photo.setUser_id(FirebaseAuth.getInstance().getCurrentUser().getUid());
    photo.setPhoto_id(newPhotoKey);

    //insert into database.
    myRef.child(mContext.getString(R.string.dbname_user_photos)).child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child(newPhotoKey).setValue(photo);

}

private void deletePhotoFromDatabase(String image_path){
    Log.d(TAG, "deletePhotoFromDatabase: deleting photo from database");
    Log.d(TAG, "deletePhotoFromDatabase: image_path is " + image_path);

    String user_id = FirebaseAuth.getInstance().getCurrentUser().getUid();
    myRef.child(mContext.getString(R.string.dbname_user_photos))
            .child(user_id)
            .orderByValue().equalTo(image_path).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

                String photoKey = dataSnapshot.getKey();
                Log.d(TAG, "onDataChange: photo key is " + photoKey);

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

}

I know I don’t have a removeValue or setValue(null) method yet, because I just can’t get to the correct level. The photoKey I was trying to retrieve is the -L-FnLvNxLauiue4InSxE which was automatically generated when adding into Firebase. However the log always returns with its parent node which is the UID.

By the way, the image_path I retrieved from the Firebase storage which was logged correctly.

Could someone give me some inspirations where I did wrong? Thanks a lot!

Strange output from java 8 file/word counting program

I’m working on an assignment which uses Java streams with lambda syntax. The program is supposed to be designed (1) To count a set of files (2) To count the words within those files (3) Print and display the result. This is an example of the output:

Count 11 files:
word length: 1 ==> 80
word length: 2 ==> 321
word length: 3 ==> 643
.....

However, I’m getting this output instead:

primes.txt
word length: [I@5c647e05 ==> hw8.WordCount@33909752
constitution.txt
word length: [I@55f96302 ==> hw8.WordCount@3d4eac69
.....
Count: 11 files

I think the problem has something to do with how the .toString() method of an array which returns a String describing the identity of the array rather than its contents. However, I checked Oracle’s java 8 website, and I couldn’t find or use anything there that could help me correct the issue. The program I’ve write is in two classes FileCatch, which counted the files and WordCount, which counts the words (In theory) If anyone has any programming tips to help, I would be appreciated.

The FileCatch class

public class FileCatch8 {
    public static void main(String args[]) {
        List<String> fileNames = new ArrayList<>();
        try {
            DirectoryStream<Path> directoryStream = Files.newDirectoryStream
        (Paths.get("files"));
            for (Path path : directoryStream) {
                int[] numbers = {1,2,3,4,5,6,7,8,9,10,11};
                System.out.println(path.getFileName());
                fileNames.add(path.getFileName().toString());
                WordCount WordCnt = new WordCount();
                System.out.println("word length: " + numbers.toString() + " ==> " + WordCnt);
            }
    }catch(IOException ex){
    }
    System.out.println("Count: "+fileNames.size()+ " files");

  }
}

The WordCount class:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import java.util.stream.Stream;

/**
 *
 * @author GeraldShields
 */
public class WordCount {

    /**
     *
     * @return 
     * @throws IOException
     */
    public Map<String, Long> WordCount()throws IOException {
        Stream<String> lines = Files.lines(Paths.get("constitution.txt"));
        Map<String, Long> wordMap = lines
                .parallel()
                .map(String::toLowerCase)
                .map(line -> line.split("\W+"))
                .flatMap(line -> Arrays.asList(line).stream())
                .filter(word -> !word.matches("\d+") && word.trim().length() != 0)
                .map(word -> new SimpleEntry<>(word, 1))
                .collect(groupingBy(SimpleEntry::getKey, counting()));
        new TreeMap(wordMap).forEach((k, v) -> 
                System.out.println(String.format("%s word length: 1 ==> %d", k, v)));
        return wordMap;
    }
}

Google My Business Api : Not able to fetch more than 200 reviews

I am trying to retrieve reviews via Google My Business Api with pageSize=200. My business has around 317 reviews. It gives me pageToken, which I append in next call to Google. But this time instead of giving remaining 117 reviews, it just returns “averageRating” and “totalReviewCount”

I used following uri for first hit :

https://mybusiness.googleapis.com/v3/accounts/<account>/locations/<location>/reviews?pageSize=200

This returned 200 reviews, averageRating and totalReviewCount

Uri used for next hit :

https://mybusiness.googleapis.com/v3/accounts/<account>/locations/<location>/reviews?pageSize=200&pageToken=<token_as_received_in_first_response>

This returned averageRating and totalReviewCount only. (expected was remaining 117 review)

Anybody else facing this issue?

Why my custom listview is not showing anything?

I am making a list view to show all the upcoming contests in codeforces. So I have made a CodeforcesContest object and a fragment which contains a ListView. I want all the information about a contest to be in a row of the ListView. Contest informations are contest name, contest time, and another string to hold the site name, which is always “Codeforces”. Here’s what I have so far. I am pretty new with Java and android. I have searched and found ways to implement custom listview with custom class. However, my listview is not showing anything. I am using android studio 3.0

CodeforcesContest.java

package com.example.redwanul.cptracker;

/**
 * Created by redwanul on 12/1/17.
 */

public class CodeforcesContest {
    private String name;
    private String time;

    public CodeforcesContest(String name, String time) {
        this.name = name;
        this.time = time;
    }

    public String getName() {
        return name;
    }
    public String getTime() {
        return time;
    }
    public void setName(String name) { this.name = name; }
    public void setTime(String time) { this.time = time; }
}

Custom layout for a single row.

row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="9">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:id="@+id/contest_name"
        android:layout_weight="3"/>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:id="@+id/contest_site"
        android:layout_weight="3"
        />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:id="@+id/date_time"
        android:layout_weight="3"/>

</LinearLayout>

My Fragment class for showing the ListView.

FragmentUpcomingContest.java

package com.example.redwanul.cptracker;

import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;

import org.json.JSONArray;
import org.json.JSONObject;

import java.util.ArrayList;


/**
 * A simple {@link Fragment} subclass.
 * Activities that contain this fragment must implement the
 * {@link FragmentUpcomingContest.OnFragmentInteractionListener} interface
 * to handle interaction events.
 * Use the {@link FragmentUpcomingContest#newInstance} factory method to
 * create an instance of this fragment.
 */
public class FragmentUpcomingContest extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    private OnFragmentInteractionListener mListener;

    public FragmentUpcomingContest() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment FragmentUpcomingContest.
     */
    // TODO: Rename and change types and number of parameters
    public static FragmentUpcomingContest newInstance(String param1, String param2) {
        FragmentUpcomingContest fragment = new FragmentUpcomingContest();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ArrayList <CodeforcesContest> arrayList = new ArrayList<>();
        CodeforcesContest[] contests;
        View myLayout = inflater.inflate(R.layout.fragment_fragment_upcoming_contest,container,false);

        ListView listView = myLayout.findViewById(R.id.listView);
        try {
            JSONArray jsonArray = new GetCodeforcesContestList().execute().get();
            for(int i = 0; i < jsonArray.length(); i++){
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String _name = jsonObject.getString("name");
                String _phase = jsonObject.getString("phase");
                String _time = jsonObject.getString("startTimeSeconds");
                Log.d("Debug: name",_name);
                Log.d("Debug: phase",_phase);
                Log.d("Debug: Time",_time);
                if(_phase.equals(new String("BEFORE"))){
                    arrayList.add(new CodeforcesContest(_name,_time));
                }
            }

        }
        catch (Exception ex){
            ex.printStackTrace();
        }
        contests = new CodeforcesContest[arrayList.size()];

        for(int i = 0; i < arrayList.size(); i++){
            contests[i] = arrayList.get(i);
        }
        Log.d("Array Length: ", new Integer(arrayList.size()).toString());
        ContestAdapter mAdapter = new ContestAdapter(getContext(),R.layout.row,contests);
        listView.setAdapter(mAdapter);

        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_fragment_upcoming_contest, container, false);
    }

    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(Uri uri) {
        if (mListener != null) {
            mListener.onFragmentInteraction(uri);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void onFragmentInteraction(Uri uri);
    }
}

The fragment layout.
fragment_fragment_upcoming_contest

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.redwanul.cptracker.FragmentUpcomingContest">

    <!-- TODO: Update blank fragment layout -->
    <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/listView">

    </ListView>

</FrameLayout>

My custom adapter for showing list.
ContestAdapter.java

package com.example.redwanul.cptracker;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.TextView;

import org.w3c.dom.Text;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

/**
 * Created by redwanul on 12/1/17.
 */

public class ContestAdapter extends ArrayAdapter<CodeforcesContest> {
    Context context;
    int layourResourceId;
    private static LayoutInflater inflater = null;
    CodeforcesContest[] data = null;

    public ContestAdapter(Context context, int layoutResourceId, CodeforcesContest[] data){
        super(context,layoutResourceId,data);
        this.layourResourceId = layoutResourceId;
        this.context = context;
        this.data = data;
        int ll = data.length;
        Log.d("Constructor",new Integer(data.length).toString());
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View vi = convertView;
        if(vi == null)
            vi = inflater.inflate(R.layout.row,null);
        CodeforcesContest contest = data[position];
        Log.d("In Adapter",contest.getName());
        TextView contestName = (TextView)vi.findViewById(R.id.contest_name);
        TextView contestSite = (TextView)vi.findViewById(R.id.contest_site);
        TextView timeDate = (TextView)vi.findViewById(R.id.date_time);

        Date date = new Date(Integer.parseInt(contest.getTime()) * 1000);
        SimpleDateFormat formatter = new SimpleDateFormat("M/D/YYYY");
        String dateString = formatter.format(date);

        contestName.setText(contest.getName());
        contestSite.setText("Codeforces");
        timeDate.setText(dateString);
        return vi;
    }
}

How to add dynamic attribute to dynamic element in JAXB

Trying to create any element with attributes dynamically and
now able to create dynamic element without attributes,
need help on add attributes to dynamically created element.

Dynamic element created bellow

  public class CustomElement {


        private  List<JAXBElement<String>> Elements;


        @XmlAnyElement
        public List<JAXBElement<String>> getElements() {
            return Elements;
        }

        public void setElements(List<JAXBElement<String>> elements) {
            Elements = elements;
        }


        public void setElements(Map<String, String>  myElements, String namespaceURI) {

               List<JAXBElement<String>> elements = new ArrayList<JAXBElement<String>>();
                for (Map.Entry<String, String> mapElement: myElements.entrySet()) 
                {               
                    JAXBElement jAXBElement=new JAXBElement(new QName(namespaceURI,mapElement.getKey()), 
                            String.class, mapElement.getValue());

                    elements.add(jAXBElement);
                }
            Elements = elements;
        }

//not working attr added to parent element not to current element
     private Map<QName, String> attr;

        @XmlAnyAttribute
        public Map<QName, String> getAttr() {
            return attr;
        }

        public void setAttr(Map<QName, String> attr) {
            this.attr = attr;
        }

    }

Setting values

 Map<String, String> myElements =new HashMap<String,String>();  

             myElements.put("connectmrf ","");  
                setElements(myElements,"www.xxxxx.xxx/xxx/vmas");

             Map<QName, String> attr=new HashMap<QName,String>();   
                attr.put(new QName("Name"),"Amit");  
                attr.put(new QName("age"),"10");  

                setAttr(attr);

Current Result :

     <state  age="10" Name="Amit">
            <vmas:connectmrf ></vmas:connectmrf >
      </state>

Expected Result : please suggest

     <state>
            <vmas:connectmrf  age="10" Name="Amit" ></vmas:connectmrf >
      </state>

How to set wildcard on System.setProperty in java

May be this a silly question but I cant find an answer.
I have project on git which contain a class where I set system property like bellow

System.setProperty("webdriver.gecko.driver", "resources/drivers/geckodriver.exe") 

when I clone this project on Linux I get an error which is obvious Linux cant run .exe file then I rename the file like

System.setProperty("webdriver.gecko.driver", "resources/drivers/geckodriver")  

and add geckodriver for Linux. But when once again I run this on windows I get same error, then I change code to geckodriver.exe

is there any way to say i set anything you find resources/drivers/ gecko* run it.

How do I convert following code to streams

I have a list how can I convert it to a Map in which I can have keys with minimum value as key value pair. I want to convert this code to streams.

Map<Long, Date> formToDate = new HashMap<>();
    for(FormInstance formInstance : formInstances) {
        if(formToDate.get(formInstance.getForm().getId()) == null) {
            formToDate.put(formInstance.getForm().getId(), formInstance.getCreatedDate());
        }
        else{
            Date prevDate = formToDate.get(formInstance.getForm().getId());
            Date thisDate = formInstance.getCreatedDate();
            formToDate.put(formInstance.getForm().getId(), prevDate.before(thisDate) ? prevDate : thisDate);
        }
    }

to something like this:

Map<Long, List<Date>> formToDate = formInstances.stream()
            .collect(
                    Collectors.groupingBy(formInstance -> formInstance.getForm().getId(),
                            Collectors.mapping(FormInstance::getCreatedDate, Collectors.toList())));

But instead of returning list all I want to have the smallest date.

Geospatial reasoning in Java

I have a system that uses a lot of logic involving geospatial containment relations (qualitative containment based on a gazetteer not geometric containment). I’m using a Lucene index of GeoNames database to store the locations and all reasoning is done using brute force Java code. Let me give an example. Suppose I have the text “Paris, Texas, USA, France”. The system first extracts all possible candidates for each location from the Lucene index. Suppose there are 2 candidates for Paris, P1 and P2 where P1 represents the capital city of France and P2 represents a town in Texas, USA. Each location is represented as a Java object. A location object may be a country, a state or a location that is more specific than state nor country (e.g. county, city, town, park, lake etc). Each location object has a country code, and when applicable a state code. Therefore, in this case, P1 will have a country code of “FR”. P2 will have a country code of “US” and state code of “TX”. For simplicity let’s suppose there is only 1 candidate for the remaining 3 locations: T1, U1, and F1 for Texas, USA and France respectively. The goal is:

1. retrieve all triples (X, Y, Z) such that X is a place in Y and Y is a place in Z
2. If no such triple exists, then retrieve all duples (X,Y) such that X is a place in Y

Therefore in the example above the answer should be “Paris, Texas, USA”. If we take out “Texas”, then the answers should be “Paris, USA” and “Paris, France”.

I currently use brute force java code to do this which is not a very elegant solution and I was looking into alternative approaches. Drools with backward chaining seemed to be a possible solution. My concern is whether that will be an overkill. I also use a lot of other rules which can be encoded in Drools. However, all websites appear to suggest that Drools should not be used unless you have rules that may change frequently and you need people without technical knowledge to understand and change the rules. Neither is true in my case. Can someone suggest whether Drools will be appropriate here? Also, how much extra memory does the use of Drools typically entail?

I have also previously considered using GeoNames ontology along with SPARQL queries. However, I want the system to be easily deployable – an user should be able to download and run it easily. It should also be efficient and enable spell correction of locations when there is no match. I tried using Apache Jena for a separate task involving the DBPedia database and the SPARQL queries tended to be pretty slow. Also, the TDB store took up a lot of space so I felt like this will not be an ideal solution for easy deployment of the system, though I have not tried it specifically for GeoNames. In addition, spell correction will also need to be done separately using a Lucene index – so I felt like it will include unnecessary overhead for enabling a single query. Therefore I chose not to use it. However, I’m not entirely sure if that was the right choice. If you have any knowledge of how long it approximately takes to run such containment queries (for GeoNames or similar resources) that will be great.