-
Notifications
You must be signed in to change notification settings - Fork 0
/
Parser.pde
297 lines (256 loc) · 11.7 KB
/
Parser.pde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
/**
* @author jakemingolla
* @since 1.2
*
* Parser class to handle reading of {@link Atom} and
* {@link Spring} objects from a CSV file.
*
* As of version 1.1, the format for the CSV files is as follows:
* first line: Atom Number = Integer n representing the number of Atoms.
* lines 1 through n + 1:
* Comma-separated values in the following order:
* Identifier (String) *** MUST BE UNIQUE ***
* Weight (Double)
*
*/
public class Parser {
/**
* @author jakemingolla
* @since 1.2
*
* Exception to be thrown when there is a duplicate identifier within
* the Atom declarations in a CSV file.
*/
public class DuplicateIdentifierException extends Exception {
public DuplicateIdentifierException() { super(); }
public DuplicateIdentifierException(String message) { super(message); }
public DuplicateIdentifierException(String message, Throwable cause) { super(message, cause); }
public DuplicateIdentifierException(Throwable cause) { super(cause); }
}
/**
* @author jakemingolla
* @since 1.2
*
* Exception to be thrown when there is an invalid Spring delcared within
* the CSV file. This can occur when one of the following happens:
*
* 1. There already exists an edge between the two Nodes
* (Note that as of v1.2, edges are only undirected.)
*
* 2. The identifier of an Atom within the Spring declaration
* does not exist.
*/
public class IllegalEdgeException extends Exception {
public IllegalEdgeException() { super(); }
public IllegalEdgeException(String message) { super(message); }
public IllegalEdgeException(String message, Throwable cause) { super(message, cause); }
public IllegalEdgeException(Throwable cause) { super(cause); }
}
/* Map to store each atom by its unique identifier */
Map<String, Atom> atomsById;
/* Set of all identifiers to check for uniqueness */
Set<String> ids;
/*
* Set of all of the neighbor identifiers of a
* given identifier.
*/
Map<String, Set<String>> neighborsById;
/**
* @author jakemingolla
*
* Default constructor for a Parser object.
*
*/
public Parser() {
atomsById = new HashMap<String, Atom>();
ids = new HashSet<String>();
neighborsById = new HashMap<String, Set<String>>();
}
/**
* @author jakemingolla
*
* Gets a list of {@link Atom} objects defined at the given CSV file.
*
* @param path Path to the CSV file with the Atoms listed.
*
* @throws DuplicateIdentifierException If any of the identifiers are not unique.
*/
public List<Node> getAtoms(String path) throws DuplicateIdentifierException {
List<Node> atoms = new ArrayList<Node>();
String[] fileLines = loadStrings(path);
String[] currLine;
Integer i = 0;
/*
* NOTE:
* Will throw a NumberFormatException
* if the Atom Number of the CSV is not
* an integer.
*/
Integer numAtoms = Integer.parseInt(fileLines[i]);
for (i = 1; i < numAtoms + 1; i++) {
currLine = split(fileLines[i], ",");
String id = currLine[0];
/*
* Note:
* Will throw a NumberFormatException if
* the weight value is not a Double
*/
Double weight = new Double(Double.parseDouble(currLine[1]));
/*
* Throws a DuplicateIdentifierException if the id has
* already been used.
*/
if (ids.contains(id)) {
throw new DuplicateIdentifierException("Duplicate = " + id);
} else {
ids.add(id);
}
/* Otherwise, add the atom with its respective weight and id to the list. */
Atom atom = new Atom(id, weight, getRandomXPosition(), getRandomYPosition(), getRandomXPosition());
atoms.add(atom);
/* In addition, add a mapping from the identifier to the atom for finding
* it in the edge creation. */
atomsById.put(id, atom);
}
return atoms;
}
public List<Edge> getSprings(String path) throws IllegalEdgeException {
List<Edge> springs = new ArrayList<Edge>();
String[] fileLines = loadStrings(path);
String[] currLine;
/*
* NOTE:
* Will throw a NumberFormatException
* if the Atom Number of the CSV is not
* an integer.
*/
Integer numAtoms = Integer.parseInt(fileLines[0]);
/* How many lines are used to hold all of the atom information. */
Integer atomOffset = numAtoms + 1;
/*
* NOTE:
* Will throw a NumberFormatException
* if the Spring Number value is not
* an integer.
*/
Integer numSprings = Integer.parseInt(fileLines[atomOffset]);
/* Offset by two since one is used to declare numSprings. */
for (int i = atomOffset + 1; i < atomOffset + numSprings + 1; i++) {
currLine = split(fileLines[i], ",");
println(i);
String id1 = currLine[0];
String id2 = currLine[1];
/*
* If the two identifiers are the same, reject this edge as
* no self loops are allowed.
*/
if (id1.equals(id2)) {
throw new IllegalEdgeException("No self-loops allowed.");
}
/*
* NOTE:
* Will throw a NumberFormatException if
* the ideal length value is not a Double.
*/
Double idealLength = new Double(Double.parseDouble(currLine[2]));
/*
* If id1 has never been declared as an identifier for an Atom,
* the edge is invalid.
*/
Atom a1 = atomsById.get(id1);
if (a1 == null) {
throw new IllegalEdgeException("The identifier '" + id1 + "' is invalid.");
}
/*
* If id2 has never been declared as an identifier for an Atom,
* the edge is invalid.
*/
Atom a2 = atomsById.get(id2);
if (a2 == null) {
throw new IllegalEdgeException("The identifier '" + id2 + "' is invalid.");
}
/*
* Attempt to get the set of neighbors for the first
* identifier. If it is a null set, create it.
*/
Set<String> id1Neighbors = neighborsById.get(id1);
if (id1Neighbors == null) {
id1Neighbors = new HashSet<String>();
}
/*
* Attempt to get the set of neighbors for the second
* identifier. If it is a null set, create it.
*/
Set<String> id2Neighbors = neighborsById.get(id2);
if (id2Neighbors == null) {
id2Neighbors = new HashSet<String>();
}
/*
* If there already exists an edge between the Atoms specified by id1 and
* id2 the current edge is invalid.
*/
if (id1Neighbors.contains(id2) ||
id2Neighbors.contains(id1)) {
throw new IllegalEdgeException("There is already a Spring declared between '" + id1 +
"' and '" + id2 + "'.");
}
/*
* Now, add in id1 and id2 as neighbors in their respective sets
* and update their sets in the master mapping.
*/
id1Neighbors.add(id2);
neighborsById.put(id1, id1Neighbors);
id2Neighbors.add(id1);
neighborsById.put(id2, id2Neighbors);
/*
* Finally, add the spring to the list of springs with the
* current information.
*/
Spring spring = new Spring(a1, a2, idealLength);
springs.add(spring);
}
return springs;
}
/**
* @author jakemingolla
*
* Gets a random x position in relation to the Bounding Box's X and W information
* stored in the {@link Constants}.
*
* @return The random x position.
*/
private Double getRandomXPosition() {
return new Double(random((float)(Constants.DEFAULT_BOUNDING_BOX_X - (Constants.DEFAULT_BOUNDING_BOX_W / 2)),
(float)(Constants.DEFAULT_BOUNDING_BOX_X + (Constants.DEFAULT_BOUNDING_BOX_W / 2))));
}
/**
* @author jakemingolla
*
* Gets a random y position in relation to the Bounding Box's Y and H information
* stored in the {@link Constants}.
*
* @return The random y position.
*/
private Double getRandomYPosition() {
return new Double(random((float)(Constants.DEFAULT_BOUNDING_BOX_Y - (Constants.DEFAULT_BOUNDING_BOX_H / 2)),
(float)(Constants.DEFAULT_BOUNDING_BOX_Y + (Constants.DEFAULT_BOUNDING_BOX_H / 2))));
}
/**
* @author jakemingolla
*
* Gets a random z position in relation to the Bounding Box's Z and L information
* stored in the {@link Constants}.
*
* @return The random z position.
*/
private Double getRandomZPosition() {
return new Double(random((float)(Constants.DEFAULT_BOUNDING_BOX_Z - (Constants.DEFAULT_BOUNDING_BOX_L / 2)),
(float)(Constants.DEFAULT_BOUNDING_BOX_Z + (Constants.DEFAULT_BOUNDING_BOX_L / 2))));
}
}