001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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.net.URL;
013: import java.util.ArrayList;
014: import java.util.HashSet;
015: import java.util.Map;
016:
017: import org.eclipse.core.resources.IContainer;
018: import org.eclipse.core.resources.IFolder;
019: import org.eclipse.core.resources.IResource;
020: import org.eclipse.core.runtime.CoreException;
021: import org.eclipse.core.runtime.IPath;
022: import org.eclipse.core.runtime.IProgressMonitor;
023: import org.eclipse.core.runtime.OperationCanceledException;
024: import org.eclipse.core.runtime.Path;
025: import org.eclipse.jdt.core.IClassFile;
026: import org.eclipse.jdt.core.ICompilationUnit;
027: import org.eclipse.jdt.core.IJavaElement;
028: import org.eclipse.jdt.core.IJavaModelStatusConstants;
029: import org.eclipse.jdt.core.IJavaProject;
030: import org.eclipse.jdt.core.IPackageFragment;
031: import org.eclipse.jdt.core.IPackageFragmentRoot;
032: import org.eclipse.jdt.core.IParent;
033: import org.eclipse.jdt.core.ISourceManipulation;
034: import org.eclipse.jdt.core.JavaCore;
035: import org.eclipse.jdt.core.JavaModelException;
036: import org.eclipse.jdt.core.WorkingCopyOwner;
037: import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
038: import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
039: import org.eclipse.jdt.internal.core.util.MementoTokenizer;
040: import org.eclipse.jdt.internal.core.util.Messages;
041: import org.eclipse.jdt.internal.core.util.Util;
042:
043: /**
044: * @see IPackageFragment
045: */
046: public class PackageFragment extends Openable implements
047: IPackageFragment, SuffixConstants {
048: /**
049: * Constant empty list of class files
050: */
051: protected static final IClassFile[] NO_CLASSFILES = new IClassFile[] {};
052: /**
053: * Constant empty list of compilation units
054: */
055: protected static final ICompilationUnit[] NO_COMPILATION_UNITS = new ICompilationUnit[] {};
056:
057: public String[] names;
058:
059: protected PackageFragment(PackageFragmentRoot root, String[] names) {
060: super (root);
061: this .names = names;
062: }
063:
064: /**
065: * @see Openable
066: */
067: protected boolean buildStructure(OpenableElementInfo info,
068: IProgressMonitor pm, Map newElements,
069: IResource underlyingResource) throws JavaModelException {
070:
071: // check whether this pkg can be opened
072: if (!underlyingResource.isAccessible())
073: throw newNotPresentException();
074:
075: // check that it is not excluded (https://bugs.eclipse.org/bugs/show_bug.cgi?id=138577)
076: int kind = getKind();
077: if (kind == IPackageFragmentRoot.K_SOURCE
078: && Util.isExcluded(this ))
079: throw newNotPresentException();
080:
081: // check that the name of the package is valid (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=108456)
082: if (!isValidPackageName())
083: throw newNotPresentException();
084:
085: // add compilation units/class files from resources
086: HashSet vChildren = new HashSet();
087: try {
088: PackageFragmentRoot root = getPackageFragmentRoot();
089: char[][] inclusionPatterns = root
090: .fullInclusionPatternChars();
091: char[][] exclusionPatterns = root
092: .fullExclusionPatternChars();
093: IResource[] members = ((IContainer) underlyingResource)
094: .members();
095: int length = members.length;
096: if (length > 0) {
097: IJavaProject project = getJavaProject();
098: String sourceLevel = project.getOption(
099: JavaCore.COMPILER_SOURCE, true);
100: String complianceLevel = project.getOption(
101: JavaCore.COMPILER_COMPLIANCE, true);
102: for (int i = 0; i < length; i++) {
103: IResource child = members[i];
104: if (child.getType() != IResource.FOLDER
105: && !Util.isExcluded(child,
106: inclusionPatterns,
107: exclusionPatterns)) {
108: IJavaElement childElement;
109: if (kind == IPackageFragmentRoot.K_SOURCE
110: && Util.isValidCompilationUnitName(
111: child.getName(), sourceLevel,
112: complianceLevel)) {
113: childElement = new CompilationUnit(this ,
114: child.getName(),
115: DefaultWorkingCopyOwner.PRIMARY);
116: vChildren.add(childElement);
117: } else if (kind == IPackageFragmentRoot.K_BINARY
118: && Util.isValidClassFileName(child
119: .getName(), sourceLevel,
120: complianceLevel)) {
121: childElement = getClassFile(child.getName());
122: vChildren.add(childElement);
123: }
124: }
125: }
126: }
127: } catch (CoreException e) {
128: throw new JavaModelException(e);
129: }
130:
131: if (kind == IPackageFragmentRoot.K_SOURCE) {
132: // add primary compilation units
133: ICompilationUnit[] primaryCompilationUnits = getCompilationUnits(DefaultWorkingCopyOwner.PRIMARY);
134: for (int i = 0, length = primaryCompilationUnits.length; i < length; i++) {
135: ICompilationUnit primary = primaryCompilationUnits[i];
136: vChildren.add(primary);
137: }
138: }
139:
140: IJavaElement[] children = new IJavaElement[vChildren.size()];
141: vChildren.toArray(children);
142: info.setChildren(children);
143: return true;
144: }
145:
146: /**
147: * Returns true if this fragment contains at least one java resource.
148: * Returns false otherwise.
149: */
150: public boolean containsJavaResources() throws JavaModelException {
151: return ((PackageFragmentInfo) getElementInfo())
152: .containsJavaResources();
153: }
154:
155: /**
156: * @see ISourceManipulation
157: */
158: public void copy(IJavaElement container, IJavaElement sibling,
159: String rename, boolean force, IProgressMonitor monitor)
160: throws JavaModelException {
161: if (container == null) {
162: throw new IllegalArgumentException(
163: Messages.operation_nullContainer);
164: }
165: IJavaElement[] elements = new IJavaElement[] { this };
166: IJavaElement[] containers = new IJavaElement[] { container };
167: IJavaElement[] siblings = null;
168: if (sibling != null) {
169: siblings = new IJavaElement[] { sibling };
170: }
171: String[] renamings = null;
172: if (rename != null) {
173: renamings = new String[] { rename };
174: }
175: getJavaModel().copy(elements, containers, siblings, renamings,
176: force, monitor);
177: }
178:
179: /**
180: * @see IPackageFragment
181: */
182: public ICompilationUnit createCompilationUnit(String cuName,
183: String contents, boolean force, IProgressMonitor monitor)
184: throws JavaModelException {
185: CreateCompilationUnitOperation op = new CreateCompilationUnitOperation(
186: this , cuName, contents, force);
187: op.runOperation(monitor);
188: return new CompilationUnit(this , cuName,
189: DefaultWorkingCopyOwner.PRIMARY);
190: }
191:
192: /**
193: * @see JavaElement
194: */
195: protected Object createElementInfo() {
196: return new PackageFragmentInfo();
197: }
198:
199: /**
200: * @see ISourceManipulation
201: */
202: public void delete(boolean force, IProgressMonitor monitor)
203: throws JavaModelException {
204: IJavaElement[] elements = new IJavaElement[] { this };
205: getJavaModel().delete(elements, force, monitor);
206: }
207:
208: public boolean equals(Object o) {
209: if (this == o)
210: return true;
211: if (!(o instanceof PackageFragment))
212: return false;
213:
214: PackageFragment other = (PackageFragment) o;
215: return Util.equalArraysOrNull(this .names, other.names)
216: && this .parent.equals(other.parent);
217: }
218:
219: public boolean exists() {
220: // super.exist() only checks for the parent and the resource existence
221: // so also ensure that:
222: // - the package is not excluded (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=138577)
223: // - its name is valide (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=108456)
224: return super .exists() && !Util.isExcluded(this )
225: && isValidPackageName();
226: }
227:
228: /**
229: * @see IPackageFragment#getClassFile(String)
230: * @exception IllegalArgumentException if the name does not end with ".class"
231: */
232: public IClassFile getClassFile(String classFileName) {
233: if (!org.eclipse.jdt.internal.compiler.util.Util
234: .isClassFileName(classFileName)) {
235: throw new IllegalArgumentException(
236: Messages.element_invalidClassFileName);
237: }
238: // don't hold on the .class file extension to save memory
239: // also make sure to not use substring as the resulting String may hold on the underlying char[] which might be much bigger than necessary
240: int length = classFileName.length() - 6;
241: char[] nameWithoutExtension = new char[length];
242: classFileName.getChars(0, length, nameWithoutExtension, 0);
243: return new ClassFile(this , new String(nameWithoutExtension));
244: }
245:
246: /**
247: * Returns a the collection of class files in this - a folder package fragment which has a root
248: * that has its kind set to <code>IPackageFragmentRoot.K_Source</code> does not
249: * recognize class files.
250: *
251: * @see IPackageFragment#getClassFiles()
252: */
253: public IClassFile[] getClassFiles() throws JavaModelException {
254: if (getKind() == IPackageFragmentRoot.K_SOURCE) {
255: return NO_CLASSFILES;
256: }
257:
258: ArrayList list = getChildrenOfType(CLASS_FILE);
259: IClassFile[] array = new IClassFile[list.size()];
260: list.toArray(array);
261: return array;
262: }
263:
264: /**
265: * @see IPackageFragment#getCompilationUnit(String)
266: * @exception IllegalArgumentException if the name does not end with ".java"
267: */
268: public ICompilationUnit getCompilationUnit(String cuName) {
269: if (!org.eclipse.jdt.internal.core.util.Util
270: .isJavaLikeFileName(cuName)) {
271: throw new IllegalArgumentException(
272: Messages.convention_unit_notJavaName);
273: }
274: return new CompilationUnit(this , cuName,
275: DefaultWorkingCopyOwner.PRIMARY);
276: }
277:
278: /**
279: * @see IPackageFragment#getCompilationUnits()
280: */
281: public ICompilationUnit[] getCompilationUnits()
282: throws JavaModelException {
283: if (getKind() == IPackageFragmentRoot.K_BINARY) {
284: return NO_COMPILATION_UNITS;
285: }
286:
287: ArrayList list = getChildrenOfType(COMPILATION_UNIT);
288: ICompilationUnit[] array = new ICompilationUnit[list.size()];
289: list.toArray(array);
290: return array;
291: }
292:
293: /**
294: * @see IPackageFragment#getCompilationUnits(WorkingCopyOwner)
295: */
296: public ICompilationUnit[] getCompilationUnits(WorkingCopyOwner owner) {
297: ICompilationUnit[] workingCopies = JavaModelManager
298: .getJavaModelManager()
299: .getWorkingCopies(owner, false/*don't add primary*/);
300: if (workingCopies == null)
301: return JavaModelManager.NO_WORKING_COPY;
302: int length = workingCopies.length;
303: ICompilationUnit[] result = new ICompilationUnit[length];
304: int index = 0;
305: for (int i = 0; i < length; i++) {
306: ICompilationUnit wc = workingCopies[i];
307: if (equals(wc.getParent()) && !Util.isExcluded(wc)) { // 59933 - excluded wc shouldn't be answered back
308: result[index++] = wc;
309: }
310: }
311: if (index != length) {
312: System.arraycopy(result, 0,
313: result = new ICompilationUnit[index], 0, index);
314: }
315: return result;
316: }
317:
318: public String getElementName() {
319: if (this .names.length == 0)
320: return DEFAULT_PACKAGE_NAME;
321: return Util.concatWith(this .names, '.');
322: }
323:
324: /**
325: * @see IJavaElement
326: */
327: public int getElementType() {
328: return PACKAGE_FRAGMENT;
329: }
330:
331: /*
332: * @see JavaElement
333: */
334: public IJavaElement getHandleFromMemento(String token,
335: MementoTokenizer memento, WorkingCopyOwner owner) {
336: switch (token.charAt(0)) {
337: case JEM_CLASSFILE:
338: if (!memento.hasMoreTokens())
339: return this ;
340: String classFileName = memento.nextToken();
341: JavaElement classFile = (JavaElement) getClassFile(classFileName);
342: return classFile.getHandleFromMemento(memento, owner);
343: case JEM_COMPILATIONUNIT:
344: if (!memento.hasMoreTokens())
345: return this ;
346: String cuName = memento.nextToken();
347: JavaElement cu = new CompilationUnit(this , cuName, owner);
348: return cu.getHandleFromMemento(memento, owner);
349: }
350: return null;
351: }
352:
353: /**
354: * @see JavaElement#getHandleMementoDelimiter()
355: */
356: protected char getHandleMementoDelimiter() {
357: return JavaElement.JEM_PACKAGEFRAGMENT;
358: }
359:
360: /**
361: * @see IPackageFragment#getKind()
362: */
363: public int getKind() throws JavaModelException {
364: return ((IPackageFragmentRoot) getParent()).getKind();
365: }
366:
367: /**
368: * Returns an array of non-java resources contained in the receiver.
369: */
370: public Object[] getNonJavaResources() throws JavaModelException {
371: if (this .isDefaultPackage()) {
372: // We don't want to show non java resources of the default package (see PR #1G58NB8)
373: return JavaElementInfo.NO_NON_JAVA_RESOURCES;
374: } else {
375: return ((PackageFragmentInfo) getElementInfo())
376: .getNonJavaResources(getResource(),
377: getPackageFragmentRoot());
378: }
379: }
380:
381: /**
382: * @see IJavaElement#getPath()
383: */
384: public IPath getPath() {
385: PackageFragmentRoot root = this .getPackageFragmentRoot();
386: if (root.isArchive()) {
387: return root.getPath();
388: } else {
389: IPath path = root.getPath();
390: for (int i = 0, length = this .names.length; i < length; i++) {
391: String name = this .names[i];
392: path = path.append(name);
393: }
394: return path;
395: }
396: }
397:
398: /**
399: * @see IJavaElement#getResource()
400: */
401: public IResource getResource() {
402: PackageFragmentRoot root = this .getPackageFragmentRoot();
403: if (root.isArchive()) {
404: return root.getResource();
405: } else {
406: int length = this .names.length;
407: if (length == 0) {
408: return root.getResource();
409: } else {
410: IPath path = new Path(this .names[0]);
411: for (int i = 1; i < length; i++)
412: path = path.append(this .names[i]);
413: return ((IContainer) root.getResource())
414: .getFolder(path);
415: }
416: }
417: }
418:
419: /**
420: * @see IJavaElement#getUnderlyingResource()
421: */
422: public IResource getUnderlyingResource() throws JavaModelException {
423: IResource rootResource = this .parent.getUnderlyingResource();
424: if (rootResource == null) {
425: //jar package fragment root that has no associated resource
426: return null;
427: }
428: // the underlying resource may be a folder or a project (in the case that the project folder
429: // is atually the package fragment root)
430: if (rootResource.getType() == IResource.FOLDER
431: || rootResource.getType() == IResource.PROJECT) {
432: IContainer folder = (IContainer) rootResource;
433: String[] segs = this .names;
434: for (int i = 0; i < segs.length; ++i) {
435: IResource child = folder.findMember(segs[i]);
436: if (child == null
437: || child.getType() != IResource.FOLDER) {
438: throw newNotPresentException();
439: }
440: folder = (IFolder) child;
441: }
442: return folder;
443: } else {
444: return rootResource;
445: }
446: }
447:
448: public int hashCode() {
449: int hash = this .parent.hashCode();
450: for (int i = 0, length = this .names.length; i < length; i++)
451: hash = Util
452: .combineHashCodes(this .names[i].hashCode(), hash);
453: return hash;
454: }
455:
456: /**
457: * @see IParent
458: */
459: public boolean hasChildren() throws JavaModelException {
460: return getChildren().length > 0;
461: }
462:
463: /**
464: * @see IPackageFragment#hasSubpackages()
465: */
466: public boolean hasSubpackages() throws JavaModelException {
467: IJavaElement[] packages = ((IPackageFragmentRoot) getParent())
468: .getChildren();
469: int namesLength = this .names.length;
470: nextPackage: for (int i = 0, length = packages.length; i < length; i++) {
471: String[] otherNames = ((PackageFragment) packages[i]).names;
472: if (otherNames.length <= namesLength)
473: continue nextPackage;
474: for (int j = 0; j < namesLength; j++)
475: if (!this .names[j].equals(otherNames[j]))
476: continue nextPackage;
477: return true;
478: }
479: return false;
480: }
481:
482: /**
483: * @see IPackageFragment#isDefaultPackage()
484: */
485: public boolean isDefaultPackage() {
486: return this .names.length == 0;
487: }
488:
489: private boolean isValidPackageName() {
490: JavaProject javaProject = (JavaProject) getJavaProject();
491: String sourceLevel = javaProject.getOption(
492: JavaCore.COMPILER_SOURCE, true);
493: String complianceLevel = javaProject.getOption(
494: JavaCore.COMPILER_COMPLIANCE, true);
495: for (int i = 0, length = this .names.length; i < length; i++) {
496: if (!Util.isValidFolderNameForPackage(this .names[i],
497: sourceLevel, complianceLevel))
498: return false;
499: }
500: return true;
501: }
502:
503: /**
504: * @see ISourceManipulation#move(IJavaElement, IJavaElement, String, boolean, IProgressMonitor)
505: */
506: public void move(IJavaElement container, IJavaElement sibling,
507: String rename, boolean force, IProgressMonitor monitor)
508: throws JavaModelException {
509: if (container == null) {
510: throw new IllegalArgumentException(
511: Messages.operation_nullContainer);
512: }
513: IJavaElement[] elements = new IJavaElement[] { this };
514: IJavaElement[] containers = new IJavaElement[] { container };
515: IJavaElement[] siblings = null;
516: if (sibling != null) {
517: siblings = new IJavaElement[] { sibling };
518: }
519: String[] renamings = null;
520: if (rename != null) {
521: renamings = new String[] { rename };
522: }
523: getJavaModel().move(elements, containers, siblings, renamings,
524: force, monitor);
525: }
526:
527: /**
528: * @see ISourceManipulation#rename(String, boolean, IProgressMonitor)
529: */
530: public void rename(String newName, boolean force,
531: IProgressMonitor monitor) throws JavaModelException {
532: if (newName == null) {
533: throw new IllegalArgumentException(
534: Messages.element_nullName);
535: }
536: IJavaElement[] elements = new IJavaElement[] { this };
537: IJavaElement[] dests = new IJavaElement[] { this .getParent() };
538: String[] renamings = new String[] { newName };
539: getJavaModel().rename(elements, dests, renamings, force,
540: monitor);
541: }
542:
543: /**
544: * Debugging purposes
545: */
546: protected void toStringChildren(int tab, StringBuffer buffer,
547: Object info) {
548: if (tab == 0) {
549: super .toStringChildren(tab, buffer, info);
550: }
551: }
552:
553: /**
554: * Debugging purposes
555: */
556: protected void toStringInfo(int tab, StringBuffer buffer,
557: Object info, boolean showResolvedInfo) {
558: buffer.append(this .tabString(tab));
559: if (this .names.length == 0) {
560: buffer.append("<default>"); //$NON-NLS-1$
561: } else {
562: toStringName(buffer);
563: }
564: if (info == null) {
565: buffer.append(" (not open)"); //$NON-NLS-1$
566: } else {
567: if (tab > 0) {
568: buffer.append(" (...)"); //$NON-NLS-1$
569: }
570: }
571: }
572:
573: /*
574: * @see IJavaElement#getAttachedJavadoc(IProgressMonitor)
575: */
576: public String getAttachedJavadoc(IProgressMonitor monitor)
577: throws JavaModelException {
578: PerProjectInfo projectInfo = JavaModelManager
579: .getJavaModelManager().getPerProjectInfoCheckExistence(
580: this .getJavaProject().getProject());
581: String cachedJavadoc = null;
582: synchronized (projectInfo.javadocCache) {
583: cachedJavadoc = (String) projectInfo.javadocCache.get(this );
584: }
585: if (cachedJavadoc != null) {
586: return cachedJavadoc;
587: }
588: URL baseLocation = getJavadocBaseLocation();
589: if (baseLocation == null) {
590: return null;
591: }
592: StringBuffer pathBuffer = new StringBuffer(baseLocation
593: .toExternalForm());
594:
595: if (!(pathBuffer.charAt(pathBuffer.length() - 1) == '/')) {
596: pathBuffer.append('/');
597: }
598: String packPath = this .getElementName().replace('.', '/');
599: pathBuffer.append(packPath).append('/').append(
600: JavadocConstants.PACKAGE_FILE_NAME);
601:
602: if (monitor != null && monitor.isCanceled())
603: throw new OperationCanceledException();
604: final String contents = getURLContents(String
605: .valueOf(pathBuffer));
606: if (monitor != null && monitor.isCanceled())
607: throw new OperationCanceledException();
608: if (contents == null)
609: throw new JavaModelException(
610: new JavaModelStatus(
611: IJavaModelStatusConstants.CANNOT_RETRIEVE_ATTACHED_JAVADOC,
612: this));
613: synchronized (projectInfo.javadocCache) {
614: projectInfo.javadocCache.put(this, contents);
615: }
616: return contents;
617: }
618: }
|