1 /*******************************************************************************
2  * Copyright (c) 2005, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 /**
15  *
16  */
17 package org.eclipse.jdt.internal.junit.ui;
18 
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.io.PrintWriter;
22 import java.io.StringReader;
23 import java.io.StringWriter;
24 
25 public class TextualTrace {
26 	public static final int LINE_TYPE_EXCEPTION = 1;
27 
28 	public static final int LINE_TYPE_NORMAL = 0;
29 
30 	public static final int LINE_TYPE_STACKFRAME = 2;
31 
32 	private final String fTrace;
33 
TextualTrace(String trace, String[] filterPatterns)34 	public TextualTrace(String trace, String[] filterPatterns) {
35 		super();
36 		fTrace = filterStack(trace, filterPatterns);
37 	}
38 
display(ITraceDisplay display, int maxLabelLength)39 	public void display(ITraceDisplay display, int maxLabelLength) {
40 		StringReader stringReader = new StringReader(fTrace);
41 		BufferedReader bufferedReader = new BufferedReader(stringReader);
42 		String line;
43 
44 		try {
45 			// first line contains the thrown exception
46 			line = readLine(bufferedReader);
47 			if (line == null)
48 				return;
49 
50 			displayWrappedLine(display, maxLabelLength, line,
51 					LINE_TYPE_EXCEPTION);
52 
53 			// the stack frames of the trace
54 			while ((line = readLine(bufferedReader)) != null) {
55 				int type = isAStackFrame(line) ? LINE_TYPE_STACKFRAME
56 						: LINE_TYPE_NORMAL;
57 				displayWrappedLine(display, maxLabelLength, line, type);
58 			}
59 		} catch (IOException e) {
60 			display.addTraceLine(LINE_TYPE_NORMAL, fTrace);
61 		}
62 	}
63 
displayWrappedLine(ITraceDisplay display, int maxLabelLength, String line, int type)64 	private void displayWrappedLine(ITraceDisplay display, int maxLabelLength,
65 			String line, int type) {
66 		final int labelLength = line.length();
67 		if (labelLength < maxLabelLength) {
68 			display.addTraceLine(type, line);
69 		} else {
70 			// workaround for bug 74647: JUnit view truncates
71 			// failure message
72 			display.addTraceLine(type, line.substring(0, maxLabelLength));
73 			int offset = maxLabelLength;
74 			while (offset < labelLength) {
75 				int nextOffset = Math.min(labelLength, offset + maxLabelLength);
76 				display.addTraceLine(LINE_TYPE_NORMAL, line.substring(offset,
77 						nextOffset));
78 				offset = nextOffset;
79 			}
80 		}
81 	}
82 
filterLine(String[] patterns, String line)83 	private boolean filterLine(String[] patterns, String line) {
84 		String pattern;
85 		int len;
86 		for (int i = (patterns.length - 1); i >= 0; --i) {
87 			pattern = patterns[i];
88 			len = pattern.length() - 1;
89 			if (pattern.charAt(len) == '*') {
90 				// strip trailing * from a package filter
91 				pattern = pattern.substring(0, len);
92 			} else if (Character.isUpperCase(pattern.charAt(0))) {
93 				// class in the default package
94 				pattern = FailureTrace.FRAME_PREFIX + pattern + '.';
95 			} else {
96 				// class names start w/ an uppercase letter after the .
97 				final int lastDotIndex = pattern.lastIndexOf('.');
98 				if ((lastDotIndex != -1)
99 					&& (lastDotIndex != len)
100 					&& Character.isUpperCase(pattern.charAt(lastDotIndex + 1)))
101 					pattern += '.'; // append . to a class filter
102 			}
103 
104 			if (line.indexOf(pattern) > 0)
105 				return true;
106 		}
107 		return false;
108 	}
109 
filterStack(String stackTrace, String[] filterPatterns)110 	private String filterStack(String stackTrace, String[] filterPatterns) {
111 		if (filterPatterns.length == 0 || stackTrace == null)
112 			return stackTrace;
113 
114 		StringWriter stringWriter = new StringWriter();
115 		PrintWriter printWriter = new PrintWriter(stringWriter);
116 		StringReader stringReader = new StringReader(stackTrace);
117 		BufferedReader bufferedReader = new BufferedReader(stringReader);
118 
119 		String line;
120 		String[] patterns = filterPatterns;
121 		boolean firstLine= true;
122 		try {
123 			while ((line= bufferedReader.readLine()) != null) {
124 				if (firstLine || !filterLine(patterns, line))
125 					printWriter.println(line);
126 				firstLine= false;
127 			}
128 		} catch (IOException e) {
129 			return stackTrace; // return the stack unfiltered
130 		}
131 		return stringWriter.toString();
132 	}
133 
isAStackFrame(String itemLabel)134 	private boolean isAStackFrame(String itemLabel) {
135 		// heuristic for detecting a stack frame - works for JDK
136 		return itemLabel.contains(" at "); //$NON-NLS-1$
137 	}
138 
readLine(BufferedReader bufferedReader)139 	private String readLine(BufferedReader bufferedReader) throws IOException {
140 		String readLine = bufferedReader.readLine();
141 		return readLine == null ? null : readLine.replace('\t', ' ');
142 	}
143 }
144