diff options
Diffstat (limited to 'src')
16 files changed, 1186 insertions, 0 deletions
diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/AbstractGraph.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/AbstractGraph.java new file mode 100644 index 0000000..c2cf433 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/AbstractGraph.java @@ -0,0 +1,52 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// foo +abstract class AbstractGraph implements Graph{ + + /// foo + AbstractGraph() {} + + /// foo + private HashMap<String,Vertex> vertexMap=new HashMap<>(); + + /// foo + private HashSet<Vertex> vertexSet=new HashSet<>(); + + /// foo + /// @param s foo + /// @return Vertex + public Vertex vertex(String s){ + if(vertexMap.containsKey(s))return vertexMap.get(s); + Vertex v=new Vertex(s); + vertexMap.put(s,v); + vertexSet.add(v); + return v; + } + + /// foo + public void insertEdge(String v, String u, int w){ + insertEdge(vertex(v),vertex(u),w); + } + + /// foo + public Collection<Vertex> vertices() { return vertexSet; } + + /// foo + /// @param v1 foo + /// @param v2 foo + /// @param w foo + abstract public void insertEdge(Vertex v1, Vertex v2, int w); + + /// foo + abstract public Collection<Edge> edges(); + + /// foo + abstract public Collection<Edge> outEdge(Vertex v); + + /// foo + abstract public Integer getWeight(Vertex v1, Vertex v2); +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/AdjListGraph.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/AdjListGraph.java new file mode 100644 index 0000000..a677d3e --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/AdjListGraph.java @@ -0,0 +1,47 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// Adjecency List Graph - A map from vertices to set of outedges from the vertex +public class AdjListGraph extends AbstractGraph { + + /// foo + public AdjListGraph() {} + + /// foo + private Map<Vertex,Set<Edge>> outEdge= new HashMap<>(); + + /// foo + public void insertEdge(Vertex v1,Vertex v2,int w){ + Edge e=new Edge(v1,v2,w); + if(!outEdge.containsKey(e.from())) + outEdge.put(e.from(),new HashSet<Edge>()); + outEdge.get(e.from()).add(e); + } + + /// foo + public Collection<Edge> edges(){ + Set<Edge> edges=new HashSet<>(); + for(Vertex v:outEdge.keySet())edges.addAll(outEdge.get(v)); + return edges; + } + + /// foo + public Collection<Edge> outEdge(Vertex v){ + if(!outEdge.containsKey(v)) + return new HashSet<Edge>(); + return outEdge.get(v); + } + + /// foo + public Integer getWeight(Vertex v1,Vertex v2){ + // linear in number of outedges from vertices + if(!outEdge.containsKey(v1))return null; + for(Edge e:outEdge.get(v1)){ + if(e.to()==v2)return e.weight(); + } + return null; + } +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/AdjMapGraph.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/AdjMapGraph.java new file mode 100644 index 0000000..85e5d04 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/AdjMapGraph.java @@ -0,0 +1,45 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// Adjecency Map Graph - A map from vertices to map of target vertex to edge +class AdjMapGraph extends AbstractGraph { + + /// foo + AdjMapGraph() {} + + /// foo + private Map<Vertex, Map<Vertex, Edge>> outEdge = new HashMap<>(); + + /// foo + public void insertEdge(Vertex v1, Vertex v2, int w) { + Edge e = new Edge(v1,v2, w); + if (!outEdge.containsKey(e.from())) + outEdge.put(e.from(), new HashMap<Vertex, Edge>()); + outEdge.get(e.from()).put(e.to(), e); + } + + /// foo + public Collection<Edge> edges() { + Set<Edge> edges = new HashSet<>(); + for (Vertex v : outEdge.keySet()) + for (Vertex w : outEdge.get(v).keySet()) + edges.add(outEdge.get(v).get(w)); + return edges; + } + + /// foo + public Collection<Edge> outEdge(Vertex v) { + return outEdge.get(v).values(); + } + + /// foo + public Integer getWeight(Vertex v1, Vertex v2) { + // constant time operation + if(!outEdge.containsKey(v1))return null; + if(!outEdge.get(v1).containsKey(v2))return null; + return outEdge.get(v1).get(v2).weight(); + } +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/Edge.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/Edge.java new file mode 100644 index 0000000..abc3c72 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/Edge.java @@ -0,0 +1,37 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// foo +class Edge{ + + /// foo + private Vertex from,to; + + /// foo + private int weight; + + /// foo + /// @return Vertex + public Vertex from(){return from;} + + /// foo + /// @return Vertex + public Vertex to(){return to;} + + /// foo + /// @return int + public int weight(){return weight;} + + /// foo + /// @param from foo + /// @param to foo + /// @param w foo + Edge(Vertex from,Vertex to,int w){this.from=from; this.to=to; weight=w;} + + /// foo + /// @return String + public String toString(){return from.name()+" - "+weight+" -> "+to.name(); } +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/EdgeGraph.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/EdgeGraph.java new file mode 100644 index 0000000..ae9cbe9 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/EdgeGraph.java @@ -0,0 +1,37 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +/// EdgeGraph - One big set of all edges in the graph +class EdgeGraph extends AbstractGraph { + + /// foo + EdgeGraph() {} + + /// foo + Set<Edge> edges=new HashSet<>(); + + /// foo + public void insertEdge(Vertex v1,Vertex v2,int w){ + edges.add(new Edge(v1,v2,w)); + } + + /// foo + public Collection<Edge> edges(){return edges;} + + /// foo + public Collection<Edge> outEdge(Vertex v){ + ArrayList<Edge> outEdge=new ArrayList<>(); + for(Edge e:edges)if(e.from()==v)outEdge.add(e); + return outEdge; + } + + /// foo + public Integer getWeight(Vertex v1,Vertex v2){ + // linear in number of edges in the graph + for(Edge e:edges){ + if(e.from()==v1 && e.to()==v2)return e.weight(); + } + return null; + } +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/Graph.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/Graph.java new file mode 100644 index 0000000..6e58029 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/Graph.java @@ -0,0 +1,34 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// foo +public interface Graph { + + /// foo + /// @param v foo + /// @param u foo + /// @param w foo + void insertEdge(String v, String u, int w); + + /// foo + /// @return Collection + Collection<Vertex> vertices(); + + /// foo + /// @return Collection + Collection<Edge> edges(); + + /// foo + /// @param v foo + /// @return Collection + Collection<Edge> outEdge(Vertex v); + + /// foo + /// @param v1 foo + /// @param v2 foo + /// @return Integer + Integer getWeight(Vertex v1, Vertex v2); +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/GraphAlgorithms.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/GraphAlgorithms.java new file mode 100644 index 0000000..3be7c70 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/GraphAlgorithms.java @@ -0,0 +1,331 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.io.*; +import java.util.*; + +/// foo +public class GraphAlgorithms { + + /// foo + GraphAlgorithms() {} + + /// Calculates the length of a path or any other collection of edes + /// + /// does not require the edges to form a path + /// @param edges foo + /// @return int + public static int pathLength(Collection<Edge> edges){ + return edges.stream().mapToInt(e-> e.weight()).sum(); + } + + /// checks whether a list of edges form a path so that + /// + /// the to-vertex in one edge is the from-vertex of the next + /// @param edges foo + /// @return boolean + public static boolean isPath(List<Edge> edges){ + for(int i=1;i<edges.size();i++){ + if(edges.get(i-1).to()!=edges.get(i).from())return false; + } + return true; + } + + ///Calculates the length of a path vertices in a graph + /// + /// return null if vertices are not connected as a path + /// @param g foo + /// @param path foo + /// @return Integer + public static Integer pathLength(Graph g,List<Vertex> path){ + int length=0; + for(int i=1;i<path.size();i++){ + Integer w=g.getWeight(path.get(i-1),path.get(i)); + if(w==null)return null; + length+=w; + } + return length; + } + + //------------------------------------------------------------ + // + // Comparators and sorting methods + + /// Comparator of edges based on weight + /// + /// can be used for sorting a list of edges + /// @param e1 foo + /// @param e2 foo + /// @return int + static int cmpEdgeWeight(Edge e1,Edge e2) { + int w1=e1.weight(),w2=e2.weight(); + if(w1!=w2)return w1-w2; + if(e1.from()!=e2.from())return e1.from().name().compareTo(e2.from().name()); + return e1.to().name().compareTo(e2.to().name()); + } + + /// Comparator of edges based on from-vertex + /// + /// can be used for sorting a list of edges + /// @param e1 foo + /// @param e2 foo + /// @return int + static int cmpEdgeFrom(Edge e1,Edge e2) { + if(e1.from()!=e2.from())return e1.from().name().compareTo(e2.from().name()); + int w1=e1.weight(),w2=e2.weight(); + if(w1!=w2)return w1-w2; + return e1.to().name().compareTo(e2.to().name()); + } + + /// Comparator of edges based on from-vertex + /// + /// can be used for sorting a list of edges + /// @param e1 foo + /// @param e2 foo + /// @return int + static int cmpEdgeTo(Edge e1,Edge e2) { + if(e1.to()!=e2.to())return e1.to().name().compareTo(e2.to().name()); + if(e1.from()!=e2.from())return e1.from().name().compareTo(e2.from().name()); + int w1=e1.weight(),w2=e2.weight(); + return w1-w2; + } + + /// sort a collection of edges based on their weights + /// @param edges foo + /// @return List<Edge> + static List<Edge> sortEdges(Collection<Edge> edges){ + ArrayList<Edge> list=new ArrayList<>(edges); + Collections.sort(list,GraphAlgorithms::cmpEdgeWeight); + return list; + } + + /// sort a collection of edges based on from-vertex + /// @param edges foo + /// @return List<Edge> + static List<Edge> sortEdgesFrom(Collection<Edge> edges){ + ArrayList<Edge> list=new ArrayList<>(edges); + Collections.sort(list,GraphAlgorithms::cmpEdgeFrom); + return list; + } + + /// sort a collection of edges based on to-vertex + /// @param edges foo + /// @return List<Edge> + static List<Edge> sortEdgesTo(Collection<Edge> edges){ + ArrayList<Edge> list=new ArrayList<>(edges); + Collections.sort(list,GraphAlgorithms::cmpEdgeTo); + return list; + } + + /// sort a collection of vertices based on their name + /// @param vertices foo + /// @return List<Vertex> + static List<Vertex> sortVertex(Collection<Vertex> vertices){ + ArrayList<Vertex> list=new ArrayList<>(vertices); + Collections.sort(list,(Vertex v1,Vertex v2)-> v1.name().compareTo(v2.name())); + return list; + } + + //------------------------------------------------------------ + // + // Algorithms for traverse and minimum spanning tree + + /// traverse a graph depth first from a given vertex + /// return the set of visited vertices + /// @param g foo + /// @param v foo + /// @return Set<Vertex> + public static Set<Vertex> visitBreadthFirst(Graph g,Vertex v){ + HashSet<Vertex> thisLevel=new HashSet<>(); + HashSet<Vertex> nextLevel=new HashSet<>(); + HashSet<Vertex> visited=new HashSet<>(); + thisLevel.add(v); + while(thisLevel.size()>0){ + System.out.println("level "+thisLevel); + for(Vertex w:thisLevel){ + //System.out.println("visited "+w); + visited.add(w); + Collection<Edge> outedge=g.outEdge(w); + if(outedge==null)continue; + for(Edge e: outedge){ + if(visited.contains(e.to()))continue; + if(thisLevel.contains(e.to()))continue; + nextLevel.add(e.to()); + } + } + thisLevel=nextLevel; + nextLevel=new HashSet<Vertex>(); + } + return visited; + } + + /// traverse a graph depth first from a given vertex + /// return the set of visited vertices + /// @param g foo + /// @param v foo + /// @return Set<Vertex> + public static Set<Vertex> visitDepthFirst(Graph g,Vertex v){ + HashSet<Vertex> visit=new HashSet<>(); + visitDepthFirst(g, v,visit); + return visit; + } + + /// foo + /// @param g foo + /// @param v foo + /// @param visited foo + private static void visitDepthFirst(Graph g,Vertex v,Set<Vertex> visited){ + if(visited.contains(v))return; + //System.out.println("visited "+v); + visited.add(v); + for(Edge e: g.outEdge(v)) + visitDepthFirst(g,e.to(),visited); + } + + /// an implementation of Prim's algorithm + /// naive implementation without priorityqueue + /// @param g foo + /// @return Set<Edge> + public static Set<Edge> minimumSpanningTree(Graph g){ + Collection<Edge> edges=g.edges(); + HashSet<Edge> mst=new HashSet<>(); + HashSet<Vertex> frontier=new HashSet<>(); + for(Edge e:edges){frontier.add(e.from());break;} + while(true) { + Edge nearest = null; + for (Edge e : edges) { + if (!frontier.contains(e.from())) continue; + if (frontier.contains(e.to())) continue; + if (nearest == null || nearest.weight() > e.weight()) + nearest = e; + } + if(nearest==null)break; + mst.add(nearest); + frontier.add(nearest.to()); + } + return mst; + } + + /// returns the tree of shortest paths from start to + /// all vertices in the graph + /// + /// naive implementation without a prorityqueue + /// @param g foo + /// @param start foo + /// @return Set<Edge> + public static Set<Edge> dijkstra(Graph g, Vertex start){ + // create table for done, prev and weight from start + int maxint =Integer.MAX_VALUE; + HashSet<Vertex> done=new HashSet<>(); + HashMap<Vertex,Edge> prev=new HashMap<>(); + HashMap<Vertex,Integer> weight=new HashMap<>(); + for(Vertex w:g.vertices())weight.put(w,maxint); + // start node is done, distance 0 from start + weight.put(start,0); + done.add(start); + + while(true){ + // find nearest from a done vertex + Vertex nearest = null; + int neardist = maxint; + Edge done2near=null; + for(Vertex w1:done){ + for (Edge e : g.outEdge(w1)) { + Vertex w2 = e.to(); + if (done.contains(w2)) continue; + if ((weight.get(w1) + e.weight()) < neardist) { + nearest = e.to(); + neardist = weight.get(w1) + e.weight(); + done2near = e; + } + } + } + // System.out.println("find nearest "+done2near); + // if no more, then we are done + if (nearest == null) break; + // update distance from this node to other nodes + for (Edge e1 : g.outEdge(nearest)) { + Vertex w3 = e1.to(); + int wght = e1.weight(); + if (weight.get(w3) > (neardist + wght)) { + weight.put(w3, neardist + wght); + } + } + done.add(nearest); + prev.put(nearest,done2near); + weight.put(nearest,neardist); + } + return new HashSet<Edge>(prev.values()); + } + + //------------------------------------------------------------ + // + // IO operations + + /// read a comma-separated file in the format + /// <vertex> , <vertex> , <weight> + /// + /// stores file as bidirectional graph + /// @param g foo + /// @param file foo + public static void readGraph(Graph g, String file) { + try{ + BufferedReader in = new BufferedReader(new FileReader(file)); + for(String line=in.readLine(); line!=null; line=in.readLine()) { + if(line.length()==0) continue; + String[] arr = line.split(","); + if(arr.length!=3) throw new RuntimeException("CSV file format error: "+line); + g.insertEdge(arr[0].trim(), arr[1].trim(), Integer.parseInt(arr[2].trim())); + g.insertEdge(arr[1].trim(), arr[0].trim(), Integer.parseInt(arr[2].trim())); + } + in.close(); + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + /// foo + /// @param g foo + public static void printGraph(Graph g) { + for(Vertex v: sortVertex(g.vertices())) { + System.out.println(v.toString()); + for(Edge e:sortEdgesTo(g.outEdge(v))) + System.out.println(" "+e.toString()); + } + } + + /// store a list of lines as a file + /// @param list foo + /// @param f foo + public static void storeStrings(List<String> list,String f){ + try{ + PrintWriter out=new PrintWriter(new FileWriter(f)); + for(String s:list){ + out.println(s); + } + out.close(); + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + /// read a file a returns a list of lines + /// @param f foo + /// @return ArrayList + public static ArrayList<String> loadStrings(String f){ + ArrayList<String> list=new ArrayList<>(); + try{ + BufferedReader in=new BufferedReader(new FileReader(f)); + while(true){ + String s=in.readLine(); + if(s==null)break; + list.add(s); + } + in.close(); + }catch(IOException e){ + throw new RuntimeException(e); + } + return list; + } +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/Graphs.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/Graphs.java new file mode 100644 index 0000000..2975e44 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/Graphs.java @@ -0,0 +1,13 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// foo +public class Graphs { + + /// foo + Graphs() {} + +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/MatrixGraph.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/MatrixGraph.java new file mode 100644 index 0000000..29005b7 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/MatrixGraph.java @@ -0,0 +1,77 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// Matrix Graph: weights are stored in a twodimensional array +public class MatrixGraph extends AbstractGraph { + + /// foo + private Integer[][] matrix=null; // made in constructor + + /// foo + // We must be able to map vertices to index in matrix and back again + private Vertex[] index2vertex; // made in constructor + + /// foo + private Map<Vertex,Integer> vertex2index=new HashMap<>(); + + /// foo + private int numVertex; // maximum number of vertices + + /// foo + /// @param numVertex maximum number of vertices allowed + public MatrixGraph(int numVertex){ + this.numVertex=numVertex; + matrix =new Integer[numVertex][numVertex]; + index2vertex=new Vertex[numVertex]; + } + + /// foo + /// @param v vertex + /// @return int + private int getIndex(Vertex v){ + if(vertex2index.containsKey(v)) return vertex2index.get(v); + int index=vertex2index.size(); + if(index>=index2vertex.length)throw new RuntimeException("Too many vertices in graph"); + vertex2index.put(v,index); + index2vertex[index]=v; + return index; + } + + /// foo + public void insertEdge(Vertex v1,Vertex v2,int w){ + matrix[getIndex(v1)][getIndex(v2)] = w; + } + + /// foo + public Collection<Edge> edges(){ + HashSet<Edge> edges=new HashSet<>(); + for(int i=0;i<numVertex;i++){ + for(int j=0;j<numVertex;j++){ + Integer weight=matrix[i][j]; // may be null + if(weight==null)continue; + edges.add(new Edge(index2vertex[i],index2vertex[j],weight)); + } + } + return edges; + } + + /// foo + public Collection<Edge> outEdge(Vertex v1){ + HashSet<Edge> edges=new HashSet<>(); + int i=vertex2index.get(v1); + for(int j=0;j<numVertex;j++){ + Integer weight=matrix[i][j]; // may be null + if(weight==null)continue; + edges.add(new Edge(v1,index2vertex[j],weight)); + } + return edges; + } + + /// foo + public Integer getWeight(Vertex v1,Vertex v2){ + // constant time operation + return matrix[vertex2index.get(v1)][vertex2index.get(v2)];} +} diff --git a/src/main/java/com.example.portfolio3/com/example/portfolio3/Vertex.java b/src/main/java/com.example.portfolio3/com/example/portfolio3/Vertex.java new file mode 100644 index 0000000..9881159 --- /dev/null +++ b/src/main/java/com.example.portfolio3/com/example/portfolio3/Vertex.java @@ -0,0 +1,24 @@ +package com.example.portfolio3; + +// origin: <https://moodle.ruc.dk/course/section.php?id=211877> + +import java.util.*; + +/// foo +public class Vertex{ + + /// foo + private String name; + + /// foo + /// @return String + public String name(){return name;} + + /// foo + /// @param s foo + public Vertex(String s){name=s;} + + /// foo + /// @return String + public String toString(){return name;} +} diff --git a/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/Control.java b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/Control.java new file mode 100644 index 0000000..b53fb9f --- /dev/null +++ b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/Control.java @@ -0,0 +1,94 @@ +// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk> +// SPDX-License-Identifier: GPL-3.0-or-later + +package dk.biks.bachelorizer; + +import java.util.List; + +import dk.biks.bachelorizer.model.GUI; +import dk.biks.bachelorizer.view.Window; + +/// Bachelorizer - Controller +public class Control{ + + /// Application model + // (declared explicitly only to silence javadoc) + private GUI model; + + /// Application view + private Window view; + + /// Parameters passed on command-line and in JNLP file + private List<String> parameters; + + /// Default constructor + /// + /// @param model Application model + /// @param view Application view + public Control(GUI model, Window view){ + this.model = model; + this.view = view; + } + + /// parse application parameters + /// + /// parse parameters as GNU-style options and arguments, + /// i.e. treat dash-prefixed words as options + /// until an optional first bare "--", + /// taking first non-option argument as name of student + /// and remaining ones as activity selections + /// + /// @param parameters Application parameters + public void setParameters(List<String> parameters) { + boolean optionsDone = false; + boolean studentAssigned = false; + for (String item : parameters) { + if (!optionsDone && item.matches("--")) { + optionsDone = true; + } else if (!item.startsWith("-")) { + if (!studentAssigned) { + model.addStudent(item); + studentAssigned = true; + showStudent(); + } else { + model.addActivity(item); + showActivities(); + } + } + } + } + + /// Enter activity + /// + /// @param s String entered + public void enterActivity(String s){ + model.addActivity(s); + view.clearActivityEntry(); + showActivities(); + } + + /// Display student + public void showStudent() { + view.setStudentName(model.getStudentName()); + } + + /// Display list of activity entries + public void showActivities() { + String toarea = ""; + for (String t : model.getActivities()) + toarea += t + "\n"; + view.setArea(toarea); + } + + /// drop last activity entry + public void delOne(){ + model.delOneActivity(); + showActivities(); + } + + /// drop all activity entries + public void delAll(){ + model.delAllActivities(); + showActivities(); + } +} diff --git a/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/Main.java b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/Main.java new file mode 100644 index 0000000..2efd5b8 --- /dev/null +++ b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/Main.java @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk> +// SPDX-License-Identifier: GPL-3.0-or-later + +package dk.biks.bachelorizer; + +import java.lang.UnsupportedOperationException; +import java.util.Arrays; + +/* TODO +import dk.biks.bachelorizer.view.Oneshot; +import dk.biks.bachelorizer.view.Prompt; +import dk.biks.bachelorizer.view.Pipe; +import dk.biks.bachelorizer.view.Screen; +*/ +import dk.biks.bachelorizer.view.Window; + +/// Bachelorizer - bachelor programme registrar +/// +/// Tool for registering students +/// for activities in their bachelor programme. +/// +/// Runner class spawning an interactive or non-interactive application +/// based on passed arguments +/// +/// Multi-framework MVC structure inspired by project Криптоанализатор +/// written by Александр Хмелев <akhmelev@gmail.com>. +/// +/// * v0.0.1-draft +/// * initial release, as part of delivery "Portfolio 1" +/// +/// @version 0.0.1-draft +/// @see <https://moodle.ruc.dk/mod/assign/view.php?id=523186> +/// @see <https://github.com/demologin/CryptoAnalyzerLed> +public class Main { + + /// Default constructor + /// + /// @param args command-line arguments or default demo data + public Main(final String[] args) { + + switch (uiFromArgs(args)) { + case "gui" -> { Window.main(args); } +// TODO case "tui" -> { Screen.main(args); } +// TODO case "cli" -> { Line.main(args); } + default -> { + throw new UnsupportedOperationException( + "Not yet implemented."); + } + } + } + + /// JVM entry point + /// + /// @param args command-line arguments + public static void main(String[] args) { + + // inject initial sample data unless passed as arguments + if ((args.length == 0) + || (!Arrays.stream(args).anyMatch( + s -> s != null && !s.startsWith("-"))) + ) { + args = new String[] { + "Jonas Smedegaard", + "CS-SMC2", + "CS-SMC3", + }; + } + + new Main(args); + } + + /// minimal argument parser to detect explicit UI choice + /// + /// @param args command-line arguments + /// @return choice of UI as String + public static String uiFromArgs(String[] args) { + // TODO: make "cli" the default when implemented + String defaultUI = "gui"; + + for (String arg : args) { + if (arg.matches("--(gui|tui|cli)")) { + return (arg.length() == 2) + ? defaultUI + : arg.substring(2); + } + } + + return defaultUI; + } +} diff --git a/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/Combi.java b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/Combi.java new file mode 100644 index 0000000..371eb79 --- /dev/null +++ b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/Combi.java @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk> +// SPDX-License-Identifier: GPL-3.0-or-later + +package dk.biks.bachelorizer.model; + +import java.util.Collection; +import java.util.Set; + +import com.example.portfolio3.AdjListGraph; +import com.example.portfolio3.Graph; +import com.example.portfolio3.GraphAlgorithms; +import com.example.portfolio3.MatrixGraph; +import com.example.portfolio3.Vertex; + +/// Combi - static sample dataset of course combinations +/// +/// Slurps and parses data from upstream-provided comma-separated file. +/// +/// @version 0.0.1 +/// @see <https://moodle.ruc.dk/mod/assign/view.php?id=523186> +public final class Combi { + + /// data about combinations as a Graph + private Graph g; + + /// Default constructor + /// + /// @param path path to data file + private Combi(final String path) { + + // read into temporary graph to resolve vertice count + // + // use Adjacency List Representation: + // * cheap to bootstrap (done once) + // * simple to count vertices (done once): O(1) + Graph _g = new AdjListGraph(); + GraphAlgorithms.readGraph(_g, path); + Integer _vertice_count = _g.vertices().size(); + + // read into final graph + // + // use Adjacency Matrix Representation: + // * expensive to bootstrap (done once) + // * simple to add edges but needs vertice count + // * simple to get edges (done repeatedly): O(1) + // + // TODO: avoid reading and parsing file twice + Graph g = new MatrixGraph(_vertice_count); + GraphAlgorithms.readGraph(g, path); + + // ensure the graph is connected + isConnected(g); + + GraphAlgorithms.printGraph(g); + + // release temporary variables for garbage collection + _g = null; + _vertice_count = null; + } + + /// JVM entry point + /// + /// @param args command-line arguments + public static void main(final String[] args) { + + // first argument, if provided, is the data file path; + // else use upstream named file in current directory. + String path = (args.length > 0) + ? args[0] + : "combi.txt"; + + Combi combi = new Combi(path); + } + + /// utility function to check that a graph is connected + /// + /// If check fails, throw an unchecked exception, + /// since it occurs at runtime and is unrecoverable. + /// + /// Time complexity of the operation is O(n²) + /// where n is the amount of vertices, + /// since visitDepthFirst() recurses out-edges of all vertices. + /// + /// @param g Graph to inspect + /// @throws IllegalArgumentException + /// https://docs.oracle.com/javase/tutorial/essential/exceptions/runtime.html + public static void isConnected(Graph g) { + + // collect all vertices in the graph + Collection<Vertex> c = g.vertices(); + + // pick a random vertice in the graph + Vertex v = g.vertices().iterator().next(); + + // collect the set of visitable vertices + Set<Vertex> visited = GraphAlgorithms.visitDepthFirst( + g, v); + + // throw exception if not all vertices were visited + if (visited.size() != c.size()) { + throw new IllegalArgumentException( + "graph is not connected"); + } + } +} diff --git a/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/GUI.java b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/GUI.java new file mode 100644 index 0000000..ca9bd86 --- /dev/null +++ b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/GUI.java @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk> +// SPDX-License-Identifier: GPL-3.0-or-later + +package dk.biks.bachelorizer.model; + +import java.util.ArrayList; + +import dk.biks.bachelorizer.model.Person; + +/// Bachelorizer - GUI model +public class GUI{ + + /// Default constructor + // (declared explicitly only to silence javadoc) + public GUI(){ + } + + /// Activity list + private Person student; + + /// Activity list + private ArrayList<String> list = new ArrayList<>(); + + /// Add student + /// + /// @param name Name of student + public void addStudent(String name){ + student = new Person(name); + } + + /// Get student name + /// + /// @return name of student + public String getStudentName(){ + return student.name; + } + + /// Add activity to list + /// + /// @param s Activity to add + public void addActivity(String s){ + list.add(s); + } + + /// Get list of activities + /// + /// @return activity list + public ArrayList<String> getActivities(){ + return list; + } + + /// Delete last activity from list + public void delOneActivity(){ + if(list.size()>0) + list.remove(list.size()-1); + } + + /// Delete all activities from list + public void delAllActivities(){ + list.clear(); + } +} diff --git a/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/Person.java b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/Person.java new file mode 100644 index 0000000..9edb7af --- /dev/null +++ b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/model/Person.java @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk> +// SPDX-License-Identifier: GPL-3.0-or-later + +package dk.biks.bachelorizer.model; + +/// Bachelorizer - Person model +public class Person { + + /// Person name + public String name; + + /// Constructor + /// + /// @param name Name of person + public Person (String name) { + this.name = name; + } +} diff --git a/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/view/Window.java b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/view/Window.java new file mode 100644 index 0000000..c75e290 --- /dev/null +++ b/src/main/java/dk.biks.bachelorizer/dk/biks/bachelorizer/view/Window.java @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2025 Jonas Smedegaard <dr@jones.dk> +// SPDX-License-Identifier: GPL-3.0-or-later + +package dk.biks.bachelorizer.view; + +import java.util.List; +import javafx.application.Application; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import dk.biks.bachelorizer.Control; +import dk.biks.bachelorizer.model.GUI; + +/// Bachelorizer - JavaFX Window view +// Class is final to forbid subclassing, +// because object is passed to controller during instatiation +public final class Window extends Application { // the View + + /// Default constructor + // (declared explicitly only to silence javadoc) + public Window() { + } + + /// Label styling + public static String LABEL_STYLE = + "-fx-font-weight: bold; -fx-font-size: 20;"; + + /// Application model + private GUI model = new GUI(); + + /// Application controller + private Control control = new Control(model, this); + + /// Name of student + private TextField nameEntry = new TextField(); + + /// Text entry for adding an activity + private TextField activityEntry = new TextField(); + + /// Text area for activity entries + private TextArea area = new TextArea(); + + /// Button to delete one activity + private Button delOne = new Button("Delete one"); + + /// Button to delete all activities + private Button delAll = new Button("Delete all"); + + /// Application instantiation + /// + /// @param args application parameters + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage stage) { + + // pass application parameters to controller + control.setParameters(getParameters().getRaw()); + + // add listeners +// NameEntry.setOnAction(e -> control.enterName( +// activityEntry.getText())); + activityEntry.setOnAction(e -> control.enterActivity( + activityEntry.getText())); + delOne.setOnAction(e -> control.delOne()); + delAll.setOnAction(e -> control.delAll()); + + // add buttons + VBox root = new VBox(10, + ourHBox("Student", nameEntry), + ourHBox("Add activity", activityEntry), + new HBox(10, delOne, delAll), + area); + + // compose stage + Scene scene = new Scene(root, 500, 500); + stage.setTitle("JavaFX Demo"); + stage.setScene(scene); + stage.show(); + } + + /// action to apply student name + /// + /// @param s Text to apply + public void setStudentName(String s) { + nameEntry.setText(s); + } + + /// action to apply text to area + /// + /// @param s Text to apply + public void setArea(String s) { + area.setText(s); + } + + /// Button action to clear field + public void clearActivityEntry() { + activityEntry.setText(""); + } + + /// Styled HBox with label and TextField + /// + /// @param s Label string + /// @param f Text field + /// @return HBox containing styled label and text field + public HBox ourHBox(String s, TextField f) { + Label label = new Label(s+":"); + label.setStyle(LABEL_STYLE); + + return new HBox(10, label, f); + } +} |