1 /*******************************************************************************
2  * Copyright (c) 2000, 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  *     Stephan Herrmann - Contribution for
14  *								Bug 458577 - IClassFile.getWorkingCopy() may lead to NPE in BecomeWorkingCopyOperation
15  *								Bug 440477 - [null] Infrastructure for feeding external annotations into compilation
16  *								Bug 462768 - [null] NPE when using linked folder for external annotations
17  *******************************************************************************/
18 package org.eclipse.jdt.internal.core;
19 
20 import java.io.File;
21 import java.io.IOException;
22 import java.util.zip.ZipEntry;
23 import java.util.zip.ZipFile;
24 
25 import org.eclipse.core.resources.IContainer;
26 import org.eclipse.core.resources.IFile;
27 import org.eclipse.core.resources.IFolder;
28 import org.eclipse.core.resources.IResource;
29 import org.eclipse.core.runtime.CoreException;
30 import org.eclipse.core.runtime.IPath;
31 import org.eclipse.core.runtime.IProgressMonitor;
32 import org.eclipse.core.runtime.IStatus;
33 import org.eclipse.core.runtime.OperationCanceledException;
34 import org.eclipse.core.runtime.Path;
35 import org.eclipse.jdt.core.*;
36 import org.eclipse.jdt.core.compiler.IProblem;
37 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
38 import org.eclipse.jdt.internal.core.util.Util;
39 
40 /**
41  * Common parts of ClassFile (containing a BinaryType) and ModularClassFile (containing a BinaryModule).
42  * Prior to Java 9, most of this content was directly in ClassFile.
43  */
44 public abstract class AbstractClassFile extends Openable implements IClassFile, SuffixConstants {
45 
46 	protected String name;
47 
AbstractClassFile(PackageFragment parent, String nameWithoutExtension)48 	protected AbstractClassFile(PackageFragment parent, String nameWithoutExtension) {
49 		super(parent);
50 		this.name = nameWithoutExtension;
51 	}
52 
53 	/*
54 	 * @see IClassFile#becomeWorkingCopy(IProblemRequestor, WorkingCopyOwner, IProgressMonitor)
55 	 */
56 	@Override
becomeWorkingCopy(IProblemRequestor problemRequestor, WorkingCopyOwner owner, IProgressMonitor monitor)57 	public ICompilationUnit becomeWorkingCopy(IProblemRequestor problemRequestor, WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException {
58 		JavaModelManager manager = JavaModelManager.getJavaModelManager();
59 		CompilationUnit workingCopy = new ClassFileWorkingCopy(this, owner == null ? DefaultWorkingCopyOwner.PRIMARY : owner);
60 		JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo = manager.getPerWorkingCopyInfo(workingCopy, false/*don't create*/, true /*record usage*/, null/*no problem requestor needed*/);
61 		if (perWorkingCopyInfo == null) {
62 			// close cu and its children
63 			close();
64 
65 			BecomeWorkingCopyOperation operation = new BecomeWorkingCopyOperation(workingCopy, problemRequestor);
66 			operation.runOperation(monitor);
67 
68 			return workingCopy;
69 		}
70 		return perWorkingCopyInfo.workingCopy;
71 	}
72 
73 	/**
74 	 * @see ICodeAssist#codeComplete(int, ICompletionRequestor)
75 	 * @deprecated
76 	 */
77 	@Override
78 	@Deprecated
codeComplete(int offset, ICompletionRequestor requestor)79 	public void codeComplete(int offset, ICompletionRequestor requestor) throws JavaModelException {
80 		codeComplete(offset, requestor, DefaultWorkingCopyOwner.PRIMARY);
81 	}
82 	/**
83 	 * @see ICodeAssist#codeComplete(int, ICompletionRequestor, WorkingCopyOwner)
84 	 * @deprecated
85 	 */
86 	@Override
87 	@Deprecated
codeComplete(int offset, ICompletionRequestor requestor, WorkingCopyOwner owner)88 	public void codeComplete(int offset, ICompletionRequestor requestor, WorkingCopyOwner owner) throws JavaModelException {
89 		if (requestor == null) {
90 			throw new IllegalArgumentException("Completion requestor cannot be null"); //$NON-NLS-1$
91 		}
92 		codeComplete(offset, new org.eclipse.jdt.internal.codeassist.CompletionRequestorWrapper(requestor), owner);
93 	}
94 
95 	@Override
codeComplete(int offset, CompletionRequestor requestor)96 	public void codeComplete(int offset, CompletionRequestor requestor) throws JavaModelException {
97 		codeComplete(offset, requestor, DefaultWorkingCopyOwner.PRIMARY);
98 	}
99 
100 	@Override
codeComplete(int offset, CompletionRequestor requestor, IProgressMonitor monitor)101 	public void codeComplete(int offset, CompletionRequestor requestor, IProgressMonitor monitor) throws JavaModelException {
102 		codeComplete(offset, requestor, DefaultWorkingCopyOwner.PRIMARY, monitor);
103 	}
104 
105 	@Override
codeComplete(int offset, CompletionRequestor requestor, WorkingCopyOwner owner)106 	public void codeComplete(int offset, CompletionRequestor requestor, WorkingCopyOwner owner) throws JavaModelException {
107 		codeComplete(offset, requestor, owner, null);
108 	}
109 	@Override
codeComplete(int offset, CompletionRequestor requestor, WorkingCopyOwner owner, IProgressMonitor monitor)110 	public abstract void codeComplete(int offset, CompletionRequestor requestor, WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException;
111 
112 	/**
113 	 * @see ICodeAssist#codeSelect(int, int)
114 	 */
115 	@Override
codeSelect(int offset, int length)116 	public IJavaElement[] codeSelect(int offset, int length) throws JavaModelException {
117 		return codeSelect(offset, length, DefaultWorkingCopyOwner.PRIMARY);
118 	}
119 	@Override
codeSelect(int offset, int length, WorkingCopyOwner owner)120 	public abstract IJavaElement[] codeSelect(int offset, int length, WorkingCopyOwner owner) throws JavaModelException;
121 
122 	/**
123 	 * Returns a new element info for this element.
124 	 */
125 	@Override
createElementInfo()126 	protected Object createElementInfo() {
127 		return new ClassFileInfo();
128 	}
129 	@Override
equals(Object o)130 	public boolean equals(Object o) {
131 		if (!(o instanceof AbstractClassFile)) return false;
132 		AbstractClassFile other = (AbstractClassFile) o;
133 		return this.name.equals(other.name) && this.parent.equals(other.parent);
134 	}
135 
136 	/**
137 	 * Finds the deepest <code>IJavaElement</code> in the hierarchy of
138 	 * <code>elt</elt>'s children (including <code>elt</code> itself)
139 	 * which has a source range that encloses <code>position</code>
140 	 * according to <code>mapper</code>.
141 	 */
findElement(IJavaElement elt, int position, SourceMapper mapper)142 	protected IJavaElement findElement(IJavaElement elt, int position, SourceMapper mapper) {
143 		SourceRange range = mapper.getSourceRange(elt);
144 		if (range == null || position < range.getOffset() || range.getOffset() + range.getLength() - 1 < position) {
145 			return null;
146 		}
147 		if (elt instanceof IParent) {
148 			try {
149 				IJavaElement[] children = ((IParent) elt).getChildren();
150 				for (int i = 0; i < children.length; i++) {
151 					IJavaElement match = findElement(children[i], position, mapper);
152 					if (match != null) {
153 						return match;
154 					}
155 				}
156 			} catch (JavaModelException npe) {
157 				// elt doesn't exist: return the element
158 			}
159 		}
160 		return elt;
161 	}
162 
163 	@Override
getBytes()164 	public byte[] getBytes() throws JavaModelException {
165 		JavaElement pkg = (JavaElement) getParent();
166 		if (pkg instanceof JarPackageFragment) {
167 			JarPackageFragmentRoot root = (JarPackageFragmentRoot) pkg.getParent();
168 			try {
169 				String entryName = Util.concatWith(((PackageFragment) pkg).names, getElementName(), '/');
170 				entryName = root.getClassFilePath(entryName);
171 				return getClassFileContent(root, entryName);
172 				// Java 9 - The below exception is not thrown in new scheme of things. Could cause issues?
173 	//			throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this));
174 			} catch (IOException ioe) {
175 				throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION);
176 			} catch (CoreException e) {
177 				if (e instanceof JavaModelException) {
178 					throw (JavaModelException)e;
179 				} else {
180 					throw new JavaModelException(e);
181 				}
182 			}
183 		} else {
184 			IFile file = (IFile) resource();
185 			return Util.getResourceContentsAsByteArray(file);
186 		}
187 	}
getClassFileContent(JarPackageFragmentRoot root, String className)188 	protected byte[] getClassFileContent(JarPackageFragmentRoot root, String className) throws CoreException, IOException {
189 		byte[] contents = null;
190 		String rootPath = root.getPath().toOSString();
191 		if (org.eclipse.jdt.internal.compiler.util.Util.isJrt(rootPath)) {
192 			contents = org.eclipse.jdt.internal.compiler.util.JRTUtil.getClassfileContent(
193 					new File(rootPath),
194 					className,
195 					root.getElementName());
196 		} else {
197 			ZipFile zip = root.getJar();
198 			try {
199 				ZipEntry ze = zip.getEntry(className);
200 				if (ze != null) {
201 					contents = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip);
202 				}
203 			} finally {
204 				JavaModelManager.getJavaModelManager().closeZipFile(zip);
205 			}
206 		}
207 		if (contents == null && Thread.interrupted()) // reading from JRT is interruptible
208 			throw new OperationCanceledException();
209 		return contents;
210 	}
211 
212 	@Override
getBuffer()213 	public IBuffer getBuffer() throws JavaModelException {
214 		IStatus status = validateClassFile();
215 		if (status.isOK()) {
216 			return super.getBuffer();
217 		} else {
218 			switch (status.getCode()) {
219 			case IJavaModelStatusConstants.ELEMENT_NOT_ON_CLASSPATH: // don't throw a JavaModelException to be able to open .class file outside the classpath (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=138507 )
220 			case IJavaModelStatusConstants.INVALID_ELEMENT_TYPES: // don't throw a JavaModelException to be able to open .class file in proj==src case without source (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=221904 )
221 				return null;
222 			default:
223 				throw new JavaModelException(status);
224 			}
225 		}
226 	}
227 	/**
228 	 * @see IMember#getTypeRoot()
229 	 */
getTypeRoot()230 	public ITypeRoot getTypeRoot() {
231 		return this;
232 	}
233 
234 	/**
235 	 * A class file has a corresponding resource unless it is contained
236 	 * in a jar.
237 	 *
238 	 * @see IJavaElement
239 	 */
240 	@Override
getCorrespondingResource()241 	public IResource getCorrespondingResource() throws JavaModelException {
242 		IPackageFragmentRoot root= (IPackageFragmentRoot)getParent().getParent();
243 		if (root.isArchive()) {
244 			return null;
245 		} else {
246 			return getUnderlyingResource();
247 		}
248 	}
getElementAtConsideringSibling(int position)249 	public IJavaElement getElementAtConsideringSibling(int position) throws JavaModelException {
250 		IPackageFragment fragment = (IPackageFragment)getParent();
251 		PackageFragmentRoot root = (PackageFragmentRoot) fragment.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
252 		SourceMapper mapper = root.getSourceMapper();
253 		if (mapper == null) {
254 			return null;
255 		} else {
256 			int index = this.name.indexOf('$');
257 			int prefixLength = index < 0 ? this.name.length() : index;
258 
259 			IType type = null;
260 			int start = -1;
261 			int end = Integer.MAX_VALUE;
262 			IJavaElement[] children = fragment.getChildren();
263 			for (int i = 0; i < children.length; i++) {
264 				if (children[i] instanceof IOrdinaryClassFile) {
265 					IOrdinaryClassFile classFile = (IOrdinaryClassFile) children[i];
266 					String childName = classFile.getElementName();
267 
268 					int childIndex = childName.indexOf('$');
269 					int childPrefixLength = childIndex < 0 ? childName.indexOf('.') : childIndex;
270 					if (prefixLength == childPrefixLength && this.name.regionMatches(0, childName, 0, prefixLength)) {
271 
272 						// ensure this class file's buffer is open so that source ranges are computed
273 						classFile.getBuffer();
274 
275 						SourceRange range = mapper.getSourceRange(classFile.getType());
276 						if (range == SourceMapper.UNKNOWN_RANGE) continue;
277 						int newStart = range.getOffset();
278 						int newEnd = newStart + range.getLength() - 1;
279 						if(newStart > start && newEnd < end
280 								&& newStart <= position && newEnd >= position) {
281 							type = classFile.getType();
282 							start = newStart;
283 							end = newEnd;
284 						}
285 					}
286 				}
287 			}
288 			if(type != null) {
289 				return findElement(type, position, mapper);
290 			}
291 			return null;
292 		}
293 	}
294 	@Override
295 	public String getElementName() {
296 		return this.name + SuffixConstants.SUFFIX_STRING_class;
297 	}
298 	/**
299 	 * @see IJavaElement
300 	 */
301 	@Override
302 	public int getElementType() {
303 		return CLASS_FILE;
304 	}
305 
306 	/*
307 	 * @see IJavaElement
308 	 */
309 	@Override
310 	public IPath getPath() {
311 		PackageFragmentRoot root = getPackageFragmentRoot();
312 		if (root.isArchive()) {
313 			return root.getPath();
314 		} else {
315 			return getParent().getPath().append(getElementName());
316 		}
317 	}
318 
319 	/*
320 	 * @see IJavaElement
321 	 */
322 	@Override
323 	public IResource resource(PackageFragmentRoot root) {
324 		return ((IContainer) ((Openable) this.parent).resource(root)).getFile(new Path(getElementName()));
325 	}
326 	/**
327 	 * @see ISourceReference
328 	 */
329 	@Override
330 	public String getSource() throws JavaModelException {
331 		IBuffer buffer = getBuffer();
332 		if (buffer == null) {
333 			return null;
334 		}
335 		return buffer.getContents();
336 	}
337 	/**
338 	 * @see ISourceReference
339 	 */
340 	@Override
341 	public ISourceRange getSourceRange() throws JavaModelException {
342 		IBuffer buffer = getBuffer();
343 		if (buffer != null) {
344 			String contents = buffer.getContents();
345 			if (contents == null) return null;
346 			return new SourceRange(0, contents.length());
347 		} else {
348 			return null;
349 		}
350 	}
351 	/**
352 	 * @see IClassFile
353 	 * @deprecated
354 	 */
355 	@Override
356 	@Deprecated
357 	public IJavaElement getWorkingCopy(IProgressMonitor monitor, org.eclipse.jdt.core.IBufferFactory factory) throws JavaModelException {
358 		return getWorkingCopy(BufferFactoryWrapper.create(factory), monitor);
359 	}
360 	/**
361 	 * @see Openable
362 	 */
363 	@Override
364 	protected boolean hasBuffer() {
365 		return true;
366 	}
367 	@Override
368 	public int hashCode() {
369 		return Util.combineHashCodes(this.name.hashCode(), this.parent.hashCode());
370 	}
371 	/**
372 	 * Returns true - class files are always read only.
373 	 */
374 	@Override
375 	public boolean isReadOnly() {
376 		return true;
377 	}
378 	private IStatus validateClassFile() {
379 		IPackageFragmentRoot root = getPackageFragmentRoot();
380 		try {
381 			if (root.getKind() != IPackageFragmentRoot.K_BINARY)
382 				return new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, root);
383 		} catch (JavaModelException e) {
384 			return e.getJavaModelStatus();
385 		}
386 		IJavaProject project = getJavaProject();
387 		return JavaConventions.validateClassFileName(getElementName(), project.getOption(JavaCore.COMPILER_SOURCE, true), project.getOption(JavaCore.COMPILER_COMPLIANCE, true));
388 	}
389 
390 
391 	/**
392 	 * @see ICodeAssist#codeComplete(int, ICodeCompletionRequestor)
393 	 * @deprecated - should use codeComplete(int, ICompletionRequestor) instead
394 	 */
395 	@Override
396 	@Deprecated
397 	public void codeComplete(int offset, final org.eclipse.jdt.core.ICodeCompletionRequestor requestor) throws JavaModelException {
398 
399 		if (requestor == null){
400 			codeComplete(offset, (ICompletionRequestor)null);
401 			return;
402 		}
403 		codeComplete(
404 			offset,
405 			new ICompletionRequestor(){
406 				@Override
407 				public void acceptAnonymousType(char[] superTypePackageName,char[] superTypeName, char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) {
408 					// ignore
409 				}
410 				@Override
411 				public void acceptClass(char[] packageName, char[] className, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) {
412 					requestor.acceptClass(packageName, className, completionName, modifiers, completionStart, completionEnd);
413 				}
414 				@Override
415 				public void acceptError(IProblem error) {
416 					// was disabled in 1.0
417 				}
418 				@Override
419 				public void acceptField(char[] declaringTypePackageName, char[] declaringTypeName, char[] fieldName, char[] typePackageName, char[] typeName, char[] completionName, int modifiers, int completionStart, int completionEnd, int relevance) {
420 					requestor.acceptField(declaringTypePackageName, declaringTypeName, fieldName, typePackageName, typeName, completionName, modifiers, completionStart, completionEnd);
421 				}
422 				@Override
423 				public void acceptInterface(char[] packageName,char[] interfaceName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance) {
424 					requestor.acceptInterface(packageName, interfaceName, completionName, modifiers, completionStart, completionEnd);
425 				}
426 				@Override
427 				public void acceptKeyword(char[] keywordName,int completionStart,int completionEnd, int relevance){
428 					requestor.acceptKeyword(keywordName, completionStart, completionEnd);
429 				}
430 				@Override
431 				public void acceptLabel(char[] labelName,int completionStart,int completionEnd, int relevance){
432 					requestor.acceptLabel(labelName, completionStart, completionEnd);
433 				}
434 				@Override
435 				public void acceptLocalVariable(char[] localVarName,char[] typePackageName,char[] typeName,int modifiers,int completionStart,int completionEnd, int relevance){
436 					// ignore
437 				}
438 				@Override
439 				public void acceptMethod(char[] declaringTypePackageName,char[] declaringTypeName,char[] selector,char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] returnTypePackageName,char[] returnTypeName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance){
440 					// skip parameter names
441 					requestor.acceptMethod(declaringTypePackageName, declaringTypeName, selector, parameterPackageNames, parameterTypeNames, returnTypePackageName, returnTypeName, completionName, modifiers, completionStart, completionEnd);
442 				}
443 				@Override
444 				public void acceptMethodDeclaration(char[] declaringTypePackageName,char[] declaringTypeName,char[] selector,char[][] parameterPackageNames,char[][] parameterTypeNames,char[][] parameterNames,char[] returnTypePackageName,char[] returnTypeName,char[] completionName,int modifiers,int completionStart,int completionEnd, int relevance){
445 					// ignore
446 				}
447 				@Override
448 				public void acceptModifier(char[] modifierName,int completionStart,int completionEnd, int relevance){
449 					requestor.acceptModifier(modifierName, completionStart, completionEnd);
450 				}
451 				@Override
452 				public void acceptPackage(char[] packageName,char[] completionName,int completionStart,int completionEnd, int relevance){
453 					requestor.acceptPackage(packageName, completionName, completionStart, completionEnd);
454 				}
455 				@Override
456 				public void acceptType(char[] packageName,char[] typeName,char[] completionName,int completionStart,int completionEnd, int relevance){
457 					requestor.acceptType(packageName, typeName, completionName, completionStart, completionEnd);
458 				}
459 				@Override
460 				public void acceptVariableName(char[] typePackageName,char[] typeName,char[] varName,char[] completionName,int completionStart,int completionEnd, int relevance){
461 					// ignore
462 				}
463 			});
464 	}
465 
466 	@Override
467 	protected IStatus validateExistence(IResource underlyingResource) {
468 		// check whether the class file can be opened
469 		IStatus status = validateClassFile();
470 		if (!status.isOK())
471 			return status;
472 		if (underlyingResource != null) {
473 			if (!underlyingResource.isAccessible())
474 				return newDoesNotExistStatus();
475 			PackageFragmentRoot root;
476 			if ((underlyingResource instanceof IFolder) && (root = getPackageFragmentRoot()).isArchive()) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=204652
477 				return root.newDoesNotExistStatus();
478 			}
479 		}
480 		return JavaModelStatus.VERIFIED_OK;
481 	}
482 
483 	@Override
484 	public ISourceRange getNameRange() {
485 		return null;
486 	}
487 }
488