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 from the documents
39 * contents, otherwise <code>null</code>.
41 protected String fTypeKeyword;
44 * The original inclusive source range of the 'class' or 'interface' keyword
47 protected int[] fTypeRange;
50 * The superclass name for the class declaration if altered from the
51 * document's contents, otherwise <code>null</code>. Also
52 * <code>null</code> when this type represents an interface.
54 protected String fSuperclass;
57 * The original inclusive source range of the superclass name in the
58 * document, or -1's of no superclass was specified in the document.
60 protected int[] fSuperclassRange;
63 * The original inclusive souce range of the 'extends' keyword in the
64 * document, including surrounding whitespace, or -1's if the keyword was
65 * not present in the document.
67 protected int[] fExtendsRange;
70 * The original inclusive souce range of the 'implements' keyword in the
71 * document, including surrounding whitespace, or -1's if the keyword was
72 * not present in the document.
74 protected int[] fImplementsRange;
77 * The comma delimited list of interfaces this type implements or extends,
78 * if altered from the document's contents, otherwise <code>null</code>.
79 * Also <code>null</code> if this type does not implement or extend any
82 protected char[] fInterfaces;
85 * The original inclusive source range of the list of interfaces this type
86 * implements or extends, not including any surrouding whitespace. If the
87 * document did not specify interfaces, this array contains -1's.
89 protected int[] fInterfacesRange;
92 * The original source range of the first character following the type name
93 * superclass name, or interface list, up to and including the first
94 * character before the first type member.
96 protected int[] fOpenBodyRange;
99 * The original source range of the first new line or non whitespace
100 * character preceding the close brace of the type's body, up to the and
101 * including the first character before the next node (if there are no
102 * following nodes, the range ends at the position of the last character in
105 protected int[] fCloseBodyRange;
108 * A list of interfaces this type extends or implements. <code>null</code>
109 * when this type does not extend or implement any interfaces.
111 protected String[] fSuperInterfaces = new String[0];
114 * This position is the position of the end of the last line separator
115 * before the closing brace starting position of the receiver.
117 // protected int fInsertionPosition;
119 * Constructs an empty type node.
126 * Creates a new detailed TYPE document fragment on the given range of the
130 * the document containing this node's original contents
131 * @param sourceRange -
132 * a two element array of integers describing the entire
133 * inclusive source range of this node within its document.
134 * Contents start on and include the character at the first
135 * position. Contents end on and include the character at the
136 * last position. An array of -1's indicates this node's contents
137 * do not exist in the document.
139 * the identifier portion of the name of this node, or
140 * <code>null</code> if this node does not have a name
142 * a two element array of integers describing the entire
143 * inclusive source range of this node's name within its
144 * document, including any array qualifiers that might
145 * immediately follow the name or -1's if this node does not have
147 * @param commentRange -
148 * a two element array describing the comments that precede the
149 * member declaration. The first matches the start of this node's
150 * sourceRange, and the second is the new-line or first
151 * non-whitespace character following the last comment. If no
152 * comments are present, this array contains two -1's.
154 * an integer representing the modifiers for this member. The
155 * integer can be analyzed with net.sourceforge.phpdt.core.Flags
156 * @param modifierRange -
157 * a two element array describing the location of modifiers for
158 * this member within its source range. The first integer is the
159 * first character of the first modifier for this member, and the
160 * second integer is the last whitespace character preceeding the
161 * next part of this member declaration. If there are no
162 * modifiers present in this node's source code (that is, package
163 * default visibility), this array contains two -1's.
165 * a two element array describing the location of the 'class' or
166 * 'interface' keyword in the type declaration - first and last
167 * character positions.
168 * @param superclassRange -
169 * a two element array describing the location of the superclass
170 * name in the type declaration - first and last character
171 * positions or two -1's if a superclass is not present in the
173 * @param extendsRange -
174 * a two element array describing the location of the 'extends'
175 * keyword in the type declaration, including any surrounding
176 * whitespace, or -1's if the 'extends' keyword is not present in
178 * @param implementsList -
179 * an array of names of the interfaces this type implements or
180 * extends, or <code>null</code> if this type does not
181 * implement or extend any interfaces.
182 * @param implementsRange -
183 * a two element array describing the location of the comment
184 * delimited list of interfaces this type implements or extends,
185 * not including any surrounding whitespace, or -1's if no
186 * interface list is present in the document.
187 * @param implementsKeywordRange -
188 * a two element array describing the location of the
189 * 'implements' keyword, including any surrounding whitespace, or
190 * -1's if no 'implements' keyword is present in the document.
191 * @param openBodyRange -
192 * a two element array describing the location of the open brace
193 * of the type's body and whitespace following the type
194 * declaration and preceeding the first member in the type.
195 * @param closeBodyRange -
196 * a two element array describing the source range of the first
197 * new line or non whitespace character preceeding the close
198 * brace of the type's body, up to the close brace
200 * true is the type is a class, false if it is an interface
202 DOMType(char[] document, int[] sourceRange, String name, int[] nameRange,
203 int[] commentRange, int flags, int[] modifierRange,
204 int[] typeRange, int[] superclassRange, int[] extendsRange,
205 String[] implementsList, int[] implementsRange,
206 int[] implementsKeywordRange, int[] openBodyRange,
207 int[] closeBodyRange, boolean isClass) {
208 super(document, sourceRange, name, nameRange, commentRange, flags,
211 fTypeRange = typeRange;
212 setMask(MASK_TYPE_IS_CLASS, isClass);
214 fExtendsRange = extendsRange;
215 fImplementsRange = implementsKeywordRange;
216 fSuperclassRange = superclassRange;
217 fInterfacesRange = implementsRange;
218 fCloseBodyRange = closeBodyRange;
219 setMask(MASK_TYPE_HAS_SUPERCLASS, superclassRange[0] > 0);
220 setMask(MASK_TYPE_HAS_INTERFACES, implementsList != null);
221 fSuperInterfaces = implementsList;
222 fOpenBodyRange = openBodyRange;
223 fCloseBodyRange = closeBodyRange;
224 setMask(MASK_DETAILED_SOURCE_INDEXES, true);
229 * Creates a new simple TYPE document fragment on the given range of the
233 * the document containing this node's original contents
234 * @param sourceRange -
235 * a two element array of integers describing the entire
236 * inclusive source range of this node within its document.
237 * Contents start on and include the character at the first
238 * position. Contents end on and include the character at the
239 * last position. An array of -1's indicates this node's contents
240 * do not exist in the document.
242 * the identifier portion of the name of this node, or
243 * <code>null</code> if this node does not have a name
245 * a two element array of integers describing the entire
246 * inclusive source range of this node's name within its
247 * document, including any array qualifiers that might
248 * immediately follow the name or -1's if this node does not have
251 * an integer representing the modifiers for this member. The
252 * integer can be analyzed with net.sourceforge.phpdt.core.Flags
253 * @param implementsList -
254 * an array of names of the interfaces this type implements or
255 * extends, or <code>null</code> if this type does not
256 * implement or extend any interfaces.
258 * true is the type is a class, false if it is an interface
260 DOMType(char[] document, int[] sourceRange, String name, int[] nameRange,
261 int flags, String[] implementsList, boolean isClass) {
262 this(document, sourceRange, name, nameRange, new int[] { -1, -1 },
263 flags, new int[] { -1, -1 }, new int[] { -1, -1 }, new int[] {
264 -1, -1 }, new int[] { -1, -1 }, implementsList,
265 new int[] { -1, -1 }, new int[] { -1, -1 },
266 new int[] { -1, -1 }, new int[] { sourceRange[1],
267 sourceRange[1] }, isClass);
268 setMask(MASK_DETAILED_SOURCE_INDEXES, false);
272 * @see IDOMType#addSuperInterface(String)
274 public void addSuperInterface(String name) throws IllegalArgumentException {
276 throw new IllegalArgumentException(Util
277 .bind("dom.addNullInterface")); //$NON-NLS-1$
279 if (fSuperInterfaces == null) {
280 fSuperInterfaces = new String[1];
281 fSuperInterfaces[0] = name;
283 fSuperInterfaces = appendString(fSuperInterfaces, name);
285 setSuperInterfaces(fSuperInterfaces);
289 * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
291 protected void appendMemberBodyContents(CharArrayBuffer buffer) {
292 buffer.append(fDocument, fOpenBodyRange[0], fOpenBodyRange[1] + 1
293 - fOpenBodyRange[0]);
294 appendContentsOfChildren(buffer);
295 buffer.append(fDocument, fCloseBodyRange[0], fCloseBodyRange[1] + 1
296 - fCloseBodyRange[0]);
297 buffer.append(fDocument, fCloseBodyRange[1] + 1, fSourceRange[1]
298 - fCloseBodyRange[1]);
302 * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer )
304 protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
306 if (fTypeKeyword != null) {
307 buffer.append(fTypeKeyword);
308 buffer.append(fDocument, fTypeRange[1], fNameRange[0]
311 buffer.append(fDocument, fTypeRange[0], fTypeRange[1] + 1
315 buffer.append(getName());
318 boolean hasSuperclass = false, hasInterfaces = false;
319 if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
320 hasSuperclass = true;
321 if (fExtendsRange[0] < 0) {
322 buffer.append(" extends "); //$NON-NLS-1$
324 buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
325 + 1 - fExtendsRange[0]);
327 if (fSuperclass != null) {
328 buffer.append(fSuperclass);
330 buffer.append(fDocument, fSuperclassRange[0],
331 fSuperclassRange[1] + 1 - fSuperclassRange[0]);
334 if (getMask(MASK_TYPE_HAS_INTERFACES)) {
335 hasInterfaces = true;
336 if (fImplementsRange[0] < 0) {
337 buffer.append(" implements "); //$NON-NLS-1$
339 buffer.append(fDocument, fImplementsRange[0],
340 fImplementsRange[1] + 1 - fImplementsRange[0]);
342 if (fInterfaces != null) {
343 buffer.append(fInterfaces);
345 buffer.append(fDocument, fInterfacesRange[0],
346 fInterfacesRange[1] + 1 - fInterfacesRange[0]);
350 if (fImplementsRange[0] < 0) {
353 buffer.append(fDocument, fInterfacesRange[1] + 1,
354 fOpenBodyRange[0] - fInterfacesRange[1] - 1);
358 if (fSuperclassRange[0] < 0) {
361 buffer.append(fDocument, fSuperclassRange[1] + 1,
362 fOpenBodyRange[0] - fSuperclassRange[1] - 1);
365 buffer.append(fDocument, fNameRange[1] + 1,
366 fOpenBodyRange[0] - fNameRange[1] - 1);
370 if (getMask(MASK_TYPE_HAS_INTERFACES)) {
371 if (fExtendsRange[0] < 0) {
372 buffer.append(" extends "); //$NON-NLS-1$
374 buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
375 + 1 - fExtendsRange[0]);
377 if (fInterfaces != null) {
378 buffer.append(fInterfaces);
381 buffer.append(fDocument, fInterfacesRange[0],
382 fInterfacesRange[1] + 1 - fInterfacesRange[0]);
385 buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[0]
386 - fNameRange[1] - 1);
393 * @see DOMNode#appendSimpleContents(CharArrayBuffer)
395 protected void appendSimpleContents(CharArrayBuffer buffer) {
396 // append eveything before my name
397 buffer.append(fDocument, fSourceRange[0], fNameRange[0]
400 buffer.append(fName);
402 // append everything after my name and before my first child
403 buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[1]
405 // append my children
406 appendContentsOfChildren(buffer);
407 // append from my last child to my end
408 buffer.append(fDocument, fCloseBodyRange[0], fSourceRange[1]
409 - fCloseBodyRange[0] + 1);
414 * @see IDOMNode#canHaveChildren()
416 public boolean canHaveChildren() {
421 * Returns the position of the closing brace for the body of this type. This
422 * value this method returns is only valid before the type has been
423 * normalized and is present only for normalization.
425 int getCloseBodyPosition() {
426 return fCloseBodyRange[0];
430 * @see DOMNode#getDetailedNode()
432 // protected DOMNode getDetailedNode() {
433 // return (DOMNode)getFactory().createType(getContents());
436 * @see DOMNode#getInsertionPosition()
438 public int getInsertionPosition() {
439 // this should return the position of the end of the last line separator
440 // before the closing brace of the type
441 // See PR 1GELSDQ: ITPJUI:WINNT - JDOM: IType.createMethod does not
442 // insert nicely for inner types
443 return fInsertionPosition;
447 * @see IDOMNode#getJavaElement
449 public IJavaElement getJavaElement(IJavaElement parent)
450 throws IllegalArgumentException {
451 if (parent.getElementType() == IJavaElement.TYPE) {
452 return ((IType) parent).getType(getName());
453 } else if (parent.getElementType() == IJavaElement.COMPILATION_UNIT) {
454 return ((ICompilationUnit) parent).getType(getName());
456 throw new IllegalArgumentException(Util
457 .bind("element.illegalParent")); //$NON-NLS-1$
462 * @see DOMMember#getMemberDeclarationStartPosition()
464 protected int getMemberDeclarationStartPosition() {
465 return fTypeRange[0];
469 * @see IDOMNode#getNodeType()
471 public int getNodeType() {
472 return IDOMNode.TYPE;
476 * Answers the open body range end position.
478 int getOpenBodyEnd() {
479 return fOpenBodyRange[1];
483 * @see IDOMType#getSuperclass()
485 public String getSuperclass() {
487 if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
488 if (fSuperclass != null) {
491 return CharArrayOps.substring(fDocument, fSuperclassRange[0],
492 fSuperclassRange[1] + 1 - fSuperclassRange[0]);
500 * @see IDOMType#getSuperInterfaces()
502 public String[] getSuperInterfaces() {
503 return fSuperInterfaces;
509 public boolean isAllowableChild(IDOMNode node) {
511 int type = node.getNodeType();
512 return type == IDOMNode.TYPE || type == IDOMNode.FIELD
513 || type == IDOMNode.METHOD || type == IDOMNode.INITIALIZER;
521 * @see IDOMType#isClass()
523 public boolean isClass() {
524 return getMask(MASK_TYPE_IS_CLASS);
530 protected DOMNode newDOMNode() {
531 return new DOMType();
535 * Normalizes this <code>DOMNode</code>'s source positions to include
536 * whitespace preceeding the node on the line on which the node starts, and
537 * all whitespace after the node up to the next node's start
539 void normalize(ILineStartFinder finder) {
540 // perform final changes to the open and close body ranges
541 int openBodyEnd, openBodyStart, closeBodyStart, closeBodyEnd;
542 DOMNode first = (DOMNode) getFirstChild();
543 DOMNode lastNode = null;
544 // look for the open body
545 Scanner scanner = new Scanner();
546 scanner.setSource(fDocument);
547 scanner.resetTo(fNameRange[1] + 1, fDocument.length);
550 int currentToken = scanner.getNextToken();
551 while (currentToken != ITerminalSymbols.TokenNameLBRACE
552 && currentToken != ITerminalSymbols.TokenNameEOF) {
553 currentToken = scanner.getNextToken();
555 if (currentToken == ITerminalSymbols.TokenNameLBRACE) {
556 openBodyEnd = scanner.currentPosition - 1;
557 openBodyStart = scanner.startPosition;
559 openBodyEnd = fDocument.length;
560 openBodyStart = fDocument.length;
562 } catch (InvalidInputException e) {
563 openBodyEnd = fDocument.length;
564 openBodyStart = fDocument.length;
567 int lineStart = finder.getLineStart(first.getStartPosition());
568 if (lineStart > openBodyEnd) {
569 openBodyEnd = lineStart - 1;
571 openBodyEnd = first.getStartPosition() - 1;
573 lastNode = (DOMNode) first.getNextNode();
574 if (lastNode == null) {
577 while (lastNode.getNextNode() != null) {
578 lastNode = (DOMNode) lastNode.getNextNode();
581 scanner.setSource(fDocument);
582 scanner.resetTo(lastNode.getEndPosition() + 1, fDocument.length);
584 int currentToken = scanner.getNextToken();
585 while (currentToken != ITerminalSymbols.TokenNameRBRACE
586 && currentToken != ITerminalSymbols.TokenNameEOF) {
587 currentToken = scanner.getNextToken();
589 if (currentToken == ITerminalSymbols.TokenNameRBRACE) {
590 closeBodyStart = scanner.startPosition;
591 closeBodyEnd = scanner.currentPosition - 1;
593 closeBodyStart = fDocument.length;
594 closeBodyEnd = fDocument.length;
596 } catch (InvalidInputException e) {
597 closeBodyStart = fDocument.length;
598 closeBodyEnd = fDocument.length;
601 scanner.resetTo(openBodyEnd, fDocument.length);
603 int currentToken = scanner.getNextToken();
604 while (currentToken != ITerminalSymbols.TokenNameRBRACE
605 && currentToken != ITerminalSymbols.TokenNameEOF) {
606 currentToken = scanner.getNextToken();
608 if (currentToken == ITerminalSymbols.TokenNameRBRACE) {
609 closeBodyStart = scanner.startPosition;
610 closeBodyEnd = scanner.currentPosition - 1;
612 closeBodyStart = fDocument.length;
613 closeBodyEnd = fDocument.length;
615 } catch (InvalidInputException e) {
616 closeBodyStart = fDocument.length;
617 closeBodyEnd = fDocument.length;
619 openBodyEnd = closeBodyEnd - 1;
621 setOpenBodyRangeEnd(openBodyEnd);
622 setOpenBodyRangeStart(openBodyStart);
623 setCloseBodyRangeStart(closeBodyStart);
624 setCloseBodyRangeEnd(closeBodyEnd);
625 fInsertionPosition = finder.getLineStart(closeBodyStart);
626 if (lastNode != null && fInsertionPosition < lastNode.getEndPosition()) {
627 fInsertionPosition = getCloseBodyPosition();
629 if (fInsertionPosition <= openBodyEnd) {
630 fInsertionPosition = getCloseBodyPosition();
632 super.normalize(finder);
636 * Normalizes this <code>DOMNode</code>'s end position.
638 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
640 // this node's end position includes all of the characters up
641 // to the end of the enclosing node
642 DOMNode parent = (DOMNode) getParent();
643 if (parent == null || parent instanceof DOMCompilationUnit) {
644 setSourceRangeEnd(fDocument.length - 1);
647 setSourceRangeEnd(((DOMType) parent).getCloseBodyPosition() - 1);
650 // this node's end position is just before the start of the next
652 next.normalizeStartPosition(getEndPosition(), finder);
653 setSourceRangeEnd(next.getStartPosition() - 1);
658 * Offsets all the source indexes in this node by the given amount.
660 protected void offset(int offset) {
661 super.offset(offset);
662 offsetRange(fCloseBodyRange, offset);
663 offsetRange(fExtendsRange, offset);
664 offsetRange(fImplementsRange, offset);
665 offsetRange(fInterfacesRange, offset);
666 offsetRange(fOpenBodyRange, offset);
667 offsetRange(fSuperclassRange, offset);
668 offsetRange(fTypeRange, offset);
672 * @see IDOMType#setClass(boolean)
674 public void setClass(boolean b) {
677 setMask(MASK_TYPE_IS_CLASS, b);
679 fTypeKeyword = "class"; //$NON-NLS-1$
681 fTypeKeyword = "interface"; //$NON-NLS-1$
687 * Sets the end of the close body range
689 void setCloseBodyRangeEnd(int end) {
690 fCloseBodyRange[1] = end;
694 * Sets the start of the close body range
696 void setCloseBodyRangeStart(int start) {
697 fCloseBodyRange[0] = start;
701 * Sets the name of this node.
704 * When the name of a type is set, all of its constructors must be marked as
705 * fragmented, since the names of the constructors must reflect the name of
708 * @see IDOMNode#setName(char[])
710 public void setName(String name) throws IllegalArgumentException {
712 throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
715 Enumeration children = getChildren();
716 while (children.hasMoreElements()) {
717 IDOMNode child = (IDOMNode) children.nextElement();
718 if (child.getNodeType() == IDOMNode.METHOD
719 && ((IDOMMethod) child).isConstructor()) {
720 ((DOMNode) child).fragment();
726 * Sets the end of the open body range
728 void setOpenBodyRangeEnd(int end) {
729 fOpenBodyRange[1] = end;
733 * Sets the start of the open body range
735 void setOpenBodyRangeStart(int start) {
736 fOpenBodyRange[0] = start;
740 * @see IDOMType#setSuperclass(char[])
742 public void setSuperclass(String superclassName) {
745 fSuperclass = superclassName;
746 setMask(MASK_TYPE_HAS_SUPERCLASS, superclassName != null);
750 * @see IDOMType#setSuperInterfaces(String[])
752 public void setSuperInterfaces(String[] names) {
755 throw new IllegalArgumentException(Util.bind("dom.nullInterfaces")); //$NON-NLS-1$
758 fSuperInterfaces = names;
759 if (names == null || names.length == 0) {
761 fSuperInterfaces = null;
762 setMask(MASK_TYPE_HAS_INTERFACES, false);
764 setMask(MASK_TYPE_HAS_INTERFACES, true);
765 CharArrayBuffer buffer = new CharArrayBuffer();
766 for (int i = 0; i < names.length; i++) {
768 buffer.append(", "); //$NON-NLS-1$
770 buffer.append(names[i]);
772 fInterfaces = buffer.getContents();
777 * Sets the type keyword
779 void setTypeKeyword(String keyword) {
780 fTypeKeyword = keyword;
784 * @see DOMNode#shareContents(DOMNode)
786 protected void shareContents(DOMNode node) {
787 super.shareContents(node);
788 DOMType type = (DOMType) node;
789 fCloseBodyRange = rangeCopy(type.fCloseBodyRange);
790 fExtendsRange = type.fExtendsRange;
791 fImplementsRange = rangeCopy(type.fImplementsRange);
792 fInterfaces = type.fInterfaces;
793 fInterfacesRange = rangeCopy(type.fInterfacesRange);
794 fOpenBodyRange = rangeCopy(type.fOpenBodyRange);
795 fSuperclass = type.fSuperclass;
796 fSuperclassRange = rangeCopy(type.fSuperclassRange);
797 fSuperInterfaces = type.fSuperInterfaces;
798 fTypeKeyword = type.fTypeKeyword;
799 fTypeRange = rangeCopy(type.fTypeRange);
803 * @see IDOMNode#toString()
805 public String toString() {
806 return "TYPE: " + getName(); //$NON-NLS-1$