-
Notifications
You must be signed in to change notification settings - Fork 1
/
ManifestParser.java
278 lines (255 loc) · 9.41 KB
/
ManifestParser.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
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
/*
* Copyright 2013 Petr Kunc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cz.muni.fi.lessappcache.parser;
import cz.muni.fi.lessappcache.filesystem.PathUtils;
import cz.muni.fi.lessappcache.importer.ImportedFile;
import cz.muni.fi.lessappcache.importer.Importer;
import cz.muni.fi.lessappcache.parser.modules.Module;
import cz.muni.fi.lessappcache.parser.modules.ModuleControl;
import cz.muni.fi.lessappcache.parser.modules.ModuleException;
import cz.muni.fi.lessappcache.parser.modules.ModuleLoader;
import cz.muni.fi.lessappcache.parser.modules.ModuleOutput;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
/**
* The entry point of library. Manifest parser provides the main functionality to parse the lesscache
* file, analyze its lines and execute appropriate modules on each line.
*
* @author Petr Kunc
*/
public class ManifestParser {
private final static String VERSION = "#version-control";
private final static Logger logger = Logger.getLogger(ManifestParser.class.getName());
private List<Module> modules;
private String mode = "CACHE:";
private Path filePath;
private final Map<String, String> loadedResources = new HashMap<>();
private String absolute;
/**
* Constructor specifying fileName to be analyzed and parsed
*
* @param fileName
*/
public ManifestParser(Path fileName) {
filePath = fileName;
modules = ModuleLoader.load();
}
/**
* Shorthand for path constructor
*
* @param fileName
*/
public ManifestParser(String fileName) {
this(Paths.get(fileName));
}
private String getAbsolute() {
return absolute;
}
/**
* Setter for absolute. Absolute attribute is important when developer wants to check
* absolute paths of processed resources. This path should be set to the path describing the root
* of server.
*
* @param absolute path of the server root
*/
public void setAbsolute(String absolute) {
this.absolute = absolute;
}
/**
* Getter of filePath
*
* @return filePath
*/
public Path getFilePath() {
return filePath;
}
/**
* Getter of current mode
*
* @return mode
*/
public String getMode() {
return mode;
}
/**
* Setter of current mode
*
* @param mode
*/
public void setMode(String mode) {
this.mode = mode;
}
/**
* getter of loaded resources
*
* @return loaded resources
*/
public Map<String, String> getLoadedResources() {
return loadedResources;
}
/**
* This method serves for executing the parser function. It adds
* correct headers and version comment based on last moified file
*
* @return list of lines of completely parsed lesscache manifest
* @throws IOException when accessing of any file or resource stated in the lesscache file failed and application was not able to continue parsing
*/
public List<String> execute() throws IOException {
logger.info("Executing LessAppcache parser for file: " + filePath);
List<String> result = new ArrayList<>();
result.addAll(createHeaders());
result.addAll(processFile());
Path p = getLastModifiedFile();
String version = "Version: " + (p == null ? new Date() : formatter(p));
result.set(result.indexOf(VERSION), "# " + version);
return result;
}
/**
* Processes lesscache file.
*
* @param context
* @return lines of processed manifest file in given context
* @throws IOException when accessing of any file or resource stated in the lesscache file failed and application was not able to continue parsing
*/
public List<String> processFile() throws IOException {
List<String> processed = new ArrayList<>();
//returned Imported File has loaded lines and normalized path saved
ImportedFile imported = Importer.importFile(filePath);
Path parent = PathUtils.getParent(filePath);
processed.add("# Imported file: " + filePath);
//make sure that imported file starts with mode set to CACHE:
String oldMode = mode;
if (!"CACHE:".equals(mode)) {
processed.add("CACHE:");
mode = "CACHE:";
}
int lineNumber = 0;
for (String line : imported.getLines()) {
lineNumber++;
try {
processed.addAll(processLine(line, parent, lineNumber));
} catch (ModuleException ex) {
logger.error("Error while processing " + imported.getFilePath() + " on line " + lineNumber + ": " + ex.getMessage());
}
}
processed.add("# End of imported file: " + filePath);
//make sure to get back to mode we got while importing
if (!mode.equals(oldMode)) {
mode = oldMode;
processed.add(oldMode);
}
return processed;
}
/**
* Processes line of the manifest file by executing module parsers.
*
* @param line to be processed
* @param context of file (relative path against its importer)
* @param lineNumber number of processed line
* @return list of created lines based on the line
* @throws ModuleException when module encountered an unrecoverable error
*/
public List<String> processLine(String line, Path context, int lineNumber) throws ModuleException {
List<String> output = new ArrayList<>();
line = line.trim();
// TODO: ensure that imports and filters can use "" and ''
for (Module m : modules) {
ModuleOutput mo = m.parse(line, new ParsingContext(loadedResources, mode, context));
for (Map.Entry<String, String> entry : mo.getLoadedResources().entrySet()) {
getLoadedResources().put(entry.getKey(), filePath.toString() + ", line: " + lineNumber + ", info: " + entry.getValue());
}
if (mo.getMode() != null) {
setMode(mo.getMode());
}
if (mo.getControl() == ModuleControl.STOP) {
output.addAll(mo.getOutput());
break;
} else if (mo.getControl() == ModuleControl.REPARSE) {
for (String l : mo.getOutput()) {
output.addAll(processLine(l, context, lineNumber));
}
break;
} else {
// DEFINE: to add or not to add?
//output.addAll(mo.getOutput());
}
}
return output;
}
private Path getLastModifiedFile() {
Path newest = null;
FileTime ftn = null;
for (Map.Entry<String, String> entry : getLoadedResources().entrySet()) {
try {
Path temp = getFile(entry.getKey());
FileTime ftt = null;
if (temp != null) {
ftt = Files.getLastModifiedTime(temp, LinkOption.NOFOLLOW_LINKS);
}
if (ftt != null && (ftn == null || ftt.toMillis() > ftn.toMillis())) {
newest = temp;
ftn = ftt;
}
} catch (FileNotFoundException | IOException ex) {
logger.warn(entry.getKey() + " is not a file. If this resource is not virtual, check for typos. Additional info: " + entry.getValue());
}
}
return newest;
}
private Path getFile(String res) throws FileNotFoundException {
if (PathUtils.isRemote(res)) {
return null;
}
StringBuilder sb = new StringBuilder(res);
if (PathUtils.isAbsolute(res)) {
if (getAbsolute() == null) {
// we cannot check absolute URL paths without given context of where
// absolute paths starts in the local filesystem
return null;
}
sb.insert(0, getAbsolute());
}
Path resource = Paths.get(sb.toString());
if (!Files.exists(resource, LinkOption.NOFOLLOW_LINKS)) {
throw new FileNotFoundException();
} else {
return resource;
}
}
private String formatter(Path p) throws IOException {
return "Last modified file - " + p.toString() + ", Date: "
+ new SimpleDateFormat("MM/dd/yyyy HH:mm:ss").format(
new Date(Files.getLastModifiedTime(p, LinkOption.NOFOLLOW_LINKS).toMillis()));
}
private List<String> createHeaders() {
logger.info("Creating headers");
List<String> header = new ArrayList<>();
header.add("CACHE MANIFEST");
header.add(VERSION);
return header;
}
}