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.io.BufferedInputStream;
013: import java.io.FileNotFoundException;
014: import java.io.IOException;
015: import java.io.InputStream;
016: import java.io.PrintWriter;
017: import java.io.StringWriter;
018: import java.net.JarURLConnection;
019: import java.net.MalformedURLException;
020: import java.net.URL;
021: import java.net.URLConnection;
022: import java.util.ArrayList;
023: import java.util.HashMap;
024:
025: import org.eclipse.core.resources.IResource;
026: import org.eclipse.core.runtime.*;
027: import org.eclipse.core.runtime.jobs.ISchedulingRule;
028: import org.eclipse.jdt.core.*;
029: import org.eclipse.jdt.core.dom.ASTNode;
030: import org.eclipse.jdt.core.dom.CompilationUnit;
031: import org.eclipse.jdt.internal.compiler.lookup.Binding;
032: import org.eclipse.jdt.internal.core.util.MementoTokenizer;
033: import org.eclipse.jdt.internal.core.util.Util;
034:
035: /**
036: * Root of Java element handle hierarchy.
037: *
038: * @see IJavaElement
039: */
040: public abstract class JavaElement extends PlatformObject implements
041: IJavaElement {
042: // private static final QualifiedName PROJECT_JAVADOC= new QualifiedName(JavaCore.PLUGIN_ID, "project_javadoc_location"); //$NON-NLS-1$
043:
044: private static final byte[] CLOSING_DOUBLE_QUOTE = new byte[] { 34 };
045: private static final byte[] CHARSET = new byte[] { 99, 104, 97,
046: 114, 115, 101, 116, 61 };
047: private static final byte[] CONTENT_TYPE = new byte[] { 34, 67,
048: 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 34 };
049: private static final byte[] CONTENT = new byte[] { 99, 111, 110,
050: 116, 101, 110, 116, 61, 34 };
051: public static final char JEM_ESCAPE = '\\';
052: public static final char JEM_JAVAPROJECT = '=';
053: public static final char JEM_PACKAGEFRAGMENTROOT = '/';
054: public static final char JEM_PACKAGEFRAGMENT = '<';
055: public static final char JEM_FIELD = '^';
056: public static final char JEM_METHOD = '~';
057: public static final char JEM_INITIALIZER = '|';
058: public static final char JEM_COMPILATIONUNIT = '{';
059: public static final char JEM_CLASSFILE = '(';
060: public static final char JEM_TYPE = '[';
061: public static final char JEM_PACKAGEDECLARATION = '%';
062: public static final char JEM_IMPORTDECLARATION = '#';
063: public static final char JEM_COUNT = '!';
064: public static final char JEM_LOCALVARIABLE = '@';
065: public static final char JEM_TYPE_PARAMETER = ']';
066:
067: /**
068: * This element's parent, or <code>null</code> if this
069: * element does not have a parent.
070: */
071: protected JavaElement parent;
072:
073: protected static final JavaElement[] NO_ELEMENTS = new JavaElement[0];
074: protected static final Object NO_INFO = new Object();
075:
076: /**
077: * Constructs a handle for a java element with
078: * the given parent element.
079: *
080: * @param parent The parent of java element
081: *
082: * @exception IllegalArgumentException if the type is not one of the valid
083: * Java element type constants
084: *
085: */
086: protected JavaElement(JavaElement parent)
087: throws IllegalArgumentException {
088: this .parent = parent;
089: }
090:
091: /**
092: * @see IOpenable
093: */
094: public void close() throws JavaModelException {
095: JavaModelManager.getJavaModelManager().removeInfoAndChildren(
096: this );
097: }
098:
099: /**
100: * This element is being closed. Do any necessary cleanup.
101: */
102: protected abstract void closing(Object info)
103: throws JavaModelException;
104:
105: /*
106: * Returns a new element info for this element.
107: */
108: protected abstract Object createElementInfo();
109:
110: /**
111: * Returns true if this handle represents the same Java element
112: * as the given handle. By default, two handles represent the same
113: * element if they are identical or if they represent the same type
114: * of element, have equal names, parents, and occurrence counts.
115: *
116: * <p>If a subclass has other requirements for equality, this method
117: * must be overridden.
118: *
119: * @see Object#equals
120: */
121: public boolean equals(Object o) {
122:
123: if (this == o)
124: return true;
125:
126: // Java model parent is null
127: if (this .parent == null)
128: return super .equals(o);
129:
130: // assume instanceof check is done in subclass
131: JavaElement other = (JavaElement) o;
132: return getElementName().equals(other.getElementName())
133: && this .parent.equals(other.parent);
134: }
135:
136: protected void escapeMementoName(StringBuffer buffer,
137: String mementoName) {
138: for (int i = 0, length = mementoName.length(); i < length; i++) {
139: char character = mementoName.charAt(i);
140: switch (character) {
141: case JEM_ESCAPE:
142: case JEM_COUNT:
143: case JEM_JAVAPROJECT:
144: case JEM_PACKAGEFRAGMENTROOT:
145: case JEM_PACKAGEFRAGMENT:
146: case JEM_FIELD:
147: case JEM_METHOD:
148: case JEM_INITIALIZER:
149: case JEM_COMPILATIONUNIT:
150: case JEM_CLASSFILE:
151: case JEM_TYPE:
152: case JEM_PACKAGEDECLARATION:
153: case JEM_IMPORTDECLARATION:
154: case JEM_LOCALVARIABLE:
155: case JEM_TYPE_PARAMETER:
156: buffer.append(JEM_ESCAPE);
157: }
158: buffer.append(character);
159: }
160: }
161:
162: /**
163: * @see IJavaElement
164: */
165: public boolean exists() {
166:
167: try {
168: getElementInfo();
169: return true;
170: } catch (JavaModelException e) {
171: // element doesn't exist: return false
172: }
173: return false;
174: }
175:
176: /**
177: * Returns the <code>ASTNode</code> that corresponds to this <code>JavaElement</code>
178: * or <code>null</code> if there is no corresponding node.
179: */
180: public ASTNode findNode(CompilationUnit ast) {
181: return null; // works only inside a compilation unit
182: }
183:
184: /**
185: * Generates the element infos for this element, its ancestors (if they are not opened) and its children (if it is an Openable).
186: * Puts the newly created element info in the given map.
187: */
188: protected abstract void generateInfos(Object info,
189: HashMap newElements, IProgressMonitor pm)
190: throws JavaModelException;
191:
192: /**
193: * @see IJavaElement
194: */
195: public IJavaElement getAncestor(int ancestorType) {
196:
197: IJavaElement element = this ;
198: while (element != null) {
199: if (element.getElementType() == ancestorType)
200: return element;
201: element = element.getParent();
202: }
203: return null;
204: }
205:
206: /**
207: * @see IParent
208: */
209: public IJavaElement[] getChildren() throws JavaModelException {
210: Object elementInfo = getElementInfo();
211: if (elementInfo instanceof JavaElementInfo) {
212: return ((JavaElementInfo) elementInfo).getChildren();
213: } else {
214: return NO_ELEMENTS;
215: }
216: }
217:
218: /**
219: * Returns a collection of (immediate) children of this node of the
220: * specified type.
221: *
222: * @param type - one of the JEM_* constants defined by JavaElement
223: */
224: public ArrayList getChildrenOfType(int type)
225: throws JavaModelException {
226: IJavaElement[] children = getChildren();
227: int size = children.length;
228: ArrayList list = new ArrayList(size);
229: for (int i = 0; i < size; ++i) {
230: JavaElement elt = (JavaElement) children[i];
231: if (elt.getElementType() == type) {
232: list.add(elt);
233: }
234: }
235: return list;
236: }
237:
238: /**
239: * @see IMember
240: */
241: public IClassFile getClassFile() {
242: return null;
243: }
244:
245: /**
246: * @see IMember
247: */
248: public ICompilationUnit getCompilationUnit() {
249: return null;
250: }
251:
252: /**
253: * Returns the info for this handle.
254: * If this element is not already open, it and all of its parents are opened.
255: * Does not return null.
256: * NOTE: BinaryType infos are NOT rooted under JavaElementInfo.
257: * @exception JavaModelException if the element is not present or not accessible
258: */
259: public Object getElementInfo() throws JavaModelException {
260: return getElementInfo(null);
261: }
262:
263: /**
264: * Returns the info for this handle.
265: * If this element is not already open, it and all of its parents are opened.
266: * Does not return null.
267: * NOTE: BinaryType infos are NOT rooted under JavaElementInfo.
268: * @exception JavaModelException if the element is not present or not accessible
269: */
270: public Object getElementInfo(IProgressMonitor monitor)
271: throws JavaModelException {
272:
273: JavaModelManager manager = JavaModelManager
274: .getJavaModelManager();
275: Object info = manager.getInfo(this );
276: if (info != null)
277: return info;
278: return openWhenClosed(createElementInfo(), monitor);
279: }
280:
281: /**
282: * @see IAdaptable
283: */
284: public String getElementName() {
285: return ""; //$NON-NLS-1$
286: }
287:
288: /*
289: * Creates a Java element handle from the given memento.
290: * The given token is the current delimiter indicating the type of the next token(s).
291: * The given working copy owner is used only for compilation unit handles.
292: */
293: public abstract IJavaElement getHandleFromMemento(String token,
294: MementoTokenizer memento, WorkingCopyOwner owner);
295:
296: /*
297: * Creates a Java element handle from the given memento.
298: * The given working copy owner is used only for compilation unit handles.
299: */
300: public IJavaElement getHandleFromMemento(MementoTokenizer memento,
301: WorkingCopyOwner owner) {
302: if (!memento.hasMoreTokens())
303: return this ;
304: String token = memento.nextToken();
305: return getHandleFromMemento(token, memento, owner);
306: }
307:
308: /**
309: * @see IJavaElement
310: */
311: public String getHandleIdentifier() {
312: return getHandleMemento();
313: }
314:
315: /**
316: * @see JavaElement#getHandleMemento()
317: */
318: public String getHandleMemento() {
319: StringBuffer buff = new StringBuffer();
320: getHandleMemento(buff);
321: return buff.toString();
322: }
323:
324: protected void getHandleMemento(StringBuffer buff) {
325: ((JavaElement) getParent()).getHandleMemento(buff);
326: buff.append(getHandleMementoDelimiter());
327: escapeMementoName(buff, getElementName());
328: }
329:
330: /**
331: * Returns the <code>char</code> that marks the start of this handles
332: * contribution to a memento.
333: */
334: protected abstract char getHandleMementoDelimiter();
335:
336: /**
337: * @see IJavaElement
338: */
339: public IJavaModel getJavaModel() {
340: IJavaElement current = this ;
341: do {
342: if (current instanceof IJavaModel)
343: return (IJavaModel) current;
344: } while ((current = current.getParent()) != null);
345: return null;
346: }
347:
348: /**
349: * @see IJavaElement
350: */
351: public IJavaProject getJavaProject() {
352: IJavaElement current = this ;
353: do {
354: if (current instanceof IJavaProject)
355: return (IJavaProject) current;
356: } while ((current = current.getParent()) != null);
357: return null;
358: }
359:
360: /*
361: * @see IJavaElement
362: */
363: public IOpenable getOpenable() {
364: return this .getOpenableParent();
365: }
366:
367: /**
368: * Return the first instance of IOpenable in the parent
369: * hierarchy of this element.
370: *
371: * <p>Subclasses that are not IOpenable's must override this method.
372: */
373: public IOpenable getOpenableParent() {
374: return (IOpenable) this .parent;
375: }
376:
377: /**
378: * @see IJavaElement
379: */
380: public IJavaElement getParent() {
381: return this .parent;
382: }
383:
384: /*
385: * @see IJavaElement#getPrimaryElement()
386: */
387: public IJavaElement getPrimaryElement() {
388: return getPrimaryElement(true);
389: }
390:
391: /*
392: * Returns the primary element. If checkOwner, and the cu owner is primary,
393: * return this element.
394: */
395: public IJavaElement getPrimaryElement(boolean checkOwner) {
396: return this ;
397: }
398:
399: /**
400: * Returns the element that is located at the given source position
401: * in this element. This is a helper method for <code>ICompilationUnit#getElementAt</code>,
402: * and only works on compilation units and types. The position given is
403: * known to be within this element's source range already, and if no finer
404: * grained element is found at the position, this element is returned.
405: */
406: protected IJavaElement getSourceElementAt(int position)
407: throws JavaModelException {
408: if (this instanceof ISourceReference) {
409: IJavaElement[] children = getChildren();
410: for (int i = children.length - 1; i >= 0; i--) {
411: IJavaElement aChild = children[i];
412: if (aChild instanceof SourceRefElement) {
413: SourceRefElement child = (SourceRefElement) children[i];
414: ISourceRange range = child.getSourceRange();
415: int start = range.getOffset();
416: int end = start + range.getLength();
417: if (start <= position && position <= end) {
418: if (child instanceof IField) {
419: // check muti-declaration case (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=39943)
420: int declarationStart = start;
421: SourceRefElement candidate = null;
422: do {
423: // check name range
424: range = ((IField) child).getNameRange();
425: if (position <= range.getOffset()
426: + range.getLength()) {
427: candidate = child;
428: } else {
429: return candidate == null ? child
430: .getSourceElementAt(position)
431: : candidate
432: .getSourceElementAt(position);
433: }
434: child = --i >= 0 ? (SourceRefElement) children[i]
435: : null;
436: } while (child != null
437: && child.getSourceRange()
438: .getOffset() == declarationStart);
439: // position in field's type: use first field
440: return candidate
441: .getSourceElementAt(position);
442: } else if (child instanceof IParent) {
443: return child.getSourceElementAt(position);
444: } else {
445: return child;
446: }
447: }
448: }
449: }
450: } else {
451: // should not happen
452: Assert.isTrue(false);
453: }
454: return this ;
455: }
456:
457: /**
458: * Returns the SourceMapper facility for this element, or
459: * <code>null</code> if this element does not have a
460: * SourceMapper.
461: */
462: public SourceMapper getSourceMapper() {
463: return ((JavaElement) getParent()).getSourceMapper();
464: }
465:
466: /* (non-Javadoc)
467: * @see org.eclipse.jdt.core.IJavaElement#getSchedulingRule()
468: */
469: public ISchedulingRule getSchedulingRule() {
470: IResource resource = getResource();
471: if (resource == null) {
472: class NoResourceSchedulingRule implements ISchedulingRule {
473: public IPath path;
474:
475: public NoResourceSchedulingRule(IPath path) {
476: this .path = path;
477: }
478:
479: public boolean contains(ISchedulingRule rule) {
480: if (rule instanceof NoResourceSchedulingRule) {
481: return this .path
482: .isPrefixOf(((NoResourceSchedulingRule) rule).path);
483: } else {
484: return false;
485: }
486: }
487:
488: public boolean isConflicting(ISchedulingRule rule) {
489: if (rule instanceof NoResourceSchedulingRule) {
490: IPath otherPath = ((NoResourceSchedulingRule) rule).path;
491: return this .path.isPrefixOf(otherPath)
492: || otherPath.isPrefixOf(this .path);
493: } else {
494: return false;
495: }
496: }
497: }
498: return new NoResourceSchedulingRule(getPath());
499: } else {
500: return resource;
501: }
502: }
503:
504: /**
505: * @see IParent
506: */
507: public boolean hasChildren() throws JavaModelException {
508: // if I am not open, return true to avoid opening (case of a Java project, a compilation unit or a class file).
509: // also see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52474
510: Object elementInfo = JavaModelManager.getJavaModelManager()
511: .getInfo(this );
512: if (elementInfo instanceof JavaElementInfo) {
513: return ((JavaElementInfo) elementInfo).getChildren().length > 0;
514: } else {
515: return true;
516: }
517: }
518:
519: /**
520: * Returns the hash code for this Java element. By default,
521: * the hash code for an element is a combination of its name
522: * and parent's hash code. Elements with other requirements must
523: * override this method.
524: */
525: public int hashCode() {
526: if (this .parent == null)
527: return super .hashCode();
528: return Util.combineHashCodes(getElementName().hashCode(),
529: this .parent.hashCode());
530: }
531:
532: /**
533: * Returns true if this element is an ancestor of the given element,
534: * otherwise false.
535: */
536: public boolean isAncestorOf(IJavaElement e) {
537: IJavaElement parentElement = e.getParent();
538: while (parentElement != null && !parentElement.equals(this )) {
539: parentElement = parentElement.getParent();
540: }
541: return parentElement != null;
542: }
543:
544: /**
545: * @see IJavaElement
546: */
547: public boolean isReadOnly() {
548: return false;
549: }
550:
551: /**
552: * Creates and returns a new not present exception for this element.
553: */
554: public JavaModelException newNotPresentException() {
555: return new JavaModelException(new JavaModelStatus(
556: IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, this ));
557: }
558:
559: /**
560: * Creates and returns a new Java model exception for this element with the given status.
561: */
562: public JavaModelException newJavaModelException(IStatus status) {
563: if (status instanceof IJavaModelStatus)
564: return new JavaModelException((IJavaModelStatus) status);
565: else
566: return new JavaModelException(new JavaModelStatus(status
567: .getSeverity(), status.getCode(), status
568: .getMessage()));
569: }
570:
571: /*
572: * Opens an <code>Openable</code> that is known to be closed (no check for <code>isOpen()</code>).
573: * Returns the created element info.
574: */
575: protected Object openWhenClosed(Object info,
576: IProgressMonitor monitor) throws JavaModelException {
577: JavaModelManager manager = JavaModelManager
578: .getJavaModelManager();
579: boolean hadTemporaryCache = manager.hasTemporaryCache();
580: try {
581: HashMap newElements = manager.getTemporaryCache();
582: generateInfos(info, newElements, monitor);
583: if (info == null) {
584: info = newElements.get(this );
585: }
586: if (info == null) { // a source ref element could not be opened
587: // close the buffer that was opened for the openable parent
588: // close only the openable's buffer (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=62854)
589: Openable openable = (Openable) getOpenable();
590: if (newElements.containsKey(openable)) {
591: openable.closeBuffer();
592: }
593: throw newNotPresentException();
594: }
595: if (!hadTemporaryCache) {
596: manager.putInfos(this , newElements);
597: }
598: } finally {
599: if (!hadTemporaryCache) {
600: manager.resetTemporaryCache();
601: }
602: }
603: return info;
604: }
605:
606: /**
607: */
608: public String readableName() {
609: return this .getElementName();
610: }
611:
612: public JavaElement resolved(Binding binding) {
613: return this ;
614: }
615:
616: public JavaElement unresolved() {
617: return this ;
618: }
619:
620: protected String tabString(int tab) {
621: StringBuffer buffer = new StringBuffer();
622: for (int i = tab; i > 0; i--)
623: buffer.append(" "); //$NON-NLS-1$
624: return buffer.toString();
625: }
626:
627: /**
628: * Debugging purposes
629: */
630: public String toDebugString() {
631: StringBuffer buffer = new StringBuffer();
632: this
633: .toStringInfo(0, buffer, NO_INFO, true/*show resolved info*/);
634: return buffer.toString();
635: }
636:
637: /**
638: * Debugging purposes
639: */
640: public String toString() {
641: StringBuffer buffer = new StringBuffer();
642: toString(0, buffer);
643: return buffer.toString();
644: }
645:
646: /**
647: * Debugging purposes
648: */
649: protected void toString(int tab, StringBuffer buffer) {
650: Object info = this .toStringInfo(tab, buffer);
651: if (tab == 0) {
652: this .toStringAncestors(buffer);
653: }
654: this .toStringChildren(tab, buffer, info);
655: }
656:
657: /**
658: * Debugging purposes
659: */
660: public String toStringWithAncestors() {
661: return toStringWithAncestors(true/*show resolved info*/);
662: }
663:
664: /**
665: * Debugging purposes
666: */
667: public String toStringWithAncestors(boolean showResolvedInfo) {
668: StringBuffer buffer = new StringBuffer();
669: this .toStringInfo(0, buffer, NO_INFO, showResolvedInfo);
670: this .toStringAncestors(buffer);
671: return buffer.toString();
672: }
673:
674: /**
675: * Debugging purposes
676: */
677: protected void toStringAncestors(StringBuffer buffer) {
678: JavaElement parentElement = (JavaElement) this .getParent();
679: if (parentElement != null && parentElement.getParent() != null) {
680: buffer.append(" [in "); //$NON-NLS-1$
681: parentElement
682: .toStringInfo(0, buffer, NO_INFO, false/*don't show resolved info*/);
683: parentElement.toStringAncestors(buffer);
684: buffer.append("]"); //$NON-NLS-1$
685: }
686: }
687:
688: /**
689: * Debugging purposes
690: */
691: protected void toStringChildren(int tab, StringBuffer buffer,
692: Object info) {
693: if (info == null || !(info instanceof JavaElementInfo))
694: return;
695: IJavaElement[] children = ((JavaElementInfo) info)
696: .getChildren();
697: for (int i = 0; i < children.length; i++) {
698: buffer.append("\n"); //$NON-NLS-1$
699: ((JavaElement) children[i]).toString(tab + 1, buffer);
700: }
701: }
702:
703: /**
704: * Debugging purposes
705: */
706: public Object toStringInfo(int tab, StringBuffer buffer) {
707: Object info = JavaModelManager.getJavaModelManager()
708: .peekAtInfo(this );
709: this
710: .toStringInfo(tab, buffer, info, true/*show resolved info*/);
711: return info;
712: }
713:
714: /**
715: * Debugging purposes
716: * @param showResolvedInfo TODO
717: */
718: protected void toStringInfo(int tab, StringBuffer buffer,
719: Object info, boolean showResolvedInfo) {
720: buffer.append(this .tabString(tab));
721: toStringName(buffer);
722: if (info == null) {
723: buffer.append(" (not open)"); //$NON-NLS-1$
724: }
725: }
726:
727: /**
728: * Debugging purposes
729: */
730: protected void toStringName(StringBuffer buffer) {
731: buffer.append(getElementName());
732: }
733:
734: protected URL getJavadocBaseLocation() throws JavaModelException {
735: IPackageFragmentRoot root = (IPackageFragmentRoot) this
736: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
737: if (root == null) {
738: return null;
739: }
740:
741: if (root.getKind() == IPackageFragmentRoot.K_BINARY) {
742: IClasspathEntry entry = root.getRawClasspathEntry();
743: if (entry == null) {
744: return null;
745: }
746: if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) {
747: entry = getRealClasspathEntry(root.getJavaProject(),
748: entry.getPath(), root.getPath());
749: if (entry == null) {
750: return null;
751: }
752: }
753: return getLibraryJavadocLocation(entry);
754: }
755: return null;
756: }
757:
758: private static IClasspathEntry getRealClasspathEntry(
759: IJavaProject jproject, IPath containerPath, IPath libPath)
760: throws JavaModelException {
761: IClasspathContainer container = JavaCore.getClasspathContainer(
762: containerPath, jproject);
763: if (container != null) {
764: IClasspathEntry[] entries = container.getClasspathEntries();
765: for (int i = 0; i < entries.length; i++) {
766: IClasspathEntry curr = entries[i];
767: if (curr == null) {
768: if (JavaModelManager.CP_RESOLVE_VERBOSE) {
769: JavaModelManager.getJavaModelManager()
770: .verbose_missbehaving_container(
771: jproject, containerPath,
772: entries);
773: }
774: break;
775: }
776: IClasspathEntry resolved = JavaCore
777: .getResolvedClasspathEntry(curr);
778: if (resolved != null
779: && libPath.equals(resolved.getPath())) {
780: return curr; // return the real entry
781: }
782: }
783: }
784: return null; // not found
785: }
786:
787: protected static URL getLibraryJavadocLocation(IClasspathEntry entry)
788: throws JavaModelException {
789: switch (entry.getEntryKind()) {
790: case IClasspathEntry.CPE_LIBRARY:
791: case IClasspathEntry.CPE_VARIABLE:
792: break;
793: default:
794: throw new IllegalArgumentException(
795: "Entry must be of kind CPE_LIBRARY or CPE_VARIABLE"); //$NON-NLS-1$
796: }
797:
798: IClasspathAttribute[] extraAttributes = entry
799: .getExtraAttributes();
800: for (int i = 0; i < extraAttributes.length; i++) {
801: IClasspathAttribute attrib = extraAttributes[i];
802: if (IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME
803: .equals(attrib.getName())) {
804: String value = attrib.getValue();
805: try {
806: return new URL(value);
807: } catch (MalformedURLException e) {
808: throw new JavaModelException(
809: new JavaModelStatus(
810: IJavaModelStatusConstants.CANNOT_RETRIEVE_ATTACHED_JAVADOC,
811: value));
812: }
813: }
814: }
815: return null;
816: }
817:
818: /*
819: * @see IJavaElement#getAttachedJavadoc(IProgressMonitor)
820: */
821: public String getAttachedJavadoc(IProgressMonitor monitor)
822: throws JavaModelException {
823: return null;
824: }
825:
826: int getIndexOf(byte[] array, byte[] toBeFound, int start) {
827: if (array == null || toBeFound == null)
828: return -1;
829: final int toBeFoundLength = toBeFound.length;
830: final int arrayLength = array.length;
831: if (arrayLength < toBeFoundLength)
832: return -1;
833: loop: for (int i = start, max = arrayLength - toBeFoundLength
834: + 1; i < max; i++) {
835: if (array[i] == toBeFound[0]) {
836: for (int j = 1; j < toBeFoundLength; j++) {
837: if (array[i + j] != toBeFound[j])
838: continue loop;
839: }
840: return i;
841: }
842: }
843: return -1;
844: }
845:
846: /*
847: * We don't use getContentEncoding() on the URL connection, because it might leave open streams behind.
848: * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=117890
849: */
850: protected String getURLContents(String docUrlValue)
851: throws JavaModelException {
852: InputStream stream = null;
853: JarURLConnection connection2 = null;
854: try {
855: URL docUrl = new URL(docUrlValue);
856: URLConnection connection = docUrl.openConnection();
857: if (connection instanceof JarURLConnection) {
858: connection2 = (JarURLConnection) connection;
859: // https://bugs.eclipse.org/bugs/show_bug.cgi?id=156307
860: connection.setUseCaches(false);
861: }
862: stream = new BufferedInputStream(connection
863: .getInputStream());
864: String encoding = connection.getContentEncoding();
865: byte[] contents = org.eclipse.jdt.internal.compiler.util.Util
866: .getInputStreamAsByteArray(stream, connection
867: .getContentLength());
868: if (encoding == null) {
869: int index = getIndexOf(contents, CONTENT_TYPE, 0);
870: if (index != -1) {
871: index = getIndexOf(contents, CONTENT, index);
872: if (index != -1) {
873: int offset = index + CONTENT.length;
874: int index2 = getIndexOf(contents,
875: CLOSING_DOUBLE_QUOTE, offset);
876: if (index2 != -1) {
877: final int charsetIndex = getIndexOf(
878: contents, CHARSET, offset);
879: if (charsetIndex != -1) {
880: int start = charsetIndex
881: + CHARSET.length;
882: encoding = new String(contents, start,
883: index2 - start, "UTF-8"); //$NON-NLS-1$
884: }
885: }
886: }
887: }
888: }
889: try {
890: if (encoding == null) {
891: encoding = this .getJavaProject().getProject()
892: .getDefaultCharset();
893: }
894: } catch (CoreException e) {
895: // ignore
896: }
897: if (contents != null) {
898: if (encoding != null) {
899: return new String(contents, encoding);
900: } else {
901: // platform encoding is used
902: return new String(contents);
903: }
904: }
905: } catch (MalformedURLException e) {
906: throw new JavaModelException(
907: new JavaModelStatus(
908: IJavaModelStatusConstants.CANNOT_RETRIEVE_ATTACHED_JAVADOC,
909: this ));
910: } catch (FileNotFoundException e) {
911: // ignore. see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=120559
912: } catch (IOException e) {
913: StringWriter stringWriter = new StringWriter();
914: PrintWriter writer = new PrintWriter(stringWriter);
915: e.printStackTrace(writer);
916: writer.flush();
917: writer.close();
918: throw new JavaModelException(
919: new JavaModelStatus(
920: IJavaModelStatusConstants.CANNOT_RETRIEVE_ATTACHED_JAVADOC,
921: this , String.valueOf(stringWriter
922: .getBuffer())));
923: } finally {
924: if (stream != null) {
925: try {
926: stream.close();
927: } catch (IOException e) {
928: // ignore
929: }
930: }
931: if (connection2 != null) {
932: try {
933: connection2.getJarFile().close();
934: } catch (IOException e) {
935: // ignore
936: } catch (IllegalStateException e) {
937: /*
938: * ignore. Can happen in case the stream.close() did close the jar file
939: * see https://bugs.eclipse.org/bugs/show_bug.cgi?id=140750
940: */
941: }
942: }
943: }
944: return null;
945: }
946: }
|