Student.java
成績と出欠データを保持する生徒のクラス。今回のプログラムの目的は、帳票印刷とプレビューなのでクラスの設計は簡略化してある。
import java.util.*; public class Student { int no; //番号 String name; //氏名 List<String> kyoukas = new ArrayList<>(); //教科名 List<String> kamokus = new ArrayList<>(); //科目名 List<Integer> tanis = new ArrayList<>(); //単位数 List<Integer> zdns = new ArrayList<>(); //授業日数 List<String> bikous = new LinkedList<>(); //所見(備考) Map<Integer,List<Integer>> tensmap = new HashMap<>(); //成績 Map<Integer,List<Integer>> kessmap = new HashMap<>(); //欠席 public Student(int no){ this(no,""); } public Student(int no, String name){ this.no = no; this.name = name; } }
HRdata.java
Studentクラスにデモ用データをセットするためのクラス。9つのファイルから読み込んで、Studentのマップを返す。これもファイルは簡便なテキストファイルにしてある。
import java.io.BufferedReader; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.*; public class HRdata { Map<Integer,Student> students = new LinkedHashMap<>(); int gakki; String HR; int nendo; String kouchou = "小泉 洋二"; String hrtan = "八木橋 孝"; public HRdata(int nendo, String HR, int gakki){ this.gakki = gakki; this.HR = HR; this.nendo = nendo; String pt="data/"; switch(gakki){ case 3: readSeisekiFile(pt+HR+"5.txt",4); readSeisekiFile(pt+HR+"33.txt",3); readShukketsuFile(pt+HR+"4-3",4); readShukketsuFile(pt+HR+"1-3",3); case 2: readSeisekiFile(pt+HR+"23.txt",2); readShukketsuFile(pt+HR+"8-12",2); case 1: readSeisekiFile(pt+HR+"13.txt",1); readShukketsuFile(pt+HR+"4-7",1); } readZdnFile(pt+HR+"ZDN"); String file = pt+String.valueOf(nendo).substring(2,4)+HR; String ame = "tsu"+gakki+".txt"; readBun(file,ame); } /**成績ファイル読み込み*/ private void readSeisekiFile(String fileName, int gakki) { try(BufferedReader br = Files.newBufferedReader(Paths.get(fileName))){ String line; line = br.readLine(); String[] kyoukas = line.split("\t"); line = br.readLine(); String[] kamokus = line.split("\t"); line = br.readLine(); String[] tanis = line.split("\t"); while ((line = br.readLine()) != null) { //if (line.startsWith("#")) continue; String[] tmp = line.split("\t"); if (3>tmp.length) continue; List<Integer> tens = new ArrayList<>(); for(int i=2;tmp.length>i;i++){ if (tmp[i].equals("( )")) tens.add(-1); else if (tmp[i].equals(" ")) tens.add(-3); else tens.add(Integer.parseInt(tmp[i])); } int no = Integer.parseInt(tmp[0]); if(!students.containsKey(no)){ students.put(no,new Student(no,tmp[1])); } Student st = students.get(no); st.tensmap.put(gakki,tens); if(gakki==1){ for(int i=2;kyoukas.length>i;i++){ st.kyoukas.add(kyoukas[i]); st.kamokus.add(kamokus[i]); st.tanis.add(Integer.parseInt(tanis[i])); } } } }catch(IOException e){ System.err.println( e); } } //"17/02/11_11:12:44" //"DIM=3 begin with 0 0 1",0,38,6 //4,9,15,0,6,8 //2,2,0,0,0,1 /**出欠ファイル読み込み*/ private void readShukketsuFile(String fileName, int gakki) { try(BufferedReader br = Files.newBufferedReader(Paths.get(fileName))){ String line; line = br.readLine(); String[] date = line.split(","); line = br.readLine(); String[] dformat = line.split(","); line = br.readLine(); String[] sum = line.split(","); int no = 0; while ((line = br.readLine()) != null) { String[] tmp = line.split(","); if (3>tmp.length) continue; List<Integer> kess = new ArrayList<>(); for(int i=0;tmp.length>i;i++){ if (tmp[i].equals("")) kess.add(0); else kess.add(Integer.parseInt(tmp[i])); } no++; if(!students.containsKey(no)){ students.put(no,new Student(no,"")); } students.get(no).kessmap.put(gakki,kess); } }catch(IOException e){ System.err.println( e); } } /**授業日数のファイルを読む*/ private void readZdnFile(String fileName) { try(BufferedReader br = Files.newBufferedReader(Paths.get(fileName))){ String line = br.readLine(); String[] tmp = line.split(","); List<Integer> zdns = new ArrayList<>(); for(int i=0;tmp.length>i;i++){ if (tmp[i].equals("")) zdns.add(0); else zdns.add(Integer.parseInt(tmp[i])); } for (Student st:students.values()) { st.zdns.addAll(zdns); } }catch(IOException e){ System.err.println( e); } } /** 161A01xxx1.txt から文章データを読む */ private void readBun( String file, String ame ) { for (Student st:students.values()) { Path path = Paths.get(file+String.format("%02d",st.no)+ame); if (Files.exists(path)){ try{ st.bikous = Files.readAllLines(path, StandardCharsets.UTF_8 ); }catch (IOException e) { System.err.println(e); } } } } public static void main(String[] args) { int gakki = 3; int n = 2; HRdata hr = new HRdata(2016,"1J",gakki); Student st = hr.students.get(n); System.out.println(st.name); List tens = st.tensmap.get(gakki); List kamokus = st.kamokus; System.out.println(kamokus.size()); for(int i=0;kamokus.size()>i;i++){ System.out.println(kamokus.get(i)+":"+tens.get(i)); } List kess = st.kessmap.get(gakki); for(int i=0;kess.size()>i;i++){ System.out.println(kess.get(i)); } if(gakki==3){ tens = st.tensmap.get(gakki+1); for(int i=0;kamokus.size()>i;i++){ System.out.println(kamokus.get(i)+":"+tens.get(i)); } kess = st.kessmap.get(gakki+1); for(int i=0;kess.size()>i;i++){ System.out.println(kess.get(i)); } System.out.println(st.zdns); } } }
AdjustString.java
横位置の均等割付けなどの配置。第4回に掲載したものに、今回禁則処理と姓名の均等割付けを追加した。
/** 指定範囲に文字列を均等割付、右寄せ、左寄せ、センタリングなどの調整をして描画する 入り切らない場合は、入りきる分だけの部分文字列を保持して 均等割付、左寄せなどの指示により部分文字列を描画する (2017-6-26 version 2.0 (2019-9-20 version 3.0 @author Adachi @version 3.0 */ import java.awt.Graphics2D; import java.awt.FontMetrics; import java.awt.image.BufferedImage; public class AdjustString { String zstr, str; //motono string, string int zcpct,cpct; //code point count int bgncpi, nxtbgncpi; //cp index begin, next cp index begin float wdmm; //width in mm float remm = 0f; //remain in mm; float gapmm = 0f; Graphics2D g2; FontMetrics fm; float mm2pt = 72/25.4f; float pt2mm = 25.4f/72; public boolean debug = false; //true; /** areawidth に string を入れる時の余り remm を計算 */ public AdjustString(Graphics2D g2, String string, float areawidth) { this(g2,string,areawidth,0); } /** areawidth に string を入れる時の余り remm を計算。ただし str の bgncpiから */ public AdjustString(Graphics2D g2, String string, float areawidth, int begincpindex) { this.g2 = g2; fm = g2.getFontMetrics(); zstr = (string!=null) ? string : ""; //nullなら"" wdmm = (areawidth>=0f) ? areawidth : 0f; //負なら0 zcpct = zstr.codePointCount(0,zstr.length()); //zstrのcp数 bgncpi = (begincpindex>=0) ? begincpindex : 0; //負なら0 if (zcpct>bgncpi){ //開始位置をindexに換算 int idxbgn = zstr.offsetByCodePoints(0,bgncpi); str = zstr.substring(idxbgn); }else{ //開始位置が文字列をはみ出していたら str = ""; } remm = wdmm - fm.stringWidth(str)*pt2mm; //あまりを計算 if(debug){System.out.println(report());} while(0>remm && !str.isEmpty()) { int newendidx = str.offsetByCodePoints(str.length(), -1); //一つ前のindexを求める str = str.substring(0,newendidx); remm = wdmm - fm.stringWidth(str)*pt2mm; if(debug){System.out.println(report());} } cpct = str.codePointCount(0,str.length()); nxtbgncpi = bgncpi + cpct; gapmm = remm/(cpct-1); } /** 文字列・余り・文字数 をレポートする。(デバッグ用) */ public String report(){ cpct = str.codePointCount(0,str.length()); nxtbgncpi = bgncpi + cpct; String rstr = String.format( "str=%s /remm=%f /str.length()=%d /cpct=%d /nxtbgncpi=%d", str, remm, str.length(), cpct, nxtbgncpi); return rstr; } /** コンストラクタの直後に呼び出し、 必要があれば kindic にある文字が次の行頭に来るとき、追い出し処理をする nextjbgn(旧版の変数) -> nxtcpibgn(新版の変数) -> nxtbgncpi */ public boolean setOidashiIfNeeded(String kindic){ if (!hasNext()) return false; if (5>str.codePointCount(0, str.length())) return false; int newidxbgn = zstr.offsetByCodePoints(0,nxtbgncpi); String nexttop = zstr.substring(newidxbgn,zstr.offsetByCodePoints(0,nxtbgncpi+1)); if(debug)System.out.println( "str="+str+ " nxtbgncpi="+nxtbgncpi +" newidxbgn="+newidxbgn); if(debug)System.out.println( "nexttop=("+nexttop+")" ); if ( 0>kindic.indexOf(nexttop) ) return false; int newlengthincp = str.codePointCount(0,str.length())-1; int newlengthinindex = str.offsetByCodePoints(0, newlengthincp); str = str.substring(0, newlengthinindex); nxtbgncpi--; remm = wdmm - fm.stringWidth(str)*pt2mm; gapmm = remm/(newlengthincp-1); if(debug)System.out.println( "xstr="+str+ " xnxtbgncpi="+nxtbgncpi ); return true; } /** zstr のなかで書ききれなかった残りがあるかを答える */ public boolean hasNext(){ return zcpct>nxtbgncpi; } /** 書ききれなかった残りの文字の開始位置(コードポイントで)を答える */ public int getNextPt(){ int retval = -1; if (zcpct>nxtbgncpi) retval=nxtbgncpi; return retval; } /** strがareawidth のなかに収まるかを答える */ public boolean isFitInto(){ return remm>=0; } /** 文字列の幅を引いたあまりをmmで返す */ public float getRemain(){ return remm; } /** 文字間の隙間をmmで返す */ public float getGap(){ return gapmm; } /** 書けるだけの文字列を左寄せで描画する */ public void drawLeft(float hm, float vm) { g2.drawString(str,hm*mm2pt,vm*mm2pt); } /** 書けるだけの文字列を右寄せで描画する */ public void drawRight(float hm, float vm) { g2.drawString(str,(hm+remm)*mm2pt,vm*mm2pt); } /** 書けるだけの文字列をセンタリングで描画する */ public void drawCenter(float hm, float vm) { g2.drawString(str,(hm+remm/2)*mm2pt,vm*mm2pt); } /** 書けるだけの文字列を均等割付で描画する */ public void drawKintou(float hm, float vm) { float hpp = 0; int strlen = str.length(); if (cpct>1){ int i=0; int nexti = 0; int ct = 0; while (strlen>i){ nexti = str.offsetByCodePoints(i,1); g2.drawString(str.substring(i,nexti),(hm+gapmm*ct)*mm2pt+hpp,vm*mm2pt); hpp = fm.stringWidth(str.substring(0,nexti)); //pt i=nexti; ct++; } }else{ drawCenter(hm, vm); } } /**半角スペースでは字間スペースを入れない。姓 名 の均等割付に使用 */ public void drawKintouSx(float hm, float vm) { float hpp = 0; int strlen = str.length(); if (cpct>1){ String[] tmp = str.split(" "); int spct = tmp.length-1; float gapmmtmp = remm/(cpct-1-spct); int i=0; int nexti = 0; int ct = 0; while (strlen>i){ nexti = str.offsetByCodePoints(i,1); g2.drawString(str.substring(i,nexti) ,(hm+gapmmtmp*ct)*mm2pt+hpp,vm*mm2pt); hpp = fm.stringWidth(str.substring(0,nexti)); //pt when 0? if (! str.substring(i,nexti).equals(" ") ) ct++; i=nexti; } }else{ drawCenter( hm, vm); } } /**マスの中に入れるため、前後に字間の半分のスペースを入れる */ public void drawInGrid(float hm, float vm) { float hpp = 0; int strlen = str.length(); float gapmmtmp = remm/cpct; int i=0; int nexti = 0; int ct = 0; while (strlen>i){ nexti = str.offsetByCodePoints(i,1); g2.drawString(str.substring(i,nexti), (hm+gapmmtmp*ct+gapmmtmp/2)*mm2pt+hpp,vm*mm2pt); hpp = fm.stringWidth(str.substring(0,nexti)); //pt when 0? i=nexti; ct++; } } }