Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: df08044e18600bba7018b123830c3d6b10189aa3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
/*******************************************************************************
 * Copyright (c) 2017 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.HashMap;
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;
		}
		
		// Read the module	
		BinaryModule module = ((ClassFileInfo) info).readBinaryModule(this, (HashMap<?,?>) newElements, moduleInfo);
		if (module != null) {
			this.binaryModule = module;
			info.setChildren(new IJavaElement[] {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;
					String rootPath = root.getPath().toOSString();
					if (org.eclipse.jdt.internal.compiler.util.Util.isJrt(rootPath)) {
						fileName = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + 
								root.getElementName() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName;
					} else {
						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
	 */
	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;
	}
}

Back to the top