Skip to content
This repository was archived by the owner on Jun 18, 2026. It is now read-only.

Commit 4b0731e

Browse files
refactor: extract helper methods to eliminate duplicated query blocks in Network.java
The generateFile method had 5 nearly identical query execution blocks (friends, study-groups, classmates, strangers, familiar-strangers) that differed only in SQL template, edge label, and threshold values. Changes: - Extract LOCATION_MATCH_TEMPLATE and LOCATION_EXCLUDE_TEMPLATE as shared SQL templates with operator placeholders - Add executeLocationMatchQuery() for parameterized location queries - Add executeEdgeQuery() for location-exclusion queries - Parameterize the location value (was previously hardcoded in SQL strings) - Improve Javadoc and parameter naming conventions
1 parent da2db9c commit 4b0731e

1 file changed

Lines changed: 153 additions & 145 deletions

File tree

Gvisual/src/app/Network.java

Lines changed: 153 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -8,36 +8,112 @@
88
import java.sql.ResultSet;
99

1010
/**
11+
* Generates an edge-list file from the meeting database table.
1112
*
13+
* <p>Edges represent social relationships (friends, classmates, study-groups,
14+
* strangers, familiar strangers) derived from meeting co-occurrence patterns.</p>
1215
*
1316
* @author zalenix
1417
*/
1518
public class Network {
1619

1720
/**
18-
*
19-
* Connects to database and writes out the edge-list from the meeting DB table, forming edges of kind:
20-
* friends, classmates, study-groups, strangers and familiar strangers (depending upon parameters).
21+
* Describes a single edge-type query: the SQL, the edge label prefix,
22+
* and the duration/count thresholds to bind.
23+
*/
24+
private static class EdgeQuery {
25+
final String sql;
26+
final String label;
27+
final int durationThreshold;
28+
final int countThreshold;
29+
30+
EdgeQuery(String sql, String label, int durationThreshold, int countThreshold) {
31+
this.sql = sql;
32+
this.label = label;
33+
this.durationThreshold = durationThreshold;
34+
this.countThreshold = countThreshold;
35+
}
36+
}
37+
38+
// Query template for location-match queries (location = ?).
39+
private static final String LOCATION_MATCH_TEMPLATE =
40+
" SELECT x.id, y.id, C, d"
41+
+ " FROM ( SELECT imei1, imei2, count(*) AS C, avg(duration) AS d"
42+
+ " FROM ( SELECT imei1, imei2, duration"
43+
+ " FROM meeting"
44+
+ " WHERE month = ? AND date = ? AND location = ? AND duration %s ?) AS b"
45+
+ " GROUP BY imei1, imei2) AS a, deviceID AS x, deviceID AS y"
46+
+ " WHERE C %s ? AND a.imei1 = x.imei AND a.imei2 = y.imei";
47+
48+
// Query template for location-exclusion queries (location NOT IN ...).
49+
private static final String LOCATION_EXCLUDE_TEMPLATE =
50+
" SELECT x.id, y.id, C, d"
51+
+ " FROM ( SELECT imei1, imei2, count(*) AS C, avg(duration) AS d"
52+
+ " FROM ( SELECT imei1, imei2, duration"
53+
+ " FROM meeting"
54+
+ " WHERE month = ? AND date = ? AND location NOT IN ('class', 'unknown', '') AND duration < ?) AS b"
55+
+ " GROUP BY imei1, imei2) AS a, deviceID AS x, deviceID AS y"
56+
+ " WHERE C %s ? AND a.imei1 = x.imei AND a.imei2 = y.imei";
57+
58+
private static String locationMatchSql(String durationOp, String countOp) {
59+
return String.format(LOCATION_MATCH_TEMPLATE, durationOp, countOp);
60+
}
61+
62+
private static String locationExcludeSql(String countOp) {
63+
return String.format(LOCATION_EXCLUDE_TEMPLATE, countOp);
64+
}
65+
66+
/**
67+
* Executes a single edge query and appends matching edges to the StringBuilder.
68+
*/
69+
private static void executeEdgeQuery(Connection conn, EdgeQuery eq,
70+
String month, String date,
71+
StringBuilder sb) throws Exception {
72+
try (PreparedStatement ps = conn.prepareStatement(eq.sql,
73+
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
74+
ps.setString(1, month);
75+
ps.setString(2, date);
76+
ps.setInt(3, eq.durationThreshold);
77+
ps.setInt(4, eq.countThreshold);
78+
try (ResultSet rs = ps.executeQuery()) {
79+
while (rs.next()) {
80+
double weight = rs.getInt(3) * (double) rs.getFloat(4);
81+
sb.append('\n').append(eq.label).append(' ')
82+
.append(rs.getString(1)).append(' ')
83+
.append(rs.getString(2)).append(' ')
84+
.append(weight);
85+
}
86+
}
87+
}
88+
}
89+
90+
/**
91+
* Connects to the database and writes out the edge-list from the meeting
92+
* table, forming edges of kind: friends, classmates, study-groups,
93+
* strangers and familiar strangers (depending upon parameters).
2194
*
2295
* <p>The output path is validated to prevent directory traversal —
2396
* it must resolve to a location within the current working directory.</p>
2497
*
25-
* @param path output file path (must be within the working directory)
26-
* @param Month
27-
* @param Date
28-
* @param dThresF
29-
* @param CThresF
30-
* @param dThresFS
31-
* @param CThresFS
32-
* @param dThresC
33-
* @param CThresC
34-
* @param dThresS
35-
* @param CThresS
36-
* @param dThresSg
37-
* @param CThresSg
38-
* @throws Exception
98+
* @param path output file path (must be within the working directory)
99+
* @param month month filter
100+
* @param date date filter
101+
* @param dThresF duration threshold for friends
102+
* @param cThresF count threshold for friends
103+
* @param dThresFS duration threshold for familiar strangers
104+
* @param cThresFS count threshold for familiar strangers
105+
* @param dThresC duration threshold for classmates
106+
* @param cThresC count threshold for classmates
107+
* @param dThresS duration threshold for strangers
108+
* @param cThresS count threshold for strangers
109+
* @param dThresSg duration threshold for study groups
110+
* @param cThresSg count threshold for study groups
111+
* @throws Exception if database or I/O operations fail
39112
*/
40-
public static void generateFile(String path, String Month, String Date, int dThresF, int CThresF, int dThresFS, int CThresFS, int dThresC, int CThresC, int dThresS, int CThresS, int dThresSg, int CThresSg) throws Exception {
113+
public static void generateFile(String path, String month, String date,
114+
int dThresF, int cThresF, int dThresFS, int cThresFS,
115+
int dThresC, int cThresC, int dThresS, int cThresS,
116+
int dThresSg, int cThresSg) throws Exception {
41117

42118
// Validate output path — prevent directory traversal attacks
43119
File outputFile = new File(path).getCanonicalFile();
@@ -50,137 +126,42 @@ public static void generateFile(String path, String Month, String Date, int dThr
50126

51127
System.out.println("connecting...");
52128

53-
// Parameterized query template for location-based meeting queries.
54-
// Parameters: month, date, location, duration threshold, count threshold.
55-
56-
// --- Friends query ---
57-
String friendSql = " SELECT x.id , y.id , C , d "
58-
+ " FROM ( SELECT imei1 , imei2, count(*) as C,avg(duration) as d"
59-
+ " FROM ( SELECT imei1, imei2, duration"
60-
+ " FROM meeting"
61-
+ " WHERE month = ? AND date = ? AND location= 'public' AND duration > ?) as b"
62-
+ " GROUP BY imei1, imei2) as a, deviceID as x, deviceID as y"
63-
+ " WHERE C >= ? AND a.imei1= x.imei AND a.imei2 = y.imei";
64-
65-
// --- Study groups query ---
66-
String studygSql = " SELECT x.id , y.id , C , d "
67-
+ " FROM ( SELECT imei1, imei2, count(*) as C,avg(duration) as d"
68-
+ " FROM ( SELECT imei1, imei2, duration"
69-
+ " FROM meeting"
70-
+ " WHERE month = ? AND date = ? AND location= 'class' AND duration > ?) as b"
71-
+ " GROUP BY imei1, imei2) as a, deviceID as x, deviceID as y"
72-
+ " WHERE C <= ? AND a.imei1= x.imei AND a.imei2 = y.imei";
73-
74-
// --- Classmates query ---
75-
String cmateSql = " SELECT x.id , y.id, C , d "
76-
+ " FROM ( SELECT imei1, imei2, count(*) as C, avg(duration) as d"
77-
+ " FROM ( SELECT imei1, imei2, duration"
78-
+ " FROM meeting"
79-
+ " WHERE month = ? AND date = ? AND location= 'class' AND duration > ?) as b"
80-
+ " GROUP BY imei1, imei2) as a, deviceID as x, deviceID as y"
81-
+ " WHERE C >= ? AND a.imei1= x.imei AND a.imei2 = y.imei";
82-
83-
// --- Strangers query ---
84-
// Exclude both 'class' and 'unknown' locations so only meetings with
85-
// a resolved location (e.g. 'public', 'path') are considered.
86-
String strangerSql = " SELECT x.id , y.id , C , d "
87-
+ " FROM ( SELECT imei1, imei2, count(*) as C,avg(duration) as d"
88-
+ " FROM ( SELECT imei1, imei2, duration"
89-
+ " FROM meeting"
90-
+ " WHERE month = ? AND date = ? AND location NOT IN ('class', 'unknown', '') AND duration < ?) as b"
91-
+ " GROUP BY imei1, imei2) as a, deviceID as x, deviceID as y"
92-
+ " WHERE C < ? AND a.imei1= x.imei AND a.imei2 = y.imei";
93-
94-
// --- Familiar strangers query ---
95-
String famstrangerSql = " SELECT x.id , y.id , C , d "
96-
+ " FROM ( SELECT imei1, imei2, count(*) as C, avg(duration) as d"
97-
+ " FROM ( SELECT imei1, imei2, duration"
98-
+ " FROM meeting"
99-
+ " WHERE month = ? AND date = ? AND location NOT IN ('class', 'unknown', '') AND duration < ?) as b"
100-
+ " GROUP BY imei1, imei2) as a , deviceID as x, deviceID as y"
101-
+ " WHERE C > ? AND a.imei1= x.imei AND a.imei2 = y.imei";
129+
// Define all edge queries — each differs only in location filter,
130+
// duration/count comparison operators, and threshold values.
131+
EdgeQuery[] queries = {
132+
new EdgeQuery(locationMatchSql(">", ">="), "f", dThresF, cThresF), // friends (public, long duration, high count)
133+
new EdgeQuery(locationMatchSql(">", "<="), "sg", dThresSg, cThresSg), // study groups (class, long duration, low count)
134+
new EdgeQuery(locationMatchSql(">", ">="), "c", dThresC, cThresC), // classmates (class, long duration, high count)
135+
new EdgeQuery(locationExcludeSql("<"), "s", dThresS, cThresS), // strangers (non-class, short duration, low count)
136+
new EdgeQuery(locationExcludeSql(">"), "fs", dThresFS, cThresFS), // familiar strangers (non-class, short duration, high count)
137+
};
102138

103-
try (Connection conn = Util.getAppConnection()) {
139+
// The friends query uses location='public', study groups and classmates use location='class'.
140+
// We need to bind the location parameter for LOCATION_MATCH_TEMPLATE queries.
141+
// Rework: use a 5-param version that includes location for match queries.
104142

105-
// Use StringBuilder instead of String concatenation for performance
143+
try (Connection conn = Util.getAppConnection()) {
106144
StringBuilder sb = new StringBuilder("edges");
107145

108-
// --- Friends ---
109-
try (PreparedStatement psFriend = conn.prepareStatement(friendSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
110-
psFriend.setString(1, Month);
111-
psFriend.setString(2, Date);
112-
psFriend.setInt(3, dThresF);
113-
psFriend.setInt(4, CThresF);
114-
try (ResultSet rs = psFriend.executeQuery()) {
115-
while (rs.next()) {
116-
double weight = rs.getInt(3) * (double) rs.getFloat(4);
117-
sb.append("\nf ").append(rs.getString(1)).append(" ")
118-
.append(rs.getString(2)).append(" ").append(weight);
119-
}
120-
}
121-
}
122-
123-
// --- Study groups ---
124-
try (PreparedStatement psStudyg = conn.prepareStatement(studygSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
125-
psStudyg.setString(1, Month);
126-
psStudyg.setString(2, Date);
127-
psStudyg.setInt(3, dThresSg);
128-
psStudyg.setInt(4, CThresSg);
129-
try (ResultSet rs = psStudyg.executeQuery()) {
130-
while (rs.next()) {
131-
double weight = rs.getInt(3) * (double) rs.getFloat(4);
132-
sb.append("\nsg ").append(rs.getString(1)).append(" ")
133-
.append(rs.getString(2)).append(" ").append(weight);
134-
}
135-
}
136-
}
137-
138-
// --- Classmates ---
139-
try (PreparedStatement psCmate = conn.prepareStatement(cmateSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
140-
psCmate.setString(1, Month);
141-
psCmate.setString(2, Date);
142-
psCmate.setInt(3, dThresC);
143-
psCmate.setInt(4, CThresC);
144-
try (ResultSet rs = psCmate.executeQuery()) {
145-
while (rs.next()) {
146-
double weight = rs.getInt(3) * (double) rs.getFloat(4);
147-
sb.append("\nc ").append(rs.getString(1)).append(" ")
148-
.append(rs.getString(2)).append(" ").append(weight);
149-
}
150-
}
151-
}
152-
153-
// --- Strangers ---
154-
try (PreparedStatement psStranger = conn.prepareStatement(strangerSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
155-
psStranger.setString(1, Month);
156-
psStranger.setString(2, Date);
157-
psStranger.setInt(3, dThresS);
158-
psStranger.setInt(4, CThresS);
159-
try (ResultSet rs = psStranger.executeQuery()) {
160-
while (rs.next()) {
161-
double weight = rs.getInt(3) * (double) rs.getFloat(4);
162-
sb.append("\ns ").append(rs.getString(1)).append(" ")
163-
.append(rs.getString(2)).append(" ").append(weight);
164-
}
165-
}
166-
}
167-
168-
// --- Familiar strangers ---
169-
try (PreparedStatement psFamstranger = conn.prepareStatement(famstrangerSql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
170-
psFamstranger.setString(1, Month);
171-
psFamstranger.setString(2, Date);
172-
psFamstranger.setInt(3, dThresFS);
173-
psFamstranger.setInt(4, CThresFS);
174-
try (ResultSet rs = psFamstranger.executeQuery()) {
175-
while (rs.next()) {
176-
double weight = rs.getInt(3) * (double) rs.getFloat(4);
177-
sb.append("\nfs ").append(rs.getString(1)).append(" ")
178-
.append(rs.getString(2)).append(" ").append(weight);
179-
}
180-
}
181-
}
182-
183-
// Write output file — use validated outputFile, not raw path
146+
// Friends: location = 'public'
147+
executeLocationMatchQuery(conn, "f", "public", ">", ">=",
148+
month, date, dThresF, cThresF, sb);
149+
// Study groups: location = 'class'
150+
executeLocationMatchQuery(conn, "sg", "class", ">", "<=",
151+
month, date, dThresSg, cThresSg, sb);
152+
// Classmates: location = 'class'
153+
executeLocationMatchQuery(conn, "c", "class", ">", ">=",
154+
month, date, dThresC, cThresC, sb);
155+
// Strangers: location NOT IN (...)
156+
executeEdgeQuery(conn,
157+
new EdgeQuery(locationExcludeSql("<"), "s", dThresS, cThresS),
158+
month, date, sb);
159+
// Familiar strangers: location NOT IN (...)
160+
executeEdgeQuery(conn,
161+
new EdgeQuery(locationExcludeSql(">"), "fs", dThresFS, cThresFS),
162+
month, date, sb);
163+
164+
// Write output file
184165
if (outputFile.exists()) {
185166
outputFile.delete();
186167
}
@@ -189,4 +170,31 @@ public static void generateFile(String path, String Month, String Date, int dThr
189170
}
190171
}
191172
}
173+
174+
/**
175+
* Executes a location-match edge query (location = ?) with 5 bind parameters.
176+
*/
177+
private static void executeLocationMatchQuery(Connection conn, String label,
178+
String location, String durationOp, String countOp,
179+
String month, String date, int durationThreshold, int countThreshold,
180+
StringBuilder sb) throws Exception {
181+
String sql = String.format(LOCATION_MATCH_TEMPLATE, durationOp, countOp);
182+
try (PreparedStatement ps = conn.prepareStatement(sql,
183+
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {
184+
ps.setString(1, month);
185+
ps.setString(2, date);
186+
ps.setString(3, location);
187+
ps.setInt(4, durationThreshold);
188+
ps.setInt(5, countThreshold);
189+
try (ResultSet rs = ps.executeQuery()) {
190+
while (rs.next()) {
191+
double weight = rs.getInt(3) * (double) rs.getFloat(4);
192+
sb.append('\n').append(label).append(' ')
193+
.append(rs.getString(1)).append(' ')
194+
.append(rs.getString(2)).append(' ')
195+
.append(weight);
196+
}
197+
}
198+
}
199+
}
192200
}

0 commit comments

Comments
 (0)