LAB15 Write a "universal calendar(萬年曆)" program in Java.
     Your program must can be run as an Application and as an Applet.
Purpose: Master the GUI programming and event handling again.
   And learn more about the "Calendar system" as well as.

Due: 2011/06/12 Sunday 23:59  
 
  請注意: 
       做好習題請到數位學習網站本題討論區依照規定貼文寫心得報告, 
   且標題 Subject: LAB15 from 學號姓名(當然是你學號與姓名!)
   報告內容是先寫心得(包括研究萬年曆在各國不同)、然後幾張執行過程中的畫面佐證、
   再接著是真的可以用的 Applet (這已經做這麼多題, 應該都會了!)
   最後才是帶 Line numbers 程式碼方便閱讀;
   注意要把所有檔案和報告資料等都壓縮成可執行的 .jar 檔案當夾檔附件佐證!
   且該 .jar 檔案須可以用 java -jar 該.jar檔案 執行,
      並記得把 該 .jar 檔案上傳到你在系上的網頁空間或其他免費網頁空間,
   還有, 你報告中的 <applet... > 的 code, archive, codebase 也是指到該.jar檔案!
 
Description:   
        大家都知道中華民國元年一月一日是1912/01/01, 但是前一天是1911/12/18你知道嗎?
     哪也安捏啊? 因為羅馬帝國雖然在1582年就已經發現閏年算法錯誤並通令修正,
     可是各個國家執行該項修正的日期卻是不一致的! 可用 Unix命令 ncal -p 看看!
       (註: 範例 Cal999.java 是文字版本, 有點類似 Unix 上的 cal 命令, 以英美曆法為基準; 
        執行  java -jar LAB15.jar  就是等於 java -cp LAB15.jar Cal999 ) 
         再測試這樣:  java -jar LAB15.jar 3  看看結果怎樣?
     (1) 你的程式必須懂公元0~9999年各日是星期幾! 須可當 Application 也可當 Applet !
     (2) 把你學號姓名寫在裡面,萬年曆上要顯示你照片。
     (3) 最好還有你的聲音, 啟動時發音或是點按某些鍵發音。
     (4) 其他功能 :
            請參考微軟的小月曆時鐘(就是點按視窗右下角時間出現的) 
         但是微軟的小月曆只能處理最近幾十年, 你的必須懂公元0~9999年!
         還有,年最好是也可以直接用鍵盤打入, 不然很不方便! (用滑鼠要點很久ㄟ!) 
           呈現的方式各自發揮創意, 最簡單就把結果丟入一個 TextArea 囉:-)
         不過要注意自型(Font)要選用每個英文數字大小都一樣的, 不然對不齊! 
     (5) 萬年曆以美國的曆法為準即可, 不必考慮各國不同情況! 
         也就是你的程式要照 UNIX 系統中 cal 命令的萬年曆算法!
         有關萬年曆年月日算星期幾的規則請看這:
         點 按這看萬年曆規則  
         註: 中國是在清朝結束隔日民國開始時才調整;
          也就是說中華民國 1 年 1月 1日 = 1912/01/01, 但是
          該日前一日是 1911/12/18, 請看 Unix 上執行 ncal -s CN 12 1911
bsd2% ncal -s CN 12 1911      (CN 代表中國; 可用 ncal -p 看各國代碼)
    December 1911
Mo     5 12
Tu     6 13
We     7 14
Th  1  8 15
Fr  2  9 16
Sa  3 10 17
Su  4 11 18    (中國與台灣 1911年12月只有 18天;因為清朝沒有調整曆法) 

bsd2% cal 9 1752
   September 1752
Su Mo Tu We Th Fr Sa
       1  2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30  

bsd2% ncal -s CN 12 1912 
    January 1912
Mo  1  8 15 22 29
Tu  2  9 16 23 30
We  3 10 17 24 31
Th  4 11 18 25
Fr  5 12 19 26
Sa  6 13 20 27
Su  7 14 21 28

Please try  ncal -s LN 9999 
(6) 為了簡單, 不必管時區, 也不用做與標準電子鐘做時間同步化 這萬年曆不必更新系統時間日期, 要否顯示時間或時鐘自由決定:-) 時鐘:在安裝完 JDK 後 Applet demo 目錄內就有 Sun clock.java 範例 找一下 Java 根目錄下 demo\applets\clock\ (7) 可以使用我給的 MyApplet.java 中的 API
可點這抓 MyApplet.java (其實現在大部分同學應該已經不用 MyApplet.java 就自己會處理圖片聲音了) (8) 參考我給的範例 http://www.cs.nctu.edu.tw/~tsaiwn/oop/ 之下 兩個子目錄: (都可當Application 和 Applet) ./java/03_sample_JavaPrograms/11_grid/ 以及 ./java/03_sample_JavaPrograms/13_soundwin/ Note: You should also write what you have learned from this Lab.(心得) 本題心得要包括研究萬年曆規則以及何以各國不同的心得. (想辦法在 FreeBSD 或 Linux 上執行 ncal -p 與 ncal -s CN 12 1911 ) Please do this Lab as soon as possible! (請早點開始做:)
http://www.cs.nctu.edu.tw/~tsaiwn/oop/java/03_sample_JavaPrograms/11_grid/00-testgrid.html http://www.cs.nctu.edu.tw/~tsaiwn/oop/java/03_sample_JavaPrograms/13_soundwin/00_SoundWin_test.html
You are the -the visitors to this page.
1:06pm bsd2:~/> ncal -p 
 AL Albania        1912-11-30      IT Italy          1582-10-04
 AT Austria        1583-10-05      JP Japan          1918-12-18
 AU Australia      1752-09-02      LI Lithuania      1918-02-01
 BE Belgium        1582-12-14      LN Latin          9999-05-31
 BG Bulgaria       1916-03-18      LU Luxembourg     1582-12-14
 CA Canada         1752-09-02      LV Latvia         1918-02-01
 CH Switzerland    1655-02-28      NL Netherlands    1582-12-14
 CN China          1911-12-18      NO Norway         1700-02-18
 CZ Czech Republic 1584-01-06      PL Poland         1582-10-04
 DE Germany        1700-02-18      PT Portugal       1582-10-04
 DK Denmark        1700-02-18      RO Romania        1919-03-31
 ES Spain          1582-10-04      RU Russia         1918-01-31
 FI Finland        1753-02-17      SI Slovenia       1919-03-04
 FR France         1582-12-09      SW Sweden         1753-02-17
 GB United Kingdom 1752-09-02      TR Turkey         1926-12-18
 GR Greece         1924-03-09     *US United States  1752-09-02
 HU Hungary        1587-10-21      YU Yugoslavia     1919-03-04
 IS Iceland        1700-11-16
1:06pm bsd2:~/>
 
bsd2% cal 1
                               1

      January               February               March
Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa  Su Mo Tu We Th Fr Sa
                   1         1  2  3  4  5         1  2  3  4  5
 2  3  4  5  6  7  8   6  7  8  9 10 11 12   6  7  8  9 10 11 12
 9 10 11 12 13 14 15  13 14 15 16 17 18 19  13 14 15 16 17 18 19
16 17 18 19 20 21 22  20 21 22 23 24 25 26  20 21 22 23 24 25 26
23 24 25 26 27 28 29  27 28                 27 28 29 30 31
30 31
...
bsd2% cal 2 4
     February 4
Su Mo Tu We Th Fr Sa
                1  2
 3  4  5  6  7  8  9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29
bsd2% cal 2 8
     February 8
Su Mo Tu We Th Fr Sa
          1  2  3  4
 5  6  7  8  9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29
bsd2% cal 2 1752
   February 1752
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
bsd2% cal 9 1752
   September 1752
Su Mo Tu We Th Fr Sa
       1  2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

You are the -the visitors to this page.       到這個作業目錄       回到作業目錄             回到課程目錄

這是文字版範例, 可以一列排三個月(類似 Unix 的 cal 命令),
也可以一個月一個月列印(比較簡單:-)

   01 //Cal999.java //萬年曆的程式  @CopyLeft by tsaiwn@csie.nctu.edu.tw
   02 //Note that the Sep. 1752 has ONLY 19 days according to USA/England
   03 //See "cal" and "ncal" commands on Unix system
   04 //javac Cal999.java
   05 //java Cal999 3
   06 import java.io.*;
   07 import java.util.*;
   08 public class Cal999 {     // USA/England 1752/09/02 next 09/14
   09    public static final int ggYear = 1752;
   10    public static final int ggMonth = 9;
   11    public static final int ggDay = 14;   // 1752/9/14 0:0:0
   12    public static final int ggChange = 4;   //  4-th day in that week
   13    public static final int skipDays = 11;   //  10 if ggYear <=1699; 1582
   14    static BufferedReader cin = null;
   15    static PrintStream cout = null;
   16    static GregorianCalendar ccc = new GregorianCalendar( );
   17    static int days[ ] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
   18    static int cols = 1;
   19    public Cal999( ) {   }
   20    public static void main(String[ ] args) throws IOException {
   21        Cal999 gg = new Cal999( );
   22        if(args.length >= 1) gg.cols = 3;    // 3 columns for year cal
   23        gg.prepareIO( );
   24        gg.main( );
   25    }
   26    void prepareIO( ) throws IOException {
   27        cout = System.out;
   28        cin = new BufferedReader(new InputStreamReader(System.in));
   29    }
   30    Cal999(BufferedReader in, PrintStream out) { // for network version
   31        cout = out; cin = in;
   32    }
   33    void start( ) { try { main( ); }catch(Exception e) {;}  }
   34    static int whichDay = 0;   // 用來存輸入的 "日", if any
   35    int main( ) throws IOException {
   36        String str = null;
   37        cout.println("\t === 歡迎使用 萬 年 曆 ===\r");
   38        Date now = new Date( );   // get current date/time
   39        int nowYY = now.getYear( ) + 1900;  // A.D.
   40        int nowMM = now.getMonth( ) + 1;   // 0..11 ==> 1..12
   41        cout.println( showCal(nowMM, nowYY) );  // current Month
   42        int tmp = getMY( );   // get Month + Year *100
   43        // day will be put in the global variable whichDay if any
   44        while(tmp != -1) {  // 注意傳回的值是 年 *100 + 月
   45           int mm = tmp % 100;   // month
   46           int yy = tmp / 100;   // year
   47           if(mm == 0) processYear( yy );    // 月是 0 表示要整年
   48           else if(whichDay !=0) processDay(mm, whichDay, yy);  //日 
   49           else if(mm >= 1 && mm <= 12) processMonth(mm, yy);  // 月 
   50           else {
   51              cout.print( "\n"+"********************************"+"\n");
   52              cout.println("Error Input 輸入 Month 錯誤!");
   53           } whichDay = mm = 0;  // clear day
   54           tmp = getMY( );   // read Day Month Year , again
   55        } // while
   56        cout.println("GregorianChange = " + ccc.getGregorianChange( ) );
   57        return 0;
   58    }// int main(
   59 
   60 static void adjustDay(GregorianCalendar g) {   // A.D.1700年是否為閏年 ?
   61     // 1752/09/02 Wednesday --> next day --> 1752/09/14 Thursday
   62    //GregorianChange: 1582/10/04 next 10/15; USA: 1752/09/02 next 09/14
   63    if(ggYear <= 1582) return; // do NOT adjust, use Default Gregorian
   64    g.setGregorianChange(new Date(ggYear-1900, ggMonth-1, ggDay, 0, 0, 0));
   65 }  
   66 static boolean isLeap(int yyyy) {   // return true if yyyy is Leap year
   67    GregorianCalendar gCal = new GregorianCalendar( );
   68    adjustDay(gCal);   // USA/England calendar; 不然是用羅馬? 1582/10/15
   69    if(38==38)return gCal.isLeapYear(yyyy);    // 用程式庫的
   70   ////// or use my own algorithm :
   71    if (yyyy%400 == 0) return true;
   72    if(yyyy >= ggYear) {   // 1752/09 for USA/England?  1582/10 for Italy?
   73        if ( (yyyy%4 == 0) && (yyyy%100 != 0) ) return true;
   74    }else{  // before 1752 in USA/England
   75        if (yyyy % 4 == 0)  return true;
   76    }
   77    return false;
   78 }// isLeap(   
   79 //////
   80  static int getMY( ) {   // get  " MM DD YYYY "  from User
   81     int ans=0, m=0, d=0, y=-1;  // mm dd yyyy
   82     cout.print("\r\n 請輸入 年 或是 月  年 或  月 日 年( -1 結束): ");
   83     whichDay = 0;  // clear the day in global variable
   84     try{  
   85        String s = cin.readLine( );   if(s == null) s = "-1";
   86        StringTokenizer stk = new StringTokenizer(s, " ,\t");  
   87        try{   // assume Year ONLY
   88           m = 0; 
   89           if(stk.hasMoreTokens( )) y = Integer.parseInt(stk.nextToken( ));
   90        }catch(Exception e) {;}
   91        if(stk.hasMoreTokens( )) {    // assume Month Year
   92           try{ 
   93               m = y;   // Month Year
   94               y = Integer.parseInt(stk.nextToken( ));
   95           }catch(Exception e) { y = -1;}
   96        }
   97        if(stk.hasMoreTokens( )) {  // is  "MM  DD  YYYY"
   98           d = y; y=2010;    // 剛才的 y 應該是 d  (日)
   99           try{
  100               y = Integer.parseInt(stk.nextToken( ));
  101           }catch(Exception e) { y = -1;}
  102           whichDay = d;  // put day in global variable
  103        }// mm, dd, yyyy  
  104     }catch(Exception e) {;;}
  105     if(m == -1  || y == -1) return -1; 
  106     if(m > 12) m = m%100;   // month should be in 1..12 ; 防呆? 
  107     ans = y*100 + m ;   // year* 100  + m
  108     return ans;
  109  }// getMY(
  110 //////
  111  static int processYear(int y) throws IOException {
  112      if(y <= 0) return 0;
  113      cout.println(showCal(y));
  114      return 0;
  115  } //
  116  static int processMonth(int m, int y) throws IOException {
  117      if(y == 0) return 0;  
  118      cout.println(showCal(m, y).replace(" _", "  ") );   
  119      return 0;
  120  } //int processMonth(  
  121  static int processDay(int m, int d, int y) throws IOException {
  122      String dn[ ]={"Sunday", "Monday", "Tuesday", "Wednesday",
  123                      "Thursday", "Friday", "Saturday" };
  124      adjustDay(ccc);   // according to USA/England
  125      ccc.set(y, m-1, d);   // 設定日期再查出星期幾; 不然自己算也可
  126      int today = ccc.get(Calendar.DAY_OF_WEEK)-1;   // 0..6:Sun..Sat
  127      cout.printf("%d/%02d/%02d is %s\n", y, m, d,  dn[today]);
  128      return 0;
  129     /// A.D.01/01/01 Saturday; 1752/09/14 Thursday前一日是 1752/09/02
  130  }// int processDay(   
  131 
  132 public static String showCal(int mon, int yyyy){
  133    int beginDay=1,lostdat=0;
  134    String msg="\r";
  135    if (mon==2 && isLeap(yyyy)) days[1] = 29; else days[1] = 28; // Feburary
  136   ///
  137    adjustDay(ccc);   // according to USA/England
  138    ccc.set(yyyy, mon-1, 1);
  139    msg += "\r\n" + " \t\t\t" + yyyy + " 年 " + mon + " 月" + ".\t.\t.\t\r\n";
  140    msg += "日 \t一 \t二 \t三 \t四 \t五 \t六 \n\r";
  141   //////
  142    int firstDay = ccc.get(Calendar.DAY_OF_WEEK)-1;   // 0..6:Sun..Sat
  143    if ( (firstDay+days[mon-1])%7 != 0) lostdat = 1;
  144    int weeks = (firstDay +days[mon-1])/7+lostdat ;
  145    if(yyyy== ggYear && mon==ggMonth) weeks=3;  // 19 days ONLY in Sep. 1752
  146    for(int i = 1;i<= weeks ;i++) {
  147       for(int j = 1;j<=7;j++) {   // 7 days a week; Sun, Mon, .. Sat
  148          if(beginDay <= firstDay || beginDay > days[mon-1] + firstDay)
  149             msg+=" _ \t";
  150          else { int dd = beginDay - firstDay ;
  151               if(dd <= 9) msg += " ";   // Align Right
  152               msg += dd +" \t";
  153          }// one day OK
  154          // check to see if it is in special month ? and is adjust day?
  155          if(yyyy==ggYear && mon== ggMonth) 
  156             if(beginDay == ggChange) beginDay += skipDays; // adjust
  157          beginDay++;   // advance one day
  158       }// for j
  159       msg+="\r\n";    // next week on next Line
  160    }// for i  
  161    if(weeks<=5) msg+= " _ \t _ \t _ \t _ \t _ \t _ \t _ \t\r\n";  
  162    return msg;
  163 }// showCal(int mon, int yyyy
  164 
  165  public static String showCal(int yyyy) {
  166     String yearMessage="";
  167     if(cols == 1) {    // one month in a Row
  168       for(int mm=1;mm<=12; mm++) {
  169          String msg = showCal(mm, yyyy);   // month, year
  170          yearMessage += msg;    // concatenate together
  171       }
  172     }else{  // 3 columns;  3 months in a row
  173       String msga="", msgb="", msgc="";
  174       StringTokenizer a, b, c;
  175       for(int mm=1;mm<=12; mm=mm+3) {
  176          msga = showCal(mm, yyyy);   // month, year
  177          msgb = showCal(mm+1, yyyy);   // month, year
  178          msgc = showCal(mm+2, yyyy);   // month, year
  179          a = new StringTokenizer(msga, "\r\n");
  180          b = new StringTokenizer(msgb, "\r\n");
  181          c = new StringTokenizer(msgc, "\r\n");
  182          while(a.hasMoreTokens( ) || b.hasMoreTokens( ) || 
  183             c.hasMoreTokens( ) ) {
  184             try{
  185                 yearMessage += a.nextToken( );    // concatenate together
  186             }catch(Exception e){;}
  187             yearMessage += "      ";
  188             try{
  189                 yearMessage += b.nextToken( );    // concatenate together
  190             }catch(Exception e){;}
  191             yearMessage += "      ";
  192             try{
  193                 yearMessage += c.nextToken( );    // concatenate together
  194             }catch(Exception e){;}
  195             yearMessage += "\r\n";
  196          }//while
  197       }//for
  198     }  
  199     yearMessage = yearMessage.replace("\t", " ");
  200     yearMessage = yearMessage.replace("  ", " ");
  201     yearMessage = yearMessage.replace(" _", "  ");   
  202     return yearMessage;
  203  }// showCal for one year
  204 }//class Cal999
  205