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.ITerminalSymbols.TokenName;
20 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
21 import net.sourceforge.phpdt.core.jdom.IDOMMethod;
22 import net.sourceforge.phpdt.core.jdom.IDOMNode;
23 import net.sourceforge.phpdt.core.jdom.IDOMType;
24 import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
25 import net.sourceforge.phpdt.internal.compiler.util.Util;
26 import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer;
27 import net.sourceforge.phpdt.internal.core.util.CharArrayOps;
30 * DOMType provides an implementation of IDOMType.
36 /* package */class DOMType extends DOMMember implements IDOMType {
39 * The 'class' or 'interface' keyword if altered from the documents
40 * contents, otherwise <code>null</code>.
42 protected String fTypeKeyword;
45 * The original inclusive source range of the 'class' or 'interface' keyword
48 protected int[] fTypeRange;
51 * The superclass name for the class declaration if altered from the
52 * document's contents, otherwise <code>null</code>. Also
53 * <code>null</code> when this type represents an interface.
55 protected String fSuperclass;
58 * The original inclusive source range of the superclass name in the
59 * document, or -1's of no superclass was specified in the document.
61 protected int[] fSuperclassRange;
64 * The original inclusive souce range of the 'extends' keyword in the
65 * document, including surrounding whitespace, or -1's if the keyword was
66 * not present in the document.
68 protected int[] fExtendsRange;
71 * The original inclusive souce range of the 'implements' keyword in the
72 * document, including surrounding whitespace, or -1's if the keyword was
73 * not present in the document.
75 protected int[] fImplementsRange;
78 * The comma delimited list of interfaces this type implements or extends,
79 * if altered from the document's contents, otherwise <code>null</code>.
80 * Also <code>null</code> if this type does not implement or extend any
83 protected char[] fInterfaces;
86 * The original inclusive source range of the list of interfaces this type
87 * implements or extends, not including any surrouding whitespace. If the
88 * document did not specify interfaces, this array contains -1's.
90 protected int[] fInterfacesRange;
93 * The original source range of the first character following the type name
94 * superclass name, or interface list, up to and including the first
95 * character before the first type member.
97 protected int[] fOpenBodyRange;
100 * The original source range of the first new line or non whitespace
101 * character preceding the close brace of the type's body, up to the and
102 * including the first character before the next node (if there are no
103 * following nodes, the range ends at the position of the last character in
106 protected int[] fCloseBodyRange;
109 * A list of interfaces this type extends or implements. <code>null</code>
110 * when this type does not extend or implement any interfaces.
112 protected String[] fSuperInterfaces = new String[0];
115 * This position is the position of the end of the last line separator
116 * before the closing brace starting position of the receiver.
118 // protected int fInsertionPosition;
120 * Constructs an empty type node.
127 * Creates a new detailed TYPE document fragment on the given range of the
131 * the document containing this node's original contents
132 * @param sourceRange -
133 * a two element array of integers describing the entire
134 * inclusive source range of this node within its document.
135 * Contents start on and include the character at the first
136 * position. Contents end on and include the character at the
137 * last position. An array of -1's indicates this node's contents
138 * do not exist in the document.
140 * the identifier portion of the name of this node, or
141 * <code>null</code> if this node does not have a name
143 * a two element array of integers describing the entire
144 * inclusive source range of this node's name within its
145 * document, including any array qualifiers that might
146 * immediately follow the name or -1's if this node does not have
148 * @param commentRange -
149 * a two element array describing the comments that precede the
150 * member declaration. The first matches the start of this node's
151 * sourceRange, and the second is the new-line or first
152 * non-whitespace character following the last comment. If no
153 * comments are present, this array contains two -1's.
155 * an integer representing the modifiers for this member. The
156 * integer can be analyzed with net.sourceforge.phpdt.core.Flags
157 * @param modifierRange -
158 * a two element array describing the location of modifiers for
159 * this member within its source range. The first integer is the
160 * first character of the first modifier for this member, and the
161 * second integer is the last whitespace character preceeding the
162 * next part of this member declaration. If there are no
163 * modifiers present in this node's source code (that is, package
164 * default visibility), this array contains two -1's.
166 * a two element array describing the location of the 'class' or
167 * 'interface' keyword in the type declaration - first and last
168 * character positions.
169 * @param superclassRange -
170 * a two element array describing the location of the superclass
171 * name in the type declaration - first and last character
172 * positions or two -1's if a superclass is not present in the
174 * @param extendsRange -
175 * a two element array describing the location of the 'extends'
176 * keyword in the type declaration, including any surrounding
177 * whitespace, or -1's if the 'extends' keyword is not present in
179 * @param implementsList -
180 * an array of names of the interfaces this type implements or
181 * extends, or <code>null</code> if this type does not
182 * implement or extend any interfaces.
183 * @param implementsRange -
184 * a two element array describing the location of the comment
185 * delimited list of interfaces this type implements or extends,
186 * not including any surrounding whitespace, or -1's if no
187 * interface list is present in the document.
188 * @param implementsKeywordRange -
189 * a two element array describing the location of the
190 * 'implements' keyword, including any surrounding whitespace, or
191 * -1's if no 'implements' keyword is present in the document.
192 * @param openBodyRange -
193 * a two element array describing the location of the open brace
194 * of the type's body and whitespace following the type
195 * declaration and preceeding the first member in the type.
196 * @param closeBodyRange -
197 * a two element array describing the source range of the first
198 * new line or non whitespace character preceeding the close
199 * brace of the type's body, up to the close brace
201 * true is the type is a class, false if it is an interface
203 DOMType(char[] document, int[] sourceRange, String name, int[] nameRange,
204 int[] commentRange, int flags, int[] modifierRange,
205 int[] typeRange, int[] superclassRange, int[] extendsRange,
206 String[] implementsList, int[] implementsRange,
207 int[] implementsKeywordRange, int[] openBodyRange,
208 int[] closeBodyRange, boolean isClass) {
209 super(document, sourceRange, name, nameRange, commentRange, flags,
212 fTypeRange = typeRange;
213 setMask(MASK_TYPE_IS_CLASS, isClass);
215 fExtendsRange = extendsRange;
216 fImplementsRange = implementsKeywordRange;
217 fSuperclassRange = superclassRange;
218 fInterfacesRange = implementsRange;
219 fCloseBodyRange = closeBodyRange;
220 setMask(MASK_TYPE_HAS_SUPERCLASS, superclassRange[0] > 0);
221 setMask(MASK_TYPE_HAS_INTERFACES, implementsList != null);
222 fSuperInterfaces = implementsList;
223 fOpenBodyRange = openBodyRange;
224 fCloseBodyRange = closeBodyRange;
225 setMask(MASK_DETAILED_SOURCE_INDEXES, true);
230 * Creates a new simple TYPE document fragment on the given range of the
234 * the document containing this node's original contents
235 * @param sourceRange -
236 * a two element array of integers describing the entire
237 * inclusive source range of this node within its document.
238 * Contents start on and include the character at the first
239 * position. Contents end on and include the character at the
240 * last position. An array of -1's indicates this node's contents
241 * do not exist in the document.
243 * the identifier portion of the name of this node, or
244 * <code>null</code> if this node does not have a name
246 * a two element array of integers describing the entire
247 * inclusive source range of this node's name within its
248 * document, including any array qualifiers that might
249 * immediately follow the name or -1's if this node does not have
252 * an integer representing the modifiers for this member. The
253 * integer can be analyzed with net.sourceforge.phpdt.core.Flags
254 * @param implementsList -
255 * an array of names of the interfaces this type implements or
256 * extends, or <code>null</code> if this type does not
257 * implement or extend any interfaces.
259 * true is the type is a class, false if it is an interface
261 DOMType(char[] document, int[] sourceRange, String name, int[] nameRange,
262 int flags, String[] implementsList, boolean isClass) {
263 this(document, sourceRange, name, nameRange, new int[] { -1, -1 },
264 flags, new int[] { -1, -1 }, new int[] { -1, -1 }, new int[] {
265 -1, -1 }, new int[] { -1, -1 }, implementsList,
266 new int[] { -1, -1 }, new int[] { -1, -1 },
267 new int[] { -1, -1 }, new int[] { sourceRange[1],
268 sourceRange[1] }, isClass);
269 setMask(MASK_DETAILED_SOURCE_INDEXES, false);
273 * @see IDOMType#addSuperInterface(String)
275 public void addSuperInterface(String name) throws IllegalArgumentException {
277 throw new IllegalArgumentException(Util
278 .bind("dom.addNullInterface")); //$NON-NLS-1$
280 if (fSuperInterfaces == null) {
281 fSuperInterfaces = new String[1];
282 fSuperInterfaces[0] = name;
284 fSuperInterfaces = appendString(fSuperInterfaces, name);
286 setSuperInterfaces(fSuperInterfaces);
290 * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
292 protected void appendMemberBodyContents(CharArrayBuffer buffer) {
293 buffer.append(fDocument, fOpenBodyRange[0], fOpenBodyRange[1] + 1
294 - fOpenBodyRange[0]);
295 appendContentsOfChildren(buffer);
296 buffer.append(fDocument, fCloseBodyRange[0], fCloseBodyRange[1] + 1
297 - fCloseBodyRange[0]);
298 buffer.append(fDocument, fCloseBodyRange[1] + 1, fSourceRange[1]
299 - fCloseBodyRange[1]);
303 * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer )
305 protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
307 if (fTypeKeyword != null) {
308 buffer.append(fTypeKeyword);
309 buffer.append(fDocument, fTypeRange[1], fNameRange[0]
312 buffer.append(fDocument, fTypeRange[0], fTypeRange[1] + 1
316 buffer.append(getName());
319 boolean hasSuperclass = false, hasInterfaces = false;
320 if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
321 hasSuperclass = true;
322 if (fExtendsRange[0] < 0) {
323 buffer.append(" extends "); //$NON-NLS-1$
325 buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
326 + 1 - fExtendsRange[0]);
328 if (fSuperclass != null) {
329 buffer.append(fSuperclass);
331 buffer.append(fDocument, fSuperclassRange[0],
332 fSuperclassRange[1] + 1 - fSuperclassRange[0]);
335 if (getMask(MASK_TYPE_HAS_INTERFACES)) {
336 hasInterfaces = true;
337 if (fImplementsRange[0] < 0) {
338 buffer.append(" implements "); //$NON-NLS-1$
340 buffer.append(fDocument, fImplementsRange[0],
341 fImplementsRange[1] + 1 - fImplementsRange[0]);
343 if (fInterfaces != null) {
344 buffer.append(fInterfaces);
346 buffer.append(fDocument, fInterfacesRange[0],
347 fInterfacesRange[1] + 1 - fInterfacesRange[0]);
351 if (fImplementsRange[0] < 0) {
354 buffer.append(fDocument, fInterfacesRange[1] + 1,
355 fOpenBodyRange[0] - fInterfacesRange[1] - 1);
359 if (fSuperclassRange[0] < 0) {
362 buffer.append(fDocument, fSuperclassRange[1] + 1,
363 fOpenBodyRange[0] - fSuperclassRange[1] - 1);
366 buffer.append(fDocument, fNameRange[1] + 1,
367 fOpenBodyRange[0] - fNameRange[1] - 1);
371 if (getMask(MASK_TYPE_HAS_INTERFACES)) {
372 if (fExtendsRange[0] < 0) {
373 buffer.append(" extends "); //$NON-NLS-1$
375 buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
376 + 1 - fExtendsRange[0]);
378 if (fInterfaces != null) {
379 buffer.append(fInterfaces);
382 buffer.append(fDocument, fInterfacesRange[0],
383 fInterfacesRange[1] + 1 - fInterfacesRange[0]);
386 buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[0]
387 - fNameRange[1] - 1);
394 * @see DOMNode#appendSimpleContents(CharArrayBuffer)
396 protected void appendSimpleContents(CharArrayBuffer buffer) {
397 // append eveything before my name
398 buffer.append(fDocument, fSourceRange[0], fNameRange[0]
401 buffer.append(fName);
403 // append everything after my name and before my first child
404 buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[1]
406 // append my children
407 appendContentsOfChildren(buffer);
408 // append from my last child to my end
409 buffer.append(fDocument, fCloseBodyRange[0], fSourceRange[1]
410 - fCloseBodyRange[0] + 1);
415 * @see IDOMNode#canHaveChildren()
417 public boolean canHaveChildren() {
422 * Returns the position of the closing brace for the body of this type. This
423 * value this method returns is only valid before the type has been
424 * normalized and is present only for normalization.
426 int getCloseBodyPosition() {
427 return fCloseBodyRange[0];
431 * @see DOMNode#getDetailedNode()
433 // protected DOMNode getDetailedNode() {
434 // return (DOMNode)getFactory().createType(getContents());
437 * @see DOMNode#getInsertionPosition()
439 public int getInsertionPosition() {
440 // this should return the position of the end of the last line separator
441 // before the closing brace of the type
442 // See PR 1GELSDQ: ITPJUI:WINNT - JDOM: IType.createMethod does not
443 // insert nicely for inner types
444 return fInsertionPosition;
448 * @see IDOMNode#getJavaElement
450 public IJavaElement getJavaElement(IJavaElement parent)
451 throws IllegalArgumentException {
452 if (parent.getElementType() == IJavaElement.TYPE) {
453 return ((IType) parent).getType(getName());
454 } else if (parent.getElementType() == IJavaElement.COMPILATION_UNIT) {
455 return ((ICompilationUnit) parent).getType(getName());
457 throw new IllegalArgumentException(Util
458 .bind("element.illegalParent")); //$NON-NLS-1$
463 * @see DOMMember#getMemberDeclarationStartPosition()
465 protected int getMemberDeclarationStartPosition() {
466 return fTypeRange[0];
470 * @see IDOMNode#getNodeType()
472 public int getNodeType() {
473 return IDOMNode.TYPE;
477 * Answers the open body range end position.
479 int getOpenBodyEnd() {
480 return fOpenBodyRange[1];
484 * @see IDOMType#getSuperclass()
486 public String getSuperclass() {
488 if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
489 if (fSuperclass != null) {
492 return CharArrayOps.substring(fDocument, fSuperclassRange[0],
493 fSuperclassRange[1] + 1 - fSuperclassRange[0]);
501 * @see IDOMType#getSuperInterfaces()
503 public String[] getSuperInterfaces() {
504 return fSuperInterfaces;
510 public boolean isAllowableChild(IDOMNode node) {
512 int type = node.getNodeType();
513 return type == IDOMNode.TYPE || type == IDOMNode.FIELD
514 || type == IDOMNode.METHOD || type == IDOMNode.INITIALIZER;
522 * @see IDOMType#isClass()
524 public boolean isClass() {
525 return getMask(MASK_TYPE_IS_CLASS);
531 protected DOMNode newDOMNode() {
532 return new DOMType();
536 * Normalizes this <code>DOMNode</code>'s source positions to include
537 * whitespace preceeding the node on the line on which the node starts, and
538 * all whitespace after the node up to the next node's start
540 void normalize(ILineStartFinder finder) {
541 // perform final changes to the open and close body ranges
542 int openBodyEnd, openBodyStart, closeBodyStart, closeBodyEnd;
543 DOMNode first = (DOMNode) getFirstChild();
544 DOMNode lastNode = null;
545 // look for the open body
546 Scanner scanner = new Scanner();
547 scanner.setSource(fDocument);
548 scanner.resetTo(fNameRange[1] + 1, fDocument.length);
551 TokenName currentToken = scanner.getNextToken();
552 while (currentToken != ITerminalSymbols.TokenName.LBRACE
553 && currentToken != ITerminalSymbols.TokenName.EOF) {
554 currentToken = scanner.getNextToken();
556 if (currentToken == ITerminalSymbols.TokenName.LBRACE) {
557 openBodyEnd = scanner.currentPosition - 1;
558 openBodyStart = scanner.startPosition;
560 openBodyEnd = fDocument.length;
561 openBodyStart = fDocument.length;
563 } catch (InvalidInputException e) {
564 openBodyEnd = fDocument.length;
565 openBodyStart = fDocument.length;
568 int lineStart = finder.getLineStart(first.getStartPosition());
569 if (lineStart > openBodyEnd) {
570 openBodyEnd = lineStart - 1;
572 openBodyEnd = first.getStartPosition() - 1;
574 lastNode = (DOMNode) first.getNextNode();
575 if (lastNode == null) {
578 while (lastNode.getNextNode() != null) {
579 lastNode = (DOMNode) lastNode.getNextNode();
582 scanner.setSource(fDocument);
583 scanner.resetTo(lastNode.getEndPosition() + 1, fDocument.length);
585 TokenName currentToken = scanner.getNextToken();
586 while (currentToken != ITerminalSymbols.TokenName.RBRACE
587 && currentToken != ITerminalSymbols.TokenName.EOF) {
588 currentToken = scanner.getNextToken();
590 if (currentToken == ITerminalSymbols.TokenName.RBRACE) {
591 closeBodyStart = scanner.startPosition;
592 closeBodyEnd = scanner.currentPosition - 1;
594 closeBodyStart = fDocument.length;
595 closeBodyEnd = fDocument.length;
597 } catch (InvalidInputException e) {
598 closeBodyStart = fDocument.length;
599 closeBodyEnd = fDocument.length;
602 scanner.resetTo(openBodyEnd, fDocument.length);
604 TokenName currentToken = scanner.getNextToken();
605 while (currentToken != ITerminalSymbols.TokenName.RBRACE
606 && currentToken != ITerminalSymbols.TokenName.EOF) {
607 currentToken = scanner.getNextToken();
609 if (currentToken == ITerminalSymbols.TokenName.RBRACE) {
610 closeBodyStart = scanner.startPosition;
611 closeBodyEnd = scanner.currentPosition - 1;
613 closeBodyStart = fDocument.length;
614 closeBodyEnd = fDocument.length;
616 } catch (InvalidInputException e) {
617 closeBodyStart = fDocument.length;
618 closeBodyEnd = fDocument.length;
620 openBodyEnd = closeBodyEnd - 1;
622 setOpenBodyRangeEnd(openBodyEnd);
623 setOpenBodyRangeStart(openBodyStart);
624 setCloseBodyRangeStart(closeBodyStart);
625 setCloseBodyRangeEnd(closeBodyEnd);
626 fInsertionPosition = finder.getLineStart(closeBodyStart);
627 if (lastNode != null && fInsertionPosition < lastNode.getEndPosition()) {
628 fInsertionPosition = getCloseBodyPosition();
630 if (fInsertionPosition <= openBodyEnd) {
631 fInsertionPosition = getCloseBodyPosition();
633 super.normalize(finder);
637 * Normalizes this <code>DOMNode</code>'s end position.
639 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
641 // this node's end position includes all of the characters up
642 // to the end of the enclosing node
643 DOMNode parent = (DOMNode) getParent();
644 if (parent == null || parent instanceof DOMCompilationUnit) {
645 setSourceRangeEnd(fDocument.length - 1);
648 setSourceRangeEnd(((DOMType) parent).getCloseBodyPosition() - 1);
651 // this node's end position is just before the start of the next
653 next.normalizeStartPosition(getEndPosition(), finder);
654 setSourceRangeEnd(next.getStartPosition() - 1);
659 * Offsets all the source indexes in this node by the given amount.
661 protected void offset(int offset) {
662 super.offset(offset);
663 offsetRange(fCloseBodyRange, offset);
664 offsetRange(fExtendsRange, offset);
665 offsetRange(fImplementsRange, offset);
666 offsetRange(fInterfacesRange, offset);
667 offsetRange(fOpenBodyRange, offset);
668 offsetRange(fSuperclassRange, offset);
669 offsetRange(fTypeRange, offset);
673 * @see IDOMType#setClass(boolean)
675 public void setClass(boolean b) {
678 setMask(MASK_TYPE_IS_CLASS, b);
680 fTypeKeyword = "class"; //$NON-NLS-1$
682 fTypeKeyword = "interface"; //$NON-NLS-1$
688 * Sets the end of the close body range
690 void setCloseBodyRangeEnd(int end) {
691 fCloseBodyRange[1] = end;
695 * Sets the start of the close body range
697 void setCloseBodyRangeStart(int start) {
698 fCloseBodyRange[0] = start;
702 * Sets the name of this node.
705 * When the name of a type is set, all of its constructors must be marked as
706 * fragmented, since the names of the constructors must reflect the name of
709 * @see IDOMNode#setName(char[])
711 public void setName(String name) throws IllegalArgumentException {
713 throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
716 Enumeration children = getChildren();
717 while (children.hasMoreElements()) {
718 IDOMNode child = (IDOMNode) children.nextElement();
719 if (child.getNodeType() == IDOMNode.METHOD
720 && ((IDOMMethod) child).isConstructor()) {
721 ((DOMNode) child).fragment();
727 * Sets the end of the open body range
729 void setOpenBodyRangeEnd(int end) {
730 fOpenBodyRange[1] = end;
734 * Sets the start of the open body range
736 void setOpenBodyRangeStart(int start) {
737 fOpenBodyRange[0] = start;
741 * @see IDOMType#setSuperclass(char[])
743 public void setSuperclass(String superclassName) {
746 fSuperclass = superclassName;
747 setMask(MASK_TYPE_HAS_SUPERCLASS, superclassName != null);
751 * @see IDOMType#setSuperInterfaces(String[])
753 public void setSuperInterfaces(String[] names) {
756 throw new IllegalArgumentException(Util.bind("dom.nullInterfaces")); //$NON-NLS-1$
759 fSuperInterfaces = names;
760 if (names == null || names.length == 0) {
762 fSuperInterfaces = null;
763 setMask(MASK_TYPE_HAS_INTERFACES, false);
765 setMask(MASK_TYPE_HAS_INTERFACES, true);
766 CharArrayBuffer buffer = new CharArrayBuffer();
767 for (int i = 0; i < names.length; i++) {
769 buffer.append(", "); //$NON-NLS-1$
771 buffer.append(names[i]);
773 fInterfaces = buffer.getContents();
778 * Sets the type keyword
780 // void setTypeKeyword(String keyword) {
781 // fTypeKeyword = keyword;
785 * @see DOMNode#shareContents(DOMNode)
787 protected void shareContents(DOMNode node) {
788 super.shareContents(node);
789 DOMType type = (DOMType) node;
790 fCloseBodyRange = rangeCopy(type.fCloseBodyRange);
791 fExtendsRange = type.fExtendsRange;
792 fImplementsRange = rangeCopy(type.fImplementsRange);
793 fInterfaces = type.fInterfaces;
794 fInterfacesRange = rangeCopy(type.fInterfacesRange);
795 fOpenBodyRange = rangeCopy(type.fOpenBodyRange);
796 fSuperclass = type.fSuperclass;
797 fSuperclassRange = rangeCopy(type.fSuperclassRange);
798 fSuperInterfaces = type.fSuperInterfaces;
799 fTypeKeyword = type.fTypeKeyword;
800 fTypeRange = rangeCopy(type.fTypeRange);
804 * @see IDOMNode#toString()
806 public String toString() {
807 return "TYPE: " + getName(); //$NON-NLS-1$