This repository has been archived by the owner on Nov 8, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SQFFileVisitor.java
127 lines (117 loc) · 6.17 KB
/
SQFFileVisitor.java
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
package org.myrskynkantaja.harakka.sqfcallgraph;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
// TODO: create separate visitors for mission.sqm, description.ext and .h files
/**
* FileVisitor for traversing a directory containing sqf script files and creating a Set of SQFEdges depicting the
* call graph edges from the traversed data.
* Created by: harakka
* Date: 9.11.2012, 1:36
*/
public class SQFFileVisitor extends SimpleFileVisitor<Path> {
private Set<SQFEdge> edgeList = new HashSet<>(); // Collection for storing the generated edge list
private final PathMatcher sqfMatcher = FileSystems.getDefault().getPathMatcher("glob:*.{sqf,sqs,sqm,ext,hpp,h}"); // Glob pattern for sqf-relevant files
private final Path workingDir; // The directory we are reading the scripts from
public SQFFileVisitor(Path workingDir) {
this.workingDir = workingDir;
System.out.println("SQFVisitor initialized with path " + workingDir);
}
/**
* Returns the generated edge list as a Collection of SQFEdges.
* Usage note: you need to call Files.walkFileTree on the SQFFileVisitor object and wait for the call to finish
* before calling this method.
* @return
*/
public Collection<SQFEdge> getResults() {
return edgeList;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (sqfMatcher.matches(file.getFileName())) {
for (SQFEdge edge: fileConnections(file)) {
edgeList.add(edge);
}
}
return super.visitFile(file, attrs); //To change body of overridden methods use File | Settings | File Templates.
}
/**
* Finds connections from this file to other files by searching for SQF script commands like execVM and #include.
* @param file search target
* @return SQFEdges representing the directed edges from target file to other files.
* @throws IOException
*/
private Set<SQFEdge> fileConnections(Path file) throws IOException {
//TODO: AddAction. Aargh, an entirely new format to parse
Set<SQFEdge> connections = new HashSet<>();
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
String line = null;
while ((line = reader.readLine()) != null) {
for (String s: line.split(";")) {
String result;
if ((result = parseFilenameFromCommand("execvm", s)) != null) {
connections.add(new SQFEdge(workingDir, file, Paths.get(workingDir.toString(), result).toRealPath(), EdgeType.execVM));
} else if ((result = parseFilenameFromCommand("#include", s)) != null) {
connections.add(new SQFEdge(workingDir, file, file.resolveSibling(result).toRealPath(), EdgeType.include));
} else if ((result = parseFilenameFromCommand("addaction", s)) != null) {
connections.add(new SQFEdge(workingDir, file, Paths.get(workingDir.toString(), result).toRealPath(), EdgeType.addAction));
} else if ((result = parseFilenameFromCommand("compile preprocessfile", s)) != null) {
connections.add(new SQFEdge(workingDir, file, Paths.get(workingDir.toString(), result).toRealPath(), EdgeType.callCompilePreprocessFile));
} else if ((result = parseFilenameFromCommand("compile preprocessfilelinenumbers", s)) != null) {
connections.add(new SQFEdge(workingDir, file, Paths.get(workingDir.toString(), result).toRealPath(), EdgeType.callCompilePreprocessFile));
}
}
}
} catch (IOException x) {
System.err.format("IOException in " + workingDir.relativize(file) + " while parsing: %s%n", x);
}
return connections;
}
/**
* Return the file targeted by the command given as parameter.
* Accepted format: ..... COMMAND "targetfile" .....
* Acceptable quotes are ", "" or '.
* @param command Command to search for in the line
* @param line target string
* @return file targeted by command
*/
private String parseFilenameFromCommand(String command, String line) {
// TODO: we already have the EdgeType enum, could be turned to CallType and have that passed as param here
// We have to handle AddAction specifically because it doesn't parse with the default mechanism
if (!line.trim().startsWith("//") && command.toLowerCase().equals("addaction") && line.toLowerCase().contains(command)) {
String[] substrings = line.split("(?i)(" + command + ")");
return parseQuotedString(substrings[1].split(",")[1]);
}
else if (!line.trim().startsWith("//") && line.toLowerCase().contains(command)) {
String[] substrings = line.split("(?i)(" + command + ")");
return parseQuotedString(substrings[1]);
}
return null;
}
/**
* Parses first quoted string from target string. Acceptable quotes are ", "" and '.
* @param line string to remove quotes from
* @return first quoted string without the quotes
*/
private String parseQuotedString(String line) {
int start, end;
if (line.trim().startsWith("\"\"")) { //Double double quoted
start = line.indexOf("\"\"") + 2;
end = line.indexOf("\"", start);
} else if (line.trim().startsWith("\"")) { // Double quoted
start = line.indexOf("\"") + 1;
end = line.indexOf("\"", start);
} else if (line.trim().startsWith("\'")) { // Single quoted
start = line.indexOf("\'") + 1;
end = line.indexOf("\'", start);
} else {
// We end up here if there are no quotes to parse
return null;
}
return line.substring(start, end);
}
}