View | Details | Raw Unified | Return to bug 97332 | Differences between
and this patch

Collapse All | Expand All

(-)batch/org/eclipse/jdt/internal/compiler/batch/messages.properties (+2 lines)
Lines 84-89 Link Here
84
configure.incorrectVMVersionforAPT = Annotation processing got disabled, since it requires a 1.6 compliant JVM
84
configure.incorrectVMVersionforAPT = Annotation processing got disabled, since it requires a 1.6 compliant JVM
85
configure.incompatibleSourceForCldcTarget=Target level ''{0}'' is incompatible with source level ''{1}''. A source level ''1.3'' or lower is required
85
configure.incompatibleSourceForCldcTarget=Target level ''{0}'' is incompatible with source level ''{1}''. A source level ''1.3'' or lower is required
86
configure.incompatibleComplianceForCldcTarget=Target level ''{0}'' is incompatible with compliance level ''{1}''. A compliance level ''1.4''or lower is required
86
configure.incompatibleComplianceForCldcTarget=Target level ''{0}'' is incompatible with compliance level ''{1}''. A compliance level ''1.4''or lower is required
87
configure.invalidClasspathSection = invalid Class-Path header in manifest of jar file: {0}
88
configure.multipleClasspathSections = multiple Class-Path headers in manifest of jar file: {0}
87
89
88
### requestor
90
### requestor
89
requestor.error = {0}. ERROR in {1}
91
requestor.error = {0}. ERROR in {1}
(-)batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java (-1 / +168 lines)
Lines 10-20 Link Here
10
 *******************************************************************************/
10
 *******************************************************************************/
11
package org.eclipse.jdt.internal.compiler.batch;
11
package org.eclipse.jdt.internal.compiler.batch;
12
12
13
import java.io.BufferedReader;
13
import java.io.File;
14
import java.io.File;
14
import java.io.IOException;
15
import java.io.IOException;
16
import java.io.InputStreamReader;
17
import java.io.Reader;
15
import java.util.ArrayList;
18
import java.util.ArrayList;
16
import java.util.Enumeration;
19
import java.util.Enumeration;
17
import java.util.Hashtable;
20
import java.util.Hashtable;
21
import java.util.Iterator;
22
import java.util.List;
18
import java.util.zip.ZipEntry;
23
import java.util.zip.ZipEntry;
19
import java.util.zip.ZipFile;
24
import java.util.zip.ZipFile;
20
25
Lines 38-43 Link Here
38
	this.file = file;
43
	this.file = file;
39
	this.closeZipFileAtEnd = closeZipFileAtEnd;
44
	this.closeZipFileAtEnd = closeZipFileAtEnd;
40
}
45
}
46
47
// manifest file analyzer limited to Class-Path sections analysis
48
public static class ManifestAnalyzer {
49
	private static final int
50
		START = 0,
51
		IN_CLASSPATH_HEADER = 1, // multistate
52
		PAST_CLASSPATH_HEADER = 2,
53
		SKIPPING_WHITESPACE = 3,
54
		READING_JAR = 4,
55
		CONTINUING = 5,
56
		SKIP_LINE = 6;
57
	private static final char[] CLASSPATH_HEADER_TOKEN = 
58
		"Class-Path:".toCharArray(); //$NON-NLS-1$
59
	private int ClasspathSectionsCount;
60
	private ArrayList calledFilesNames;
61
	public boolean analyzeManifestContents(Reader reader) throws IOException {
62
		int state = START, substate = 0;
63
		StringBuffer currentJarToken = new StringBuffer();
64
		int currentChar;
65
		this.ClasspathSectionsCount = 0;
66
		this.calledFilesNames = null;
67
		for (;;) {
68
			currentChar = reader.read();
69
			switch (state) {
70
				case START:
71
					if (currentChar == -1) {
72
						return true;
73
					} else if (currentChar == CLASSPATH_HEADER_TOKEN[0]) {
74
						state = IN_CLASSPATH_HEADER;
75
						substate = 1;
76
					} else {
77
						state = SKIP_LINE;
78
					}
79
					break;
80
				case IN_CLASSPATH_HEADER:
81
					if (currentChar == -1) {
82
						return true;
83
					} else if (currentChar == '\n') {
84
						state = START;
85
					} else if (currentChar != CLASSPATH_HEADER_TOKEN[substate++]) {
86
						state = SKIP_LINE;
87
					} else if (substate == CLASSPATH_HEADER_TOKEN.length) {
88
						state = PAST_CLASSPATH_HEADER;
89
					}
90
					break;
91
				case PAST_CLASSPATH_HEADER:
92
					if (currentChar == ' ') {
93
						state = SKIPPING_WHITESPACE;
94
						this.ClasspathSectionsCount++;
95
					} else {
96
						return false;
97
					}
98
					break;
99
				case SKIPPING_WHITESPACE:
100
					if (currentChar == -1) {
101
						return true;
102
					} else if (currentChar == '\n') {
103
						state = CONTINUING;
104
					} else if (currentChar != ' ') {
105
						currentJarToken.append((char) currentChar);
106
						state = READING_JAR;
107
					}
108
					break;
109
				case CONTINUING:
110
					if (currentChar == -1) {
111
						return true;
112
					} else if (currentChar == '\n') {
113
						state = START;
114
					} else if (currentChar == ' ') {
115
						state = SKIPPING_WHITESPACE;
116
					} else if (currentChar == CLASSPATH_HEADER_TOKEN[0]) {
117
						state = IN_CLASSPATH_HEADER;
118
						substate = 1;
119
					} else if (this.calledFilesNames == null) {
120
						return false;
121
					} else {
122
						state = SKIP_LINE;
123
					}
124
					break;
125
				case SKIP_LINE:
126
					if (currentChar == -1) {
127
						return true;
128
					} else if (currentChar == '\n') {
129
						state = START;
130
					}
131
					break;
132
				case READING_JAR:	
133
					if (currentChar == -1) {
134
						return false;
135
					} else if (currentChar == '\n') {
136
						// appends token below
137
						state = CONTINUING;
138
					} else if (currentChar == ' ') {
139
						// appends token below
140
						state = SKIPPING_WHITESPACE;
141
					} else {
142
						currentJarToken.append((char) currentChar);
143
						break;
144
					}
145
					if (this.calledFilesNames == null) {
146
						this.calledFilesNames = new ArrayList();
147
					}
148
					this.calledFilesNames.add(currentJarToken.toString());
149
					currentJarToken.setLength(0);
150
					break;
151
			}
152
		}	
153
	}
154
	public int getClasspathSectionsCount() {
155
		return this.ClasspathSectionsCount;
156
	}
157
	public List getCalledFileNames() {
158
		return this.calledFilesNames;
159
	}
160
}
161
public static final ManifestAnalyzer MANIFEST_ANALYZER = new ManifestAnalyzer();
162
163
public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) {
164
	// expected to be called once only - if multiple calls desired, consider
165
	// using a cache
166
	BufferedReader reader = null;
167
	try {
168
		initialize();
169
		ArrayList result = new ArrayList();
170
		ZipEntry manifest =	this.zipFile.getEntry("META-INF/MANIFEST.MF"); //$NON-NLS-1$
171
		if (manifest != null) { // non-null implies regular file
172
			reader = new BufferedReader(new InputStreamReader(this.zipFile.getInputStream(manifest)));
173
			boolean success = MANIFEST_ANALYZER.analyzeManifestContents(reader);
174
			List calledFileNames = MANIFEST_ANALYZER.getCalledFileNames();
175
			if (problemReporter != null) {
176
				if (!success || 
177
						MANIFEST_ANALYZER.getClasspathSectionsCount() == 1 &&  calledFileNames == null) {
178
					problemReporter.invalidClasspathSection(this.getPath());
179
				} else if (MANIFEST_ANALYZER.getClasspathSectionsCount() > 1) {
180
					problemReporter.multipleClasspathSections(this.getPath());				
181
				}
182
			}
183
			if (calledFileNames != null) {
184
				Iterator calledFilesIterator = calledFileNames.iterator();
185
				String directoryPath = this.getPath();
186
				int lastSeparator = directoryPath.lastIndexOf(File.separatorChar);
187
				directoryPath = directoryPath.substring(0, lastSeparator + 1); // potentially empty (see bug 214731)
188
				while (calledFilesIterator.hasNext()) {
189
					result.add(new ClasspathJar(new File(directoryPath + (String) calledFilesIterator.next()), this.closeZipFileAtEnd, this.accessRuleSet, this.destinationPath));
190
				}
191
			}
192
		}
193
		return result;
194
	} catch (IOException e) {
195
		return null;
196
	} finally {
197
		if (reader != null) {
198
			try {
199
				reader.close();
200
			} catch (IOException e) {
201
				// best effort
202
			}
203
		}
204
	}
205
}
41
public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName) {
206
public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName) {
42
	return findClass(typeName, qualifiedPackageName, qualifiedBinaryFileName, false);
207
	return findClass(typeName, qualifiedPackageName, qualifiedBinaryFileName, false);
43
}
208
}
Lines 91-97 Link Here
91
	return null;
256
	return null;
92
}
257
}
93
public void initialize() throws IOException {
258
public void initialize() throws IOException {
94
	this.zipFile = new ZipFile(this.file);
259
	if (this.zipFile == null) {
260
		this.zipFile = new ZipFile(this.file);
261
	}
95
}
262
}
96
public boolean isPackage(String qualifiedPackageName) {
263
public boolean isPackage(String qualifiedPackageName) {
97
	if (this.packageCache != null)
264
	if (this.packageCache != null)
(-)batch/org/eclipse/jdt/internal/compiler/batch/FileSystem.java (+15 lines)
Lines 15-20 Link Here
15
import java.util.ArrayList;
15
import java.util.ArrayList;
16
import java.util.HashSet;
16
import java.util.HashSet;
17
import java.util.Iterator;
17
import java.util.Iterator;
18
import java.util.List;
18
import java.util.Set;
19
import java.util.Set;
19
20
20
import org.eclipse.jdt.core.compiler.CharOperation;
21
import org.eclipse.jdt.core.compiler.CharOperation;
Lines 31-36 Link Here
31
		NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName, boolean asBinaryOnly);
32
		NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName, boolean asBinaryOnly);
32
		boolean isPackage(String qualifiedPackageName);
33
		boolean isPackage(String qualifiedPackageName);
33
		/**
34
		/**
35
		 * Return a list of the jar file names defined in the Class-Path section
36
		 * of the jar file manifest if any, null else. Only ClasspathJar (and
37
		 * extending classes) instances may return a non-null result. 
38
		 * @param  problemReporter problem reporter with which potential 
39
		 *         misconfiguration issues are raised
40
		 * @return a list of the jar file names defined in the Class-Path 
41
		 *         section of the jar file manifest if any
42
		 */
43
		List fetchLinkedJars(ClasspathSectionProblemReporter problemReporter);
44
		/**
34
		 * This method resets the environment. The resulting state is equivalent to
45
		 * This method resets the environment. The resulting state is equivalent to
35
		 * a new name environment without creating a new object.
46
		 * a new name environment without creating a new object.
36
		 */
47
		 */
Lines 57-62 Link Here
57
		 */
68
		 */
58
		void initialize() throws IOException;
69
		void initialize() throws IOException;
59
	}
70
	}
71
	public interface ClasspathSectionProblemReporter {
72
		void invalidClasspathSection(String jarFilePath);
73
		void multipleClasspathSections(String jarFilePath);
74
	}
60
75
61
	/**
76
	/**
62
	 * This class is defined how to normalize the classpath entries.
77
	 * This class is defined how to normalize the classpath entries.
(-)batch/org/eclipse/jdt/internal/compiler/batch/Main.java (-2 / +26 lines)
Lines 33-38 Link Here
33
import java.util.Date;
33
import java.util.Date;
34
import java.util.HashMap;
34
import java.util.HashMap;
35
import java.util.Iterator;
35
import java.util.Iterator;
36
import java.util.List;
36
import java.util.Locale;
37
import java.util.Locale;
37
import java.util.Map;
38
import java.util.Map;
38
import java.util.MissingResourceException;
39
import java.util.MissingResourceException;
Lines 1511-1517 Link Here
1511
		addPendingErrors(this.bind("configure.incorrectClasspath", currentClasspathName));//$NON-NLS-1$
1512
		addPendingErrors(this.bind("configure.incorrectClasspath", currentClasspathName));//$NON-NLS-1$
1512
	}
1513
	}
1513
}
1514
}
1514
private void addPendingErrors(String message) {
1515
void addPendingErrors(String message) {
1515
	if (this.pendingErrors == null) {
1516
	if (this.pendingErrors == null) {
1516
		this.pendingErrors = new ArrayList();
1517
		this.pendingErrors = new ArrayList();
1517
	}
1518
	}
Lines 2916-2922 Link Here
2916
			}
2917
			}
2917
		}
2918
		}
2918
	}
2919
	}
2919
	return classpaths;
2920
	ArrayList result = new ArrayList();
2921
	HashMap knownNames = new HashMap();
2922
	FileSystem.ClasspathSectionProblemReporter problemReporter =
2923
		new FileSystem.ClasspathSectionProblemReporter() {
2924
			public void invalidClasspathSection(String jarFilePath) {
2925
				addPendingErrors(bind("configure.invalidClasspathSection", jarFilePath)); //$NON-NLS-1$
2926
			}
2927
			public void multipleClasspathSections(String jarFilePath) {
2928
				addPendingErrors(bind("configure.multipleClasspathSections", jarFilePath)); //$NON-NLS-1$
2929
			}
2930
		};
2931
	while (! classpaths.isEmpty()) {
2932
		Classpath current = (Classpath) classpaths.remove(0);
2933
		String currentPath = current.getPath();
2934
		if (knownNames.get(currentPath) == null) {
2935
			knownNames.put(currentPath, current);
2936
			result.add(current);
2937
			List linkedJars = current.fetchLinkedJars(problemReporter);
2938
			if (linkedJars != null) {
2939
				classpaths.addAll(0, linkedJars);
2940
			}
2941
		}
2942
	}
2943
	return result;
2920
}
2944
}
2921
/*
2945
/*
2922
 * External API
2946
 * External API
(-)batch/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java (+4 lines)
Lines 14-19 Link Here
14
import java.io.FilenameFilter;
14
import java.io.FilenameFilter;
15
import java.io.IOException;
15
import java.io.IOException;
16
import java.util.Hashtable;
16
import java.util.Hashtable;
17
import java.util.List;
17
18
18
import org.eclipse.jdt.core.compiler.CharOperation;
19
import org.eclipse.jdt.core.compiler.CharOperation;
19
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
20
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
Lines 79-84 Link Here
79
			return true;
80
			return true;
80
	return false;
81
	return false;
81
}
82
}
83
public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) {
84
	return null;
85
}
82
public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName) {
86
public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName) {
83
	return findClass(typeName, qualifiedPackageName, qualifiedBinaryFileName, false);
87
	return findClass(typeName, qualifiedPackageName, qualifiedBinaryFileName, false);
84
}
88
}
(-)src/org/eclipse/jdt/core/tests/compiler/regression/AbstractRegressionTest.java (-1 / +2 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2007 IBM Corporation and others.
2
 * Copyright (c) 2000, 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 104-109 Link Here
104
	protected static IPath jdkRootDirPath;
104
	protected static IPath jdkRootDirPath;
105
105
106
	public static final String OUTPUT_DIR = Util.getOutputDirectory() + File.separator + "regression";
106
	public static final String OUTPUT_DIR = Util.getOutputDirectory() + File.separator + "regression";
107
	public static final String LIB_DIR = Util.getOutputDirectory() + File.separator + "lib";
107
108
108
	public final static String PACKAGE_INFO_NAME = new String(TypeConstants.PACKAGE_INFO_NAME);
109
	public final static String PACKAGE_INFO_NAME = new String(TypeConstants.PACKAGE_INFO_NAME);
109
	
110
	
(-)src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java (-63 / +1589 lines)
Lines 34-39 Link Here
34
34
35
public class BatchCompilerTest extends AbstractRegressionTest {
35
public class BatchCompilerTest extends AbstractRegressionTest {
36
	public static final String OUTPUT_DIR_PLACEHOLDER = "---OUTPUT_DIR_PLACEHOLDER---";
36
	public static final String OUTPUT_DIR_PLACEHOLDER = "---OUTPUT_DIR_PLACEHOLDER---";
37
	public static final String LIB_DIR_PLACEHOLDER = "---LIB_DIR_PLACEHOLDER---";
37
	static final String JRE_HOME_DIR = Util.getJREDirectory();
38
	static final String JRE_HOME_DIR = Util.getJREDirectory();
38
	private static final Main MAIN = new Main(null, null, false);
39
	private static final Main MAIN = new Main(null, null, false);
39
40
Lines 55-60 Link Here
55
	return buildUniqueComplianceTestSuite(testClass(), ClassFileConstants.JDK1_5);
56
	return buildUniqueComplianceTestSuite(testClass(), ClassFileConstants.JDK1_5);
56
}
57
}
57
58
59
private static boolean CASCADED_JARS_CREATED;
60
private void createCascadedJars() {
61
	if (!CASCADED_JARS_CREATED) {
62
		File libDir = new File(LIB_DIR);
63
		Util.delete(libDir); // make sure we recycle the libs
64
 		libDir.mkdirs();
65
		try {
66
			Util.createJar(
67
				new String[] {
68
					"p/A.java",
69
					"package p;\n" +
70
					"public class A {\n" +
71
					"}",
72
				},
73
				new String[] {
74
					"META-INF/MANIFEST.MF",
75
					"Manifest-Version: 1.0\n" +
76
					"Created-By: Eclipse JDT Test Harness\n" +
77
					"Class-Path: lib2.jar\n",
78
					"p/S1.java",
79
					"package p;\n" +
80
					"public class S1 {\n" +
81
					"}",
82
				},
83
				LIB_DIR + "/lib1.jar",
84
				JavaCore.VERSION_1_4);
85
			Util.createJar(
86
				new String[] {
87
					"p/B.java",
88
					"package p;\n" +
89
					"public class B {\n" +
90
					"}",
91
					"p/R.java",
92
					"package p;\n" +
93
					"public class R {\n" +
94
					"  public static final int R2 = 2;\n" +
95
					"}",
96
				},
97
				new String[] {
98
					"p/S2.java",
99
					"package p;\n" +
100
					"public class S2 {\n" +
101
					"}",
102
				},
103
				LIB_DIR + "/lib2.jar",
104
				JavaCore.VERSION_1_4);
105
			Util.createJar(
106
				new String[] {
107
					"p/C.java",
108
					"package p;\n" +
109
					"public class C {\n" +
110
					"}",
111
					"p/R.java",
112
					"package p;\n" +
113
					"public class R {\n" +
114
					"  public static final int R3 = 3;\n" +
115
					"}",
116
				},
117
				new String[] {
118
					"META-INF/MANIFEST.MF",
119
					"Manifest-Version: 1.0\n" +
120
					"Created-By: Eclipse JDT Test Harness\n" +
121
					"Class-Path: lib4.jar\n",
122
				},
123
				LIB_DIR + "/lib3.jar",
124
				JavaCore.VERSION_1_4);
125
			Util.createJar(
126
				new String[] {
127
					"p/D.java",
128
					"package p;\n" +
129
					"public class D {\n" +
130
					"}",
131
				},
132
				new String[] {
133
					"META-INF/MANIFEST.MF",
134
					"Manifest-Version: 1.0\n" +
135
					"Created-By: Eclipse JDT Test Harness\n" +
136
					"Class-Path: lib1.jar lib3.jar\n",
137
				},
138
				LIB_DIR + "/lib4.jar",
139
				JavaCore.VERSION_1_4);
140
			Util.createJar(
141
				new String[] {
142
					"p/C.java",
143
					"package p;\n" +
144
					"public class C {\n" +
145
					"}",
146
					"p/R.java",
147
					"package p;\n" +
148
					"public class R {\n" +
149
					"  public static final int R3 = 3;\n" +
150
					"}",
151
				},
152
				new String[] {
153
					"META-INF/MANIFEST.MF",
154
					"Manifest-Version: 1.0\n" +
155
					"Created-By: Eclipse JDT Test Harness\n" +
156
					"Class-Path: s/lib6.jar\n",
157
				},
158
				LIB_DIR + "/lib5.jar",
159
				JavaCore.VERSION_1_4);
160
			new File(LIB_DIR + "/s").mkdir();
161
			Util.createJar(
162
				new String[] {
163
					"p/D.java",
164
					"package p;\n" +
165
					"public class D {\n" +
166
					"}",
167
				},
168
				new String[] {
169
					"META-INF/MANIFEST.MF",
170
					"Manifest-Version: 1.0\n" +
171
					"Created-By: Eclipse JDT Test Harness\n" +
172
					"Class-Path: ../lib7.jar\n",
173
				},
174
				LIB_DIR + "/s/lib6.jar",
175
				JavaCore.VERSION_1_4);
176
			Util.createJar(
177
				new String[] {
178
					"p/A.java",
179
					"package p;\n" +
180
					"public class A {\n" +
181
					"}",
182
				},
183
				new String[] {
184
					"META-INF/MANIFEST.MF",
185
					"Manifest-Version: 1.0\n" +
186
					"Created-By: Eclipse JDT Test Harness\n" +
187
					"Class-Path: lib2.jar\n",
188
				},
189
				LIB_DIR + "/lib7.jar",
190
				JavaCore.VERSION_1_4);
191
			Util.createJar(
192
				new String[] {
193
					"p/F.java",
194
					"package p;\n" +
195
					"public class F {\n" +
196
					"}",
197
				},
198
				new String[] {
199
					"META-INF/MANIFEST.MF",
200
					"Manifest-Version: 1.0\n" +
201
					"Created-By: Eclipse JDT Test Harness\n" +
202
					"Class-Path: " + LIB_DIR + "/lib3.jar lib1.jar\n",
203
				},
204
				LIB_DIR + "/lib8.jar",
205
				JavaCore.VERSION_1_4);
206
			Util.createJar(
207
				new String[] {
208
					"p/G.java",
209
					"package p;\n" +
210
					"public class G {\n" +
211
					"}",
212
				},
213
				new String[] {
214
					"META-INF/MANIFEST.MF",
215
					"Manifest-Version: 1.0\n" +
216
					"Created-By: Eclipse JDT Test Harness\n" +
217
					"Class-Path: lib1.jar\n" +
218
					"Class-Path: lib3.jar\n",
219
				},
220
				LIB_DIR + "/lib9.jar",
221
				JavaCore.VERSION_1_4);
222
			Util.createJar(
223
				new String[] {
224
					"p/A.java",
225
					"package p;\n" +
226
					"public class A {\n" +
227
					"}",
228
				},
229
				// spoiled jar: MANIFEST.MF is a directory
230
				new String[] {
231
					"META-INF/MANIFEST.MF/MANIFEST.MF",
232
					"Manifest-Version: 1.0\n" +
233
					"Created-By: Eclipse JDT Test Harness\n" +
234
					"Class-Path: lib2.jar\n",
235
				},
236
				LIB_DIR + "/lib10.jar",
237
				JavaCore.VERSION_1_4);
238
			Util.createJar(
239
				new String[] {
240
					"p/A.java",
241
					"package p;\n" +
242
					"public class A {\n" +
243
					"}",
244
				},
245
				new String[] {
246
					"META-INF/MANIFEST.MF",
247
					"Manifest-Version: 1.0\n" +
248
					"Created-By: Eclipse JDT Test Harness\n" +
249
					"Class-Path:\n",
250
				},
251
				LIB_DIR + "/lib11.jar",
252
				JavaCore.VERSION_1_4);
253
			Util.createJar(
254
				null,
255
				new String[] {
256
					"META-INF/MANIFEST.MF",
257
					"Manifest-Version: 1.0\n" +
258
					"Created-By: Eclipse JDT Test Harness\n" +
259
					"Class-Path:lib1.jar\n", // missing space
260
				},
261
				LIB_DIR + "/lib12.jar",
262
				JavaCore.VERSION_1_4);
263
			Util.createJar(
264
				null,
265
				new String[] {
266
					"META-INF/MANIFEST.MF",
267
					"Manifest-Version: 1.0\n" +
268
					"Created-By: Eclipse JDT Test Harness\n" +
269
					"Class-Path:lib1.jar lib1.jar\n", // missing space
270
				},
271
				LIB_DIR + "/lib13.jar",
272
				JavaCore.VERSION_1_4);			
273
			Util.createJar(
274
				null,
275
				new String[] {
276
					"META-INF/MANIFEST.MF",
277
					"Manifest-Version: 1.0\n" +
278
					"Created-By: Eclipse JDT Test Harness\n" +
279
					" Class-Path: lib1.jar\n", // extra space at line start
280
				},
281
				LIB_DIR + "/lib14.jar",
282
				JavaCore.VERSION_1_4);			
283
			Util.createJar(
284
				null,
285
				new String[] {
286
					"META-INF/MANIFEST.MF",
287
					"Manifest-Version: 1.0\n" +
288
					"Created-By: Eclipse JDT Test Harness\n" +
289
					"Class-Path: lib1.jar", // missing newline at end
290
				},
291
				LIB_DIR + "/lib15.jar",
292
				JavaCore.VERSION_1_4);			
293
			Util.createJar(
294
				new String[] {
295
					"p/A.java",
296
					"package p;\n" +
297
					"public class A {\n" +
298
					"}",
299
				},
300
				new String[] {
301
					"META-INF/MANIFEST.MF",
302
					"Manifest-Version: 1.0\n" +
303
					"Created-By: Eclipse JDT Test Harness\n" +
304
					"Class-Path: \n" +
305
					" lib2.jar\n",
306
					"p/S1.java",
307
					"package p;\n" +
308
					"public class S1 {\n" +
309
					"}",
310
				},
311
				LIB_DIR + "/lib16.jar",
312
				JavaCore.VERSION_1_4);
313
			new File(LIB_DIR + "/dir").mkdir();
314
			Util.createJar(
315
				new String[] {
316
					"p/A.java",
317
					"package p;\n" +
318
					"public class A {\n" +
319
					"}",
320
				},
321
				new String[] {
322
					"META-INF/MANIFEST.MF",
323
					"Manifest-Version: 1.0\n" +
324
					"Created-By: Eclipse JDT Test Harness\n" +
325
					"Class-Path: ../lib2.jar\n",
326
				},
327
				LIB_DIR + "/dir/lib17.jar",
328
				JavaCore.VERSION_1_4);
329
			CASCADED_JARS_CREATED = true;
330
		} catch (IOException e) {
331
			// ignore
332
		}
333
	}
334
}
335
58
private String getLibraryClassesAsQuotedString() {
336
private String getLibraryClassesAsQuotedString() {
59
	String[] paths = Util.getJavaClassLibs();
337
	String[] paths = Util.getJavaClassLibs();
60
	StringBuffer buffer = new StringBuffer();
338
	StringBuffer buffer = new StringBuffer();
Lines 159-165 Link Here
159
	 *            pass true to get the output directory flushed before the test
437
	 *            pass true to get the output directory flushed before the test
160
	 *            runs
438
	 *            runs
161
	 */
439
	 */
162
	private void runTest(boolean shouldCompileOK, String[] testFiles, String commandLine,
440
	private void runTest(
441
			boolean shouldCompileOK, 
442
			String[] testFiles, 
443
			String commandLine,
163
			String expectedOutOutputString,
444
			String expectedOutOutputString,
164
			String expectedErrOutputString,
445
			String expectedErrOutputString,
165
			boolean shouldFlushOutputDirectory) {
446
			boolean shouldFlushOutputDirectory) {
Lines 285-290 Link Here
285
					errOutputString);
566
					errOutputString);
286
		}
567
		}
287
	}
568
	}
569
private void runTest(
570
		boolean shouldCompileOK, 
571
		String[] testFiles, 
572
		String commandLine,
573
		Matcher outOutputStringMatcher,
574
		Matcher errOutputStringMatcher,
575
		boolean shouldFlushOutputDirectory) {
576
	File outputDirectory = new File(OUTPUT_DIR);
577
	if (shouldFlushOutputDirectory)
578
		Util.flushDirectoryContent(outputDirectory);
579
	try {
580
		if (!outputDirectory.isDirectory()) {
581
			outputDirectory.mkdirs();
582
		}
583
		PrintWriter sourceFileWriter;
584
		for (int i = 0; i < testFiles.length; i += 2) {
585
			String fileName = OUTPUT_DIR + File.separator + testFiles[i];
586
			File file = new File(fileName), innerOutputDirectory = file
587
					.getParentFile();
588
			if (!innerOutputDirectory.isDirectory()) {
589
				innerOutputDirectory.mkdirs();
590
			}
591
			sourceFileWriter = new PrintWriter(new FileOutputStream(file));
592
			sourceFileWriter.write(testFiles[i + 1]);
593
			sourceFileWriter.close();
594
		}
595
	} catch (FileNotFoundException e) {
596
		e.printStackTrace();
597
		throw new RuntimeException(e);
598
	}
599
	String printerWritersNameRoot = OUTPUT_DIR + File.separator + testName();
600
	String outFileName = printerWritersNameRoot + "out.txt",
601
		   errFileName = printerWritersNameRoot + "err.txt";
602
	Main batchCompiler;
603
	PrintWriter out = null;
604
	PrintWriter err = null;
605
	boolean compileOK;
606
	try {
607
		try {
608
			out = new PrintWriter(new FileOutputStream(outFileName));
609
			err = new PrintWriter(new FileOutputStream(errFileName));
610
			batchCompiler = new Main(out, err, false);
611
		} catch (FileNotFoundException e) {
612
			System.out.println(getClass().getName() + '#' + getName());
613
			e.printStackTrace();
614
			throw new RuntimeException(e);
615
		}
616
		try {
617
			final String[] tokenizeCommandLine = Main.tokenize(commandLine);
618
			compileOK = batchCompiler.compile(tokenizeCommandLine);
619
		} catch (RuntimeException e) {
620
			compileOK = false;
621
			System.out.println(getClass().getName() + '#' + getName());
622
			e.printStackTrace();
623
			throw e;
624
		}
625
	} finally {
626
		if (out != null)
627
			out.close();
628
		if (err != null)
629
			err.close();
630
	}
631
	String outOutputString = Util.fileContent(outFileName),
632
	       errOutputString = Util.fileContent(errFileName);
633
	boolean compareOK = false, outCompareOK = false, errCompareOK = false;
634
	String expectedErrOutputString = null, expectedOutOutputString = null;
635
	if (compileOK == shouldCompileOK) {
636
		if (outOutputStringMatcher == null) {
637
			outCompareOK = true;
638
		} else {
639
			outCompareOK = outOutputStringMatcher.match(outOutputString);
640
			expectedOutOutputString = outOutputStringMatcher.expected();
641
		}
642
		if (errOutputStringMatcher == null) {
643
			errCompareOK = true;
644
		} else {
645
			errCompareOK = errOutputStringMatcher.match(errOutputString);
646
			expectedErrOutputString = errOutputStringMatcher.expected();
647
		}
648
		compareOK = outCompareOK && errCompareOK;
649
	}
650
	if (compileOK != shouldCompileOK || !compareOK) {
651
		System.out.println(getClass().getName() + '#' + getName());
652
		for (int i = 0; i < testFiles.length; i += 2) {
653
			System.out.print(testFiles[i]);
654
			System.out.println(" [");
655
			System.out.println(testFiles[i + 1]);
656
			System.out.println("]");
657
		}
658
	}
659
	if (compileOK != shouldCompileOK)
660
		System.out.println(errOutputString);
661
	if (compileOK == shouldCompileOK && !compareOK) {
662
		System.out.println(
663
				    "------------ [START OUT] ------------\n"
664
				+   "------------- Expected: -------------\n"
665
				+ expectedOutOutputString
666
				+ "\n------------- but was:  -------------\n"
667
				+ outOutputString
668
				+ "\n--------- (cut and paste:) ----------\n"
669
				+ Util.displayString(outputDirNormalizer
670
						.normalized(outOutputString))
671
				+ "\n------------- [END OUT] -------------\n"
672
				+   "------------ [START ERR] ------------\n"
673
				+   "------------- Expected: -------------\n"
674
				+ expectedErrOutputString
675
				+ "\n------------- but was:  -------------\n"
676
				+ errOutputString
677
				+ "\n--------- (cut and paste:) ----------\n"
678
				+ Util.displayString(outputDirNormalizer
679
						.normalized(errOutputString))
680
				+ "\n------------- [END ERR] -------------\n");
681
	}
682
	if (shouldCompileOK)
683
		assertTrue("Unexpected problems: " + errOutputString, compileOK);
684
	else
685
		assertTrue("Unexpected success: " + errOutputString, !compileOK);
686
	if (!outCompareOK) {
687
		// calling assertEquals to benefit from the comparison UI
688
		// (need appropriate exception)
689
		assertEquals(
690
				"Unexpected standard output for invocation with arguments ["
691
					+ commandLine + "]",
692
				expectedOutOutputString,
693
				outOutputString);
694
	}
695
	if (!errCompareOK) {
696
		assertEquals(
697
				"Unexpected error output for invocation with arguments ["
698
					+ commandLine + "]",
699
				expectedErrOutputString,
700
				errOutputString);
701
	}
702
}	
288
private void runClasspathTest(String classpathInput, String[] expectedClasspathEntries,
703
private void runClasspathTest(String classpathInput, String[] expectedClasspathEntries,
289
		String expectedError) {
704
		String expectedError) {
290
	File outputDirectory = new File(OUTPUT_DIR);
705
	File outputDirectory = new File(OUTPUT_DIR);
Lines 362-367 Link Here
362
	}
777
	}
363
}
778
}
364
779
780
static abstract class Matcher {
781
	abstract boolean match(String effective);
782
	abstract String expected(); // for use in JUnit comparison framework
783
}
784
static class StringMatcher extends Matcher {
785
	private String expected;
786
	private Normalizer normalizer;
787
	StringMatcher(String expected, Normalizer normalizer) {
788
		this.expected = expected;
789
		this.normalizer = normalizer;
790
	}
791
	boolean match(String effective) {
792
		if (this.expected == null) {
793
			return effective == null;
794
		}
795
		if (this.normalizer == null) {
796
			return this.expected.equals(effective);
797
		}
798
		return this.expected.equals(this.normalizer.normalized(effective));
799
	}
800
	String expected() {
801
		return this.expected;
802
	}
803
}
804
static class SubstringMatcher extends Matcher {
805
	private String substring;
806
	SubstringMatcher(String substring) {
807
		this.substring = substring;
808
	}
809
	boolean match(String effective) {
810
		effective = outputDirNormalizer.normalized(effective);
811
		return effective.indexOf(this.substring) != -1;
812
	}
813
	String expected() {
814
		return "*" + this.substring + "*";
815
	}
816
}
817
static final Matcher EMPTY_STRING_MATCHER = new Matcher() {
818
	String expected() {
819
		return org.eclipse.jdt.internal.compiler.util.Util.EMPTY_STRING;
820
	}
821
	boolean match(String effective) {
822
		return effective != null && effective.length() == 0;
823
	}
824
};
825
static final Matcher ONE_FILE_GENERATED_MATCHER = new SubstringMatcher("[1 .class file generated]");
826
static final Matcher TWO_FILES_GENERATED_MATCHER = new SubstringMatcher("[2 .class files generated]");
365
	/**
827
	/**
366
	 * Abstract normalizer for output comparison. This class merely embodies a
828
	 * Abstract normalizer for output comparison. This class merely embodies a
367
	 * chain of responsibility, plus the signature of the method of interest
829
	 * chain of responsibility, plus the signature of the method of interest
Lines 562-578 Link Here
562
	 * OUTPUT_DIR_PLACEHOLDER and changes file separator to / if the
1024
	 * OUTPUT_DIR_PLACEHOLDER and changes file separator to / if the
563
	 * platform file separator is different from /.
1025
	 * platform file separator is different from /.
564
	 */
1026
	 */
565
	private static Normalizer outputDirNormalizer;
1027
	static Normalizer outputDirNormalizer;
566
	static {
1028
	static {
567
		if (File.separatorChar == '/') {
1029
		if (File.separatorChar == '/') {
568
			outputDirNormalizer = new StringNormalizer(
1030
			outputDirNormalizer = 
569
					null, OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER);
1031
				new StringNormalizer(
1032
					new StringNormalizer(
1033
						null, OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER),
1034
					LIB_DIR, LIB_DIR_PLACEHOLDER);
570
		}
1035
		}
571
		else {
1036
		else {
572
			outputDirNormalizer = new StringNormalizer(
1037
			outputDirNormalizer = 
1038
				new StringNormalizer(
573
					new StringNormalizer(
1039
					new StringNormalizer(
1040
						new StringNormalizer(
574
							null, File.separator, "/"),
1041
							null, File.separator, "/"),
575
					OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER);
1042
						OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER),
1043
					LIB_DIR, LIB_DIR_PLACEHOLDER);
576
		}
1044
		}
577
	}
1045
	}
578
1046
Lines 1510-1591 Link Here
1510
	        "incorrect classpath: dummmy_dir\n",
1978
	        "incorrect classpath: dummmy_dir\n",
1511
	        true);
1979
	        true);
1512
	}
1980
	}
1513
// we tolerate inexisting jars on the classpath
1981
// we tolerate inexisting jars on the classpath, and we don't even warn about
1514
// TODO (maxime) check and document
1982
// them (javac does the same as us)
1515
public void _test017b(){
1983
public void test017b(){
1516
	this.runConformTest(
1984
	this.runTest(
1985
		true,
1517
		new String[] {
1986
		new String[] {
1518
				"X.java",
1987
			"X.java",
1519
				"/** */\n" +
1988
			"/** */\n" +
1520
				"public class X {\n" +
1989
			"public class X {\n" +
1521
				"	OK1 ok1;\n" +
1990
			"	OK1 ok1;\n" +
1522
				"}",
1991
			"}",
1523
				"OK1.java",
1992
			"OK1.java",
1524
				"/** */\n" +
1993
			"/** */\n" +
1525
				"public class OK1 {\n" +
1994
			"public class OK1 {\n" +
1526
				"	// empty\n" +
1995
			"	// empty\n" +
1527
				"}"
1996
			"}"
1528
		},
1997
		},
1529
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
1998
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
1530
        + " -1.5 -g -preserveAllLocals"
1999
        + " -1.5 -g -preserveAllLocals"
1531
        + " -cp dummy.jar" + File.pathSeparator + File.pathSeparator + "\"" + OUTPUT_DIR + "\""
2000
        + " -cp dummy.jar" + File.pathSeparator + File.pathSeparator + "\"" + OUTPUT_DIR + "\""
1532
        + " -verbose -proceedOnError -referenceInfo"
2001
        + " -verbose -proceedOnError -referenceInfo"
1533
        + " -d \"" + OUTPUT_DIR + "\"",
2002
        + " -d \"" + OUTPUT_DIR + "\"",
1534
        "[2 .class files generated]\n",
2003
        TWO_FILES_GENERATED_MATCHER,
1535
        "incorrect classpath: dummmy.jar\n",
2004
        EMPTY_STRING_MATCHER,
1536
        true);
2005
        true);
1537
}
2006
}
1538
// we tolerate empty classpath entries
2007
// we tolerate empty classpath entries, and we don't even warn about
1539
// TODO (maxime) check and document
2008
// them (javac does the same as us)
1540
public void _test017c(){
2009
public void test017c(){
1541
	this.runConformTest(
2010
	this.runTest(
2011
		true,
1542
		new String[] {
2012
		new String[] {
1543
				"X.java",
2013
			"X.java",
1544
				"/** */\n" +
2014
			"/** */\n" +
1545
				"public class X {\n" +
2015
			"public class X {\n" +
1546
				"	OK1 ok1;\n" +
2016
			"	OK1 ok1;\n" +
1547
				"}",
2017
			"}",
1548
				"OK1.java",
2018
			"OK1.java",
1549
				"/** */\n" +
2019
			"/** */\n" +
1550
				"public class OK1 {\n" +
2020
			"public class OK1 {\n" +
1551
				"	// empty\n" +
2021
			"	// empty\n" +
1552
				"}"
2022
			"}"
1553
		},
2023
		},
1554
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
2024
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
1555
        + " -1.5 -g -preserveAllLocals"
2025
        + " -1.5 -g -preserveAllLocals"
1556
        + " -cp " + File.pathSeparator + File.pathSeparator + "\"" + OUTPUT_DIR + "\""
2026
        + " -cp " + File.pathSeparator + File.pathSeparator + "\"" + OUTPUT_DIR + "\""
1557
        + " -verbose -proceedOnError -referenceInfo"
2027
        + " -verbose -proceedOnError -referenceInfo"
1558
        + " -d \"" + OUTPUT_DIR + "\"",
2028
        + " -d \"" + OUTPUT_DIR + "\"",
1559
        "[2 .class files generated]\n",
2029
        TWO_FILES_GENERATED_MATCHER,
1560
        "incorrect classpath\n",
2030
        EMPTY_STRING_MATCHER,
1561
        true);
2031
        true);
1562
}
2032
}
1563
// command line - unusual classpath (empty, but using current directory, still OK provided
2033
// command line - unusual classpath (empty)
1564
//	that we execute from the appropriate directory); since there is no notion
2034
// ok provided we explicit the sourcepath
1565
// of current directory for this tests suite, the test is not executed
2035
public void test018a(){
1566
// TODO (maxime) enforce working directory
2036
	String currentWorkingDirectoryPath = System.getProperty("user.dir");
1567
	public void _test018(){
2037
	if (currentWorkingDirectoryPath == null) {
1568
		this.runConformTest(
2038
		System.err.println("BatchCompilerTest#18a could not access the current working directory " + currentWorkingDirectoryPath);
1569
			new String[] {
2039
	} else if (!new File(currentWorkingDirectoryPath).isDirectory()) {
1570
					"X.java",
2040
		System.err.println("BatchCompilerTest#18a current working directory is not a directory " + currentWorkingDirectoryPath);
1571
					"/** */\n" +
2041
	} else {
1572
					"public class X {\n" +
2042
		String xPath = currentWorkingDirectoryPath + File.separator + "X.java";
1573
					"	OK1 ok1;\n" +
2043
		String ok1Path = currentWorkingDirectoryPath + File.separator + "OK1.java";
1574
					"}",
2044
		PrintWriter sourceFileWriter;
1575
					"OK1.java",
2045
		try {
1576
					"/** */\n" +
2046
			File file = new File(xPath);
1577
					"public class OK1 {\n" +
2047
			sourceFileWriter = new PrintWriter(new FileOutputStream(file));
1578
					"	// empty\n" +
2048
			sourceFileWriter.write(
1579
					"}"
2049
				"/** */\n" +
1580
			},
2050
				"public class X {\n" +
1581
	        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
2051
				"	OK1 ok1;\n" +
1582
	        + " -1.5 -g -preserveAllLocals"
2052
				"}");
1583
	        + " -verbose -proceedOnError -referenceInfo"
2053
			sourceFileWriter.close();
1584
	        + " -d \"" + OUTPUT_DIR + "\"",
2054
			file = new File(ok1Path);
1585
	        "[2 .class files generated]\n",
2055
			sourceFileWriter = new PrintWriter(new FileOutputStream(file));
1586
	        "",
2056
			sourceFileWriter.write(
1587
	        true);
2057
				"/** */\n" +
2058
				"public class OK1 {\n" +
2059
				"	// empty\n" +
2060
				"}");
2061
			sourceFileWriter.close();
2062
			this.runTest(
2063
				true,
2064
				new String[] {
2065
					"dummy.java", // enforce output directory creation
2066
					""
2067
				},
2068
		        "X.java"
2069
		        + " -1.5 -g -preserveAllLocals"
2070
		        + " -verbose -proceedOnError"
2071
		        + " -sourcepath ."
2072
		        + " -d \"" + OUTPUT_DIR + "\"",
2073
		        TWO_FILES_GENERATED_MATCHER,
2074
		        EMPTY_STRING_MATCHER,
2075
		        false);
2076
		} catch (FileNotFoundException e) {
2077
			System.err.println("BatchCompilerTest#18a could not write to current working directory " + currentWorkingDirectoryPath);
2078
		} finally {
2079
			new File(xPath).delete();
2080
			new File(ok1Path).delete();
2081
		}
2082
	}
2083
}
2084
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=214725
2085
// empty sourcepath works with javac but not with ecj
2086
public void _test018b(){
2087
	String currentWorkingDirectoryPath = System.getProperty("user.dir");
2088
	if (currentWorkingDirectoryPath == null) {
2089
		System.err.println("BatchCompilerTest#18b could not access the current working directory " + currentWorkingDirectoryPath);
2090
	} else if (!new File(currentWorkingDirectoryPath).isDirectory()) {
2091
		System.err.println("BatchCompilerTest#18b current working directory is not a directory " + currentWorkingDirectoryPath);
2092
	} else {
2093
		String xPath = currentWorkingDirectoryPath + File.separator + "X.java";
2094
		String ok1Path = currentWorkingDirectoryPath + File.separator + "OK1.java";
2095
		PrintWriter sourceFileWriter;
2096
		try {
2097
			File file = new File(xPath);
2098
			sourceFileWriter = new PrintWriter(new FileOutputStream(file));
2099
			sourceFileWriter.write(
2100
				"/** */\n" +
2101
				"public class X {\n" +
2102
				"	OK1 ok1;\n" +
2103
				"}");
2104
			sourceFileWriter.close();
2105
			file = new File(ok1Path);
2106
			sourceFileWriter = new PrintWriter(new FileOutputStream(file));
2107
			sourceFileWriter.write(
2108
				"/** */\n" +
2109
				"public class OK1 {\n" +
2110
				"	// empty\n" +
2111
				"}");
2112
			sourceFileWriter.close();
2113
			this.runTest(
2114
				true,
2115
				new String[] {
2116
					"dummy.java", // enforce output directory creation
2117
					""
2118
				},
2119
		        "X.java"
2120
		        + " -1.5 -g -preserveAllLocals"
2121
		        + " -verbose -proceedOnError"
2122
		        + " -d \"" + OUTPUT_DIR + "\"",
2123
		        TWO_FILES_GENERATED_MATCHER,
2124
		        EMPTY_STRING_MATCHER,
2125
		        false);
2126
		} catch (FileNotFoundException e) {
2127
			System.err.println("BatchCompilerTest#18b could not write to current working directory " + currentWorkingDirectoryPath);
2128
		} finally {
2129
			new File(xPath).delete();
2130
			new File(ok1Path).delete();
2131
		}
1588
	}
2132
	}
2133
}
1589
public void test019(){
2134
public void test019(){
1590
		this.runNegativeTest(
2135
		this.runNegativeTest(
1591
			new String[] {
2136
			new String[] {
Lines 8050-8055 Link Here
8050
			new ClasspathJar(new File("relative.jar"), true, null, null).
8595
			new ClasspathJar(new File("relative.jar"), true, null, null).
8051
			normalizedPath()) == -1);
8596
			normalizedPath()) == -1);
8052
}
8597
}
8598
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8599
// basic link: a jar only referenced in the manifest of the first one is found
8600
public void test216_jar_ref_in_jar(){
8601
	createCascadedJars();
8602
	this.runConformTest(
8603
		new String[] {
8604
			"src/p/X.java",
8605
			"package p;\n" +
8606
			"/** */\n" +
8607
			"public class X {\n" +
8608
			"  A a;\n" +
8609
			"  B b;\n" +
8610
			"}",
8611
		},
8612
        "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8613
		+ " -cp \"" + LIB_DIR + File.separator + "lib1.jar\""
8614
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8615
        + " -1.5 -g -preserveAllLocals"
8616
        + " -proceedOnError -referenceInfo"
8617
        + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8618
        "",
8619
        "",
8620
        true);
8621
}
8622
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8623
// may want a specific option to mimick javac 1.4
8624
// caveat: javac 1.5 with -source 1.4 and -target 1.4 still uses the links
8625
public void _test216_jar_ref_in_jar_suppress(){
8626
	createCascadedJars();
8627
	this.runNegativeTest(
8628
		new String[] {
8629
			"src/p/X.java",
8630
			"package p;\n" +
8631
			"/** */\n" +
8632
			"public class X {\n" +
8633
			"  A a;\n" +
8634
			"  B b;\n" +
8635
			"}",
8636
		},
8637
     "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8638
		+ " -cp \"" + LIB_DIR + File.separator + "lib1.jar\""
8639
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8640
     + " -ignoreJarClassPath -g -preserveAllLocals"
8641
     + " -proceedOnError -referenceInfo"
8642
     + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8643
     "",
8644
     "----------\n" + 
8645
     "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 5)\n" + 
8646
     "	B b;\n" + 
8647
     "	^\n" + 
8648
     "B cannot be resolved to a type\n" + 
8649
     "----------\n" + 
8650
     "1 problem (1 error)",
8651
     true);
8652
}
8653
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8654
// links are followed recursively, eliminating dupes
8655
public void test217_jar_ref_in_jar(){
8656
	createCascadedJars();
8657
	this.runConformTest(
8658
		new String[] {
8659
			"src/p/X.java",
8660
			"package p;\n" +
8661
			"/** */\n" +
8662
			"public class X {\n" +
8663
			"  A a;\n" +
8664
			"  B b;\n" +
8665
			"  C c;\n" +
8666
			"  D d;\n" +
8667
			"}",
8668
		},
8669
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8670
		+ " -cp \"" + LIB_DIR + File.separator + "lib3.jar\""
8671
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8672
		+ " -1.5 -g -preserveAllLocals"
8673
		+ " -proceedOnError -referenceInfo"
8674
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8675
		"",
8676
		"",
8677
		true);
8678
}
8679
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8680
// at first level, this is depth first, masking tailing libs
8681
public void test218_jar_ref_in_jar(){
8682
	createCascadedJars();
8683
	this.runNegativeTest(
8684
		new String[] {
8685
			"src/p/X.java",
8686
			"package p;\n" +
8687
			"/** */\n" +
8688
			"public class X {\n" +
8689
			"  int i = R.R2;\n" +
8690
			"  int j = R.R3;\n" +
8691
			"}",
8692
		},
8693
	  "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8694
			+ " -cp \"" + LIB_DIR + File.separator + "lib1.jar\""
8695
			+ " -cp \"" + LIB_DIR + File.separator + "lib3.jar\""
8696
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8697
	  + " -1.5 -g -preserveAllLocals"
8698
	  + " -proceedOnError -referenceInfo"
8699
	  + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8700
	  "",
8701
	  "----------\n" + 
8702
	  "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 5)\n" + 
8703
	  "	int j = R.R3;\n" + 
8704
	  "	        ^^^^\n" + 
8705
	  "R.R3 cannot be resolved\n" + 
8706
	  "----------\n" + 
8707
	  "1 problem (1 error)",
8708
	  true);
8709
}
8710
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8711
// using only links, we adopt a depth first algorithm
8712
public void test219_jar_ref_in_jar(){
8713
	createCascadedJars();
8714
	this.runNegativeTest(
8715
		new String[] {
8716
			"src/p/X.java",
8717
			"package p;\n" +
8718
			"/** */\n" +
8719
			"public class X {\n" +
8720
			"  int i = R.R2;\n" +
8721
			"  int j = R.R3;\n" +
8722
			"}",
8723
		},
8724
	  "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8725
			+ " -cp \"" + LIB_DIR + File.separator + "lib4.jar\""
8726
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8727
	  + " -1.5 -g -preserveAllLocals"
8728
	  + " -proceedOnError -referenceInfo"
8729
	  + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8730
	  "",
8731
	  "----------\n" + 
8732
	  "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 5)\n" + 
8733
	  "	int j = R.R3;\n" + 
8734
	  "	        ^^^^\n" + 
8735
	  "R.R3 cannot be resolved\n" + 
8736
	  "----------\n" + 
8737
	  "1 problem (1 error)",
8738
	  true);
8739
}
8740
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8741
// managing subdirectories and .. properly
8742
public void test220_jar_ref_in_jar(){
8743
	createCascadedJars();
8744
	this.runConformTest(
8745
		new String[] {
8746
			"src/p/X.java",
8747
			"package p;\n" +
8748
			"/** */\n" +
8749
			"public class X {\n" +
8750
			"  A a;\n" +
8751
			"  B b;\n" +
8752
			"  C c;\n" +
8753
			"  D d;\n" +
8754
			"}",
8755
		},
8756
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8757
		+ " -cp \"" + LIB_DIR + File.separator + "lib5.jar\""
8758
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8759
		+ " -1.5 -g -preserveAllLocals"
8760
		+ " -proceedOnError -referenceInfo"
8761
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8762
		"",
8763
		"",
8764
		true);
8765
}
8766
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8767
// variant: the second jar on a line is found as well
8768
public void test221_jar_ref_in_jar(){
8769
	createCascadedJars();
8770
	this.runConformTest(
8771
		new String[] {
8772
			"src/p/X.java",
8773
			"package p;\n" +
8774
			"/** */\n" +
8775
			"public class X {\n" +
8776
			"  C c;\n" +
8777
			"}",
8778
		},
8779
     "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8780
		+ " -cp \"" + LIB_DIR + File.separator + "lib4.jar\""
8781
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8782
     + " -1.5 -g -preserveAllLocals"
8783
     + " -proceedOnError -referenceInfo"
8784
     + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8785
     "",
8786
     "",
8787
     true);
8788
}
8789
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8790
// we eat up absolute links silently
8791
public void test222_jar_ref_in_jar(){
8792
	createCascadedJars();
8793
	this.runConformTest(
8794
		new String[] {
8795
			"src/p/X.java",
8796
			"package p;\n" +
8797
			"/** */\n" +
8798
			"public class X {\n" +
8799
			"  F f;\n" +
8800
			"}",
8801
		},
8802
	"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8803
			+ " -cp \"" + LIB_DIR + File.separator + "lib8.jar\""
8804
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8805
	+ " -1.5 -g -preserveAllLocals"
8806
	+ " -proceedOnError -referenceInfo"
8807
	+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8808
	"",
8809
	"",
8810
	true);
8811
}
8812
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8813
// absolute links do not mask following relative links
8814
public void test223_jar_ref_in_jar(){
8815
	createCascadedJars();
8816
	this.runConformTest(
8817
		new String[] {
8818
			"src/p/X.java",
8819
			"package p;\n" +
8820
			"/** */\n" +
8821
			"public class X {\n" +
8822
			"  A a;\n" +
8823
			"  F f;\n" +
8824
			"}",
8825
		},
8826
	"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8827
			+ " -cp \"" + LIB_DIR + File.separator + "lib8.jar\""
8828
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8829
	+ " -1.5 -g -preserveAllLocals"
8830
	+ " -proceedOnError -referenceInfo"
8831
	+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8832
	"",
8833
	"",
8834
	true);
8835
}
8836
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8837
// absolute links are not followed
8838
public void test224_jar_ref_in_jar(){
8839
	createCascadedJars();
8840
	this.runNegativeTest(
8841
		new String[] {
8842
			"src/p/X.java",
8843
			"package p;\n" +
8844
			"/** */\n" +
8845
			"public class X {\n" +
8846
			"  C c;\n" +
8847
			"}",
8848
		},
8849
	"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8850
			+ " -cp \"" + LIB_DIR + File.separator + "lib8.jar\""
8851
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8852
	+ " -1.5 -g -preserveAllLocals"
8853
	+ " -proceedOnError -referenceInfo"
8854
	+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8855
	"",
8856
	"----------\n" + 
8857
	"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
8858
	"	C c;\n" + 
8859
	"	^\n" + 
8860
	"C cannot be resolved to a type\n" + 
8861
	"----------\n" + 
8862
	"1 problem (1 error)",
8863
	true);
8864
}
8865
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8866
// we accept duplicate classpath lines in manifest and we follow the jars of the
8867
// second and following lines as well as the first line (emit a warning as javac does)
8868
public void test225_jar_ref_in_jar(){
8869
	createCascadedJars();
8870
	this.runConformTest(
8871
		new String[] {
8872
			"src/p/X.java",
8873
			"package p;\n" +
8874
			"/** */\n" +
8875
			"public class X {\n" +
8876
			"  A a;\n" +
8877
			"  G g;\n" +
8878
			"}",
8879
		},
8880
	"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8881
			+ " -cp \"" + LIB_DIR + File.separator + "lib9.jar\""
8882
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8883
	+ " -1.5 -g -preserveAllLocals"
8884
	+ " -proceedOnError -referenceInfo"
8885
	+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8886
	"",
8887
	"multiple Class-Path headers in manifest of jar file: ---LIB_DIR_PLACEHOLDER---/lib9.jar\n",
8888
	true);
8889
}
8890
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8891
// we accept duplicate classpath lines in manifest and we follow the jars of the
8892
// second and following lines as well as the first line (emit a warning as javac does)
8893
public void test226_jar_ref_in_jar(){
8894
	createCascadedJars();
8895
	this.runConformTest(
8896
		new String[] {
8897
			"src/p/X.java",
8898
			"package p;\n" +
8899
			"/** */\n" +
8900
			"public class X {\n" +
8901
			"  C c;\n" +
8902
			"  G g;\n" +
8903
			"}",
8904
		},
8905
	"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8906
			+ " -cp \"" + LIB_DIR + File.separator + "lib9.jar\""
8907
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8908
	+ " -1.5 -g -preserveAllLocals"
8909
	+ " -proceedOnError -referenceInfo"
8910
	+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8911
	"",
8912
	"multiple Class-Path headers in manifest of jar file: ---LIB_DIR_PLACEHOLDER---/lib9.jar\n",
8913
	true);
8914
}
8915
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8916
// bootclasspath does not get expanded with linked files
8917
public void test227_jar_ref_in_jar(){
8918
	createCascadedJars();
8919
	this.runNegativeTest(
8920
		new String[] {
8921
			"src/p/X.java",
8922
			"package p;\n" +
8923
			"/** */\n" +
8924
			"public class X {\n" +
8925
			"  A a;\n" +
8926
			"  B b;\n" +
8927
			"}",
8928
		},
8929
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8930
	  	+ " -bootclasspath " + getLibraryClassesAsQuotedString() 
8931
	  	+ File.pathSeparator + "\"" + LIB_DIR + File.separator + "lib1.jar\""
8932
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8933
		+ " -1.5 -g -preserveAllLocals"
8934
		+ " -proceedOnError -referenceInfo"
8935
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8936
		"",
8937
		"----------\n" + 
8938
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 5)\n" + 
8939
		"	B b;\n" + 
8940
		"	^\n" + 
8941
		"B cannot be resolved to a type\n" + 
8942
		"----------\n" + 
8943
		"1 problem (1 error)",
8944
		true);
8945
}
8946
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8947
// jar files reached indirectly bear the access rules of the entry that 
8948
// references them
8949
public void test228_jar_ref_in_jar(){
8950
	createCascadedJars();
8951
	this.runConformTest(
8952
		new String[] {
8953
			"src/p/X.java",
8954
			"package p;\n" +
8955
			"/** */\n" +
8956
			"public class X {\n" +
8957
			"  A a;\n" +
8958
			"}",
8959
		},
8960
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8961
		+ " -cp \"" + LIB_DIR + File.separator + "lib3.jar[~p/A]\""
8962
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8963
		+ " -1.5 -g -preserveAllLocals"
8964
		+ " -proceedOnError -referenceInfo"
8965
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8966
		"",
8967
		"----------\n" + 
8968
		"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
8969
		"	A a;\n" + 
8970
		"	^\n" + 
8971
		"Discouraged access: The type A is not accessible due to restriction on classpath entry ---LIB_DIR_PLACEHOLDER---/lib3.jar\n" + 
8972
		"----------\n" + 
8973
		"1 problem (1 warning)",
8974
		true);
8975
}
8976
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
8977
// jar files reached indirectly bear the access rules of the entry that 
8978
// references them - this hides the access rules of further instances of the
8979
// same jar on the classpath
8980
public void test229_jar_ref_in_jar(){
8981
	createCascadedJars();
8982
	this.runConformTest(
8983
		new String[] {
8984
			"src/p/X.java",
8985
			"package p;\n" +
8986
			"/** */\n" +
8987
			"public class X {\n" +
8988
			"  A a;\n" +
8989
			"}",
8990
		},
8991
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
8992
		+ " -cp \"" + LIB_DIR + File.separator + "lib3.jar[~p/A]\""
8993
		+ " -cp \"" + LIB_DIR + File.separator + "lib1.jar[-p/A]\""
8994
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
8995
		+ " -1.5 -g -preserveAllLocals"
8996
		+ " -proceedOnError -referenceInfo"
8997
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
8998
		"",
8999
		"----------\n" + 
9000
		"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
9001
		"	A a;\n" + 
9002
		"	^\n" + 
9003
		"Discouraged access: The type A is not accessible due to restriction on classpath entry ---LIB_DIR_PLACEHOLDER---/lib3.jar\n" + 
9004
		"----------\n" + 
9005
		"1 problem (1 warning)",
9006
		true);
9007
}
9008
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9009
// jar files reached indirectly bear the access rules of the entry that 
9010
// references them - this hides the access rules of further instances of the
9011
// same jar on the classpath
9012
public void test230_jar_ref_in_jar(){
9013
	createCascadedJars();
9014
	this.runConformTest(
9015
		new String[] {
9016
			"src/p/X.java",
9017
			"package p;\n" +
9018
			"/** */\n" +
9019
			"public class X {\n" +
9020
			"  A a;\n" +
9021
			"}",
9022
		},
9023
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9024
		+ " -cp \"" + LIB_DIR + File.separator + "lib3.jar[-DUMMY]\""
9025
		+ " -cp \"" + LIB_DIR + File.separator + "lib1.jar[-p/A]\""
9026
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
9027
		+ " -1.5 -g -preserveAllLocals"
9028
		+ " -proceedOnError -referenceInfo"
9029
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9030
		"",
9031
		"",
9032
		true);
9033
}
9034
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9035
// jar files reached indirectly bear the access rules of the entry that 
9036
// references them - this hides the access rules of further instances of the
9037
// same jar on the classpath, to the point of absorbing it if none is specified
9038
public void test231_jar_ref_in_jar(){
9039
	createCascadedJars();
9040
	this.runConformTest(
9041
		new String[] {
9042
			"src/p/X.java",
9043
			"package p;\n" +
9044
			"/** */\n" +
9045
			"public class X {\n" +
9046
			"  A a;\n" +
9047
			"}",
9048
		},
9049
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9050
		+ " -cp \"" + LIB_DIR + File.separator + "lib3.jar\""
9051
		+ " -cp \"" + LIB_DIR + File.separator + "lib1.jar[-p/A]\""
9052
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
9053
		+ " -1.5 -g -preserveAllLocals"
9054
		+ " -proceedOnError -referenceInfo"
9055
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9056
		"",
9057
		"",
9058
		true);
9059
}
9060
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9061
// -sourcepath is OK at first level
9062
public void test232_jar_ref_in_jar(){
9063
	createCascadedJars();
9064
	this.runConformTest(
9065
		new String[] {
9066
			"src/p/X.java",
9067
			"package p;\n" +
9068
			"/** */\n" +
9069
			"public class X {\n" +
9070
			"  S1 s;\n" +
9071
			"}",
9072
		},
9073
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9074
		+ " -sourcepath \"" + LIB_DIR + File.separator + "lib1.jar\""
9075
		+ " -1.5 -g -preserveAllLocals"
9076
		+ " -proceedOnError -referenceInfo"
9077
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9078
		"",
9079
		"",
9080
		true);
9081
}
9082
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9083
// -sourcepath is KO at second level (that is, it does not leverage the links
9084
// at all)
9085
public void test233_jar_ref_in_jar(){
9086
	createCascadedJars();
9087
	this.runNegativeTest(
9088
		new String[] {
9089
			"src/p/X.java",
9090
			"package p;\n" +
9091
			"/** */\n" +
9092
			"public class X {\n" +
9093
			"  S2 s;\n" +
9094
			"}",
9095
		},
9096
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9097
		+ " -sourcepath \"" + LIB_DIR + File.separator + "lib1.jar\""
9098
		+ " -1.5 -g -preserveAllLocals"
9099
		+ " -proceedOnError -referenceInfo"
9100
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9101
		"",
9102
		"----------\n" + 
9103
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
9104
		"	S2 s;\n" + 
9105
		"	^^\n" + 
9106
		"S2 cannot be resolved to a type\n" + 
9107
		"----------\n" + 
9108
		"1 problem (1 error)",
9109
		true);
9110
}
9111
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9112
// error case: the MANIFEST.MF is a directory; should fail gracefully
9113
public void test234_jar_ref_in_jar(){
9114
	createCascadedJars();
9115
	this.runNegativeTest(
9116
		new String[] {
9117
			"src/p/X.java",
9118
			"package p;\n" +
9119
			"/** */\n" +
9120
			"public class X {\n" +
9121
			"  A a;\n" +
9122
			"  B b;\n" +
9123
			"}",
9124
		},
9125
     "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9126
		+ " -cp \"" + LIB_DIR + File.separator + "lib10.jar\""
9127
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
9128
     + " -1.5 -g -preserveAllLocals"
9129
     + " -proceedOnError -referenceInfo"
9130
     + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9131
     "",
9132
     "----------\n" + 
9133
     "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 5)\n" + 
9134
     "	B b;\n" + 
9135
     "	^\n" + 
9136
     "B cannot be resolved to a type\n" + 
9137
     "----------\n" + 
9138
     "1 problem (1 error)",
9139
     true);
9140
}
9141
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9142
// using relative paths for libs
9143
public void test235_jar_ref_in_jar(){
9144
	String currentWorkingDirectoryPath = System.getProperty("user.dir");
9145
	if (currentWorkingDirectoryPath == null) {
9146
		System.err.println("BatchCompilerTest#235 could not access the current working directory " + currentWorkingDirectoryPath);
9147
	} else if (!new File(currentWorkingDirectoryPath).isDirectory()) {
9148
		System.err.println("BatchCompilerTest#235 current working directory is not a directory " + currentWorkingDirectoryPath);
9149
	} else {
9150
		String lib1Path = currentWorkingDirectoryPath + File.separator + "lib1.jar";
9151
		String lib2Path = currentWorkingDirectoryPath + File.separator + "lib2.jar";
9152
		try {
9153
			Util.createJar(
9154
				null,
9155
				new String[] {
9156
					"META-INF/MANIFEST.MF",
9157
					"Manifest-Version: 1.0\n" +
9158
					"Created-By: Eclipse JDT Test Harness\n" +
9159
					"Class-Path: lib2.jar\n",
9160
				},
9161
				lib1Path,
9162
				JavaCore.VERSION_1_4);
9163
			Util.createJar(
9164
				new String[] {
9165
					"p/A.java",
9166
					"package p;\n" +
9167
					"public class A {\n" +
9168
					"}",
9169
				},
9170
				null,
9171
				lib2Path,
9172
				JavaCore.VERSION_1_4);
9173
			this.runConformTest(
9174
				new String[] {
9175
					"src/p/X.java",
9176
					"package p;\n" +
9177
					"/** */\n" +
9178
					"public class X {\n" +
9179
					"  A a;\n" +
9180
					"}",
9181
				},
9182
		        "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9183
				+ " -cp lib1.jar" // relative
9184
				+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
9185
		        + " -1.5 -g -preserveAllLocals"
9186
		        + " -proceedOnError -referenceInfo"
9187
		        + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9188
		        "",
9189
		        "",
9190
		        true);
9191
		} catch (IOException e) {
9192
			System.err.println("BatchCompilerTest#235 could not write to current working directory " + currentWorkingDirectoryPath);
9193
		} finally {
9194
			new File(lib1Path).delete();
9195
			new File(lib2Path).delete();
9196
		}
9197
	}
9198
}
9199
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9200
// empty Class-Path header
9201
// javac 1.4.2 passes, later versions fail in error
9202
// java accepts the same jar (which makes the compiler responsible for the 
9203
// error detection)
9204
// design: will issue a warning
9205
public void test236_jar_ref_in_jar(){
9206
	createCascadedJars();
9207
	this.runTest(
9208
		true,
9209
		new String[] {
9210
			"src/p/X.java",
9211
			"package p;\n" +
9212
			"/** */\n" +
9213
			"public class X {\n" +
9214
			"  A a;\n" +
9215
			"}",
9216
		},
9217
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9218
		+ " -classpath \"" + LIB_DIR + File.separator + "lib11.jar\""
9219
		+ " -1.5 -g -preserveAllLocals"
9220
		+ " -verbose -proceedOnError -referenceInfo"
9221
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9222
		ONE_FILE_GENERATED_MATCHER,
9223
		new StringMatcher(
9224
			"invalid Class-Path header in manifest of jar file: ---LIB_DIR_PLACEHOLDER---/lib11.jar\n", 
9225
			outputDirNormalizer),
9226
		true);
9227
}
9228
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9229
// missing space after ClassPath:
9230
public void test237_jar_ref_in_jar(){
9231
	createCascadedJars();
9232
	this.runTest(
9233
		false,
9234
		new String[] {
9235
			"src/p/X.java",
9236
			"package p;\n" +
9237
			"/** */\n" +
9238
			"public class X {\n" +
9239
			"  A a;\n" +
9240
			"}",
9241
		},
9242
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9243
		+ " -classpath \"" + LIB_DIR + File.separator + "lib12.jar\""
9244
		+ " -1.5 -g -preserveAllLocals"
9245
		+ " -referenceInfo"
9246
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9247
		"",
9248
		"invalid Class-Path header in manifest of jar file: ---LIB_DIR_PLACEHOLDER---/lib12.jar\n" + 
9249
		"----------\n" + 
9250
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
9251
		"	A a;\n" + 
9252
		"	^\n" + 
9253
		"A cannot be resolved to a type\n" + 
9254
		"----------\n" + 
9255
		"1 problem (1 error)",
9256
		true);
9257
}
9258
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9259
// missing space after ClassPath
9260
// javac reports an error (including an explicit manifest header error since
9261
// version 1.5); moreover, it stops interpreting the said header
9262
// design: we report a warning and eat up the remainding of the line
9263
public void test238_jar_ref_in_jar(){
9264
	createCascadedJars();
9265
	this.runTest(
9266
		false,
9267
		new String[] {
9268
			"src/p/X.java",
9269
			"package p;\n" +
9270
			"/** */\n" +
9271
			"public class X {\n" +
9272
			"  A a;\n" +
9273
			"}",
9274
		},
9275
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9276
		+ " -classpath \"" + LIB_DIR + File.separator + "lib13.jar\""
9277
		+ " -1.5 -g -preserveAllLocals"
9278
		+ " -proceedOnError -referenceInfo"
9279
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9280
		"",
9281
		"invalid Class-Path header in manifest of jar file: ---LIB_DIR_PLACEHOLDER---/lib13.jar\n" + 
9282
		"----------\n" + 
9283
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
9284
		"	A a;\n" + 
9285
		"	^\n" + 
9286
		"A cannot be resolved to a type\n" + 
9287
		"----------\n" + 
9288
		"1 problem (1 error)",
9289
		true);
9290
}
9291
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9292
// extra space before Class-Path header
9293
// the net result is that the line is part of the value of the previous header
9294
// we then simply don't see the remainding of the line as jars
9295
public void test239_jar_ref_in_jar(){
9296
	createCascadedJars();
9297
	this.runTest(
9298
		false,
9299
		new String[] {
9300
			"src/p/X.java",
9301
			"package p;\n" +
9302
			"/** */\n" +
9303
			"public class X {\n" +
9304
			"  A a;\n" +
9305
			"}",
9306
		},
9307
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9308
		+ " -classpath \"" + LIB_DIR + File.separator + "lib14.jar\""
9309
		+ " -1.5 -g -preserveAllLocals"
9310
		+ " -proceedOnError -referenceInfo"
9311
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9312
		"",
9313
		"----------\n" + 
9314
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
9315
		"	A a;\n" + 
9316
		"	^\n" + 
9317
		"A cannot be resolved to a type\n" + 
9318
		"----------\n" + 
9319
		"1 problem (1 error)",
9320
		true);
9321
}
9322
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9323
// missing newline at the end of the line
9324
// javac eats the line silently, which results into not finding A
9325
// design: we report a warning and eat up the remainding of the line
9326
public void test240_jar_ref_in_jar(){
9327
	createCascadedJars();
9328
	this.runTest(
9329
		false,
9330
		new String[] {
9331
			"src/p/X.java",
9332
			"package p;\n" +
9333
			"/** */\n" +
9334
			"public class X {\n" +
9335
			"  A a;\n" +
9336
			"}",
9337
		},
9338
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9339
		+ " -classpath \"" + LIB_DIR + File.separator + "lib15.jar\""
9340
		+ " -1.5 -g -preserveAllLocals"
9341
		+ " -proceedOnError -referenceInfo"
9342
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9343
		"",
9344
		"invalid Class-Path header in manifest of jar file: ---LIB_DIR_PLACEHOLDER---/lib15.jar\n" + 
9345
		"----------\n" + 
9346
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 4)\n" + 
9347
		"	A a;\n" + 
9348
		"	^\n" + 
9349
		"A cannot be resolved to a type\n" + 
9350
		"----------\n" + 
9351
		"1 problem (1 error)",
9352
		true);
9353
}
9354
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9355
// white-box test for duplicate classpath lines variant (empty line between the
9356
// entries)
9357
public void test241_jar_ref_in_jar(){
9358
	try {
9359
		assertTrue(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9360
			new StringReader(
9361
				"Manifest-Version: 1.0\n" +
9362
				"Created-By: Eclipse JDT Test Harness\n" +
9363
				"Class-Path: lib1.jar\n" +
9364
				"\n" +
9365
				"Class-Path: lib3.jar\n")));
9366
		assertEquals(2, ClasspathJar.MANIFEST_ANALYZER.getClasspathSectionsCount());
9367
		assertEquals(2, ClasspathJar.MANIFEST_ANALYZER.getCalledFileNames().size());
9368
	} catch (IOException e) {
9369
		e.printStackTrace();
9370
		fail();
9371
	}
9372
}
9373
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9374
// white-box test for duplicate classpath lines variant (other header between the
9375
// entries - note that since we are not doing a full-fledged manifest analysis,
9376
// a dummy header passes)
9377
public void test242_jar_ref_in_jar(){
9378
	try {
9379
		assertTrue(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9380
			new StringReader(
9381
				"Manifest-Version: 1.0\n" +
9382
				"Created-By: Eclipse JDT Test Harness\n" +
9383
				"Class-Path: lib1.jar\n" +
9384
				"Dummy:\n" +
9385
				"Class-Path: lib3.jar\n")));
9386
		assertEquals(2, ClasspathJar.MANIFEST_ANALYZER.getClasspathSectionsCount());
9387
		assertEquals(2, ClasspathJar.MANIFEST_ANALYZER.getCalledFileNames().size());
9388
	} catch (IOException e) {
9389
		e.printStackTrace();
9390
		fail();
9391
	}
9392
}
9393
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9394
// white-box test: tabs are not seen as URI separator, but as parts of URI instead
9395
// will trigger downstream errors if the jars are really needed
9396
public void test243_jar_ref_in_jar(){
9397
	try {
9398
		assertTrue(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9399
			new StringReader(
9400
				"Manifest-Version: 1.0\n" +
9401
				"Created-By: Eclipse JDT Test Harness\n" +
9402
				"Class-Path: lib1.jar\tlib2.jar\n")));
9403
		assertEquals(1, ClasspathJar.MANIFEST_ANALYZER.getClasspathSectionsCount());
9404
		assertEquals(1, ClasspathJar.MANIFEST_ANALYZER.getCalledFileNames().size());
9405
	} catch (IOException e) {
9406
		e.printStackTrace();
9407
		fail();
9408
	}
9409
}
9410
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9411
// managing continuations properly
9412
public void test244_jar_ref_in_jar(){
9413
	createCascadedJars();
9414
	this.runConformTest(
9415
		new String[] {
9416
			"src/p/X.java",
9417
			"package p;\n" +
9418
			"/** */\n" +
9419
			"public class X {\n" +
9420
			"  A a;\n" +
9421
			"  B b;\n" +
9422
			"}",
9423
		},
9424
     "\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9425
		+ " -cp \"" + LIB_DIR + File.separator + "lib16.jar\""
9426
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
9427
     + " -1.5 -g -preserveAllLocals"
9428
     + " -proceedOnError -referenceInfo"
9429
     + " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9430
     "",
9431
     "",
9432
     true);
9433
}
9434
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9435
// white-box test: variants on continuations
9436
public void test245_jar_ref_in_jar(){
9437
	try {
9438
		assertTrue(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9439
			new StringReader(
9440
				"Manifest-Version: 1.0\n" +
9441
				"Created-By: Eclipse JDT Test Harness\n" +
9442
				"Class-Path: \n" +
9443
				"            lib1.jar       \n" +
9444
				"\n")));
9445
		assertEquals(1, ClasspathJar.MANIFEST_ANALYZER.getClasspathSectionsCount());
9446
		assertEquals(1, ClasspathJar.MANIFEST_ANALYZER.getCalledFileNames().size());
9447
	} catch (IOException e) {
9448
		e.printStackTrace();
9449
		fail();
9450
	}
9451
}
9452
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9453
// white-box test: variants on continuations
9454
public void test246_jar_ref_in_jar(){
9455
	try {
9456
		assertTrue(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9457
			new StringReader(
9458
				"Manifest-Version: 1.0\n" +
9459
				"Created-By: Eclipse JDT Test Harness\n" +
9460
				"Class-Path: \n" +
9461
				" \n" +
9462
				"            lib1.jar       \n" +
9463
				" \n" +
9464
				"            lib1.jar       \n" +
9465
				"\n")));
9466
		assertEquals(1, ClasspathJar.MANIFEST_ANALYZER.getClasspathSectionsCount());
9467
		assertEquals(2, ClasspathJar.MANIFEST_ANALYZER.getCalledFileNames().size());
9468
	} catch (IOException e) {
9469
		e.printStackTrace();
9470
		fail();
9471
	}
9472
}
9473
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9474
// white-box test: variants on continuations
9475
public void test247_jar_ref_in_jar(){
9476
	try {
9477
		assertFalse(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9478
			new StringReader(
9479
				"Manifest-Version: 1.0\n" +
9480
				"Created-By: Eclipse JDT Test Harness\n" +
9481
				"Class-Path: \n" +
9482
				"            lib1.jar")));
9483
	} catch (IOException e) {
9484
		e.printStackTrace();
9485
		fail();
9486
	}
9487
}
9488
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9489
// white-box test: variants on continuations
9490
public void test248_jar_ref_in_jar(){
9491
	try {
9492
		assertFalse(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9493
			new StringReader(
9494
				"Manifest-Version: 1.0\n" +
9495
				"Created-By: Eclipse JDT Test Harness\n" +
9496
				"Class-Path: \n" +
9497
				" \n" +
9498
				"            lib1.jar")));
9499
	} catch (IOException e) {
9500
		e.printStackTrace();
9501
		fail();
9502
	}
9503
}
9504
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9505
// white-box test: variants on continuations
9506
public void test249_jar_ref_in_jar(){
9507
	try {
9508
		assertFalse(ClasspathJar.MANIFEST_ANALYZER.analyzeManifestContents(
9509
			new StringReader(
9510
				"Manifest-Version: 1.0\n" +
9511
				"Created-By: Eclipse JDT Test Harness\n" +
9512
				"Class-Path:      \n" +
9513
				"lib1.jar")));
9514
	} catch (IOException e) {
9515
		e.printStackTrace();
9516
		fail();
9517
	}
9518
}
9519
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9520
// extdirs jars do not follow links
9521
public void test250_jar_ref_in_jar(){
9522
	createCascadedJars();
9523
	this.runNegativeTest(
9524
		new String[] {
9525
			"src/p/X.java",
9526
			"package p;\n" +
9527
			"/** */\n" +
9528
			"public class X {\n" +
9529
			"  A a;\n" +
9530
			"  B b;\n" +
9531
			"}",
9532
		},
9533
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9534
	  	+ " -extdirs \"" + LIB_DIR + File.separator + "dir\""
9535
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
9536
		+ " -1.5 -g -preserveAllLocals"
9537
		+ " -proceedOnError -referenceInfo"
9538
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9539
		"",
9540
		"----------\n" + 
9541
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 5)\n" + 
9542
		"	B b;\n" + 
9543
		"	^\n" + 
9544
		"B cannot be resolved to a type\n" + 
9545
		"----------\n" + 
9546
		"1 problem (1 error)",
9547
		true);
9548
}
9549
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=97332 - jars pointed by jars
9550
// endorseddirs does not get expanded with linked files
9551
public void test251_jar_ref_in_jar(){
9552
	createCascadedJars();
9553
	this.runNegativeTest(
9554
		new String[] {
9555
			"src/p/X.java",
9556
			"package p;\n" +
9557
			"/** */\n" +
9558
			"public class X {\n" +
9559
			"  A a;\n" +
9560
			"  B b;\n" +
9561
			"}",
9562
		},
9563
		"\"" + OUTPUT_DIR +  File.separator + "src/p/X.java\""
9564
	  	+ " -endorseddirs \"" + LIB_DIR + File.separator + "dir\""
9565
		+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src\""
9566
		+ " -1.5 -g -preserveAllLocals"
9567
		+ " -proceedOnError -referenceInfo"
9568
		+ " -d \"" + OUTPUT_DIR + File.separator + "bin\" ",
9569
		"",
9570
		"----------\n" + 
9571
		"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/src/p/X.java (at line 5)\n" + 
9572
		"	B b;\n" + 
9573
		"	^\n" + 
9574
		"B cannot be resolved to a type\n" + 
9575
		"----------\n" + 
9576
		"1 problem (1 error)",
9577
		true);
9578
}
8053
public static Class testClass() {
9579
public static Class testClass() {
8054
	return BatchCompilerTest.class;
9580
	return BatchCompilerTest.class;
8055
}
9581
}
(-)src/org/eclipse/jdt/core/tests/util/Util.java (-4 / +17 lines)
Lines 1-5 Link Here
1
/*******************************************************************************
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2007 IBM Corporation and others.
2
 * Copyright (c) 2000, 2008 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
5
 * which accompanies this distribution, and is available at
Lines 317-330 Link Here
317
    compile(pathsAndContents, getCompileOptions(compliance), folderPath);
317
    compile(pathsAndContents, getCompileOptions(compliance), folderPath);
318
}
318
}
319
public static void createJar(String[] pathsAndContents, Map options, String jarPath) throws IOException {
319
public static void createJar(String[] pathsAndContents, Map options, String jarPath) throws IOException {
320
	createJar(pathsAndContents, null, options, jarPath);
321
}
322
public static void createJar(String[] pathsAndContents, String[] extraPathsAndContents, Map options, String jarPath) throws IOException {
320
    String classesPath = getOutputDirectory() + File.separator + "classes";
323
    String classesPath = getOutputDirectory() + File.separator + "classes";
321
    File classesDir = new File(classesPath);
324
    File classesDir = new File(classesPath);
322
    flushDirectoryContent(classesDir);
325
    flushDirectoryContent(classesDir);
323
    compile(pathsAndContents, options, classesPath);
326
	if (pathsAndContents != null) {
327
		compile(pathsAndContents, options, classesPath);
328
	}
329
	for (int i = 0, l = extraPathsAndContents == null ? 0 : extraPathsAndContents.length; i < l; /* inc in loop */) {
330
		File  outputFile = new File(classesPath, extraPathsAndContents[i++]);
331
		outputFile.getParentFile().mkdirs();
332
		Util.writeToFile(extraPathsAndContents[i++], outputFile.getAbsolutePath());
333
	}
324
    zip(classesDir, jarPath);
334
    zip(classesDir, jarPath);
325
}
335
}
326
public static void createJar(String[] pathsAndContents, String jarPath, String compliance) throws IOException {
336
public static void createJar(String[] javaPathsAndContents, String jarPath, String compliance) throws IOException {
327
    createJar(pathsAndContents, getCompileOptions(compliance), jarPath);
337
	createJar(javaPathsAndContents, null, jarPath, compliance);
338
}
339
public static void createJar(String[] javaPathsAndContents, String[] extraPathsAndContents, String jarPath, String compliance) throws IOException {
340
	createJar(javaPathsAndContents, extraPathsAndContents, getCompileOptions(compliance), jarPath);
328
}
341
}
329
public static void createSourceZip(String[] pathsAndContents, String zipPath) throws IOException {
342
public static void createSourceZip(String[] pathsAndContents, String zipPath) throws IOException {
330
    String sourcesPath = getOutputDirectory() + File.separator + "sources";
343
    String sourcesPath = getOutputDirectory() + File.separator + "sources";

Return to bug 97332