| /******************************************************************************* |
| * Copyright (c) 2017, 2018 GK Software AG, and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Stephan Herrmann - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core; |
| |
| import java.io.IOException; |
| import java.util.Map; |
| |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jdt.core.*; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; |
| import org.eclipse.jdt.internal.compiler.env.IBinaryModule; |
| import org.eclipse.jdt.internal.compiler.env.IDependent; |
| import org.eclipse.jdt.internal.compiler.env.IModule; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.jdt.internal.core.nd.java.model.BinaryModuleDescriptor; |
| import org.eclipse.jdt.internal.core.nd.java.model.BinaryModuleFactory; |
| import org.eclipse.jdt.internal.core.util.MementoTokenizer; |
| import org.eclipse.jdt.internal.core.util.Util; |
| |
| /** |
| * A handle to a modular class file. |
| */ |
| public class ModularClassFile extends AbstractClassFile implements IModularClassFile { |
| |
| private BinaryModule binaryModule; |
| |
| protected ModularClassFile(PackageFragment parent) { |
| super(parent, TypeConstants.MODULE_INFO_NAME_STRING); |
| } |
| |
| /** |
| * Creates the single child element for this class file adding the resulting |
| * new handle (of type {@link IBinaryModule}) and info object to the newElements table. |
| * Returns true if successful, or false if an error is encountered parsing the class file. |
| * |
| * @see Openable |
| * @see Signature |
| */ |
| @Override |
| protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException { |
| IBinaryModule moduleInfo = getBinaryModuleInfo(); |
| if (moduleInfo == null) { |
| // The structure of a class file is unknown if a class file format errors occurred |
| //during the creation of the diet class file representative of this ClassFile. |
| info.setChildren(JavaElement.NO_ELEMENTS); |
| return false; |
| } |
| |
| // Create & link a handle: |
| BinaryModule module = new BinaryModule(this, moduleInfo); |
| newElements.put(module, moduleInfo); |
| info.setChildren(new IJavaElement[] {module}); |
| this.binaryModule = module; |
| if (info instanceof ClassFileInfo) { |
| ((ClassFileInfo) info).setModule(module); |
| } |
| ((PackageFragmentRootInfo) getPackageFragmentRoot().getElementInfo()).setModule(module); |
| return true; |
| } |
| |
| @Override |
| public void codeComplete(int offset, CompletionRequestor requestor, WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException { |
| String source = getSource(); |
| if (source != null) { |
| BasicCompilationUnit cu = |
| new BasicCompilationUnit( |
| getSource().toCharArray(), |
| null, |
| TypeConstants.MODULE_INFO_FILE_NAME_STRING, |
| getJavaProject()); // use project to retrieve corresponding .java IFile |
| codeComplete(cu, cu, offset, requestor, owner, null/*extended context isn't computed*/, monitor); |
| } |
| } |
| |
| @Override |
| public IJavaElement[] codeSelect(int offset, int length, WorkingCopyOwner owner) throws JavaModelException { |
| IBuffer buffer = getBuffer(); |
| char[] contents; |
| if (buffer != null && (contents = buffer.getCharacters()) != null) { |
| BasicCompilationUnit cu = new BasicCompilationUnit(contents, null, TypeConstants.MODULE_INFO_FILE_NAME_STRING, this); |
| return super.codeSelect(cu, offset, length, owner); |
| } else { |
| // has no associated source |
| return new IJavaElement[] {}; |
| } |
| } |
| |
| @Override |
| public IType findPrimaryType() { |
| return null; |
| } |
| |
| @Override |
| public boolean isClass() throws JavaModelException { |
| return false; |
| } |
| |
| @Override |
| public boolean isInterface() throws JavaModelException { |
| return false; |
| } |
| |
| @Override |
| public IType getType() { |
| throw new UnsupportedOperationException("IClassFile#getType() cannot be used on an IModularClassFile"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns the <code>IBinaryModule</code> specific for this IClassFile, based |
| * on its underlying resource, or <code>null</code> if unable to create |
| * the diet class file. |
| * There are two cases to consider:<ul> |
| * <li>a class file corresponding to an IFile resource</li> |
| * <li>a class file corresponding to a zip entry in a JAR</li> |
| * </ul> |
| * |
| * @exception JavaModelException when the IFile resource or JAR is not available |
| * or when this class file is not present in the JAR |
| */ |
| public IBinaryModule getBinaryModuleInfo() throws JavaModelException { |
| try { |
| IBinaryModule info = getJarBinaryModuleInfo(); |
| if (info == null) { |
| throw newNotPresentException(); |
| } |
| return info; |
| } catch (ClassFormatException cfe) { |
| //the structure remains unknown |
| if (JavaCore.getPlugin().isDebugging()) { |
| cfe.printStackTrace(System.err); |
| } |
| return null; |
| } catch (IOException ioe) { |
| throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION); |
| } catch (CoreException e) { |
| if (e instanceof JavaModelException) { |
| throw (JavaModelException)e; |
| } else { |
| throw new JavaModelException(e); |
| } |
| } |
| } |
| |
| private IBinaryModule getJarBinaryModuleInfo() throws CoreException, IOException, ClassFormatException { |
| BinaryModuleDescriptor descriptor = BinaryModuleFactory.createDescriptor(this); |
| |
| if (descriptor == null) { |
| return null; |
| } |
| IBinaryModule result = null; |
| IPackageFragmentRoot root = getPackageFragmentRoot(); |
| if (getPackageFragmentRoot() instanceof JarPackageFragmentRoot) { |
| if (root instanceof JrtPackageFragmentRoot || this.name.equals(IModule.MODULE_INFO)) { |
| PackageFragment pkg = (PackageFragment) getParent(); |
| JarPackageFragmentRoot jarRoot = (JarPackageFragmentRoot) getPackageFragmentRoot(); |
| String entryName = jarRoot.getClassFilePath(Util.concatWith(pkg.names, getElementName(), '/')); |
| byte[] contents = getClassFileContent(jarRoot, entryName); |
| if (contents != null) { |
| String fileName = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName; |
| ClassFileReader classFileReader = new ClassFileReader(contents, fileName.toCharArray(), false); |
| return classFileReader.getModuleDeclaration(); |
| } |
| } else { |
| result = BinaryModuleFactory.readModule(descriptor, null); |
| } |
| } else { |
| result = BinaryModuleFactory.readModule(descriptor, null); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * @see ITypeRoot |
| */ |
| @Override |
| public IJavaElement getElementAt(int position) throws JavaModelException { |
| IJavaElement parentElement = getParent(); |
| while (parentElement.getElementType() != IJavaElement.PACKAGE_FRAGMENT_ROOT) { |
| parentElement = parentElement.getParent(); |
| } |
| PackageFragmentRoot root = (PackageFragmentRoot) parentElement; |
| SourceMapper mapper = root.getSourceMapper(); |
| if (mapper == null) { |
| return null; |
| } else { |
| // ensure this class file's buffer is open so that source ranges are computed |
| getBuffer(); |
| |
| IModuleDescription module = getModule(); |
| return findElement(module, position, mapper); |
| } |
| } |
| @Override |
| public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) { |
| switch (token.charAt(0)) { |
| case JEM_MODULE: |
| if (!memento.hasMoreTokens()) return this; |
| String modName = memento.nextToken(); |
| JavaElement mod = new BinaryModule(this, modName); |
| return mod.getHandleFromMemento(memento, owner); |
| } |
| return null; |
| } |
| /** |
| * @see JavaElement#getHandleMemento() |
| */ |
| @Override |
| protected char getHandleMementoDelimiter() { |
| return JavaElement.JEM_MODULAR_CLASSFILE; |
| } |
| @Override |
| protected void escapeMementoName(StringBuffer buffer, String mementoName) { |
| // nop, name is irrelevant |
| } |
| @Override |
| public ICompilationUnit getWorkingCopy(WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException { |
| CompilationUnit workingCopy = new ClassFileWorkingCopy(this, owner == null ? DefaultWorkingCopyOwner.PRIMARY : owner); |
| JavaModelManager manager = JavaModelManager.getJavaModelManager(); |
| JavaModelManager.PerWorkingCopyInfo perWorkingCopyInfo = |
| manager.getPerWorkingCopyInfo(workingCopy, false/*don't create*/, true/*record usage*/, null/*not used since don't create*/); |
| if (perWorkingCopyInfo != null) { |
| return perWorkingCopyInfo.getWorkingCopy(); // return existing handle instead of the one created above |
| } |
| BecomeWorkingCopyOperation op = new BecomeWorkingCopyOperation(workingCopy, null); |
| op.runOperation(monitor); |
| return workingCopy; |
| } |
| /** |
| * Opens and returns buffer on the source code associated with this class file. |
| * Maps the source code to the children elements of this class file. |
| * If no source code is associated with this class file, |
| * <code>null</code> is returned. |
| * |
| * @see Openable |
| */ |
| @Override |
| protected IBuffer openBuffer(IProgressMonitor pm, Object info) throws JavaModelException { |
| SourceMapper mapper = getSourceMapper(); |
| if (mapper != null) { |
| return mapSource(mapper); |
| } |
| return null; |
| } |
| |
| /** Loads the buffer via SourceMapper, and maps it in SourceMapper */ |
| private IBuffer mapSource(SourceMapper mapper) throws JavaModelException { |
| char[] contents = mapper.findSource(getModule()); |
| if (contents != null) { |
| // create buffer |
| IBuffer buffer = BufferManager.createBuffer(this); |
| if (buffer == null) return null; |
| BufferManager bufManager = getBufferManager(); |
| bufManager.addBuffer(buffer); |
| |
| // set the buffer source |
| if (buffer.getCharacters() == null){ |
| buffer.setContents(contents); |
| } |
| |
| // listen to buffer changes |
| buffer.addBufferChangedListener(this); |
| |
| // do the source mapping |
| mapper.mapSource((NamedMember) getModule(), contents, null); |
| |
| return buffer; |
| } else { |
| // create buffer |
| IBuffer buffer = BufferManager.createNullBuffer(this); |
| if (buffer == null) return null; |
| BufferManager bufManager = getBufferManager(); |
| bufManager.addBuffer(buffer); |
| |
| // listen to buffer changes |
| buffer.addBufferChangedListener(this); |
| return buffer; |
| } |
| } |
| |
| @Override |
| public IModuleDescription getModule() throws JavaModelException { |
| if (this.binaryModule == null) { |
| openWhenClosed(createElementInfo(), false, null); |
| if (this.binaryModule == null) |
| throw newNotPresentException(); |
| } |
| return this.binaryModule; |
| } |
| } |