001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core;
011:
012: import java.util.Enumeration;
013: import java.util.HashMap;
014: import java.util.Map;
015:
016: import org.eclipse.core.resources.*;
017: import org.eclipse.core.runtime.IProgressMonitor;
018: import org.eclipse.core.runtime.OperationCanceledException;
019: import org.eclipse.core.runtime.PerformanceStats;
020: import org.eclipse.jdt.core.*;
021: import org.eclipse.jdt.internal.codeassist.CompletionEngine;
022: import org.eclipse.jdt.internal.codeassist.SelectionEngine;
023: import org.eclipse.jdt.internal.core.util.Util;
024:
025: /**
026: * Abstract class for implementations of java elements which are IOpenable.
027: *
028: * @see IJavaElement
029: * @see IOpenable
030: */
031: public abstract class Openable extends JavaElement implements
032: IOpenable, IBufferChangedListener {
033:
034: protected Openable(JavaElement parent) {
035: super (parent);
036: }
037:
038: /**
039: * The buffer associated with this element has changed. Registers
040: * this element as being out of synch with its buffer's contents.
041: * If the buffer has been closed, this element is set as NOT out of
042: * synch with the contents.
043: *
044: * @see IBufferChangedListener
045: */
046: public void bufferChanged(BufferChangedEvent event) {
047: if (event.getBuffer().isClosed()) {
048: JavaModelManager.getJavaModelManager()
049: .getElementsOutOfSynchWithBuffers().remove(this );
050: getBufferManager().removeBuffer(event.getBuffer());
051: } else {
052: JavaModelManager.getJavaModelManager()
053: .getElementsOutOfSynchWithBuffers().add(this );
054: }
055: }
056:
057: /**
058: * Builds this element's structure and properties in the given
059: * info object, based on this element's current contents (reuse buffer
060: * contents if this element has an open buffer, or resource contents
061: * if this element does not have an open buffer). Children
062: * are placed in the given newElements table (note, this element
063: * has already been placed in the newElements table). Returns true
064: * if successful, or false if an error is encountered while determining
065: * the structure of this element.
066: */
067: protected abstract boolean buildStructure(OpenableElementInfo info,
068: IProgressMonitor pm, Map newElements,
069: IResource underlyingResource) throws JavaModelException;
070:
071: /*
072: * Returns whether this element can be removed from the Java model cache to make space.
073: */
074: public boolean canBeRemovedFromCache() {
075: try {
076: return !hasUnsavedChanges();
077: } catch (JavaModelException e) {
078: return false;
079: }
080: }
081:
082: /*
083: * Returns whether the buffer of this element can be removed from the Java model cache to make space.
084: */
085: public boolean canBufferBeRemovedFromCache(IBuffer buffer) {
086: return !buffer.hasUnsavedChanges();
087: }
088:
089: /**
090: * Close the buffer associated with this element, if any.
091: */
092: protected void closeBuffer() {
093: if (!hasBuffer())
094: return; // nothing to do
095: IBuffer buffer = getBufferManager().getBuffer(this );
096: if (buffer != null) {
097: buffer.close();
098: buffer.removeBufferChangedListener(this );
099: }
100: }
101:
102: /**
103: * This element is being closed. Do any necessary cleanup.
104: */
105: protected void closing(Object info) {
106: closeBuffer();
107: }
108:
109: protected void codeComplete(
110: org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu,
111: org.eclipse.jdt.internal.compiler.env.ICompilationUnit unitToSkip,
112: int position, CompletionRequestor requestor,
113: WorkingCopyOwner owner) throws JavaModelException {
114: if (requestor == null) {
115: throw new IllegalArgumentException(
116: "Completion requestor cannot be null"); //$NON-NLS-1$
117: }
118: PerformanceStats performanceStats = CompletionEngine.PERF ? PerformanceStats
119: .getStats(JavaModelManager.COMPLETION_PERF, this )
120: : null;
121: if (performanceStats != null) {
122: performanceStats.startRun(new String(cu.getFileName())
123: + " at " + position); //$NON-NLS-1$
124: }
125: IBuffer buffer = getBuffer();
126: if (buffer == null) {
127: return;
128: }
129: if (position < -1 || position > buffer.getLength()) {
130: throw new JavaModelException(new JavaModelStatus(
131: IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
132: }
133: JavaProject project = (JavaProject) getJavaProject();
134: SearchableEnvironment environment = project
135: .newSearchableNameEnvironment(owner);
136:
137: // set unit to skip
138: environment.unitToSkip = unitToSkip;
139:
140: // code complete
141: CompletionEngine engine = new CompletionEngine(environment,
142: requestor, project.getOptions(true), project);
143: engine.complete(cu, position, 0);
144: if (performanceStats != null) {
145: performanceStats.endRun();
146: }
147: if (NameLookup.VERBOSE) {
148: System.out
149: .println(Thread.currentThread()
150: + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " + environment.nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
151: System.out
152: .println(Thread.currentThread()
153: + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " + environment.nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
154: }
155: }
156:
157: protected IJavaElement[] codeSelect(
158: org.eclipse.jdt.internal.compiler.env.ICompilationUnit cu,
159: int offset, int length, WorkingCopyOwner owner)
160: throws JavaModelException {
161: PerformanceStats performanceStats = SelectionEngine.PERF ? PerformanceStats
162: .getStats(JavaModelManager.SELECTION_PERF, this )
163: : null;
164: if (performanceStats != null) {
165: performanceStats.startRun(new String(cu.getFileName())
166: + " at [" + offset + "," + length + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
167: }
168:
169: JavaProject project = (JavaProject) getJavaProject();
170: SearchableEnvironment environment = project
171: .newSearchableNameEnvironment(owner);
172:
173: SelectionRequestor requestor = new SelectionRequestor(
174: environment.nameLookup, this );
175: IBuffer buffer = getBuffer();
176: if (buffer == null) {
177: return requestor.getElements();
178: }
179: int end = buffer.getLength();
180: if (offset < 0 || length < 0 || offset + length > end) {
181: throw new JavaModelException(new JavaModelStatus(
182: IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS));
183: }
184:
185: // fix for 1FVXGDK
186: SelectionEngine engine = new SelectionEngine(environment,
187: requestor, project.getOptions(true));
188: engine.select(cu, offset, offset + length - 1);
189:
190: if (performanceStats != null) {
191: performanceStats.endRun();
192: }
193: if (NameLookup.VERBOSE) {
194: System.out
195: .println(Thread.currentThread()
196: + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " + environment.nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
197: System.out
198: .println(Thread.currentThread()
199: + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " + environment.nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
200: }
201: return requestor.getElements();
202: }
203:
204: /*
205: * Returns a new element info for this element.
206: */
207: protected Object createElementInfo() {
208: return new OpenableElementInfo();
209: }
210:
211: /**
212: * @see IJavaElement
213: */
214: public boolean exists() {
215: JavaModelManager manager = JavaModelManager
216: .getJavaModelManager();
217: if (manager.getInfo(this ) != null)
218: return true;
219: if (!parentExists())
220: return false;
221: PackageFragmentRoot root = getPackageFragmentRoot();
222: if (root != null && (root == this || !root.isArchive())) {
223: return resourceExists();
224: }
225: return super .exists();
226: }
227:
228: public String findRecommendedLineSeparator()
229: throws JavaModelException {
230: IBuffer buffer = getBuffer();
231: String source = buffer == null ? null : buffer.getContents();
232: return Util.getLineSeparator(source, getJavaProject());
233: }
234:
235: protected void generateInfos(Object info, HashMap newElements,
236: IProgressMonitor monitor) throws JavaModelException {
237:
238: if (JavaModelCache.VERBOSE) {
239: String element;
240: switch (getElementType()) {
241: case JAVA_PROJECT:
242: element = "project"; //$NON-NLS-1$
243: break;
244: case PACKAGE_FRAGMENT_ROOT:
245: element = "root"; //$NON-NLS-1$
246: break;
247: case PACKAGE_FRAGMENT:
248: element = "package"; //$NON-NLS-1$
249: break;
250: case CLASS_FILE:
251: element = "class file"; //$NON-NLS-1$
252: break;
253: case COMPILATION_UNIT:
254: element = "compilation unit"; //$NON-NLS-1$
255: break;
256: default:
257: element = "element"; //$NON-NLS-1$
258: }
259: System.out
260: .println(Thread.currentThread()
261: + " OPENING " + element + " " + this .toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
262: }
263:
264: // open the parent if necessary
265: openParent(info, newElements, monitor);
266: if (monitor != null && monitor.isCanceled())
267: throw new OperationCanceledException();
268:
269: // puts the info before building the structure so that questions to the handle behave as if the element existed
270: // (case of compilation units becoming working copies)
271: newElements.put(this , info);
272:
273: // build the structure of the openable (this will open the buffer if needed)
274: try {
275: OpenableElementInfo openableElementInfo = (OpenableElementInfo) info;
276: boolean isStructureKnown = buildStructure(
277: openableElementInfo, monitor, newElements,
278: getResource());
279: openableElementInfo.setIsStructureKnown(isStructureKnown);
280: } catch (JavaModelException e) {
281: newElements.remove(this );
282: throw e;
283: }
284:
285: // remove out of sync buffer for this element
286: JavaModelManager.getJavaModelManager()
287: .getElementsOutOfSynchWithBuffers().remove(this );
288:
289: if (JavaModelCache.VERBOSE) {
290: System.out.println(JavaModelManager.getJavaModelManager()
291: .cacheToString("-> ")); //$NON-NLS-1$
292: }
293: }
294:
295: /**
296: * Note: a buffer with no unsaved changes can be closed by the Java Model
297: * since it has a finite number of buffers allowed open at one time. If this
298: * is the first time a request is being made for the buffer, an attempt is
299: * made to create and fill this element's buffer. If the buffer has been
300: * closed since it was first opened, the buffer is re-created.
301: *
302: * @see IOpenable
303: */
304: public IBuffer getBuffer() throws JavaModelException {
305: if (hasBuffer()) {
306: // ensure element is open
307: Object info = getElementInfo();
308: IBuffer buffer = getBufferManager().getBuffer(this );
309: if (buffer == null) {
310: // try to (re)open a buffer
311: buffer = openBuffer(null, info);
312: }
313: if (buffer instanceof NullBuffer) {
314: return null;
315: }
316: return buffer;
317: } else {
318: return null;
319: }
320: }
321:
322: /**
323: * Answers the buffer factory to use for creating new buffers
324: * @deprecated
325: */
326: public IBufferFactory getBufferFactory() {
327: return getBufferManager().getDefaultBufferFactory();
328: }
329:
330: /**
331: * Returns the buffer manager for this element.
332: */
333: protected BufferManager getBufferManager() {
334: return BufferManager.getDefaultBufferManager();
335: }
336:
337: /**
338: * Return my underlying resource. Elements that may not have a
339: * corresponding resource must override this method.
340: *
341: * @see IJavaElement
342: */
343: public IResource getCorrespondingResource()
344: throws JavaModelException {
345: return getUnderlyingResource();
346: }
347:
348: /*
349: * @see IJavaElement
350: */
351: public IOpenable getOpenable() {
352: return this ;
353: }
354:
355: /**
356: * @see IJavaElement
357: */
358: public IResource getUnderlyingResource() throws JavaModelException {
359: IResource parentResource = this .parent.getUnderlyingResource();
360: if (parentResource == null) {
361: return null;
362: }
363: int type = parentResource.getType();
364: if (type == IResource.FOLDER || type == IResource.PROJECT) {
365: IContainer folder = (IContainer) parentResource;
366: IResource resource = folder.findMember(getElementName());
367: if (resource == null) {
368: throw newNotPresentException();
369: } else {
370: return resource;
371: }
372: } else {
373: return parentResource;
374: }
375: }
376:
377: /**
378: * Returns true if this element may have an associated source buffer,
379: * otherwise false. Subclasses must override as required.
380: */
381: protected boolean hasBuffer() {
382: return false;
383: }
384:
385: /**
386: * @see IOpenable
387: */
388: public boolean hasUnsavedChanges() throws JavaModelException {
389:
390: if (isReadOnly() || !isOpen()) {
391: return false;
392: }
393: IBuffer buf = this .getBuffer();
394: if (buf != null && buf.hasUnsavedChanges()) {
395: return true;
396: }
397: // for package fragments, package fragment roots, and projects must check open buffers
398: // to see if they have an child with unsaved changes
399: int elementType = getElementType();
400: if (elementType == PACKAGE_FRAGMENT
401: || elementType == PACKAGE_FRAGMENT_ROOT
402: || elementType == JAVA_PROJECT
403: || elementType == JAVA_MODEL) { // fix for 1FWNMHH
404: Enumeration openBuffers = getBufferManager()
405: .getOpenBuffers();
406: while (openBuffers.hasMoreElements()) {
407: IBuffer buffer = (IBuffer) openBuffers.nextElement();
408: if (buffer.hasUnsavedChanges()) {
409: IJavaElement owner = (IJavaElement) buffer
410: .getOwner();
411: if (isAncestorOf(owner)) {
412: return true;
413: }
414: }
415: }
416: }
417:
418: return false;
419: }
420:
421: /**
422: * Subclasses must override as required.
423: *
424: * @see IOpenable
425: */
426: public boolean isConsistent() {
427: return true;
428: }
429:
430: /**
431: *
432: * @see IOpenable
433: */
434: public boolean isOpen() {
435: return JavaModelManager.getJavaModelManager().getInfo(this ) != null;
436: }
437:
438: /**
439: * Returns true if this represents a source element.
440: * Openable source elements have an associated buffer created
441: * when they are opened.
442: */
443: protected boolean isSourceElement() {
444: return false;
445: }
446:
447: /**
448: * @see IJavaElement
449: */
450: public boolean isStructureKnown() throws JavaModelException {
451: return ((OpenableElementInfo) getElementInfo())
452: .isStructureKnown();
453: }
454:
455: /**
456: * @see IOpenable
457: */
458: public void makeConsistent(IProgressMonitor monitor)
459: throws JavaModelException {
460: // only compilation units can be inconsistent
461: // other openables cannot be inconsistent so default is to do nothing
462: }
463:
464: /**
465: * @see IOpenable
466: */
467: public void open(IProgressMonitor pm) throws JavaModelException {
468: getElementInfo(pm);
469: }
470:
471: /**
472: * Opens a buffer on the contents of this element, and returns
473: * the buffer, or returns <code>null</code> if opening fails.
474: * By default, do nothing - subclasses that have buffers
475: * must override as required.
476: */
477: protected IBuffer openBuffer(IProgressMonitor pm, Object info)
478: throws JavaModelException {
479: return null;
480: }
481:
482: /**
483: * Open the parent element if necessary.
484: */
485: protected void openParent(Object childInfo, HashMap newElements,
486: IProgressMonitor pm) throws JavaModelException {
487:
488: Openable openableParent = (Openable) getOpenableParent();
489: if (openableParent != null && !openableParent.isOpen()) {
490: openableParent.generateInfos(openableParent
491: .createElementInfo(), newElements, pm);
492: }
493: }
494:
495: /**
496: * Answers true if the parent exists (null parent is answering true)
497: *
498: */
499: protected boolean parentExists() {
500:
501: IJavaElement parentElement = getParent();
502: if (parentElement == null)
503: return true;
504: return parentElement.exists();
505: }
506:
507: /**
508: * Returns whether the corresponding resource or associated file exists
509: */
510: protected boolean resourceExists() {
511: IWorkspace workspace = ResourcesPlugin.getWorkspace();
512: if (workspace == null)
513: return false; // workaround for http://bugs.eclipse.org/bugs/show_bug.cgi?id=34069
514: return JavaModel.getTarget(workspace.getRoot(), this .getPath()
515: .makeRelative(), // ensure path is relative (see http://dev.eclipse.org/bugs/show_bug.cgi?id=22517)
516: true) != null;
517: }
518:
519: /**
520: * @see IOpenable
521: */
522: public void save(IProgressMonitor pm, boolean force)
523: throws JavaModelException {
524: if (isReadOnly()) {
525: throw new JavaModelException(new JavaModelStatus(
526: IJavaModelStatusConstants.READ_ONLY, this ));
527: }
528: IBuffer buf = getBuffer();
529: if (buf != null) { // some Openables (like a JavaProject) don't have a buffer
530: buf.save(pm, force);
531: this .makeConsistent(pm); // update the element info of this element
532: }
533: }
534:
535: /**
536: * Find enclosing package fragment root if any
537: */
538: public PackageFragmentRoot getPackageFragmentRoot() {
539: return (PackageFragmentRoot) getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
540: }
541:
542: }
|