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++;
}
}
}
