/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package net.sourceforge.phpdt.internal.core.jdom;

import java.util.Enumeration;

import net.sourceforge.phpdt.core.ICompilationUnit;
import net.sourceforge.phpdt.core.IJavaElement;
import net.sourceforge.phpdt.core.IType;
import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
import net.sourceforge.phpdt.core.compiler.InvalidInputException;
import net.sourceforge.phpdt.core.jdom.IDOMMethod;
import net.sourceforge.phpdt.core.jdom.IDOMNode;
import net.sourceforge.phpdt.core.jdom.IDOMType;
import net.sourceforge.phpdt.internal.compiler.parser.Scanner;
import net.sourceforge.phpdt.internal.compiler.util.Util;
import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer;
import net.sourceforge.phpdt.internal.core.util.CharArrayOps;

/**
 * DOMType provides an implementation of IDOMType.
 * 
 * @see IDOMType
 * @see DOMNode
 */

/* package */class DOMType extends DOMMember implements IDOMType {

	/**
	 * The 'class' or 'interface' keyword if altered from the documents
	 * contents, otherwise <code>null</code>.
	 */
	protected String fTypeKeyword;

	/**
	 * The original inclusive source range of the 'class' or 'interface' keyword
	 * in the document.
	 */
	protected int[] fTypeRange;

	/**
	 * The superclass name for the class declaration if altered from the
	 * document's contents, otherwise <code>null</code>. Also
	 * <code>null</code> when this type represents an interface.
	 */
	protected String fSuperclass;

	/**
	 * The original inclusive source range of the superclass name in the
	 * document, or -1's of no superclass was specified in the document.
	 */
	protected int[] fSuperclassRange;

	/**
	 * The original inclusive souce range of the 'extends' keyword in the
	 * document, including surrounding whitespace, or -1's if the keyword was
	 * not present in the document.
	 */
	protected int[] fExtendsRange;

	/**
	 * The original inclusive souce range of the 'implements' keyword in the
	 * document, including surrounding whitespace, or -1's if the keyword was
	 * not present in the document.
	 */
	protected int[] fImplementsRange;

	/**
	 * The comma delimited list of interfaces this type implements or extends,
	 * if altered from the document's contents, otherwise <code>null</code>.
	 * Also <code>null</code> if this type does not implement or extend any
	 * interfaces.
	 */
	protected char[] fInterfaces;

	/**
	 * The original inclusive source range of the list of interfaces this type
	 * implements or extends, not including any surrouding whitespace. If the
	 * document did not specify interfaces, this array contains -1's.
	 */
	protected int[] fInterfacesRange;

	/**
	 * The original source range of the first character following the type name
	 * superclass name, or interface list, up to and including the first
	 * character before the first type member.
	 */
	protected int[] fOpenBodyRange;

	/**
	 * The original source range of the first new line or non whitespace
	 * character preceding the close brace of the type's body, up to the and
	 * including the first character before the next node (if there are no
	 * following nodes, the range ends at the position of the last character in
	 * the document).
	 */
	protected int[] fCloseBodyRange;

	/**
	 * A list of interfaces this type extends or implements. <code>null</code>
	 * when this type does not extend or implement any interfaces.
	 */
	protected String[] fSuperInterfaces = new String[0];

	/**
	 * This position is the position of the end of the last line separator
	 * before the closing brace starting position of the receiver.
	 */
	// protected int fInsertionPosition;
	/**
	 * Constructs an empty type node.
	 */
	DOMType() {

	}

	/**
	 * Creates a new detailed TYPE document fragment on the given range of the
	 * document.
	 * 
	 * @param document -
	 *            the document containing this node's original contents
	 * @param sourceRange -
	 *            a two element array of integers describing the entire
	 *            inclusive source range of this node within its document.
	 *            Contents start on and include the character at the first
	 *            position. Contents end on and include the character at the
	 *            last position. An array of -1's indicates this node's contents
	 *            do not exist in the document.
	 * @param name -
	 *            the identifier portion of the name of this node, or
	 *            <code>null</code> if this node does not have a name
	 * @param nameRange -
	 *            a two element array of integers describing the entire
	 *            inclusive source range of this node's name within its
	 *            document, including any array qualifiers that might
	 *            immediately follow the name or -1's if this node does not have
	 *            a name.
	 * @param commentRange -
	 *            a two element array describing the comments that precede the
	 *            member declaration. The first matches the start of this node's
	 *            sourceRange, and the second is the new-line or first
	 *            non-whitespace character following the last comment. If no
	 *            comments are present, this array contains two -1's.
	 * @param flags -
	 *            an integer representing the modifiers for this member. The
	 *            integer can be analyzed with net.sourceforge.phpdt.core.Flags
	 * @param modifierRange -
	 *            a two element array describing the location of modifiers for
	 *            this member within its source range. The first integer is the
	 *            first character of the first modifier for this member, and the
	 *            second integer is the last whitespace character preceeding the
	 *            next part of this member declaration. If there are no
	 *            modifiers present in this node's source code (that is, package
	 *            default visibility), this array contains two -1's.
	 * @param typeRange -
	 *            a two element array describing the location of the 'class' or
	 *            'interface' keyword in the type declaration - first and last
	 *            character positions.
	 * @param superclassRange -
	 *            a two element array describing the location of the superclass
	 *            name in the type declaration - first and last character
	 *            positions or two -1's if a superclass is not present in the
	 *            document.
	 * @param extendsRange -
	 *            a two element array describing the location of the 'extends'
	 *            keyword in the type declaration, including any surrounding
	 *            whitespace, or -1's if the 'extends' keyword is not present in
	 *            the document.
	 * @param implementsList -
	 *            an array of names of the interfaces this type implements or
	 *            extends, or <code>null</code> if this type does not
	 *            implement or extend any interfaces.
	 * @param implementsRange -
	 *            a two element array describing the location of the comment
	 *            delimited list of interfaces this type implements or extends,
	 *            not including any surrounding whitespace, or -1's if no
	 *            interface list is present in the document.
	 * @param implementsKeywordRange -
	 *            a two element array describing the location of the
	 *            'implements' keyword, including any surrounding whitespace, or
	 *            -1's if no 'implements' keyword is present in the document.
	 * @param openBodyRange -
	 *            a two element array describing the location of the open brace
	 *            of the type's body and whitespace following the type
	 *            declaration and preceeding the first member in the type.
	 * @param closeBodyRange -
	 *            a two element array describing the source range of the first
	 *            new line or non whitespace character preceeding the close
	 *            brace of the type's body, up to the close brace
	 * @param isClass -
	 *            true is the type is a class, false if it is an interface
	 */
	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) {
		super(document, sourceRange, name, nameRange, commentRange, flags,
				modifierRange);

		fTypeRange = typeRange;
		setMask(MASK_TYPE_IS_CLASS, isClass);

		fExtendsRange = extendsRange;
		fImplementsRange = implementsKeywordRange;
		fSuperclassRange = superclassRange;
		fInterfacesRange = implementsRange;
		fCloseBodyRange = closeBodyRange;
		setMask(MASK_TYPE_HAS_SUPERCLASS, superclassRange[0] > 0);
		setMask(MASK_TYPE_HAS_INTERFACES, implementsList != null);
		fSuperInterfaces = implementsList;
		fOpenBodyRange = openBodyRange;
		fCloseBodyRange = closeBodyRange;
		setMask(MASK_DETAILED_SOURCE_INDEXES, true);

	}

	/**
	 * Creates a new simple TYPE document fragment on the given range of the
	 * document.
	 * 
	 * @param document -
	 *            the document containing this node's original contents
	 * @param sourceRange -
	 *            a two element array of integers describing the entire
	 *            inclusive source range of this node within its document.
	 *            Contents start on and include the character at the first
	 *            position. Contents end on and include the character at the
	 *            last position. An array of -1's indicates this node's contents
	 *            do not exist in the document.
	 * @param name -
	 *            the identifier portion of the name of this node, or
	 *            <code>null</code> if this node does not have a name
	 * @param nameRange -
	 *            a two element array of integers describing the entire
	 *            inclusive source range of this node's name within its
	 *            document, including any array qualifiers that might
	 *            immediately follow the name or -1's if this node does not have
	 *            a name.
	 * @param flags -
	 *            an integer representing the modifiers for this member. The
	 *            integer can be analyzed with net.sourceforge.phpdt.core.Flags
	 * @param implementsList -
	 *            an array of names of the interfaces this type implements or
	 *            extends, or <code>null</code> if this type does not
	 *            implement or extend any interfaces.
	 * @param isClass -
	 *            true is the type is a class, false if it is an interface
	 */
	DOMType(char[] document, int[] sourceRange, String name, int[] nameRange,
			int flags, String[] implementsList, boolean isClass) {
		this(document, sourceRange, name, nameRange, new int[] { -1, -1 },
				flags, new int[] { -1, -1 }, new int[] { -1, -1 }, new int[] {
						-1, -1 }, new int[] { -1, -1 }, implementsList,
				new int[] { -1, -1 }, new int[] { -1, -1 },
				new int[] { -1, -1 }, new int[] { sourceRange[1],
						sourceRange[1] }, isClass);
		setMask(MASK_DETAILED_SOURCE_INDEXES, false);
	}

	/**
	 * @see IDOMType#addSuperInterface(String)
	 */
	public void addSuperInterface(String name) throws IllegalArgumentException {
		if (name == null) {
			throw new IllegalArgumentException(Util
					.bind("dom.addNullInterface")); //$NON-NLS-1$
		}
		if (fSuperInterfaces == null) {
			fSuperInterfaces = new String[1];
			fSuperInterfaces[0] = name;
		} else {
			fSuperInterfaces = appendString(fSuperInterfaces, name);
		}
		setSuperInterfaces(fSuperInterfaces);
	}

	/**
	 * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
	 */
	protected void appendMemberBodyContents(CharArrayBuffer buffer) {
		buffer.append(fDocument, fOpenBodyRange[0], fOpenBodyRange[1] + 1
				- fOpenBodyRange[0]);
		appendContentsOfChildren(buffer);
		buffer.append(fDocument, fCloseBodyRange[0], fCloseBodyRange[1] + 1
				- fCloseBodyRange[0]);
		buffer.append(fDocument, fCloseBodyRange[1] + 1, fSourceRange[1]
				- fCloseBodyRange[1]);
	}

	/**
	 * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer )
	 */
	protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {

		if (fTypeKeyword != null) {
			buffer.append(fTypeKeyword);
			buffer.append(fDocument, fTypeRange[1], fNameRange[0]
					- fTypeRange[1]);
		} else {
			buffer.append(fDocument, fTypeRange[0], fTypeRange[1] + 1
					- fTypeRange[0]);
		}

		buffer.append(getName());

		if (isClass()) {
			boolean hasSuperclass = false, hasInterfaces = false;
			if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
				hasSuperclass = true;
				if (fExtendsRange[0] < 0) {
					buffer.append(" extends "); //$NON-NLS-1$
				} else {
					buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
							+ 1 - fExtendsRange[0]);
				}
				if (fSuperclass != null) {
					buffer.append(fSuperclass);
				} else {
					buffer.append(fDocument, fSuperclassRange[0],
							fSuperclassRange[1] + 1 - fSuperclassRange[0]);
				}
			}
			if (getMask(MASK_TYPE_HAS_INTERFACES)) {
				hasInterfaces = true;
				if (fImplementsRange[0] < 0) {
					buffer.append(" implements "); //$NON-NLS-1$
				} else {
					buffer.append(fDocument, fImplementsRange[0],
							fImplementsRange[1] + 1 - fImplementsRange[0]);
				}
				if (fInterfaces != null) {
					buffer.append(fInterfaces);
				} else {
					buffer.append(fDocument, fInterfacesRange[0],
							fInterfacesRange[1] + 1 - fInterfacesRange[0]);
				}
			}
			if (hasInterfaces) {
				if (fImplementsRange[0] < 0) {
					buffer.append(' ');
				} else {
					buffer.append(fDocument, fInterfacesRange[1] + 1,
							fOpenBodyRange[0] - fInterfacesRange[1] - 1);
				}
			} else {
				if (hasSuperclass) {
					if (fSuperclassRange[0] < 0) {
						buffer.append(' ');
					} else {
						buffer.append(fDocument, fSuperclassRange[1] + 1,
								fOpenBodyRange[0] - fSuperclassRange[1] - 1);
					}
				} else {
					buffer.append(fDocument, fNameRange[1] + 1,
							fOpenBodyRange[0] - fNameRange[1] - 1);
				}
			}
		} else {
			if (getMask(MASK_TYPE_HAS_INTERFACES)) {
				if (fExtendsRange[0] < 0) {
					buffer.append(" extends "); //$NON-NLS-1$
				} else {
					buffer.append(fDocument, fExtendsRange[0], fExtendsRange[1]
							+ 1 - fExtendsRange[0]);
				}
				if (fInterfaces != null) {
					buffer.append(fInterfaces);
					buffer.append(' ');
				} else {
					buffer.append(fDocument, fInterfacesRange[0],
							fInterfacesRange[1] + 1 - fInterfacesRange[0]);
				}
			} else {
				buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[0]
						- fNameRange[1] - 1);
			}
		}

	}

	/**
	 * @see DOMNode#appendSimpleContents(CharArrayBuffer)
	 */
	protected void appendSimpleContents(CharArrayBuffer buffer) {
		// append eveything before my name
		buffer.append(fDocument, fSourceRange[0], fNameRange[0]
				- fSourceRange[0]);
		// append my name
		buffer.append(fName);

		// append everything after my name and before my first child
		buffer.append(fDocument, fNameRange[1] + 1, fOpenBodyRange[1]
				- fNameRange[1]);
		// append my children
		appendContentsOfChildren(buffer);
		// append from my last child to my end
		buffer.append(fDocument, fCloseBodyRange[0], fSourceRange[1]
				- fCloseBodyRange[0] + 1);

	}

	/**
	 * @see IDOMNode#canHaveChildren()
	 */
	public boolean canHaveChildren() {
		return true;
	}

	/**
	 * Returns the position of the closing brace for the body of this type. This
	 * value this method returns is only valid before the type has been
	 * normalized and is present only for normalization.
	 */
	int getCloseBodyPosition() {
		return fCloseBodyRange[0];
	}

	/**
	 * @see DOMNode#getDetailedNode()
	 */
	// protected DOMNode getDetailedNode() {
	// return (DOMNode)getFactory().createType(getContents());
	// }
	/**
	 * @see DOMNode#getInsertionPosition()
	 */
	public int getInsertionPosition() {
		// this should return the position of the end of the last line separator
		// before the closing brace of the type
		// See PR 1GELSDQ: ITPJUI:WINNT - JDOM: IType.createMethod does not
		// insert nicely for inner types
		return fInsertionPosition;
	}

	/**
	 * @see IDOMNode#getJavaElement
	 */
	public IJavaElement getJavaElement(IJavaElement parent)
			throws IllegalArgumentException {
		if (parent.getElementType() == IJavaElement.TYPE) {
			return ((IType) parent).getType(getName());
		} else if (parent.getElementType() == IJavaElement.COMPILATION_UNIT) {
			return ((ICompilationUnit) parent).getType(getName());
		} else {
			throw new IllegalArgumentException(Util
					.bind("element.illegalParent")); //$NON-NLS-1$
		}
	}

	/**
	 * @see DOMMember#getMemberDeclarationStartPosition()
	 */
	protected int getMemberDeclarationStartPosition() {
		return fTypeRange[0];
	}

	/**
	 * @see IDOMNode#getNodeType()
	 */
	public int getNodeType() {
		return IDOMNode.TYPE;
	}

	/**
	 * Answers the open body range end position.
	 */
	int getOpenBodyEnd() {
		return fOpenBodyRange[1];
	}

	/**
	 * @see IDOMType#getSuperclass()
	 */
	public String getSuperclass() {
		becomeDetailed();
		if (getMask(MASK_TYPE_HAS_SUPERCLASS)) {
			if (fSuperclass != null) {
				return fSuperclass;
			} else {
				return CharArrayOps.substring(fDocument, fSuperclassRange[0],
						fSuperclassRange[1] + 1 - fSuperclassRange[0]);
			}
		} else {
			return null;
		}
	}

	/**
	 * @see IDOMType#getSuperInterfaces()
	 */
	public String[] getSuperInterfaces() {
		return fSuperInterfaces;
	}

	/**
	 * @see IDOMNode
	 */
	public boolean isAllowableChild(IDOMNode node) {
		if (node != null) {
			int type = node.getNodeType();
			return type == IDOMNode.TYPE || type == IDOMNode.FIELD
					|| type == IDOMNode.METHOD || type == IDOMNode.INITIALIZER;
		} else {
			return false;
		}

	}

	/**
	 * @see IDOMType#isClass()
	 */
	public boolean isClass() {
		return getMask(MASK_TYPE_IS_CLASS);
	}

	/**
	 * @see DOMNode
	 */
	protected DOMNode newDOMNode() {
		return new DOMType();
	}

	/**
	 * Normalizes this <code>DOMNode</code>'s source positions to include
	 * whitespace preceeding the node on the line on which the node starts, and
	 * all whitespace after the node up to the next node's start
	 */
	void normalize(ILineStartFinder finder) {
		// perform final changes to the open and close body ranges
		int openBodyEnd, openBodyStart, closeBodyStart, closeBodyEnd;
		DOMNode first = (DOMNode) getFirstChild();
		DOMNode lastNode = null;
		// look for the open body
		Scanner scanner = new Scanner();
		scanner.setSource(fDocument);
		scanner.resetTo(fNameRange[1] + 1, fDocument.length);

		try {
			int currentToken = scanner.getNextToken();
			while (currentToken != ITerminalSymbols.TokenNameLBRACE
					&& currentToken != ITerminalSymbols.TokenNameEOF) {
				currentToken = scanner.getNextToken();
			}
			if (currentToken == ITerminalSymbols.TokenNameLBRACE) {
				openBodyEnd = scanner.currentPosition - 1;
				openBodyStart = scanner.startPosition;
			} else {
				openBodyEnd = fDocument.length;
				openBodyStart = fDocument.length;
			}
		} catch (InvalidInputException e) {
			openBodyEnd = fDocument.length;
			openBodyStart = fDocument.length;
		}
		if (first != null) {
			int lineStart = finder.getLineStart(first.getStartPosition());
			if (lineStart > openBodyEnd) {
				openBodyEnd = lineStart - 1;
			} else {
				openBodyEnd = first.getStartPosition() - 1;
			}
			lastNode = (DOMNode) first.getNextNode();
			if (lastNode == null) {
				lastNode = first;
			} else {
				while (lastNode.getNextNode() != null) {
					lastNode = (DOMNode) lastNode.getNextNode();
				}
			}
			scanner.setSource(fDocument);
			scanner.resetTo(lastNode.getEndPosition() + 1, fDocument.length);
			try {
				int currentToken = scanner.getNextToken();
				while (currentToken != ITerminalSymbols.TokenNameRBRACE
						&& currentToken != ITerminalSymbols.TokenNameEOF) {
					currentToken = scanner.getNextToken();
				}
				if (currentToken == ITerminalSymbols.TokenNameRBRACE) {
					closeBodyStart = scanner.startPosition;
					closeBodyEnd = scanner.currentPosition - 1;
				} else {
					closeBodyStart = fDocument.length;
					closeBodyEnd = fDocument.length;
				}
			} catch (InvalidInputException e) {
				closeBodyStart = fDocument.length;
				closeBodyEnd = fDocument.length;
			}
		} else {
			scanner.resetTo(openBodyEnd, fDocument.length);
			try {
				int currentToken = scanner.getNextToken();
				while (currentToken != ITerminalSymbols.TokenNameRBRACE
						&& currentToken != ITerminalSymbols.TokenNameEOF) {
					currentToken = scanner.getNextToken();
				}
				if (currentToken == ITerminalSymbols.TokenNameRBRACE) {
					closeBodyStart = scanner.startPosition;
					closeBodyEnd = scanner.currentPosition - 1;
				} else {
					closeBodyStart = fDocument.length;
					closeBodyEnd = fDocument.length;
				}
			} catch (InvalidInputException e) {
				closeBodyStart = fDocument.length;
				closeBodyEnd = fDocument.length;
			}
			openBodyEnd = closeBodyEnd - 1;
		}
		setOpenBodyRangeEnd(openBodyEnd);
		setOpenBodyRangeStart(openBodyStart);
		setCloseBodyRangeStart(closeBodyStart);
		setCloseBodyRangeEnd(closeBodyEnd);
		fInsertionPosition = finder.getLineStart(closeBodyStart);
		if (lastNode != null && fInsertionPosition < lastNode.getEndPosition()) {
			fInsertionPosition = getCloseBodyPosition();
		}
		if (fInsertionPosition <= openBodyEnd) {
			fInsertionPosition = getCloseBodyPosition();
		}
		super.normalize(finder);
	}

	/**
	 * Normalizes this <code>DOMNode</code>'s end position.
	 */
	void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
		if (next == null) {
			// this node's end position includes all of the characters up
			// to the end of the enclosing node
			DOMNode parent = (DOMNode) getParent();
			if (parent == null || parent instanceof DOMCompilationUnit) {
				setSourceRangeEnd(fDocument.length - 1);
			} else {
				// parent is a type
				setSourceRangeEnd(((DOMType) parent).getCloseBodyPosition() - 1);
			}
		} else {
			// this node's end position is just before the start of the next
			// node
			next.normalizeStartPosition(getEndPosition(), finder);
			setSourceRangeEnd(next.getStartPosition() - 1);
		}
	}

	/**
	 * Offsets all the source indexes in this node by the given amount.
	 */
	protected void offset(int offset) {
		super.offset(offset);
		offsetRange(fCloseBodyRange, offset);
		offsetRange(fExtendsRange, offset);
		offsetRange(fImplementsRange, offset);
		offsetRange(fInterfacesRange, offset);
		offsetRange(fOpenBodyRange, offset);
		offsetRange(fSuperclassRange, offset);
		offsetRange(fTypeRange, offset);
	}

	/**
	 * @see IDOMType#setClass(boolean)
	 */
	public void setClass(boolean b) {
		becomeDetailed();
		fragment();
		setMask(MASK_TYPE_IS_CLASS, b);
		if (b) {
			fTypeKeyword = "class"; //$NON-NLS-1$
		} else {
			fTypeKeyword = "interface"; //$NON-NLS-1$
			setSuperclass(null);
		}
	}

	/**
	 * Sets the end of the close body range
	 */
	void setCloseBodyRangeEnd(int end) {
		fCloseBodyRange[1] = end;
	}

	/**
	 * Sets the start of the close body range
	 */
	void setCloseBodyRangeStart(int start) {
		fCloseBodyRange[0] = start;
	}

	/**
	 * Sets the name of this node.
	 * 
	 * <p>
	 * When the name of a type is set, all of its constructors must be marked as
	 * fragmented, since the names of the constructors must reflect the name of
	 * this type.
	 * 
	 * @see IDOMNode#setName(char[])
	 */
	public void setName(String name) throws IllegalArgumentException {
		if (name == null) {
			throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
		}
		super.setName(name);
		Enumeration children = getChildren();
		while (children.hasMoreElements()) {
			IDOMNode child = (IDOMNode) children.nextElement();
			if (child.getNodeType() == IDOMNode.METHOD
					&& ((IDOMMethod) child).isConstructor()) {
				((DOMNode) child).fragment();
			}
		}
	}

	/**
	 * Sets the end of the open body range
	 */
	void setOpenBodyRangeEnd(int end) {
		fOpenBodyRange[1] = end;
	}

	/**
	 * Sets the start of the open body range
	 */
	void setOpenBodyRangeStart(int start) {
		fOpenBodyRange[0] = start;
	}

	/**
	 * @see IDOMType#setSuperclass(char[])
	 */
	public void setSuperclass(String superclassName) {
		becomeDetailed();
		fragment();
		fSuperclass = superclassName;
		setMask(MASK_TYPE_HAS_SUPERCLASS, superclassName != null);
	}

	/**
	 * @see IDOMType#setSuperInterfaces(String[])
	 */
	public void setSuperInterfaces(String[] names) {
		becomeDetailed();
		if (names == null) {
			throw new IllegalArgumentException(Util.bind("dom.nullInterfaces")); //$NON-NLS-1$
		}
		fragment();
		fSuperInterfaces = names;
		if (names == null || names.length == 0) {
			fInterfaces = null;
			fSuperInterfaces = null;
			setMask(MASK_TYPE_HAS_INTERFACES, false);
		} else {
			setMask(MASK_TYPE_HAS_INTERFACES, true);
			CharArrayBuffer buffer = new CharArrayBuffer();
			for (int i = 0; i < names.length; i++) {
				if (i > 0) {
					buffer.append(", "); //$NON-NLS-1$
				}
				buffer.append(names[i]);
			}
			fInterfaces = buffer.getContents();
		}
	}

	/**
	 * Sets the type keyword
	 */
	void setTypeKeyword(String keyword) {
		fTypeKeyword = keyword;
	}

	/**
	 * @see DOMNode#shareContents(DOMNode)
	 */
	protected void shareContents(DOMNode node) {
		super.shareContents(node);
		DOMType type = (DOMType) node;
		fCloseBodyRange = rangeCopy(type.fCloseBodyRange);
		fExtendsRange = type.fExtendsRange;
		fImplementsRange = rangeCopy(type.fImplementsRange);
		fInterfaces = type.fInterfaces;
		fInterfacesRange = rangeCopy(type.fInterfacesRange);
		fOpenBodyRange = rangeCopy(type.fOpenBodyRange);
		fSuperclass = type.fSuperclass;
		fSuperclassRange = rangeCopy(type.fSuperclassRange);
		fSuperInterfaces = type.fSuperInterfaces;
		fTypeKeyword = type.fTypeKeyword;
		fTypeRange = rangeCopy(type.fTypeRange);
	}

	/**
	 * @see IDOMNode#toString()
	 */
	public String toString() {
		return "TYPE: " + getName(); //$NON-NLS-1$
	}
}
