/* * 03/21/2010 * * Copyright (C) 2010 Robert Futrell * robert_futrell at users.sourceforge.net * http://fifesoft.com/rsyntaxtextarea * * This library is distributed under a modified BSD license. See the included * RSTALanguageSupport.License.txt file for details. */ package org.fife.rsta.ac.java.rjc.ast; import java.awt.Point; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.fife.rsta.ac.java.rjc.lexer.Offset; import org.fife.rsta.ac.java.rjc.lexer.Token; import org.fife.rsta.ac.java.rjc.notices.ParserNotice; /** * A <code>CompilationUnit</code> is the root node of an AST for a Java * source file. * * <pre> * CompilationUnit: * [[Annotations] 'package' QualifiedIdentifier ';' ] {ImportDeclaration} {TypeDeclaration} * </pre> * * @author Robert Futrell * @version 1.0 */ public class CompilationUnit extends AbstractASTNode implements TypeDeclarationContainer { private List annotations; private Package pkg; private List imports; private List typeDeclarations; private List notices; private static final Offset ZERO_OFFSET = new ZeroOffset(); public CompilationUnit(String name) { super(name, ZERO_OFFSET); imports = new ArrayList(3); // Usually not many, typeDeclarations = new ArrayList(1); // Usually only 1 } public void addImportDeclaration(ImportDeclaration dec) { imports.add(dec); } /** * Shorthand for "<tt>addParserNotice(new ParserNotice(t, msg))</tt>". * * @param t * @param msg */ public void addParserNotice(Token t, String msg) { addParserNotice(new ParserNotice(t, msg)); } public void addParserNotice(ParserNotice notice) { if (notices==null) { notices = new ArrayList(); notices.add(notice); } } public void addTypeDeclaration(TypeDeclaration typeDec) { typeDeclarations.add(typeDec); } public int getAnnotationCount() { return annotations.size(); } public Iterator getAnnotationIterator() { return annotations.iterator(); } /** * Returns the deepest-nested type declaration that contains a given * offset. * * @param offs The offset. * @return The deepest-nested type declaration containing the offset, or * <code>null</code> if the offset is outside of any type * declaration (such as in the import statements, etc.). * @see #getTypeDeclarationAtOffset(int) */ public TypeDeclaration getDeepestTypeDeclarationAtOffset(int offs) { TypeDeclaration td = getTypeDeclarationAtOffset(offs); if (td!=null) { TypeDeclaration next = td.getChildTypeAtOffset(offs); while (next!=null) { td = next; next = td.getChildTypeAtOffset(offs); } } return td; } /** * TODO: Return range for more instances than just class methods. * Also handle child TypeDeclarations. * * @param offs * @return The starting and ending offset of the enclosing method range. */ public Point getEnclosingMethodRange(int offs) { Point range = null; for (Iterator i=getTypeDeclarationIterator(); i.hasNext(); ) { TypeDeclaration td = (TypeDeclaration)i.next(); int start = td.getBodyStartOffset(); int end = td.getBodyEndOffset(); if (offs>=start && offs<=end) { if (td instanceof NormalClassDeclaration) { NormalClassDeclaration ncd = (NormalClassDeclaration)td; for (Iterator j=ncd.getMemberIterator(); j.hasNext(); ) { Member m = (Member)j.next(); if (m instanceof Method) { Method method = (Method)m; CodeBlock body = method.getBody(); if (body!=null) { int start2 = method.getNameStartOffset(); //int start2 = body.getStartOffset(); int end2 = body.getNameEndOffset(); if (offs>=start2 && offs<=end2) { range = new Point(start2, end2); break; } } } } } if (range==null) { // Default to the entire class' body. range = new Point(start, end); } } } return range; } public int getImportCount() { return imports.size(); } /** * Returns the import declarations of this compilation unit. This is a * copy of the list of imports, but the actual individual * {@link ImportDeclaration}s are not copies, so modifying them will modify * this compilation unit! * * @return A list or imports, or an empty list if there are none. */ public List getImports() { return new ArrayList(imports); } public Iterator getImportIterator() { return imports.iterator(); } /** * Returns the package of this compilation unit. * * @return The package of this compilation unit, or <code>null</code> if * this compilation unit is not in a package. * @see #getPackageName() */ public Package getPackage() { return pkg; } /** * Returns the fully-qualified package name of this compilation unit. * * @return The package name, or <code>null</code> if this compilation unit * is not in a package (in the default package). * @see #getPackage() */ public String getPackageName() { return pkg==null ? null : pkg.getName(); } public ParserNotice getParserNotice(int index) { if (notices==null) { throw new IndexOutOfBoundsException("No parser notices available"); } return (ParserNotice)notices.get(index); } public int getParserNoticeCount() { return notices==null ? 0 : notices.size(); } public TypeDeclaration getTypeDeclaration(int index) { return (TypeDeclaration)typeDeclarations.get(index); } /** * Returns the type declaration in this file that contains the specified * offset. * * @param offs The offset. * @return The type declaration, or <code>null</code> if the offset is * outside of any type declaration. * @see #getDeepestTypeDeclarationAtOffset(int) */ public TypeDeclaration getTypeDeclarationAtOffset(int offs) { TypeDeclaration typeDec = null; for (Iterator i=getTypeDeclarationIterator(); i.hasNext(); ) { TypeDeclaration td = (TypeDeclaration)i.next(); if (td.getBodyContainsOffset(offs)) { typeDec = td; break; } } return typeDec; } public int getTypeDeclarationCount() { return typeDeclarations.size(); } public Iterator getTypeDeclarationIterator() { return typeDeclarations.iterator(); } public void setPackage(Package pkg) { this.pkg = pkg; } /** * An offset that always returns 0. */ private static class ZeroOffset implements Offset { public int getOffset() { return 0; } } }