2023年11月24日 星期五

how to add libraries for a NetBeans project

 如何為 NetBeans 專案添加類別庫,下面以添加 Apache Commons-CSV 讀取CSV檔案的類別庫為例。

下載及解開 Apache Commons-CSV 類別庫
    URL: https://commons.apache.org/proper/commons-csv/download_csv.cgi
	     點選 Binaries/commons-csv-1.10.0-bin.zip
    解壓後路徑: C:\...\commons-csv-1.10.0 

從 NetBeans 左欄面板,如下點選填入名稱,類別檔,原始碼,註解檔路徑。
Projects/Project Name/Libraries/Add Library.../Create
	Library Name: Commons-CSV
	Library Type: Class Libraries

	Classpath/Library Classpath: 類別檔路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\commons-csv-1.10.0.jar

	Sources/Library Sources: 原始碼路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\commons-csv-1.10.0-sources.jar

	Javadoc/Library Javadoc: 註解檔路徑
		Add JAR/Folder...
		C:\...\commons-csv-1.10.0\apidocs

設定類別檔,原始碼,註解檔的完整路徑,安裝類別庫之後,NetBeans專案就可以使用如下類別庫套件:
  org.apache.commons.csv.*

遇到有疑問的類別或方法,可選擇其名字,按如下選單或快速鍵,查看其註解或原始碼:
看註解: Source/Show Documentation (Ctrl-Shift-Space)
看原始碼: Navigate/Go to Source (Ctrl-Shift-B)

2023年10月18日 星期三

how to solve the tower of hanoi by recursion versus simulated call stack?


/*
   TowerOfHanoi.java  遞迴版 及 模擬呼叫堆疊版 求解河內塔 

> java TowerOfHanoi
Hanoi Tower by Implicit Call Stack 遞迴版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]

Hanoi Tower by Explicit Stack 模擬呼叫堆疊版
A:[3, 2, 1], B:[], C:[]		1: Move disk 1 from A to C
        A:[3, 2], B:[], C:[1]		2: Move disk 2 from A to B
        A:[3], B:[2], C:[1]		3: Move disk 1 from C to B
A:[3], B:[2, 1], C:[]		4: Move disk 3 from A to C
A:[], B:[2, 1], C:[3]		5: Move disk 1 from B to A
        A:[1], B:[2], C:[3]		6: Move disk 2 from B to C
        A:[1], B:[], C:[3, 2]		7: Move disk 1 from A to C
A:[], B:[], C:[3, 2, 1]
*/
import java.util.Stack;

public class TowerOfHanoi 
{
    static Stack stackA = new Stack<>();  // 柱A
    static Stack stackB = new Stack<>();  // 柱B
    static Stack stackC = new Stack<>();  // 柱C
    static int nDisks = 5; // Number of disks 盤數
    static int count = 0;  // 步數
    static boolean printGoal = false;  // 列印目標否
    static boolean printOperation = true;  // 列印步驟否
    static boolean printStack = false;  // 列印模擬呼叫堆疊否

    // 印n格空白
    public static void printSpaces(int n)
    {
        for(int i=0; i <= n -1; i++) System.out.print(" ");
    }
    
    // 印柱A,柱B,柱C堆疊,前面n層內縮
    public static void printStacks(int n)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("A:" + stackA);
        sb.append(", B:" + stackB);
        sb.append(", C:" + stackC);

        System.out.println();
        printSpaces(n*8);  // 每層內縮8格
        System.out.print(sb.toString());
    }
    
    // 搬移柱from頂一個盤子到柱to
    public static void transfer(char from, char to)
    {
        if(from=='A' && to=='B') stackB.push(stackA.pop());
        if(from=='A' && to=='C') stackC.push(stackA.pop());
        if(from=='B' && to=='A') stackA.push(stackB.pop());
        if(from=='B' && to=='C') stackC.push(stackB.pop());
        if(from=='C' && to=='A') stackA.push(stackC.pop());
        if(from=='C' && to=='B') stackB.push(stackC.pop());
    }
    
    // 遞迴版解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void solveHanoi(int n, char source, char auxiliary, char target) 
    {        
        if(printGoal) 
        {
            System.out.println();
            printSpaces((nDisks - n)*8);
            System.out.print(String.format("hanoi(n:%d,s:%c -> t:%c)",n,source,target));
        }

        if (n == 1) 
        {
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

            transfer(source, target);            
        } 
        else 
        {
            solveHanoi(n - 1, source, target, auxiliary);

            printStacks(nDisks - n);            
            if(printOperation) 
                System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
            transfer(source, target);
            printStacks(nDisks - n);

            solveHanoi(n - 1, auxiliary, source, target);
        }
    }

    // 模擬呼叫記錄    
    static class HanoiCallRecord
    {
        int num;
        char source;
        char auxiliary;
        char target;
        int stage; // 0 for moving n-1 disks from source to auxiliary rods; 
                   // 1 for moving the disk n from source to target rods
                   //   and moving n-1 disks from auxiliary to target rods
                   // 2 for backtracking to the previous call record
        
        // 建構子
        public HanoiCallRecord(int num, char source, char auxiliary, char target)
        {
            this.num = num;
            this.source = source;
            this.auxiliary = auxiliary;
            this.target = target;
            this.stage = 0;  // 預設從階段0開始
        }
        
        // 列印呼叫記錄
        public String toString()
        {
            return String.format("(n:%d,s:%c,a:%c,t:%c,s:%d)",
                    num, source, auxiliary, target, stage);
        }
    }
    
    // 模擬呼叫堆疊,解河內塔,將n個盤子從柱sourc,搬到柱target,透過柱auxiliary
    public static void hanoiUsingStacks(int num, char src, char aux, char tgt) 
    {
        Stack stack = new Stack<>();
        
        HanoiCallRecord initial = new HanoiCallRecord(num, src, aux, tgt);
        stack.push(initial);  // 壓入第1層呼叫記錄

        while (stack.isEmpty()==false) 
        {
            if(printStack) System.out.print("\n" + stack);
            
            HanoiCallRecord current = stack.peek();  // 檢視本層呼叫記錄
            int n = current.num;
            char source = current.source;
            char auxiliary = current.auxiliary;
            char target = current.target;
            int stage = current.stage;
            
           if (n == 1) // 執行特別任務,然後退回上一層任務
           {
                if(printOperation) 
                    System.out.print(String.format("\t\t%d: Move disk 1 from %c to %c", ++count, source, target));

                transfer(source, target);
                stack.pop();  // 彈出本層呼叫記錄,
            } 
            else if(stage == 0) // 階段0, 執行本層第0階段任務
            {
                // solveHanoi(n - 1, source, target, auxiliary);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, source, target, auxiliary);
                stack.push(next); // 壓入下層呼叫記錄
                current.stage++;  // 更新本層呼叫記錄的階段欄位
            }
            else if(stage == 1) // 階段1, 執行本層第1階段任務
            {
                printStacks(nDisks - n);            
                if(printOperation)
                    System.out.print(String.format("\t\t%d: Move disk %d from %c to %c", ++count, n, source, target));
                transfer(source, target);
                printStacks(nDisks - n);

                // solveHanoi(n - 1, auxiliary, source, target);
                HanoiCallRecord next = new HanoiCallRecord(n - 1, auxiliary, source, target);
                stack.push(next);  // 壓入下層呼叫記錄
                current.stage++; // 更新本層呼叫記錄的階段欄位
            }
            else if(current.stage == 2) // 階段2,本層任務完成,退回上一層任務
            {
                stack.pop();  // 彈出本層呼叫記錄,
            }
        }
    } 

    // 測試主程式
    public static void main(String[] args) 
    {
        nDisks = 3; // Number of disks
        count = 0;
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
        
        System.out.print("Hanoi Tower by Implicit Call Stack 遞迴版");

        printStacks(0);
        solveHanoi(nDisks, 'A', 'B', 'C');
        printStacks(0);
        
        // ===================================
    
        count = 0;
        stackA.clear();
        stackB.clear();
        stackC.clear();
        for(int i=nDisks; i >= 1; i--) stackA.push(i);
                    
        System.out.print("\n\nHanoi Tower by Explicit Stack 模擬呼叫堆疊版");
        
        printStacks(0);
        hanoiUsingStacks(nDisks, 'A', 'B', 'C');
        printStacks(0);
    }
}    

2022年11月2日 星期三

how to correct garbled code when printing Chinese in NetBeans Output Window?

因為 NetBeans 某些版本設定其 Output 結果視窗接收字碼為 UTF8,故用 Java 列印中文時若出現亂碼,請在列印指令前,加上修改輸出串流編碼為 UTF8 指令。寫法如下:

   import java.io.PrintStream;

   System.setOut(new PrintStream(System.out, true, "UTF8"));

   // 執行上述指令須處理 UnsupportedEncodingException 未支援編碼例外

   // 可用try-catch結構自己處理例外,或宣告執行該指令的方法丟出(throws)該例外給上一層處理

若想知道輸出串流目前編碼為何,可用如下指令診斷。

   System.out.println(System.out.charset());  // 常見為 x-windows-950 (繁中Big5碼) 或 UTF-8

: Windows 繁中版使用 Big5 編碼,故終端機下使用 javac.exe 編譯器若出現 unmappable character 無法映射字元錯誤,可利用如下選項,通知編譯器選用適當編碼,讀取中文:

      javac.exe -encoding big5 My.java    適用於My.java中文用Big5編碼

      javac.exe -encoding utf8 My.java    適用於My.java中文用UTF8編碼

2021年8月27日 星期五

how to query OSM data in Java?

開放街圖OSM的公共圖資可以透過overpass-turbo介面查詢獲得。
若想利用Java程式碼查詢,可參考以下查詢範例。
其中,查詢指令可參考overpass-turbo範例指令,測試成功再代入程式。


/*
  QueryOSM.java
        展示如何用retrofit套件,同步及非同步(適用於Android),存取如下OSM圖資服務
                http://overpass-api.de/api/interpreter?data=xxx

        // 建立服務連線客戶端及請求內容
        OverpassService requestClient = OverpassServiceProvider.get();
        String request = composeRequest();
        
        // 非同步查詢圖資,適用於手機平板Android平台
        asyncRequest(requestClient, request);
        
        // 同步查詢圖資,適用於桌機Application應用
        OverpassQueryResult result = syncRequest(requestClient, request);
        postProcess(result);
 
執行步驟:
  javac QueryOSM.java
  java QueryOSM
run:
[out:json][timeout:25];
(
node[tourism](25.1735, 121.446, 25.1775, 121.455);
relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);
);
out center;
end of asyncRequest()
21 elements...
1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon

Response{protocol=http/1.1, code=200, message=OK, url=http://overpass-api.de/api/interpreter?data=%5Bout%3Ajson%5D%5Btimeout%3A25%5D%3B%0A%28%0Anode%5Btourism%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0Arelation%5B%21highway%5D%5Btype%21%3D%27route%27%5D%5B%21boundary%5D%2825.1735%2C%20121.446%2C%2025.1775%2C%20121.455%29%3B%0A%29%3B%0Aout%20center%3B}
end of syncRequest()
21 elements...
1. id:4326372453, type:node, lat:25.1761808, lon:121.4490491, name:五虎碑, wheelchair:limited
2. id:4384988658, type:node, lat:25.1750071, lon:121.4522078, name:文錙藝術中心, wheelchair:yes
3. id:4492221396, type:node, lat:25.1736267, lon:121.4473809, name:三化牆
4. id:4492221397, type:node, lat:25.1744069, lon:121.4474037, name:地球村雕像, wheelchair:limited
5. id:4492221399, type:node, lat:25.1735175, lon:121.4484658, name:淡江大學花牆
6. id:4492221425, type:node, lat:25.1749949, lon:121.450678, name:閱讀的少女, wheelchair:yes
7. id:4502075211, type:node, lat:25.1751384, lon:121.4523232, name:旅者
8. id:4502075212, type:node, lat:25.1739938, lon:121.4505047, name:李雙澤紀念碑, wheelchair:yes
9. id:4502075213, type:node, lat:25.1761919, lon:121.4499374, name:福園金鷹銅雕, wheelchair:no
10. id:4502075222, type:node, lat:25.1738907, lon:121.4475716, name:驚聲銅像, wheelchair:limited
11. id:4507662408, type:node, lat:25.1741082, lon:121.4474671, name:溫馨, wheelchair:yes
12. id:5012978611, type:node, lat:25.1741784, lon:121.4507282, name:黃河母親, wheelchair:no
13. id:5072580167, type:node, lat:25.1769149, lon:121.4495309
14. id:5130535622, type:node, lat:25.1757202, lon:121.4496844, name:會文館, wheelchair:yes
15. id:5132288341, type:node, lat:25.1741586, lon:121.4508061
16. id:5132288342, type:node, lat:25.174208, lon:121.4475417
17. id:6050843218, type:node, lat:25.1770813, lon:121.449821
18. id:8991981256, type:node, lat:25.1750049, lon:121.4480033, name:淡江願景牆, wheelchair:yes
19. id:3974590, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:操場
20. id:3983402, type:relation, lat:0.0, lon:0.0, type:multipolygon, name:松濤廣場
21. id:7530081, type:relation, lat:0.0, lon:0.0, type:multipolygon

end of main()
BUILD SUCCESSFUL (total time: 1 minute 2 seconds)

  參考:
  a. Retrofit 2 – Synchronous and asynchronous call example
        https://howtodoinjava.com/retrofit2/retrofit-sync-async-calls/
  b. https://github.com/zsoltk/overpasser
        hu.supercluster.overpasser.adapter
            OverpassQueryResult
            OverpassQueryResult.Element
            OverpassQueryResult.Element.Tags
            OverpassService
  c. 引用函數庫
      compile/run: 
        okhttp-3.14.9.jar
        okio-1.17.2.jar
        converter-gson-2.10.2.jar
        gson-2.8.5.jar
        retrofit-2.10.2.jar
      compile/run tests:
        byte-buddy-1.11.3.jar
        byte-buddy-agent-1.11.3.jar
        mockito-core-3.11.2.jar
        objenesis-3.2.jar
 */
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

import hu.supercluster.overpasser.library.output.OutputFormat;
import hu.supercluster.overpasser.library.output.OutputModificator;
import hu.supercluster.overpasser.library.output.OutputOrder;
import hu.supercluster.overpasser.library.output.OutputVerbosity;
import hu.supercluster.overpasser.library.query.OverpassQuery;
 
import hu.supercluster.overpasser.adapter.OverpassQueryResult;
import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element;
import hu.supercluster.overpasser.adapter.OverpassQueryResult.Element.Tags;
import hu.supercluster.overpasser.adapter.OverpassService;
import hu.supercluster.overpasser.adapter.OverpassServiceProvider;

import java.lang.reflect.Field;
import java.util.List;
/**
 *
 * @author seke
 */

public class QueryOSM {
    
    // 可利用下址測試查詢指令
    //     http://overpass-turbo.eu/
    public static String composeRequest()
    {
/* 查詢範例1: 
        在(47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427)範圍內
        列出所有非私人停車場
        
    A. 查詢指令
    ["out":"json"]["timeout":"30"];
    (
        node
            ["amenity"="parking"]
            ["access"!="private"]
            (47.48047027491862,19.039797484874725,47.51331674014172,19.07404761761427);
            <;
    );
    out body center qt 100;

    B. 組合查詢指令方法
      String query = new OverpassQuery()
        .format(OutputFormat.JSON)
        .timeout(30)
        .filterQuery()
            .node()
            .amenity("parking")
            .tagNot("access", "private")
            .boundingBox(
                47.48047027491862, 19.039797484874725,
                47.51331674014172, 19.07404761761427
            )
        .end()
        .output(OutputVerbosity.BODY, OutputModificator.CENTER, OutputOrder.QT, 100)
        .build()
        ;
*/

/* 查詢範例2: 
        在(25.1735, 121.446, 25.1775, 121.455)範圍內,撈取
           非座椅設施,商店,觀光點,辦公室,繄急設施之節點
           屬於大學設施之線條,關係
           不屬於公路、路線、森林、邊界之線條,關係
        列出其中心位置及相關屬性(標籤)

        A. 查詢指令
    [out:json][timeout:25];
    // gather results
    (
      node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);
      node[shop](25.1735, 121.446, 25.1775, 121.455);
      node[tourism](25.1735, 121.446, 25.1775, 121.455);
      node[office](25.1735, 121.446, 25.1775, 121.455);
      node[emergency](25.1735, 121.446, 25.1775, 121.455);
      //way[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
      relation[amenity="university"](25.1735, 121.446, 25.1775, 121.455);
      way[!highway][type!="route"][landuse!="forest"][!boundary](25.1735, 121.446, 25.1775, 121.455);
      relation[!highway][type!="route"][!boundary](25.1735, 121.446, 25.1775, 121.455);
    );
    // print results
    out center;
---
  註:  淡江大學之範圍為 25.1735, 121.446, 25.1775, 121.455
*/       
       String query = String.join("\n",
               "[out:json][timeout:25];",
               "(",
//               "node[amenity][amenity!=bench](25.1735, 121.446, 25.1775, 121.455);",
//               "node[shop](25.1735, 121.446, 25.1775, 121.455);",
               "node[tourism](25.1735, 121.446, 25.1775, 121.455);",
//               "node[office](25.1735, 121.446, 25.1775, 121.455);",
//               "node[emergency](25.1735, 121.446, 25.1775, 121.455);",
//               "relation[amenity='university'](25.1735, 121.446, 25.1775, 121.455);",
//               "way[!highway][type!='route'][landuse!='forest'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
               "relation[!highway][type!='route'][!boundary](25.1735, 121.446, 25.1775, 121.455);",
               ");",
               "out center;");

        System.out.println(query);
        return query;
    }
    
    public static void asyncRequest(OverpassService apiClient, String request)
    {
       //Call call = service.interpreter(query);
       apiClient.interpreter(request).enqueue(new Callback()
       {
        @Override
        public void onResponse(Call call, Response response)
        {
            if(response.isSuccessful()==false)
            {
                System.out.println("asyncRequest: response.isSuccessful(): false");
                System.out.println(response.errorBody());
                return;
            }
            
            OverpassQueryResult result = response.body();
            postProcess(result);
         }

        public void onFailure(Call call, Throwable t) {
        // DO failure handling 
          System.out.println("onFailure");
          System.out.println(t.getLocalizedMessage());
        }
       });
       
       System.out.println("end of asyncRequest()");
    }
    
    public static OverpassQueryResult syncRequest(OverpassService apiClient, String request)
    {
        OverpassQueryResult result = null;
        Call callSync =   apiClient.interpreter(request); 

        try
        {
            Response response = callSync.execute();
            //OverpassQueryResult apiResponse = response.body();
     
            //API response
            System.out.println(response);
            result = response.body();
        }
        catch (Exception ex) 
        { 
            ex.printStackTrace();
        }
        
        System.out.println("end of syncRequest()");
        return result;
    }
    
    public static void postProcess(OverpassQueryResult result)
    {
        if(result==null) return;

        // DO success handling 
        StringBuilder sb = new StringBuilder();
        System.out.println(result.elements.size() + " elements...");
        int count = 1;
        for (Element p : result.elements) 
        {
            sb.append(count); count++;
            sb.append(String.format(". id:%s, type:%s, lat:%s, lon:%s", p.id, p.type, p.lat, p.lon));
            Tags tags = p.tags;
            for (Field f : tags.getClass().getFields()) {
                f.setAccessible(true);
                try 
                {
                    if (f.get(tags) != null) {
                       sb.append(String.format(", %s:%s", f.getName(), f.get(tags)));
                    }
                }
                catch (IllegalAccessException e)
                { // shouldn't happen because I used setAccessible
                }

            }
            //if(p.tags. != null)
            //  sb.append(String.format("tags:%s", p.tags.name));
            sb.append("\n");
        }
        System.out.println(sb.toString());
    }
    
    public static void main(String args[])
    {
        // 建立服務連線客戶端及請求內容
        OverpassService requestClient = OverpassServiceProvider.get();
        String request = composeRequest();
        
        // 非同步請求
        asyncRequest(requestClient, request);
        
        // 同步請求
        OverpassQueryResult result = syncRequest(requestClient, request);
        postProcess(result);
        
        System.out.println("end of main()");
    }
}

how to add libraries for a NetBeans project

 如何為 NetBeans 專案添加類別庫,下面以添加 Apache Commons-CSV 讀取CSV檔案的類別庫為例。 下載及解開 Apache Commons-CSV 類別庫 URL: https://commons.apache.org/proper/common...