diff --git a/src/main/java/qxsl/local/LocalCityBase.java b/src/main/java/qxsl/local/LocalCityBase.java index 01738d31..52156cb3 100644 --- a/src/main/java/qxsl/local/LocalCityBase.java +++ b/src/main/java/qxsl/local/LocalCityBase.java @@ -53,6 +53,20 @@ public final List toList() { return Collections.unmodifiableList(list); } + /** + * 指定された識別子の有無を確認します。 + * + * + * @param code 地域の識別子 + * + * @return 存在する場合は真 + * + * @since 2024/10/19 + */ + public final boolean containsCode(String code) { + return this.forwardMap.containsKey(code); + } + /** * 指定された識別子に対応する地域を返します。 * diff --git a/src/main/java/qxsl/ruler/Absence.java b/src/main/java/qxsl/ruler/Absence.java index 31818a03..2bed7856 100644 --- a/src/main/java/qxsl/ruler/Absence.java +++ b/src/main/java/qxsl/ruler/Absence.java @@ -94,4 +94,39 @@ public final int result(Summary items) { public final List getCityList() { return List.of(); } + + /** + * 標準的な実装です。 + * + * + * @author 無線部開発班 + * + * @since 2024/10/19 + */ + public static abstract class Default extends Absence { + private final String name; + private final String code; + + /** + * 指定された部門を構築します。 + * + * + * @param name 部門の名前 + * @param code 部門の分類 + */ + public Default(String name, String code) { + this.name = name; + this.code = code; + } + + @Override + public final String name() { + return name; + } + + @Override + public final String code() { + return code; + } + } } diff --git a/src/main/java/qxsl/ruler/Program.java b/src/main/java/qxsl/ruler/Program.java index b3065bfb..959979f5 100644 --- a/src/main/java/qxsl/ruler/Program.java +++ b/src/main/java/qxsl/ruler/Program.java @@ -194,7 +194,9 @@ public boolean finish(ZoneId zone) { * * @since 2022/07/17 */ - public abstract int limitMultipleEntry(String code); + public int limitMultipleEntry(String code) { + return 1; + } /** * 指定された部門にまとめて登録可能か確認します。 @@ -206,5 +208,108 @@ public boolean finish(ZoneId zone) { * * @since 2022/07/17 */ - public abstract boolean conflict(Section[] entries); + public boolean conflict(Section[] entries) { + return entries.length > 1; + } + + /** + * 標準的な実装です。 + * + * + * @author 無線部開発班 + * + * @since 2024/10/19 + */ + public static abstract class Default extends Program { + private String name; + private String host; + private String mail; + private String link; + private String help; + + /** + * 指定された名前の規約を構築します。 + * + * + * @param name 規約の名前 + */ + public Default(String name) { + this.name = name; + } + + @Override + public final String name() { + return name; + } + + @Override + public final String host() { + return host; + } + + @Override + public final String mail() { + return mail; + } + + @Override + public final String link() { + return link; + } + + @Override + public final String help() { + return help; + } + + /** + * 規約の名前を設定します。 + * + * + * @param name 規約の名前 + */ + public final void setName(String name) { + this.name = name; + } + + /** + * 運営の名前を設定します。 + * + * + * @param host 運営の名前 + */ + public final void setHost(String host) { + this.host = host; + } + + /** + * 運営の連絡先を設定します。 + * + * + * @param mail 運営の連絡先 + */ + public final void setMail(String mail) { + this.mail = mail; + } + + /** + * 規約の参照先を設定します。 + * + * + * @param link 規約の参照先 + */ + public final void setLink(String link) { + this.link = link; + } + + /** + * 規約のヘルプを返します。 + * + * + * @param help ヘルプ + */ + public final void setHelp(String help) { + this.help = help; + } + } } diff --git a/src/main/java/qxsl/ruler/Section.java b/src/main/java/qxsl/ruler/Section.java index b2b86646..1c61c151 100644 --- a/src/main/java/qxsl/ruler/Section.java +++ b/src/main/java/qxsl/ruler/Section.java @@ -5,8 +5,12 @@ *******************************************************************************/ package qxsl.ruler; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.List; +import qxsl.draft.*; import qxsl.local.LocalCityBase; import qxsl.local.LocalCityItem; import qxsl.model.Item; @@ -158,4 +162,251 @@ public final LocalCityBase getCityBase() { * @since 2022/06/22 */ public abstract List getCityList(); + + /** + * 標準的な実装です。 + * + * + * @author 無線部開発班 + * + * @since 2024/10/19 + */ + public static abstract class Default extends Section { + private ZoneId zoneId = ZoneId.systemDefault(); + private LocalCityBase codes; + private List hours; + private List bands; + private List modes; + private String name; + private String code; + + /** + * 指定された部門を構築します。 + * + * + * @param name 部門の名前 + * @param code 部門の分類 + */ + public Default(String name, String code) { + this.name = name; + this.code = code; + } + + @Override + public String name() { + return name; + } + + @Override + public String code() { + return code; + } + + /** + * 部門の名前を設定します。 + * + * + * @param name 部門の名前 + */ + public final void setName(String name) { + this.name = name; + } + + /** + * 部門の分類を設定します。 + * + * + * @param code 部門の分類 + */ + public final void setCode(String code) { + this.code = code; + } + + /** + * 指定された周波数帯を受容します。 + * + * + * @param bands 周波数帯 + */ + public final void setBands(Band...bands) { + this.bands = List.of(bands); + } + + /** + * 指定された通信方式を受容します。 + * + * + * @param modes 通信方式 + */ + public final void setModes(Mode...modes) { + this.modes = List.of(modes); + } + + /** + * 指定された時間帯を受容します。 + * + * + * @param hours 時間帯 + */ + public final void setHours(int...hours) { + this.hours = new ArrayList(); + for(int hr: hours) this.hours.add(hr); + } + + /** + * 指定された時間帯を設定します。 + * + * + * @param zoneId 時間帯 + */ + public final void setZoneId(ZoneId zoneId) { + this.zoneId = zoneId; + } + + /** + * 指定された地域を受容します。 + * + * + * @param codes 地域 + */ + public final void set(LocalCityBase codes) { + this.codes = codes; + } + + @Override + public final List getCityList() { + return codes == null? List.of(): codes.toList(); + } + + @Override + public Message verify(Item item) { + final var band = (Band) item.getBoth(Qxsl.BAND); + final var mode = (Mode) item.getBoth(Qxsl.MODE); + final var code = (Code) item.getRcvd(Qxsl.CODE); + final var time = (Time) item.getBoth(Qxsl.TIME); + final var hour = time.atZone(zoneId).value(); + if(!verifyBand(band)) return new Failure(item, band); + if(!verifyMode(mode)) return new Failure(item, mode); + if(!verifyTime(hour)) return new Failure(item, time); + if(!verifyCode(code)) return new Failure(item, code); + return new Success(item, points(item)); + } + + /** + * 指定された周波数帯が規約に準拠するか確認します。 + * + * + * @param band 周波数帯 + * + * @return 準拠する場合は真 + */ + public boolean verifyBand(Band band) { + return bands == null || bands.contains(band); + } + + /** + * 指定された通信方式が規約に準拠するか確認します。 + * + * + * @param mode 通信方式 + * + * @return 準拠する場合は真 + */ + public boolean verifyMode(Mode mode) { + return modes == null || modes.contains(mode); + } + + /** + * 指定された交信時刻が規約に準拠するか確認します。 + * + * + * @param time 交信時刻 + * + * @return 準拠する場合は真 + */ + public boolean verifyTime(ZonedDateTime time) { + return hours == null || hours.contains(time.getHour()); + } + + /** + * 指定されたナンバーが規約に準拠するか確認します。 + * + * + * @param code ナンバー + * + * @return 準拠する場合は真 + */ + public boolean verifyCode(Code code) { + return codes == null || codes.containsCode(cityOf(code)); + } + + /** + * 指定されたナンバーから市区町村番号を抽出します。 + * + * + * @param code ナンバー + * + * @return 市区町村番号 + */ + public String cityOf(Code code) { + return code.value().replaceFirst("^599?", ""); + } + + /** + * 指定された交信の得点を計算します。 + * + * + * @param item 交信 + * + * @return 得点 + */ + public int points(Item item) { + return 1; + } + + /** + * 周波数毎の呼出符号を数えます。 + * + * + * @param item 検査対象の交信記録 + * + * @return 周波数と呼出符号の組 + */ + @Override + public Element unique(Item item) { + final var call = (Call) item.getBoth(Qxsl.CALL); + final var band = (Band) item.getBoth(Qxsl.BAND); + return new Element(call, band); + } + + /** + * 周波数毎のナンバーを数えます。 + * + * + * @param item 検査対象の交信記録 + * + * @return 周波数とナンバーの組 + */ + @Override + public Element entity(Item item) { + final var band = (Band) item.getBoth(Qxsl.BAND); + final var code = (Code) item.getRcvd(Qxsl.CODE); + return new Element(List.of(band, cityOf(code))); + } + + /** + * 交信の点数とマルチの個数の積を返します。 + * + * + * @param items 集計結果 + * + * @return 総得点 + */ + @Override + public int result(Summary items) { + final int score = items.score(); + final var mults = items.keys(0); + return score > 0? score * mults.size(): 0; + } + } } diff --git a/src/main/java/qxsl/ruler/Summary.java b/src/main/java/qxsl/ruler/Summary.java index 2e712468..9ca18b37 100644 --- a/src/main/java/qxsl/ruler/Summary.java +++ b/src/main/java/qxsl/ruler/Summary.java @@ -134,7 +134,7 @@ public final int total() { * * @since 2020/02/26 */ - private final Set keys(int rank) { + public final Set keys(int rank) { final var set = new HashSet(); for(var el: mul) set.add(el.get(rank)); return Collections.unmodifiableSet(set);