uni

University stuff
git clone git://git.christosmarg.xyz/uni-assignments.git
Log | Files | Refs | README | LICENSE

commit 36aa22af7df23e097eaf90fb809250fdb958ba84
parent e4080fe882a9cfc78099598fab4dc520e7bccf20
Author: Christos Margiolis <christos@margiolis.net>
Date:   Fri,  4 Jun 2021 01:23:44 +0300

progress, very useful message

Diffstat:
Mcpp_oop/game/Engine.cc | 293++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mcpp_oop/game/Engine.hpp | 37++++++++++++++++++++++++++++---------
Acpp_oop/game/Gem.cc | 24++++++++++++++++++++++++
Acpp_oop/game/Gem.hpp | 18++++++++++++++++++
Mcpp_oop/game/Makefile | 1+
Mcpp_oop/game/Movable.cc | 8++++----
Mcpp_oop/game/Movable.hpp | 8++++----
Mcpp_oop/game/Score.cc | 66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcpp_oop/game/Score.hpp | 18++++++++++++++++--
Mcpp_oop/game/main.cc | 9++++-----
Mcpp_oop/game/res/score | 0
Mjava_development/population/.classpath | 2+-
Mjava_development/population/.settings/org.eclipse.jdt.core.prefs | 6+++---
Mjava_development/population/bin/population/Country.class | 0
Mjava_development/population/bin/population/ExcelParser.class | 0
Mjava_development/population/bin/population/Main.class | 0
Mjava_development/population/src/population/Country.java | 29++++++++++++++++++++++-------
Mjava_development/population/src/population/ExcelParser.java | 93++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mjava_development/population/src/population/Main.java | 46+++++++++++++++++++++++++++++++++++++---------
19 files changed, 509 insertions(+), 149 deletions(-)

diff --git a/cpp_oop/game/Engine.cc b/cpp_oop/game/Engine.cc @@ -6,30 +6,59 @@ enum Color { POTTER, GNOME, TRAAL, + STONE, + PARCHMENT, LAST }; -/* TODO: intro message and stuff? */ Engine::Engine(const char *mapfile, const char *scorefile) { if (!init_curses()) - throw "init_curses failed"; + throw std::runtime_error("init_curses failed"); /* * We'll use exceptions here because we want to display a useful - * error message since `load_map` has many points of failure. - * If we do catch an exception, we'll just "forward" it to `main`. + * error message since `load_map` and `Score`'s constructor + * have many points of failure. If we do catch an exception, + * we'll just "forward" it to `main`. */ try { load_map(mapfile); - } catch (std::runtime_error& e) { - throw "load_map failed: " + std::string(mapfile) + ": " + e.what(); + score = new Score(scorefile); + } catch (const std::runtime_error& e) { + throw std::runtime_error("error: " + std::string(e.what())); + } catch (const std::ios_base::failure& e) { + throw std::runtime_error("error: " + std::string(e.what())); } + if (!init_gamewin()) - throw "init_gamewin failed"; - if (!init_entities()) - throw "init_entities failed"; - if (!init_score(scorefile)) - throw "init_score failed: " + std::string(scorefile); + throw std::runtime_error("init_gamewin failed"); + init_entities(); + prch = nullptr; + + /* Initialize messages for the popup windows. */ + ctrls = { + "Up Move up", + "Down Move down", + "Left Move left", + "Right Move right", + "ESC Quit", + "s High Scores", + "c Controls menu", + "Press any key to quit the menu", + }; + rules = { + " Babis Potter", + "--------------------------------------------------------", + "The objective is to collect all the gems without getting", + "caught by the Gnomes and Traals!", + "", + "You can always see what controls are available by", + "pressing the 'c' key.", + " Press any key to continue", + }; + + /* Display a welcome message. */ + popup(rules); f_running = 1; } @@ -39,16 +68,24 @@ Engine::~Engine() player = nullptr; for (auto&& e : entities) delete e; + for (auto&& g : gems) + delete g; + if (prch != nullptr) + delete prch; delete score; map.clear(); colors.clear(); + ctrls.clear(); + rules.clear(); (void)delwin(gw); (void)endwin(); } /* Private methods */ -/* Initialize curses(3) environment */ +/* + * Initialize curses(3) environment + */ bool Engine::init_curses() { @@ -70,6 +107,8 @@ Engine::init_curses() colors.push_back(COLOR_CYAN); /* Potter */ colors.push_back(COLOR_GREEN); /* Gnome */ colors.push_back(COLOR_YELLOW); /* Traal */ + colors.push_back(COLOR_MAGENTA);/* Stone */ + colors.push_back(COLOR_WHITE); /* Parchment */ start_color(); use_default_colors(); @@ -107,13 +146,15 @@ Engine::load_map(const char *mapfile) f.exceptions(std::ifstream::badbit); f.open(mapfile); if (!f.is_open()) - throw std::runtime_error("cannot open file"); + throw std::runtime_error(std::string(mapfile) + + ": cannot open file"); /* * Read first row outside the loop so we can get an initial * row length. */ if (!std::getline(f, str)) - throw "cannot read first row"; + throw std::runtime_error(std::string(mapfile) + + ": cannot read first row"); map.push_back(str); l = str.length(); while (std::getline(f, str)) { @@ -123,14 +164,19 @@ Engine::load_map(const char *mapfile) * have the same length. */ if (l != str.length()) - throw std::runtime_error("rows must have an equal " - "length: line " + std::to_string(curline)); + throw std::runtime_error(std::string(mapfile) + + ": rows must have an equal length: line " + + std::to_string(curline)); - /* The map must not contain anything other than ' ' and '*'. */ + /* + * The map must not contain anything other than SYM_PATH + * and SYM_WALL. + */ for (char& c : str) - if (c != ' ' && c != '*') - throw std::runtime_error("the map must contain " - "only spaces and asterisks: line: " + + if (c != SYM_PATH && c != SYM_WALL) + throw std::runtime_error(std::string(mapfile) + + "the map must contain only spaces and " + "asterisks: line: " + std::to_string(curline)); map.push_back(str); @@ -150,14 +196,15 @@ Engine::load_map(const char *mapfile) * the Y axis are reserved for the status bar. */ if (w > xmax || h > ymax - 2) - throw std::runtime_error("the map doesn't fit to screen"); + throw std::runtime_error(std::string(mapfile) + + ": the map doesn't fit to screen"); } bool -Engine::collides_with_wall(int x, int y) +Engine::collides_with_wall(int x, int y) const { if (x < w && y < h) - return map[y][x] == '*'; + return map[y][x] == SYM_WALL; return true; } @@ -174,18 +221,20 @@ Engine::calc_pos(int *x, int *y) for (const auto& e : entities) if (*x == e->get_x() && *y == e->get_y()) continue; + for (const auto& g : gems) + if (*x == g->get_x() && *y == g->get_y()) + continue; } while (collides_with_wall(*x, *y)); } -bool +void Engine::init_entities() { int i, x, y, type; srand(time(nullptr)); - calc_pos(&x, &y); - entities.push_back(new Potter(x, y, Movable::Direction::DOWN, 'P')); + entities.push_back(new Potter(x, y, Movable::Direction::DOWN, SYM_POTTER)); for (i = 0; i < nenemies; i++) { calc_pos(&x, &y); /* @@ -195,58 +244,78 @@ Engine::init_entities() switch (type = rand() % 2) { case 0: entities.push_back(new Gnome(x, y, - Movable::Direction::DOWN, 'G')); + Movable::Direction::DOWN, SYM_GNOME)); break; case 1: entities.push_back(new Traal(x, y, - Movable::Direction::DOWN, 'T')); + Movable::Direction::DOWN, SYM_TRAAL)); break; } } + for (i = 0; i < ngems; i++) { + calc_pos(&x, &y); + gems.push_back(new Gem(x, y, SYM_STONE)); + } player = (Potter *)entities[0]; - - return true; } -bool -Engine::init_score(const char *scorefile) +void +Engine::spawn_parchment() { - score = new Score(scorefile); + int x, y; - return true; + calc_pos(&x, &y); + prch = new Gem(x, y, SYM_PARCHMENT); } +/* + * Draw a popup window with the `lines` argument as contents. + */ void -Engine::ctrl_menu() +Engine::popup(const std::vector<std::string>& lines) const { - WINDOW *opts; - int wr, wc, wy, wx; + WINDOW *win; + auto lencmp = [](const std::string& a, const std::string& b) { + return a.length() < b.length(); + }; + int vecsz, wr, wc, wy, wx; - wc = 32; - wr = 9; + vecsz = lines.size(); + /* + * Find longest string to set the right window width. +2 columns + * for the box. + */ + wc = std::max_element(lines.begin(), lines.end(), lencmp)->length() + 2; + /* + * +2 lines for the box, and +1 for the space between the last message + * and the rest of the messages. + */ + wr = vecsz + 3; wx = CENTER(xmax, wc); wy = CENTER(ymax, wr); - if ((opts = newwin(wr, wc, wy, wx)) == NULL) + if ((win = newwin(wr, wc, wy, wx)) == NULL) return; - werase(opts); - box(opts, 0, 0); - - mvwprintw(opts, 1, 1, "Up Move up"); - mvwprintw(opts, 2, 1, "Down Move down"); - mvwprintw(opts, 3, 1, "Left Move left"); - mvwprintw(opts, 4, 1, "Right Move right"); - mvwprintw(opts, 5, 1, "ESC Quit"); - mvwprintw(opts, 7, 1, "Press any key to quit the menu"); - - wrefresh(opts); - (void)wgetch(opts); - werase(opts); - wrefresh(opts); - (void)delwin(opts); + werase(win); + box(win, 0, 0); + for (std::size_t i = 0; i < vecsz; i++) { + if (i != vecsz - 1) + mvwprintw(win, i + 1, 1, lines[i].c_str()); + /* + * The last message is always the "quit menu" message, so + * we'll leave an empty line before we print it. + */ + else + mvwprintw(win, i + 2, 1, lines[i].c_str()); + } + wrefresh(win); + (void)wgetch(win); + werase(win); + wrefresh(win); + (void)delwin(win); } void -Engine::calc_dist(std::map<int, int>& dists, int ex, int ey, int dir) +Engine::calc_dist(std::map<int, int>& dists, int ex, int ey, int dir) const { int px, py, dx, dy, d; @@ -286,8 +355,11 @@ Engine::kbd_input() dir = Movable::Direction::DOWN; break; case 'c': - ctrl_menu(); + popup(ctrls); return; + case 's': + popup(score->scores_strfmt()); + break; case ESC: /* FALLTHROUGH */ /* FIXME: what? */ f_running = 0; @@ -336,55 +408,103 @@ Engine::enemies_move() } } +/* TODO: define constants for scores */ void Engine::collisions() { - int px, py, ex, ey; + int px, py, x, y; px = player->get_x(); py = player->get_y(); for (const auto& e : entities) { - ex = e->get_x(); - ey = e->get_y(); - if (e != player && px == ex && py == ey) { - /* TODO: increase score */ + x = e->get_x(); + y = e->get_y(); + if (e != player && px == x && py == y) { + /* TODO: Handle this. */ } } + for (auto& g : gems) { + x = g->get_x(); + y = g->get_y(); + if (px == x && py == y) { + upd_score(10); + delete g; + gems.erase(std::remove(gems.begin(), gems.end(), g), + gems.end()); + } + } + if (gems.empty() && prch != nullptr) { + x = prch->get_x(); + y = prch->get_y(); + if (px == x && py == y) { + upd_score(100); + /* TODO: YOU WON + delete + reset */ + } + } else if (gems.empty() && prch == nullptr) + spawn_parchment(); } void -Engine::upd_score() +Engine::upd_score(int n) { + *score << score->get_curname() << score->get_curscore() + n; } void -Engine::redraw() +Engine::redraw() const { - char msg_score[] = "Score: %d"; - char msg_opts[] = "c Controls"; - int color; + const char msg_pos[] = "Potter: (%d, %d)"; + const char msg_score[] = "Score: %d (%s)"; + const char msg_opts[] = "c Controls"; - /* TODO: print more info */ werase(gw); erase(); - mvprintw(0, 0, "Potter: (%d, %d)", player->get_x(), player->get_y()); - mvprintw(0, CENTER(xmax, strlen(msg_score)), msg_score, 10); + + /* Draw top bar info. */ + mvprintw(0, 0, msg_pos, player->get_x(), player->get_y()); + mvprintw(0, CENTER(xmax, strlen(msg_score)), msg_score, + score->get_curscore(), score->get_curname()); mvprintw(0, xmax - strlen(msg_opts), msg_opts); mvhline(1, 0, ACS_HLINE, xmax); + + /* Draw everything */ wattron(gw, A_REVERSE); + draw_map(); + draw_entities(); + draw_gems(); + if (prch != nullptr) + draw_parchment(); + wattroff(gw, A_REVERSE); + refresh(); + wrefresh(gw); +} + +void +Engine::draw_map() const +{ + int color; + for (const auto& row : map) { for (const auto& c : row) { - if (c == '*') + if (c == SYM_WALL) color = COLOR_PAIR(Color::WALL); - else if (c == ' ') + else if (c == SYM_PATH) color = COLOR_PAIR(Color::PATH); wattron(gw, color); waddch(gw, c); wattroff(gw, color); } } +} + +void +Engine::draw_entities() const +{ + int color; + for (const auto& e : entities) { + /* Determine subclass type and assign colors accordingly. */ if (dynamic_cast<Potter *>(e) != nullptr) color = COLOR_PAIR(Color::POTTER); else if (dynamic_cast<Gnome *>(e) != nullptr) @@ -395,13 +515,34 @@ Engine::redraw() mvwaddch(gw, e->get_y(), e->get_x(), e->get_sym()); wattroff(gw, color); } - wattroff(gw, A_REVERSE); - refresh(); - wrefresh(gw); +} + +void +Engine::draw_gems() const +{ + int color; + + for (const auto& g : gems) { + if (g->get_sym() == SYM_STONE) + color = COLOR_PAIR(Color::STONE); + else if (g->get_sym() == SYM_PARCHMENT) + color = COLOR_PAIR(Color::PARCHMENT); + wattron(gw, color); + mvwaddch(gw, g->get_y(), g->get_x(), g->get_sym()); + wattroff(gw, color); + } +} + +void +Engine::draw_parchment() const +{ + wattron(gw, COLOR_PAIR(Color::PARCHMENT)); + mvwaddch(gw, prch->get_y(), prch->get_x(), prch->get_sym()); + wattroff(gw, COLOR_PAIR(Color::PARCHMENT)); } bool -Engine::is_running() +Engine::is_running() const { return f_running; } diff --git a/cpp_oop/game/Engine.hpp b/cpp_oop/game/Engine.hpp @@ -4,6 +4,7 @@ #include <cmath> #include <csignal> #include <cstdlib> +#include <cstring> #include <ctime> #include <iostream> #include <fstream> @@ -14,6 +15,7 @@ #include <ncurses.h> +#include "Gem.hpp" #include "Gnome.hpp" #include "Potter.hpp" #include "Traal.hpp" @@ -25,14 +27,26 @@ #define CENTER(x, y) (((x) >> 1) - ((y) >> 1)) +#define SYM_WALL '*' +#define SYM_PATH ' ' +#define SYM_POTTER 'P' +#define SYM_GNOME 'G' +#define SYM_TRAAL 'T' +#define SYM_STONE 'S' +#define SYM_PARCHMENT 'O' + class Engine { private: std::vector<Movable *> entities; + std::vector<Gem *> gems; std::vector<std::string> map; std::vector<int> colors; + std::vector<std::string> ctrls; + std::vector<std::string> rules; Potter *player; Score *score; + Gem *prch; WINDOW *gw; int xmax; int ymax; @@ -40,7 +54,8 @@ private: int wymax; int w; int h; - int nenemies = 5; + int nenemies = 3; + int ngems = 2; volatile sig_atomic_t f_running; public: @@ -50,20 +65,24 @@ public: void kbd_input(); void enemies_move(); void collisions(); - void upd_score(); - void redraw(); - bool is_running(); + void redraw() const; + bool is_running() const; private: bool init_curses(); bool init_gamewin(); void load_map(const char *mapfile); void calc_pos(int *x, int *y); - bool init_entities(); - bool init_score(const char *scorefile); - bool collides_with_wall(int x, int y); - void ctrl_menu(); - void calc_dist(std::map<int, int>& dists, int ex, int ey, int dir); + void init_entities(); + void spawn_parchment(); + bool collides_with_wall(int x, int y) const; + void popup(const std::vector<std::string>& lines) const; + void calc_dist(std::map<int, int>& dists, int ex, int ey, int dir) const; + void upd_score(int n); + void draw_map() const; + void draw_entities() const; + void draw_gems() const; + void draw_parchment() const; }; #endif /* _ENGINE_HPP_ */ diff --git a/cpp_oop/game/Gem.cc b/cpp_oop/game/Gem.cc @@ -0,0 +1,24 @@ +#include "Gem.hpp" + +Gem::Gem(int x, int y, char sym) + :x(x), y(y), sym(sym) +{ +} + +int +Gem::get_x() const +{ + return x; +} + +int +Gem::get_y() const +{ + return y; +} + +char +Gem::get_sym() const +{ + return sym; +} diff --git a/cpp_oop/game/Gem.hpp b/cpp_oop/game/Gem.hpp @@ -0,0 +1,18 @@ +#ifndef _GEM_HPP_ +#define _GEM_HPP_ + +class Gem { +private: + int x; + int y; + char sym; + +public: + Gem(int x, int y, char sym); + + int get_x() const; + int get_y() const; + char get_sym() const; +}; + +#endif /* _GEM_HPP_ */ diff --git a/cpp_oop/game/Makefile b/cpp_oop/game/Makefile @@ -18,6 +18,7 @@ SRC = main.cc \ Potter.cc \ Score.cc \ Traal.cc \ + Gem.cc OBJ = ${SRC:.cc=.o} diff --git a/cpp_oop/game/Movable.cc b/cpp_oop/game/Movable.cc @@ -34,25 +34,25 @@ Movable::set_newpos(int dir, int xmax, int ymax) } int -Movable::get_x() +Movable::get_x() const { return x; } int -Movable::get_y() +Movable::get_y() const { return y; } int -Movable::get_dir() +Movable::get_dir() const { return dir; } char -Movable::get_sym() +Movable::get_sym() const { return sym; } diff --git a/cpp_oop/game/Movable.hpp b/cpp_oop/game/Movable.hpp @@ -16,10 +16,10 @@ public: virtual ~Movable(); void set_newpos(int dir, int xmax, int ymax); - int get_x(); - int get_y(); - int get_dir(); - char get_sym(); + int get_x() const; + int get_y() const; + int get_dir() const; + char get_sym() const; }; #endif /* _MOVABLE_HPP_ */ diff --git a/cpp_oop/game/Score.cc b/cpp_oop/game/Score.cc @@ -2,9 +2,75 @@ Score::Score(const char *scorefile) { + fpath = std::string(scorefile); + sf.exceptions(std::fstream::badbit | std::fstream::failbit); + sf.open(fpath, std::fstream::in | std::fstream::binary); + if (!sf.is_open()) + throw std::runtime_error(fpath + ": cannot open file"); + for (int i = 0; i < SCORES_LEN; i++) { + sf.read((char *)&hiscores[i].name, sizeof(hiscores[i].name)); + sf.read((char *)&hiscores[i].score, sizeof(hiscores[i].score)); + } + sf.close(); + + /* FIXME: how to get player name? */ + (void)strncpy(curname, "christos", sizeof(curname)); curscore = 0; } Score::~Score() { + sf.open(fpath, std::fstream::out | std::fstream::binary); + if (sf.is_open()) { + for (int i = 0; i < SCORES_LEN; i++) { + sf.write((char *)&hiscores[i].name, + sizeof(hiscores[i].name)); + sf.write((char *)&hiscores[i].score, + sizeof(hiscores[i].score)); + } + sf.close(); + } +} + +Score& +Score::operator<< (const char name[NAME_LEN]) +{ + (void)strncpy(curname, name, sizeof(curname)); + return *this; +} + +Score& +Score::operator<< (const int score) +{ + curscore = score; + return *this; +} + +/* + * Convert the hiscores array to something the `popup` method in + * `Engine` can print. + */ +std::vector<std::string> +Score::scores_strfmt() const +{ + std::vector<std::string> v; + + for (int i = 0; i < SCORES_LEN; i++) + v.push_back(std::string(hiscores[i].name) + ": " + + std::to_string(hiscores[i].score)); + v.push_back("Press any key to continue"); + + return v; +} + +const char * +Score::get_curname() const +{ + return curname; +} + +int +Score::get_curscore() const +{ + return curscore; } diff --git a/cpp_oop/game/Score.hpp b/cpp_oop/game/Score.hpp @@ -1,22 +1,36 @@ #ifndef _SCORE_HPP_ #define _SCORE_HPP_ +#include <cstring> #include <fstream> +#include <stdexcept> +#include <string> #include <vector> class Score { private: + static const int NAME_LEN = 10; + static const int SCORES_LEN = 5; + struct HighScore { - char name[10]; + char name[NAME_LEN]; int score; - } hiscores[5]; + } hiscores[SCORES_LEN]; + std::string fpath; std::fstream sf; + char curname[NAME_LEN]; int curscore; public: Score(const char *scorefile); ~Score(); + + Score& operator<< (const char name[NAME_LEN]); + Score& operator<< (const int score); + std::vector<std::string> scores_strfmt() const; + const char *get_curname() const; + int get_curscore() const; }; #endif /* _SCORE_HPP_ */ diff --git a/cpp_oop/game/main.cc b/cpp_oop/game/main.cc @@ -8,14 +8,14 @@ static char *argv0; static void die(const std::string& str) { - std::cerr << argv0 << ": " << str << std::endl; + std::cerr << argv0 << ": " << str << "\n"; exit(1); } static void usage() { - std::cerr << "usage: " << argv0 << " <map_file> <score_file>" << std::endl; + std::cerr << "usage: " << argv0 << " <map_file> <score_file>\n"; exit(1); } @@ -43,15 +43,14 @@ main(int argc, char *argv[]) try { eng = new Engine(mapfile, scorefile); - } catch (const std::string& e) { - die(e); + } catch (const std::runtime_error& e) { + die(e.what()); } while (eng->is_running()) { eng->redraw(); eng->kbd_input(); eng->enemies_move(); eng->collisions(); - eng->upd_score(); } delete eng; diff --git a/cpp_oop/game/res/score b/cpp_oop/game/res/score Binary files differ. diff --git a/java_development/population/.classpath b/java_development/population/.classpath @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"> <attributes> <attribute name="module" value="true"/> </attributes> diff --git a/java_development/population/.settings/org.eclipse.jdt.core.prefs b/java_development/population/.settings/org.eclipse.jdt.core.prefs @@ -1,8 +1,8 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 -org.eclipse.jdt.core.compiler.compliance=1.5 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.5 +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/java_development/population/bin/population/Country.class b/java_development/population/bin/population/Country.class Binary files differ. diff --git a/java_development/population/bin/population/ExcelParser.class b/java_development/population/bin/population/ExcelParser.class Binary files differ. diff --git a/java_development/population/bin/population/Main.class b/java_development/population/bin/population/Main.class Binary files differ. diff --git a/java_development/population/src/population/Country.java b/java_development/population/src/population/Country.java @@ -3,17 +3,28 @@ package population; import java.util.HashMap; public class Country { - private String index = ""; + private String index;; private String variant = ""; private String name = ""; private String notes = ""; - private String ctrycode = ""; + private String ctrycode; private String type = ""; - private String pntcode = ""; - private HashMap<String, String> population; - - public void add_population(String k, String v) { - population.put(k, v); + private String pntcode; + private HashMap<Integer, Integer> population; + private Integer recentpop; + + Country(String index, String variant, String name, String notes, + String ctrycode, String type, String pntcode, + HashMap<Integer, Integer> population) { + this.index = index; + this.variant = variant; + this.name = name; + this.notes = notes; + this.ctrycode = ctrycode; + this.type = type; + this.pntcode = pntcode; + this.population = population; + recentpop = population.get(population.size()); } public String get_index() { @@ -43,4 +54,8 @@ public class Country { public String get_pntcode() { return pntcode; } + + public HashMap<Integer, Integer> get_population() { + return population; + } } diff --git a/java_development/population/src/population/ExcelParser.java b/java_development/population/src/population/ExcelParser.java @@ -2,7 +2,9 @@ package population; import java.io.File; import java.io.FileInputStream; +import java.util.List; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.*; @@ -13,7 +15,8 @@ public class ExcelParser { private final int TARGET_TYPE_CELL = 5; private final int HEADER_ROW = 16; private final int COUNTRIES_ROW = 44; - private ArrayList<Country> countries; + private final int STARTING_YEAR = 1950; + private List<Country> countries; private XSSFRow row; ExcelParser(String path) throws Exception { @@ -44,41 +47,73 @@ public class ExcelParser { } private Country read_country(XSSFRow row) throws Exception { - Country ctry = new Country(); - Iterator<Cell> cit = row.cellIterator(); + final int MIN_CELLNUM = 7; + Country ctry; + Iterator<Cell> cit; Cell cell; - String str = ""; - + Integer year = STARTING_YEAR; + String[] fields = new String[MIN_CELLNUM]; + HashMap<Integer, Integer> population = new HashMap<Integer, Integer>(); + Double d; + Integer n; + int i = 0; + + cit = row.cellIterator(); + if (row.getLastCellNum() < MIN_CELLNUM) { + throw new Exception("file must have at least " + + MIN_CELLNUM + " columns"); + } while (cit.hasNext()) { cell = cit.next(); - switch (cell.getCellType()) { - case STRING: - str = cell.getStringCellValue(); - break; - case NUMERIC: - str = String.valueOf( - cell.getNumericCellValue()); - break; - case BOOLEAN: - str = String.valueOf( - cell.getBooleanCellValue()); - break; - case BLANK: - str = "0"; - case ERROR: /* FALLTHROUGH */ - case FORMULA: - case _NONE: - default: - break; - } - System.out.print(str + "\t\t"); + if (i < MIN_CELLNUM) { + switch (cell.getCellType()) { + case STRING: + fields[i++] = cell.getStringCellValue(); + break; + case NUMERIC: + /* + * We'll need another conversion back to + * Integer, but it's easier to do it that + * way than to keep lots of variables + * and hardcode everything. + */ + n = dtoi(cell.getNumericCellValue()); + fields[i++] = Integer.toString(n); + break; + case BOOLEAN: + fields[i++] = String.valueOf( + cell.getBooleanCellValue()); + break; + case BLANK: + fields[i++] = "0"; + break; + case ERROR: /* FALLTHROUGH */ + case FORMULA: + case _NONE: + default: + fields[i++] = ""; + break; + } + } else if (cell.getCellType() == CellType.NUMERIC) + population.put(year++, + dtoi(cell.getNumericCellValue())); } - System.out.println(); - + ctry = new Country(fields[0], fields[1], fields[2], fields[3], + fields[4], fields[5], fields[5], population); + return ctry; } - public ArrayList<Country> get_countries() { + private Integer dtoi(double n) { + Integer i; + Double d; + + d = Double.valueOf(n); + i = Integer.valueOf(d.intValue()); + return i; + } + + public List<Country> get_countries() { return countries; } } diff --git a/java_development/population/src/population/Main.java b/java_development/population/src/population/Main.java @@ -1,27 +1,55 @@ package population; import java.io.*; -import java.util.ArrayList; +import java.util.List; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; -public class Main { - public static void main(String[] args) throws Exception { +public class Main extends Application { + private final String APP_TITLE = "Population Statistics"; + List<Country> countries; + + @Override + public void start(Stage primstage) throws Exception { + Parameters params; + List<String> args; ExcelParser ep; - ArrayList<Country> countries; String path = ""; + /* Parse Excel file. */ + params = getParameters(); + args = params.getRaw(); try { - path = args[0]; + path = args.get(0); ep = new ExcelParser(path); - countries = ep.get_countries(); } catch (FileNotFoundException e) { - System.err.println(path + ": no such file"); + err(primstage, path + ": no such file"); return; } catch (IndexOutOfBoundsException e) { - System.err.println("usage: population xlsx_file"); + err(primstage, "usage: population xlsx_file"); return; } catch (IOException e) { - System.err.println(path + ": io error"); + err(primstage, path + ": io error"); return; } + countries = ep.get_countries(); + + /* Set up GUI. */ + } + + /* TODO: make it an actual popup error window. */ + public void err(Stage stg, String errstr) { + Label lbl = new Label(errstr); + Scene scene = new Scene(new StackPane(lbl), 300, 200); + + stg.setScene(scene); + stg.show(); + } + + public static void main(String[] args) { + launch(args); } } \ No newline at end of file