Commit | Line | Data |
---|---|---|
c3c5c786 FC |
1 | /*******************************************************************************\r |
2 | * Copyright (c) 2010 Ericsson\r | |
3 | * \r | |
4 | * All rights reserved. This program and the accompanying materials are\r | |
5 | * made available under the terms of the Eclipse Public License v1.0 which\r | |
6 | * accompanies this distribution, and is available at\r | |
7 | * http://www.eclipse.org/legal/epl-v10.html\r | |
8 | * \r | |
9 | * Contributors:\r | |
10 | * Patrick Tasse - Initial API and implementation\r | |
11 | *******************************************************************************/\r | |
12 | \r | |
13 | package org.eclipse.linuxtools.tmf.ui.parsers.custom;\r | |
14 | \r | |
15 | import java.io.ByteArrayInputStream;\r | |
16 | import java.io.File;\r | |
17 | import java.io.FileNotFoundException;\r | |
18 | import java.io.IOException;\r | |
19 | import java.io.RandomAccessFile;\r | |
20 | \r | |
21 | import javax.xml.parsers.DocumentBuilder;\r | |
22 | import javax.xml.parsers.DocumentBuilderFactory;\r | |
23 | import javax.xml.parsers.ParserConfigurationException;\r | |
24 | \r | |
6c13869b FC |
25 | import org.eclipse.linuxtools.tmf.core.event.TmfEvent;\r |
26 | import org.eclipse.linuxtools.tmf.core.event.TmfEventReference;\r | |
27 | import org.eclipse.linuxtools.tmf.core.event.TmfEventSource;\r | |
28 | import org.eclipse.linuxtools.tmf.core.event.TmfTimestamp;\r | |
29 | import org.eclipse.linuxtools.tmf.core.io.BufferedRandomAccessFile;\r | |
30 | import org.eclipse.linuxtools.tmf.core.trace.ITmfContext;\r | |
31 | import org.eclipse.linuxtools.tmf.core.trace.ITmfLocation;\r | |
32 | import org.eclipse.linuxtools.tmf.core.trace.ITmfTrace;\r | |
33 | import org.eclipse.linuxtools.tmf.core.trace.TmfContext;\r | |
34 | import org.eclipse.linuxtools.tmf.core.trace.TmfLocation;\r | |
35 | import org.eclipse.linuxtools.tmf.core.trace.TmfTrace;\r | |
c3c5c786 FC |
36 | import org.eclipse.linuxtools.tmf.ui.parsers.custom.CustomXmlTraceDefinition.InputAttribute;\r |
37 | import org.eclipse.linuxtools.tmf.ui.parsers.custom.CustomXmlTraceDefinition.InputElement;\r | |
38 | import org.w3c.dom.Document;\r | |
39 | import org.w3c.dom.Element;\r | |
40 | import org.w3c.dom.Node;\r | |
41 | import org.w3c.dom.NodeList;\r | |
42 | import org.xml.sax.EntityResolver;\r | |
43 | import org.xml.sax.ErrorHandler;\r | |
44 | import org.xml.sax.InputSource;\r | |
45 | import org.xml.sax.SAXException;\r | |
46 | import org.xml.sax.SAXParseException;\r | |
47 | \r | |
48 | public class CustomXmlTrace extends TmfTrace<CustomXmlEvent> {\r | |
49 | \r | |
d7fcacc9 | 50 | private static final TmfLocation<Long> NULL_LOCATION = new TmfLocation<Long>((Long) null);\r |
1b70b6dc PT |
51 | private static final int CACHE_SIZE = 100;\r |
52 | \r | |
c3c5c786 | 53 | private CustomXmlTraceDefinition fDefinition;\r |
d7fcacc9 | 54 | private CustomXmlEventType fEventType;\r |
c3c5c786 FC |
55 | private InputElement fRecordInputElement;\r |
56 | \r | |
4bf17f4a | 57 | public CustomXmlTrace(CustomXmlTraceDefinition definition) {\r |
58 | fDefinition = definition;\r | |
59 | fEventType = new CustomXmlEventType(fDefinition);\r | |
60 | fRecordInputElement = getRecordInputElement(fDefinition.rootInputElement);\r | |
61 | }\r | |
62 | \r | |
c3c5c786 FC |
63 | public CustomXmlTrace(String name, CustomXmlTraceDefinition definition, String path, int cacheSize) throws FileNotFoundException {\r |
64 | super(name, CustomXmlEvent.class, path, cacheSize);\r | |
65 | fDefinition = definition;\r | |
d7fcacc9 | 66 | fEventType = new CustomXmlEventType(fDefinition);\r |
c3c5c786 FC |
67 | fRecordInputElement = getRecordInputElement(fDefinition.rootInputElement);\r |
68 | }\r | |
69 | \r | |
1b70b6dc PT |
70 | @Override\r |
71 | public void initTrace(String path, Class<CustomXmlEvent> eventType, boolean indexTrace) throws FileNotFoundException {\r | |
72 | super.initTrace(path, eventType, CACHE_SIZE, indexTrace);\r | |
73 | }\r | |
74 | \r | |
c3c5c786 FC |
75 | @Override\r |
76 | public TmfContext seekLocation(ITmfLocation<?> location) {\r | |
d7fcacc9 FC |
77 | CustomXmlTraceContext context = new CustomXmlTraceContext(NULL_LOCATION, ITmfContext.INITIAL_RANK);\r |
78 | if (NULL_LOCATION.equals(location) || !new File(getPath()).isFile()) {\r | |
c3c5c786 FC |
79 | return context;\r |
80 | }\r | |
81 | try {\r | |
d7fcacc9 | 82 | context.raFile = new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$\r |
c3c5c786 FC |
83 | if (location != null && location.getLocation() instanceof Long) {\r |
84 | context.raFile.seek((Long)location.getLocation());\r | |
85 | }\r | |
86 | \r | |
87 | String line;\r | |
3b38ea61 | 88 | String recordElementStart = "<" + fRecordInputElement.elementName; //$NON-NLS-1$\r |
c3c5c786 FC |
89 | long rawPos = context.raFile.getFilePointer();\r |
90 | \r | |
d7fcacc9 | 91 | while ((line = context.raFile.getNextLine()) != null) {\r |
c3c5c786 FC |
92 | int idx = line.indexOf(recordElementStart); \r |
93 | if (idx != -1) {\r | |
94 | context.setLocation(new TmfLocation<Long>(rawPos + idx));\r | |
95 | return context;\r | |
96 | }\r | |
97 | rawPos = context.raFile.getFilePointer();\r | |
98 | }\r | |
99 | return context;\r | |
100 | } catch (FileNotFoundException e) {\r | |
101 | e.printStackTrace();\r | |
102 | return context;\r | |
103 | } catch (IOException e) {\r | |
104 | e.printStackTrace();\r | |
105 | return context;\r | |
106 | }\r | |
107 | \r | |
108 | }\r | |
109 | \r | |
c76c54bb FC |
110 | @Override\r |
111 | public TmfContext seekLocation(double ratio) {\r | |
112 | try {\r | |
a79913eb FC |
113 | BufferedRandomAccessFile raFile = new BufferedRandomAccessFile(getPath(), "r"); //$NON-NLS-1$\r |
114 | long pos = (long) (ratio * raFile.length());\r | |
115 | while (pos > 0) {\r | |
116 | raFile.seek(pos - 1);\r | |
117 | if (raFile.read() == '\n') break;\r | |
118 | pos--;\r | |
119 | }\r | |
120 | ITmfLocation<?> location = new TmfLocation<Long>(new Long(pos));\r | |
c76c54bb FC |
121 | TmfContext context = seekLocation(location);\r |
122 | context.setRank(ITmfContext.UNKNOWN_RANK);\r | |
123 | return context;\r | |
124 | } catch (FileNotFoundException e) {\r | |
125 | e.printStackTrace();\r | |
126 | return new CustomXmlTraceContext(NULL_LOCATION, ITmfContext.INITIAL_RANK);\r | |
127 | } catch (IOException e) {\r | |
128 | e.printStackTrace();\r | |
129 | return new CustomXmlTraceContext(NULL_LOCATION, ITmfContext.INITIAL_RANK);\r | |
130 | }\r | |
131 | }\r | |
132 | \r | |
133 | @Override\r | |
134 | public double getLocationRatio(ITmfLocation<?> location) {\r | |
135 | try {\r | |
136 | if (location.getLocation() instanceof Long) {\r | |
137 | RandomAccessFile raFile = new RandomAccessFile(getPath(), "r"); //$NON-NLS-1$\r | |
138 | return (double) ((Long) location.getLocation()) / raFile.length();\r | |
139 | }\r | |
140 | } catch (FileNotFoundException e) {\r | |
141 | e.printStackTrace();\r | |
142 | } catch (IOException e) {\r | |
143 | e.printStackTrace();\r | |
144 | }\r | |
145 | return 0;\r | |
146 | }\r | |
147 | \r | |
d4011df2 | 148 | @Override\r |
12c155f5 FC |
149 | @SuppressWarnings({ "rawtypes", "unchecked" })\r |
150 | public ITmfTrace copy() {\r | |
c3c5c786 FC |
151 | // TODO Auto-generated method stub\r |
152 | return null;\r | |
153 | }\r | |
154 | \r | |
155 | @Override\r | |
156 | public ITmfLocation<?> getCurrentLocation() {\r | |
a79913eb FC |
157 | // TODO Auto-generated method stub\r |
158 | return null;\r | |
c3c5c786 FC |
159 | }\r |
160 | \r | |
161 | @Override\r | |
162 | public synchronized TmfEvent getNextEvent(TmfContext context) {\r | |
163 | ITmfContext savedContext = context.clone();\r | |
164 | TmfEvent event = parseEvent(context);\r | |
165 | if (event != null) {\r | |
166 | updateIndex(savedContext, savedContext.getRank(), event.getTimestamp());\r | |
167 | context.updateRank(1);\r | |
168 | }\r | |
169 | return event;\r | |
170 | }\r | |
171 | \r | |
172 | @Override\r | |
173 | public TmfEvent parseEvent(TmfContext tmfContext) {\r | |
c3c5c786 FC |
174 | if (!(tmfContext instanceof CustomXmlTraceContext)) {\r |
175 | return null;\r | |
176 | }\r | |
177 | \r | |
178 | CustomXmlTraceContext context = (CustomXmlTraceContext) tmfContext;\r | |
d7fcacc9 | 179 | if (!(context.getLocation().getLocation() instanceof Long) || NULL_LOCATION.equals(context.getLocation())) {\r |
c3c5c786 FC |
180 | return null;\r |
181 | }\r | |
182 | \r | |
183 | synchronized (context.raFile) {\r | |
184 | CustomXmlEvent event = null;\r | |
185 | try {\r | |
186 | if (context.raFile.getFilePointer() != (Long)context.getLocation().getLocation() + 1) {\r | |
187 | context.raFile.seek((Long)context.getLocation().getLocation() + 1); // +1 is for the <\r | |
188 | }\r | |
3b38ea61 | 189 | StringBuffer elementBuffer = new StringBuffer("<"); //$NON-NLS-1$\r |
c3c5c786 FC |
190 | readElement(elementBuffer, context.raFile);\r |
191 | Element element = parseElementBuffer(elementBuffer);\r | |
192 | \r | |
193 | event = extractEvent(element, fRecordInputElement);\r | |
d7fcacc9 | 194 | ((StringBuffer) event.getContent().getContent()).append(elementBuffer);\r |
c3c5c786 FC |
195 | \r |
196 | String line;\r | |
3b38ea61 | 197 | String recordElementStart = "<" + fRecordInputElement.elementName; //$NON-NLS-1$\r |
c3c5c786 FC |
198 | long rawPos = context.raFile.getFilePointer();\r |
199 | \r | |
d7fcacc9 | 200 | while ((line = context.raFile.getNextLine()) != null) {\r |
c3c5c786 FC |
201 | int idx = line.indexOf(recordElementStart); \r |
202 | if (idx != -1) {\r | |
203 | context.setLocation(new TmfLocation<Long>(rawPos + idx));\r | |
204 | return event;\r | |
205 | }\r | |
206 | rawPos = context.raFile.getFilePointer();\r | |
207 | }\r | |
208 | } catch (IOException e) {\r | |
209 | e.printStackTrace();\r | |
210 | }\r | |
d7fcacc9 | 211 | context.setLocation(NULL_LOCATION);\r |
c3c5c786 FC |
212 | return event;\r |
213 | }\r | |
214 | }\r | |
215 | \r | |
216 | private Element parseElementBuffer(StringBuffer elementBuffer) {\r | |
217 | try {\r | |
218 | DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\r | |
219 | DocumentBuilder db = dbf.newDocumentBuilder();\r | |
220 | \r | |
221 | // The following allows xml parsing without access to the dtd\r | |
222 | EntityResolver resolver = new EntityResolver () {\r | |
d4011df2 FC |
223 | @Override\r |
224 | public InputSource resolveEntity (String publicId, String systemId) {\r | |
3b38ea61 | 225 | String empty = ""; //$NON-NLS-1$\r |
c3c5c786 FC |
226 | ByteArrayInputStream bais = new ByteArrayInputStream(empty.getBytes());\r |
227 | return new InputSource(bais);\r | |
228 | }\r | |
229 | };\r | |
230 | db.setEntityResolver(resolver);\r | |
231 | \r | |
232 | // The following catches xml parsing exceptions\r | |
233 | db.setErrorHandler(new ErrorHandler(){\r | |
d4011df2 FC |
234 | @Override\r |
235 | public void error(SAXParseException saxparseexception) throws SAXException {}\r | |
236 | @Override\r | |
237 | public void warning(SAXParseException saxparseexception) throws SAXException {}\r | |
238 | @Override\r | |
239 | public void fatalError(SAXParseException saxparseexception) throws SAXException {\r | |
c3c5c786 FC |
240 | throw saxparseexception;\r |
241 | }});\r | |
242 | \r | |
243 | Document doc = db.parse(new ByteArrayInputStream(elementBuffer.toString().getBytes()));\r | |
244 | return doc.getDocumentElement();\r | |
245 | } catch (ParserConfigurationException e) {\r | |
246 | e.printStackTrace();\r | |
247 | } catch (SAXException e) {\r | |
248 | e.printStackTrace();\r | |
249 | } catch (IOException e) {\r | |
250 | e.printStackTrace();\r | |
251 | }\r | |
252 | return null;\r | |
253 | }\r | |
254 | \r | |
255 | private void readElement(StringBuffer buffer, RandomAccessFile raFile) {\r | |
256 | try {\r | |
257 | int numRead = 0;\r | |
258 | boolean startTagClosed = false;\r | |
259 | int i;\r | |
260 | while ((i = raFile.read()) != -1) {\r | |
261 | numRead++;\r | |
262 | char c = (char)i;\r | |
263 | buffer.append(c);\r | |
264 | if (c == '"') {\r | |
265 | readQuote(buffer, raFile, '"');\r | |
266 | } else if (c == '\'') {\r | |
267 | readQuote(buffer, raFile, '\'');\r | |
268 | } else if (c == '<') {\r | |
269 | readElement(buffer, raFile);\r | |
270 | } else if (c == '/' && numRead == 1) {\r | |
271 | break; // found "</"\r | |
3b38ea61 | 272 | } else if (c == '-' && numRead == 3 && buffer.substring(buffer.length() - 3, buffer.length() - 1).equals("!-")) { //$NON-NLS-1$\r |
c3c5c786 FC |
273 | readComment(buffer, raFile); // found "<!--"\r |
274 | } else if (i == '>') {\r | |
275 | if (buffer.charAt(buffer.length() - 2) == '/') {\r | |
276 | break; // found "/>"\r | |
277 | } else if (startTagClosed) {\r | |
278 | break; // found "<...>...</...>"\r | |
279 | } else {\r | |
280 | startTagClosed = true; // found "<...>"\r | |
281 | }\r | |
282 | }\r | |
283 | }\r | |
284 | return;\r | |
285 | } catch (IOException e) {\r | |
286 | return;\r | |
287 | }\r | |
288 | }\r | |
289 | \r | |
290 | private void readQuote(StringBuffer buffer, RandomAccessFile raFile, char eq) {\r | |
291 | try {\r | |
292 | int i;\r | |
293 | while ((i = raFile.read()) != -1) {\r | |
294 | char c = (char)i;\r | |
295 | buffer.append(c);\r | |
296 | if (c == eq) {\r | |
297 | break; // found matching end-quote\r | |
298 | }\r | |
299 | }\r | |
300 | return;\r | |
301 | } catch (IOException e) {\r | |
302 | return;\r | |
303 | }\r | |
304 | }\r | |
305 | \r | |
306 | private void readComment(StringBuffer buffer, RandomAccessFile raFile) {\r | |
307 | try {\r | |
308 | int numRead = 0;\r | |
309 | int i;\r | |
310 | while ((i = raFile.read()) != -1) {\r | |
311 | numRead++;\r | |
312 | char c = (char)i;\r | |
313 | buffer.append(c);\r | |
3b38ea61 | 314 | if (c == '>' && numRead >= 2 && buffer.substring(buffer.length() - 3, buffer.length() - 1).equals("--")) { //$NON-NLS-1$\r |
c3c5c786 FC |
315 | break; // found "-->"\r |
316 | }\r | |
317 | }\r | |
318 | return;\r | |
319 | } catch (IOException e) {\r | |
320 | return;\r | |
321 | }\r | |
322 | }\r | |
323 | \r | |
324 | public static StringBuffer parseElement(Element parentElement, StringBuffer buffer) {\r | |
325 | NodeList nodeList = parentElement.getChildNodes();\r | |
326 | String separator = null;\r | |
327 | for (int i = 0; i < nodeList.getLength(); i++) {\r | |
328 | Node node = nodeList.item(i);\r | |
329 | if (node.getNodeType() == Node.ELEMENT_NODE) {\r | |
330 | if (separator == null) {\r | |
3b38ea61 | 331 | separator = " | "; //$NON-NLS-1$\r |
c3c5c786 FC |
332 | } else {\r |
333 | buffer.append(separator);\r | |
334 | }\r | |
335 | Element element = (Element) node;\r | |
336 | if (element.hasChildNodes() == false) {\r | |
337 | buffer.append(element.getNodeName());\r | |
338 | } else if (element.getChildNodes().getLength() == 1 && element.getFirstChild().getNodeType() == Node.TEXT_NODE) {\r | |
3b38ea61 | 339 | buffer.append(element.getNodeName() + ":" + element.getFirstChild().getNodeValue().trim()); //$NON-NLS-1$\r |
c3c5c786 FC |
340 | } else {\r |
341 | buffer.append(element.getNodeName());\r | |
3b38ea61 | 342 | buffer.append(" [ "); //$NON-NLS-1$\r |
c3c5c786 | 343 | parseElement(element, buffer);\r |
3b38ea61 | 344 | buffer.append(" ]"); //$NON-NLS-1$\r |
c3c5c786 FC |
345 | }\r |
346 | } else if (node.getNodeType() == Node.TEXT_NODE) {\r | |
347 | if (node.getNodeValue().trim().length() != 0) {\r | |
348 | buffer.append(node.getNodeValue().trim());\r | |
349 | }\r | |
350 | }\r | |
351 | }\r | |
352 | return buffer;\r | |
353 | }\r | |
354 | \r | |
355 | public InputElement getRecordInputElement(InputElement inputElement) {\r | |
356 | if (inputElement.logEntry) {\r | |
357 | return inputElement;\r | |
358 | } else if (inputElement.childElements != null) {\r | |
359 | for (InputElement childInputElement : inputElement.childElements) {\r | |
360 | InputElement recordInputElement = getRecordInputElement(childInputElement);\r | |
361 | if (recordInputElement != null) {\r | |
362 | return recordInputElement;\r | |
363 | }\r | |
364 | }\r | |
365 | }\r | |
366 | return null;\r | |
367 | }\r | |
368 | \r | |
369 | public CustomXmlEvent extractEvent(Element element, InputElement inputElement) {\r | |
4bf17f4a | 370 | CustomXmlEvent event = new CustomXmlEvent(fDefinition, this, TmfTimestamp.Zero, new TmfEventSource(""), fEventType, new TmfEventReference("")); //$NON-NLS-1$ //$NON-NLS-2$\r |
d7fcacc9 | 371 | event.setContent(new CustomEventContent(event, new StringBuffer()));\r |
c3c5c786 FC |
372 | parseElement(element, event, inputElement);\r |
373 | return event;\r | |
374 | }\r | |
375 | \r | |
376 | private void parseElement(Element element, CustomXmlEvent event, InputElement inputElement) {\r | |
377 | if (inputElement.inputName != null && !inputElement.inputName.equals(CustomXmlTraceDefinition.TAG_IGNORE)) {\r | |
378 | event.parseInput(parseElement(element, new StringBuffer()).toString(), inputElement.inputName, inputElement.inputAction, inputElement.inputFormat);\r | |
379 | }\r | |
380 | if (inputElement.attributes != null) {\r | |
381 | for (InputAttribute attribute : inputElement.attributes) {\r | |
382 | event.parseInput(element.getAttribute(attribute.attributeName), attribute.inputName, attribute.inputAction, attribute.inputFormat);\r | |
383 | }\r | |
384 | }\r | |
385 | NodeList childNodes = element.getChildNodes();\r | |
386 | if (inputElement.childElements != null) {\r | |
387 | for (int i = 0; i < childNodes.getLength(); i++) {\r | |
388 | Node node = childNodes.item(i);\r | |
389 | if (node instanceof Element) {\r | |
390 | for (InputElement child : inputElement.childElements) {\r | |
391 | if (node.getNodeName().equals(child.elementName)) {\r | |
392 | parseElement((Element) node, event, child);\r | |
393 | break;\r | |
394 | }\r | |
395 | }\r | |
396 | }\r | |
397 | }\r | |
398 | }\r | |
399 | return;\r | |
400 | }\r | |
401 | \r | |
402 | public CustomTraceDefinition getDefinition() {\r | |
403 | return fDefinition;\r | |
404 | }\r | |
405 | }\r |