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