1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.jdom;
13 import java.util.Enumeration;
15 import net.sourceforge.phpdt.core.ICompilationUnit;
16 import net.sourceforge.phpdt.core.IJavaElement;
17 import net.sourceforge.phpdt.core.IType;
18 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
19 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
20 import net.sourceforge.phpdt.core.jdom.IDOMMethod;
21 import net.sourceforge.phpdt.core.jdom.IDOMNode;
22 import net.sourceforge.phpdt.core.jdom.IDOMType;
23 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
24 import net.sourceforge.phpdt.internal.compiler.util.Util;
25 import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer;
26 import net.sourceforge.phpdt.internal.core.util.CharArrayOps;
29 * DOMType provides an implementation of IDOMType.
35 /* package */ class DOMType extends DOMMember implements IDOMType {
38 * The 'class' or 'interface' keyword if altered
39 * from the documents contents, otherwise <code>null</code>.
41 protected String fTypeKeyword;
44 * The original inclusive source range of the 'class'
45 * or 'interface' keyword in the document.
47 protected int[] fTypeRange;
50 * The superclass name for the class declaration
51 * if altered from the document's contents, otherwise
52 * <code>null</code>. Also <code>null</code> when this
53 * type represents an interface.
55 protected String fSuperclass;
58 * The original inclusive source range of the superclass
59 * name in the document, or -1's of no superclass was
60 * specified in the document.
62 protected int[] fSuperclassRange;
66 * The original inclusive souce range of the 'extends' keyword
67 * in the document, including surrounding whitespace, or -1's if
68 * the keyword was not present in the document.
70 protected int[] fExtendsRange;
73 * The original inclusive souce range of the 'implements' keyword
74 * in the document, including surrounding whitespace, or -1's if
75 * the keyword was not present in the document.
77 protected int[] fImplementsRange;
80 * The comma delimited list of interfaces this type implements
81 * or extends, if altered from the document's contents, otherwise
82 * <code>null</code>. Also <code>null</code> if this type does
83 * not implement or extend any interfaces.
85 protected char[] fInterfaces;
88 * The original inclusive source range of the list of interfaces this
89 * type implements or extends, not including any surrouding whitespace.
90 * If the document did not specify interfaces, this array contains -1's.
92 protected int[] fInterfacesRange;
97 * The original source range of the first character following the
98 * type name superclass name, or interface list, up to and including
99 * the first character before the first type member.
101 protected int[] fOpenBodyRange;
104 * The original source range of the first new line or non whitespace
105 * character preceding the close brace of the type's body, up to the
106 * and including the first character before the next node (if there are
107 * no following nodes, the range ends at the position of the last
108 * character in the document).
110 protected int[] fCloseBodyRange;
113 * A list of interfaces this type extends or implements.
114 * <code>null</code> when this type does not extend
115 * or implement any interfaces.
117 protected String[] fSuperInterfaces= new String[0];
120 * This position is the position of the end of the last line separator before the closing brace starting
121 * position of the receiver.
123 // protected int fInsertionPosition;
126 * Constructs an empty type node.
132 * Creates a new detailed TYPE document fragment on the given range of the document.
134 * @param document - the document containing this node's original contents
135 * @param sourceRange - a two element array of integers describing the
136 * entire inclusive source range of this node within its document.
137 * Contents start on and include the character at the first position.
138 * Contents end on and include the character at the last position.
139 * An array of -1's indicates this node's contents do not exist
141 * @param name - the identifier portion of the name of this node, or
142 * <code>null</code> if this node does not have a name
143 * @param nameRange - a two element array of integers describing the
144 * entire inclusive source range of this node's name within its document,
145 * including any array qualifiers that might immediately follow the name
146 * or -1's if this node does not have a name.
147 * @param commentRange - a two element array describing the comments that precede
148 * the member declaration. The first matches the start of this node's
149 * sourceRange, and the second is the new-line or first non-whitespace
150 * character following the last comment. If no comments are present,
151 * this array contains two -1's.
152 * @param flags - an integer representing the modifiers for this member. The
153 * integer can be analyzed with org.eclipse.jdt.core.Flags
154 * @param modifierRange - a two element array describing the location of
155 * modifiers for this member within its source range. The first integer
156 * is the first character of the first modifier for this member, and
157 * the second integer is the last whitespace character preceeding the
158 * next part of this member declaration. If there are no modifiers present
159 * in this node's source code (that is, package default visibility), this array
161 * @param typeRange - a two element array describing the location of the 'class'
162 * or 'interface' keyword in the type declaration - first and last character
164 * @param superclassRange - a two element array describing the location of the
165 * superclass name in the type declaration - first and last character
166 * positions or two -1's if a superclass is not present in the document.
167 * @param extendsRange - a two element array describing the location of the
168 * 'extends' keyword in the type declaration, including any surrounding
169 * whitespace, or -1's if the 'extends' keyword is not present in the document.
170 * @param implementsList - an array of names of the interfaces this type implements
171 * or extends, or <code>null</code> if this type does not implement or extend
173 * @param implementsRange - a two element array describing the location of the
174 * comment delimited list of interfaces this type implements or extends,
175 * not including any surrounding whitespace, or -1's if no interface list
176 * is present in the document.
177 * @param implementsKeywordRange - a two element array describing the location of the
178 * 'implements' keyword, including any surrounding whitespace, or -1's if no
179 * 'implements' keyword is present in the document.
180 * @param openBodyRange - a two element array describing the location of the
181 * open brace of the type's body and whitespace following the type declaration
182 * and preceeding the first member in the type.
183 * @param closeBodyRange - a two element array describing the source range of the
184 * first new line or non whitespace character preceeding the close brace of the
185 * type's body, up to the close brace
186 * @param isClass - true is the type is a class, false if it is an interface
188 DOMType(char[] document, int[] sourceRange, String name, int[] nameRange, int[] commentRange, int flags, int[] modifierRange, int[] typeRange, int[] superclassRange, int[] extendsRange, String[] implementsList, int[] implementsRange, int[] implementsKeywordRange, int[] openBodyRange, int[] closeBodyRange, boolean isClass) {
189 super(document, sourceRange, name, nameRange, commentRange, flags, modifierRange);
191 fTypeRange= typeRange;
192 setMask(MASK_TYPE_IS_CLASS, isClass);
194 fExtendsRange= extendsRange;
195 fImplementsRange= implementsKeywordRange;
196 fSuperclassRange= superclassRange;
197 fInterfacesRange= implementsRange;
198 fCloseBodyRange= closeBodyRange;
199 setMask(MASK_TYPE_HAS_SUPERCLASS, superclassRange[0] > 0);
200 setMask(MASK_TYPE_HAS_INTERFACES, implementsList != null);
201 fSuperInterfaces= implementsList;
202 fOpenBodyRange= openBodyRange;
203 fCloseBodyRange= closeBodyRange;
204 setMask(MASK_DETAILED_SOURCE_INDEXES, true);
208 * Creates a new simple TYPE document fragment on the given range of the document.
210 * @param document - the document containing this node's original contents
211 * @param sourceRange - a two element array of integers describing the
212 * entire inclusive source range of this node within its document.
213 * Contents start on and include the character at the first position.
214 * Contents end on and include the character at the last position.
215 * An array of -1's indicates this node's contents do not exist
217 * @param name - the identifier portion of the name of this node, or
218 * <code>null</code> if this node does not have a name
219 * @param nameRange - a two element array of integers describing the
220 * entire inclusive source range of this node's name within its document,
221 * including any array qualifiers that might immediately follow the name
222 * or -1's if this node does not have a name.
223 * @param flags - an integer representing the modifiers for this member. The
224 * integer can be analyzed with org.eclipse.jdt.core.Flags
225 * @param implementsList - an array of names of the interfaces this type implements
226 * or extends, or <code>null</code> if this type does not implement or extend
228 * @param isClass - true is the type is a class, false if it is an interface
230 DOMType(char[] document, int[] sourceRange, String name, int[] nameRange, int flags, String[] implementsList, boolean isClass) {
231 this(document, sourceRange, name, nameRange, new int[] {-1, -1}, flags,
232 new int[] {-1, -1}, new int[] {-1, -1}, new int[] {-1, -1}, new int[] {-1, -1},
233 implementsList, new int[] {-1, -1}, new int[] {-1, -1}, new int[] {-1, -1}, new int[] {sourceRange[1], sourceRange[1]}, isClass);
234 setMask(MASK_DETAILED_SOURCE_INDEXES, false);
237 * @see IDOMType#addSuperInterface(String)
239 public void addSuperInterface(String name) throws IllegalArgumentException {
241 throw new IllegalArgumentException(Util.bind("dom.addNullInterface")); //$NON-NLS-1$
243 if (fSuperInterfaces == null) {
244 fSuperInterfaces= new String[1];
245 fSuperInterfaces[0]= name;
247 fSuperInterfaces= appendString(fSuperInterfaces, name);
249 setSuperInterfaces(fSuperInterfaces);
252 * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
254 protected void appendMemberBodyContents(CharArrayBuffer buffer) {
255 buffer.append(fDocument, fOpenBodyRange[0], fOpenBodyRange[1] + 1 - fOpenBodyRange[0]);
256 appendContentsOfChildren(buffer);
257 buffer.append(fDocument, fCloseBodyRange[0], fCloseBodyRange[1] + 1 - fCloseBodyRange[0]);
258 buffer.append(fDocument, fCloseBodyRange[1] + 1, fSourceRange[1] - fCloseBodyRange[1]);
261 * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer )
263 protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
265 if (fTypeKeyword != null) {
266 buffer.append(fTypeKeyword);
267 buffer.append(fDocument, fTypeRange[1], fNameRange[0] - fTypeRange[1] );
269 buffer.append(fDocument, fTypeRange[0], fTypeRange[1] + 1 - fTypeRange[0]);
272 buffer.append(getName());
275 boolean hasSuperclass = false, hasInterfaces = false;
276 if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
277 hasSuperclass = true;
278 if (fExtendsRange[0] < 0) {
279 buffer.append(" extends "); //$NON-NLS-1$
281 buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1] + 1 - fExtendsRange[0]);
283 if (fSuperclass != null) {
284 buffer.append(fSuperclass);
286 buffer.append(fDocument, fSuperclassRange[0], fSuperclassRange[1] + 1 - fSuperclassRange[0]);
289 if (getMask(MASK_TYPE_HAS_INTERFACES)) {
290 hasInterfaces = true;
291 if (fImplementsRange[0] < 0) {
292 buffer.append(" implements "); //$NON-NLS-1$
294 buffer.append(fDocument, fImplementsRange[0], fImplementsRange[1] + 1 - fImplementsRange[0]);
296 if (fInterfaces != null) {
297 buffer.append(fInterfaces);
299 buffer.append(fDocument, fInterfacesRange[0], fInterfacesRange[1] + 1 - fInterfacesRange[0]);
303 if (fImplementsRange[0] < 0) {
306 buffer.append(fDocument, fInterfacesRange[1] + 1, fOpenBodyRange[0] - fInterfacesRange[1] - 1);
310 if (fSuperclassRange[0] < 0) {
313 buffer.append(fDocument, fSuperclassRange[1] + 1, fOpenBodyRange[0] - fSuperclassRange[1] - 1);
316 buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[0] - fNameRange[1] - 1);
320 if (getMask(MASK_TYPE_HAS_INTERFACES)) {
321 if (fExtendsRange[0] < 0) {
322 buffer.append(" extends "); //$NON-NLS-1$
324 buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1] + 1 - fExtendsRange[0]);
326 if (fInterfaces != null) {
327 buffer.append(fInterfaces);
330 buffer.append(fDocument, fInterfacesRange[0], fInterfacesRange[1] + 1 - fInterfacesRange[0]);
333 buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[0] - fNameRange[1] - 1);
339 * @see DOMNode#appendSimpleContents(CharArrayBuffer)
341 protected void appendSimpleContents(CharArrayBuffer buffer) {
342 // append eveything before my name
343 buffer.append(fDocument, fSourceRange[0], fNameRange[0] - fSourceRange[0]);
345 buffer.append(fName);
348 // append everything after my name and before my first child
349 buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[1] - fNameRange[1]);
350 // append my children
351 appendContentsOfChildren(buffer);
352 // append from my last child to my end
353 buffer.append(fDocument, fCloseBodyRange[0], fSourceRange[1] - fCloseBodyRange[0] + 1);
358 * @see IDOMNode#canHaveChildren()
360 public boolean canHaveChildren() {
364 * Returns the position of the closing brace for the body of this type.
365 * This value this method returns is only valid before the type has
366 * been normalized and is present only for normalization.
368 int getCloseBodyPosition() {
369 return fCloseBodyRange[0];
372 * @see DOMNode#getDetailedNode()
374 //protected DOMNode getDetailedNode() {
375 // return (DOMNode)getFactory().createType(getContents());
378 * @see DOMNode#getInsertionPosition()
380 public int getInsertionPosition() {
381 // this should return the position of the end of the last line separator before the closing brace of the type
382 // See PR 1GELSDQ: ITPJUI:WINNT - JDOM: IType.createMethod does not insert nicely for inner types
383 return fInsertionPosition;
386 * @see IDOMNode#getJavaElement
388 public IJavaElement getJavaElement(IJavaElement parent) throws IllegalArgumentException {
389 if (parent.getElementType() == IJavaElement.TYPE) {
390 return ((IType)parent).getType(getName());
391 } else if (parent.getElementType() == IJavaElement.COMPILATION_UNIT) {
392 return ((ICompilationUnit)parent).getType(getName());
394 throw new IllegalArgumentException(Util.bind("element.illegalParent")); //$NON-NLS-1$
398 * @see DOMMember#getMemberDeclarationStartPosition()
400 protected int getMemberDeclarationStartPosition() {
401 return fTypeRange[0];
404 * @see IDOMNode#getNodeType()
406 public int getNodeType() {
407 return IDOMNode.TYPE;
410 * Answers the open body range end position.
412 int getOpenBodyEnd() {
413 return fOpenBodyRange[1];
416 * @see IDOMType#getSuperclass()
418 public String getSuperclass() {
420 if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
421 if (fSuperclass != null) {
424 return CharArrayOps.substring(fDocument, fSuperclassRange[0], fSuperclassRange[1] + 1 - fSuperclassRange[0]);
431 * @see IDOMType#getSuperInterfaces()
433 public String[] getSuperInterfaces() {
434 return fSuperInterfaces;
439 public boolean isAllowableChild(IDOMNode node) {
441 int type= node.getNodeType();
442 return type == IDOMNode.TYPE || type == IDOMNode.FIELD|| type == IDOMNode.METHOD ||
443 type == IDOMNode.INITIALIZER;
450 * @see IDOMType#isClass()
452 public boolean isClass() {
453 return getMask(MASK_TYPE_IS_CLASS);
458 protected DOMNode newDOMNode() {
459 return new DOMType();
462 * Normalizes this <code>DOMNode</code>'s source positions to include whitespace preceeding
463 * the node on the line on which the node starts, and all whitespace after the node up to
464 * the next node's start
466 void normalize(ILineStartFinder finder) {
467 // perform final changes to the open and close body ranges
468 int openBodyEnd, openBodyStart, closeBodyStart, closeBodyEnd;
469 DOMNode first = (DOMNode) getFirstChild();
470 DOMNode lastNode = null;
471 // look for the open body
472 Scanner scanner = new Scanner();
473 scanner.setSource(fDocument);
474 scanner.resetTo(fNameRange[1] + 1, fDocument.length);
477 int currentToken = scanner.getNextToken();
478 while(currentToken != ITerminalSymbols.TokenNameLBRACE &&
479 currentToken != ITerminalSymbols.TokenNameEOF) {
480 currentToken = scanner.getNextToken();
482 if(currentToken == ITerminalSymbols.TokenNameLBRACE) {
483 openBodyEnd = scanner.currentPosition - 1;
484 openBodyStart = scanner.startPosition;
486 openBodyEnd = fDocument.length;
487 openBodyStart = fDocument.length;
489 } catch(InvalidInputException e) {
490 openBodyEnd = fDocument.length;
491 openBodyStart = fDocument.length;
494 int lineStart = finder.getLineStart(first.getStartPosition());
495 if (lineStart > openBodyEnd) {
496 openBodyEnd = lineStart - 1;
498 openBodyEnd = first.getStartPosition() - 1;
500 lastNode = (DOMNode) first.getNextNode();
501 if (lastNode == null) {
504 while (lastNode.getNextNode() != null) {
505 lastNode = (DOMNode) lastNode.getNextNode();
508 scanner.setSource(fDocument);
509 scanner.resetTo(lastNode.getEndPosition() + 1, fDocument.length);
511 int currentToken = scanner.getNextToken();
512 while(currentToken != ITerminalSymbols.TokenNameRBRACE &&
513 currentToken != ITerminalSymbols.TokenNameEOF) {
514 currentToken = scanner.getNextToken();
516 if(currentToken == ITerminalSymbols.TokenNameRBRACE) {
517 closeBodyStart = scanner.startPosition;
518 closeBodyEnd = scanner.currentPosition - 1;
520 closeBodyStart = fDocument.length;
521 closeBodyEnd = fDocument.length;
523 } catch(InvalidInputException e) {
524 closeBodyStart = fDocument.length;
525 closeBodyEnd = fDocument.length;
528 scanner.resetTo(openBodyEnd, fDocument.length);
530 int currentToken = scanner.getNextToken();
531 while(currentToken != ITerminalSymbols.TokenNameRBRACE &&
532 currentToken != ITerminalSymbols.TokenNameEOF) {
533 currentToken = scanner.getNextToken();
535 if(currentToken == ITerminalSymbols.TokenNameRBRACE) {
536 closeBodyStart = scanner.startPosition;
537 closeBodyEnd = scanner.currentPosition - 1;
539 closeBodyStart = fDocument.length;
540 closeBodyEnd = fDocument.length;
542 } catch(InvalidInputException e) {
543 closeBodyStart = fDocument.length;
544 closeBodyEnd = fDocument.length;
546 openBodyEnd = closeBodyEnd - 1;
548 setOpenBodyRangeEnd(openBodyEnd);
549 setOpenBodyRangeStart(openBodyStart);
550 setCloseBodyRangeStart(closeBodyStart);
551 setCloseBodyRangeEnd(closeBodyEnd);
552 fInsertionPosition = finder.getLineStart(closeBodyStart);
553 if (lastNode != null && fInsertionPosition < lastNode.getEndPosition()) {
554 fInsertionPosition = getCloseBodyPosition();
556 if (fInsertionPosition <= openBodyEnd) {
557 fInsertionPosition = getCloseBodyPosition();
559 super.normalize(finder);
563 * Normalizes this <code>DOMNode</code>'s end position.
565 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
567 // this node's end position includes all of the characters up
568 // to the end of the enclosing node
569 DOMNode parent = (DOMNode) getParent();
570 if (parent == null || parent instanceof DOMCompilationUnit) {
571 setSourceRangeEnd(fDocument.length - 1);
574 setSourceRangeEnd(((DOMType)parent).getCloseBodyPosition() - 1);
577 // this node's end position is just before the start of the next node
578 next.normalizeStartPosition(getEndPosition(), finder);
579 setSourceRangeEnd(next.getStartPosition() - 1);
584 * Offsets all the source indexes in this node by the given amount.
586 protected void offset(int offset) {
587 super.offset(offset);
588 offsetRange(fCloseBodyRange, offset);
589 offsetRange(fExtendsRange, offset);
590 offsetRange(fImplementsRange, offset);
591 offsetRange(fInterfacesRange, offset);
592 offsetRange(fOpenBodyRange, offset);
593 offsetRange(fSuperclassRange, offset);
594 offsetRange(fTypeRange, offset);
597 * @see IDOMType#setClass(boolean)
599 public void setClass(boolean b) {
602 setMask(MASK_TYPE_IS_CLASS, b);
604 fTypeKeyword= "class"; //$NON-NLS-1$
606 fTypeKeyword= "interface"; //$NON-NLS-1$
611 * Sets the end of the close body range
613 void setCloseBodyRangeEnd(int end) {
614 fCloseBodyRange[1] = end;
617 * Sets the start of the close body range
619 void setCloseBodyRangeStart(int start) {
620 fCloseBodyRange[0] = start;
623 * Sets the name of this node.
625 * <p>When the name of a type is set, all of its constructors must be marked
626 * as fragmented, since the names of the constructors must reflect the name
629 * @see IDOMNode#setName(char[])
631 public void setName(String name) throws IllegalArgumentException {
633 throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
636 Enumeration children= getChildren();
637 while (children.hasMoreElements()) {
638 IDOMNode child= (IDOMNode)children.nextElement();
639 if (child.getNodeType() == IDOMNode.METHOD && ((IDOMMethod)child).isConstructor()) {
640 ((DOMNode)child).fragment();
645 * Sets the end of the open body range
647 void setOpenBodyRangeEnd(int end) {
648 fOpenBodyRange[1] = end;
651 * Sets the start of the open body range
653 void setOpenBodyRangeStart(int start) {
654 fOpenBodyRange[0] = start;
657 * @see IDOMType#setSuperclass(char[])
659 public void setSuperclass(String superclassName) {
662 fSuperclass= superclassName;
663 setMask(MASK_TYPE_HAS_SUPERCLASS, superclassName != null);
666 * @see IDOMType#setSuperInterfaces(String[])
668 public void setSuperInterfaces(String[] names) {
671 throw new IllegalArgumentException(Util.bind("dom.nullInterfaces")); //$NON-NLS-1$
674 fSuperInterfaces= names;
675 if (names == null || names.length == 0) {
677 fSuperInterfaces= null;
678 setMask(MASK_TYPE_HAS_INTERFACES, false);
680 setMask(MASK_TYPE_HAS_INTERFACES, true);
681 CharArrayBuffer buffer = new CharArrayBuffer();
682 for (int i = 0; i < names.length; i++) {
684 buffer.append(", "); //$NON-NLS-1$
686 buffer.append(names[i]);
688 fInterfaces = buffer.getContents();
692 * Sets the type keyword
694 void setTypeKeyword(String keyword) {
695 fTypeKeyword = keyword;
698 * @see DOMNode#shareContents(DOMNode)
700 protected void shareContents(DOMNode node) {
701 super.shareContents(node);
702 DOMType type= (DOMType)node;
703 fCloseBodyRange= rangeCopy(type.fCloseBodyRange);
704 fExtendsRange= type.fExtendsRange;
705 fImplementsRange= rangeCopy(type.fImplementsRange);
706 fInterfaces= type.fInterfaces;
707 fInterfacesRange= rangeCopy(type.fInterfacesRange);
708 fOpenBodyRange= rangeCopy(type.fOpenBodyRange);
709 fSuperclass= type.fSuperclass;
710 fSuperclassRange= rangeCopy(type.fSuperclassRange);
711 fSuperInterfaces= type.fSuperInterfaces;
712 fTypeKeyword= type.fTypeKeyword;
713 fTypeRange= rangeCopy(type.fTypeRange);
716 * @see IDOMNode#toString()
718 public String toString() {
719 return "TYPE: " + getName(); //$NON-NLS-1$