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.IJavaElement;
 
  16 import net.sourceforge.phpdt.core.IType;
 
  17 import net.sourceforge.phpdt.core.jdom.DOMException;
 
  18 import net.sourceforge.phpdt.core.jdom.IDOMField;
 
  19 import net.sourceforge.phpdt.core.jdom.IDOMMember;
 
  20 import net.sourceforge.phpdt.core.jdom.IDOMNode;
 
  21 import net.sourceforge.phpdt.internal.compiler.util.Util;
 
  22 import net.sourceforge.phpdt.internal.core.util.CharArrayBuffer;
 
  23 import net.sourceforge.phpdt.internal.core.util.CharArrayOps;
 
  26  * DOMField provides an implementation of IDOMField.
 
  31 class DOMField extends DOMMember implements IDOMField {
 
  34          * Contains the type of the field when the type
 
  35          * has been altered from the contents in the
 
  36          * document, otherwise <code>null</code>.
 
  38         protected String fType;
 
  41          * The original inclusive source range of the
 
  42          * field's type in the document.
 
  44         protected int[] fTypeRange;
 
  47          * The contents of the initializer when the
 
  48          * initializer has been altered from the
 
  49          * original state in the document, otherwise
 
  52         protected String fInitializer;
 
  55          * The original inclusive source range of the
 
  56          * initializer in the document.
 
  58         protected int[] fInitializerRange;
 
  61  * Constructs an empty field node.
 
  66  * Creates a new detailed FIELD document fragment on the given range of the document.
 
  68  * @param document - the document containing this node's original contents
 
  69  * @param sourceRange - a two element array of integers describing the
 
  70  *              entire inclusive source range of this node within its document.
 
  71  *              Contents start on and include the character at the first position.
 
  72  *              Contents end on and include the character at the last position.
 
  73  *              An array of -1's indicates this node's contents do not exist
 
  75  * @param name - the identifier portion of the name of this field,
 
  76  *              corresponding to VariableDeclaratorId (JLS 8.3).
 
  77  * @param nameRange - a two element array of integers describing the
 
  78  *              entire inclusive source range of this node's name within its document,
 
  79  *              including any array qualifiers that might follow the name.
 
  80  * @param commentRange - a two element array describing the comments that precede
 
  81  *              the member declaration. The first matches the start of this node's
 
  82  *              sourceRange, and the second is the new-line or first non-whitespace
 
  83  *              character following the last comment. If no comments are present,
 
  84  *              this array contains two -1's.
 
  85  * @param flags - an integer representing the modifiers for this member. The
 
  86  *              integer can be analyzed with org.eclipse.jdt.core.Flags
 
  87  * @param modifierRange - a two element array describing the location of
 
  88  *              modifiers for this member within its source range. The first integer
 
  89  *              is the first character of the first modifier for this member, and
 
  90  *              the second integer is the last whitespace character preceeding the
 
  91  *              next part of this member declaration. If there are no modifiers present
 
  92  *              in this node's source code (that is, package default visibility), this array
 
  94  * @param typeRange- a two element array describing the location of the
 
  95  *              typeName in the document - the positions of the first and last characters
 
  97  * @param type - the type of the field, in normalized form, as defined in
 
  98  *      Type in Field Declaration (JLS 8.3)
 
  99  * @param hasInitializer - true if this field declaration includes an initializer,
 
 101  * @param initRange - a two element array describing the location of the initializer
 
 102  *              in the declaration. The first integer is the position of the character
 
 103  *              following the equals sign, and the position of the last character is the last
 
 104  *              in the initializer. If this field has no initializer, this array contains
 
 106  * @param isVariableDeclarator - true if the field is a seconday variable declarator
 
 107  *              for a previous field declaration.
 
 109 DOMField(char[] document, int[] sourceRange, String name, int[] nameRange, int[] commentRange, int flags, int[] modifierRange, int[] typeRange, String type, boolean hasInitializer, int[] initRange, boolean isVariableDeclarator) {
 
 110         super(document, sourceRange, name, nameRange, commentRange, flags, modifierRange);
 
 113         fTypeRange= typeRange;
 
 114         setHasInitializer(hasInitializer);
 
 115         fInitializerRange= initRange;
 
 116         setIsVariableDeclarator(isVariableDeclarator);
 
 117         setMask(MASK_DETAILED_SOURCE_INDEXES, true);
 
 121  * Creates a new simple FIELD document fragment on the given range of the document.
 
 123  * @param document - the document containing this node's original contents
 
 124  * @param sourceRange - a two element array of integers describing the
 
 125  *              entire inclusive source range of this node within its document.
 
 126  *              Contents start on and include the character at the first position.
 
 127  *              Contents end on and include the character at the last position.
 
 128  *              An array of -1's indicates this node's contents do not exist
 
 130  * @param name - the identifier portion of the name of this field,
 
 131  *              corresponding to VariableDeclaratorId (JLS 8.3).
 
 132  * @param nameRange - a two element array of integers describing the
 
 133  *              entire inclusive source range of this node's name within its document,
 
 134  *              including any array qualifiers that might follow the name.
 
 135  * @param flags - an integer representing the modifiers for this member. The
 
 136  *              integer can be analyzed with org.eclipse.jdt.core.Flags
 
 137  * @param type - the type of the field, in normalized form, as defined in
 
 138  *      Type in Field Declaration (JLS 8.3)
 
 139  * @param isVariableDeclarator - true if the field is a seconday variable declarator
 
 140  *              for a previous field declaration.
 
 142 DOMField(char[] document, int[] sourceRange, String name, int[] nameRange, int flags, String type, boolean isVariableDeclarator) {
 
 143         this(document, sourceRange, name, nameRange, new int[] {-1, -1}, flags, new int[] {-1, -1}, new int[] {-1, -1}, type, false, new int[] {-1, -1}, isVariableDeclarator);
 
 144         setMask(MASK_DETAILED_SOURCE_INDEXES, false);
 
 147  * Appends this member's body contents to the given CharArrayBuffer.
 
 148  * Body contents include the member body and any trailing whitespace.
 
 150  * <p>A field does not have a body.
 
 152  * @see DOMMember#appendMemberBodyContents(CharArrayBuffer)
 
 154 protected void appendMemberBodyContents(CharArrayBuffer buffer) {}
 
 156  * @see DOMMember#appendMemberDeclarationContents(CharArrayBuffer)
 
 158 protected void appendMemberDeclarationContents(CharArrayBuffer buffer) {
 
 160         if (isVariableDeclarator()) {
 
 161                 buffer.append(fDocument, fSourceRange[0], fNameRange[0] - fSourceRange[0]);
 
 164                         .append(getTypeContents())
 
 165                         .append(fDocument, fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1);
 
 168         buffer.append(getNameContents());
 
 169         if (hasInitializer()) {
 
 170                 if (fInitializerRange[0] < 0) {
 
 173                                 .append(fInitializer)
 
 174                                 .append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]);
 
 177                                 .append(fDocument, fNameRange[1] + 1, fInitializerRange[0] - fNameRange[1] - 1)
 
 178                                 .append(getInitializer())
 
 179                                 .append(fDocument, fInitializerRange[1] + 1, fSourceRange[1] - fInitializerRange[1]);
 
 182                 if (fInitializerRange[0] < 0) {
 
 183                         buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]);
 
 185                         buffer.append(fDocument, fInitializerRange[1] + 1, fSourceRange[1] - fInitializerRange[1]);
 
 191  * Appends this member's header contents to the given CharArrayBuffer.
 
 192  * Header contents include any preceding comments and modifiers.
 
 194  * <p>If this field is a secondary variable declarator, there is no header.
 
 196  * @see DOMMember#appendMemberHeaderFragment(CharArrayBuffer)
 
 198 protected void appendMemberHeaderFragment(CharArrayBuffer buffer) {
 
 200         if (isVariableDeclarator()) {
 
 203                 super.appendMemberHeaderFragment(buffer);
 
 208  * @see DOMMember#appendSimpleContents(CharArrayBuffer)
 
 210 protected void appendSimpleContents(CharArrayBuffer buffer) {
 
 211         // append eveything before my name
 
 212         buffer.append(fDocument, fSourceRange[0], fNameRange[0] - fSourceRange[0]);
 
 214         buffer.append(fName);
 
 215         // append everything after my name
 
 216         buffer.append(fDocument, fNameRange[1] + 1, fSourceRange[1] - fNameRange[1]);
 
 219  * Generates detailed source indexes for this node if possible.
 
 221  * @exception DOMException if unable to generate detailed source indexes
 
 224 //protected void becomeDetailed() throws DOMException {
 
 225 //      if (!isDetailed()) {
 
 226 //              if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
 
 227 //                      DOMNode first = getFirstFieldDeclaration();
 
 228 //                      DOMNode last = getLastFieldDeclaration();
 
 229 //                      DOMNode node= first;
 
 230 //                      String source= first.getContents();
 
 231 //                      while (node != last) {
 
 232 //                              node= node.fNextNode;
 
 233 //                              source+=node.getContents();
 
 235 //                      DOMBuilder builder = new DOMBuilder();
 
 236 //                      IDOMField[] details= builder.createFields(source.toCharArray());
 
 237 //                      if (details.length == 0) {
 
 238 //                              throw new DOMException(ProjectPrefUtil.bind("dom.cannotDetail")); //$NON-NLS-1$
 
 241 //                              for (int i= 0; i < details.length; i++) {
 
 242 //                                      node.shareContents((DOMNode)details[i]);
 
 243 //                                      node= node.fNextNode;
 
 247 //                      super.becomeDetailed();
 
 253  * @see IDOMNode#clone()
 
 255 public Object clone() {
 
 256 //      if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
 
 257 //              return getFactory().createField(new String(getSingleVariableDeclaratorContents()));
 
 259                 return super.clone();
 
 263  * Expands all variable declarators in this field declaration into
 
 264  * stand-alone field declarations. 
 
 266 protected void expand() {
 
 267         if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
 
 268                 Enumeration siblings= new SiblingEnumeration(getFirstFieldDeclaration());
 
 269                 DOMField field= (DOMField)siblings.nextElement();
 
 270                 DOMNode next= field.fNextNode;
 
 271                 while (siblings.hasMoreElements() && (next instanceof DOMField) && (((DOMField)next).isVariableDeclarator())) {
 
 272                         field.localizeContents();
 
 273                         if (field.fParent != null) {
 
 274                                 field.fParent.fragment();
 
 276                         field= (DOMField)siblings.nextElement();
 
 277                         next= field.fNextNode;
 
 279                 field.localizeContents();
 
 283  * @see DOMNode#getDetailedNode()
 
 285 //protected DOMNode getDetailedNode() {
 
 286 //      if (isVariableDeclarator() || hasMultipleVariableDeclarators()) {
 
 287 //              return (DOMNode)getFactory().createField(new String(getSingleVariableDeclaratorContents()));
 
 289 //              return (DOMNode)getFactory().createField(getContents());
 
 293  * Returns the first field document fragment that defines
 
 294  * the type for this variable declarator.
 
 296 protected DOMField getFirstFieldDeclaration() {
 
 297         if (isVariableDeclarator()) {
 
 298                 return ((DOMField)fPreviousNode).getFirstFieldDeclaration();
 
 304  * @see IDOMField#getInitializer()
 
 306 public String getInitializer() {
 
 308         if (hasInitializer()) {
 
 309                 if (fInitializer != null) {
 
 312                         return CharArrayOps.substring(fDocument, fInitializerRange[0], fInitializerRange[1] + 1 - fInitializerRange[0]);
 
 319  * @see IDOMNode#getJavaElement
 
 321 public IJavaElement getJavaElement(IJavaElement parent) throws IllegalArgumentException {
 
 322         if (parent.getElementType() == IJavaElement.TYPE) {
 
 323                 return ((IType)parent).getField(getName());
 
 325                 throw new IllegalArgumentException(Util.bind("element.illegalParent")); //$NON-NLS-1$
 
 329  * Returns the last field document fragment in this muli-declarator statement.
 
 331 protected DOMField getLastFieldDeclaration() {
 
 332         DOMField field = this;
 
 333         while (field.isVariableDeclarator() || field.hasMultipleVariableDeclarators()) {
 
 334                 if (field.fNextNode instanceof DOMField && ((DOMField)field.fNextNode).isVariableDeclarator()) {
 
 335                         field= (DOMField)field.fNextNode;
 
 343  * @see DOMMember#getMemberDeclarationStartPosition()
 
 345 protected int getMemberDeclarationStartPosition() {
 
 346         return fTypeRange[0];
 
 349  * @see IDOMNode#getNodeType()
 
 351 public int getNodeType() {
 
 352         return IDOMNode.FIELD;
 
 355  * Returns a String representing this field declaration as a field
 
 356  * declaration with one variable declarator.
 
 358 protected char[] getSingleVariableDeclaratorContents() {
 
 361         CharArrayBuffer buffer= new CharArrayBuffer();
 
 362         DOMField first= getFirstFieldDeclaration();
 
 363         if (first.isDetailed()) {
 
 364                 first.appendMemberHeaderFragment(buffer);
 
 365                 buffer.append(getType());
 
 366                 if (isVariableDeclarator()) {
 
 369                         buffer.append(fDocument, fTypeRange[1] + 1, fNameRange[0] - fTypeRange[1] - 1);
 
 372                 buffer.append(first.fDocument, first.fSourceRange[0], first.fNameRange[0] - first.fSourceRange[0]);
 
 375         buffer.append(getName());
 
 376         if (hasInitializer()) {
 
 377                 if (fInitializerRange[0] < 0) {
 
 380                                 .append(fInitializer)
 
 382                                 .append(Util.LINE_SEPARATOR);
 
 385                                 .append(fDocument, fNameRange[1] + 1, fInitializerRange[0] - fNameRange[1] - 1)
 
 386                                 .append(getInitializer())
 
 388                                 .append(Util.LINE_SEPARATOR);
 
 391                 buffer.append(';').append(Util.LINE_SEPARATOR);
 
 393         return buffer.getContents();
 
 396  * @see IDOMField#getType()
 
 398 public String getType() {
 
 402  * Returns the souce code to be used for this
 
 405 protected char[] getTypeContents() {
 
 406         if (isTypeAltered()) {
 
 407                 return fType.toCharArray();
 
 409                 return CharArrayOps.subarray(fDocument, fTypeRange[0], fTypeRange[1] + 1 - fTypeRange[0]); 
 
 413  * Returns true if this field has an initializer expression,
 
 416 protected boolean hasInitializer() {
 
 417         return getMask(MASK_FIELD_HAS_INITIALIZER);
 
 420  * Returns true is this field declarations has more than one
 
 421  * variable declarator, otherwise false;
 
 423 protected boolean hasMultipleVariableDeclarators() {
 
 424         return fNextNode != null && (fNextNode instanceof DOMField) &&
 
 425                 ((DOMField)fNextNode).isVariableDeclarator();
 
 428  * Inserts the given un-parented node as a sibling of this node, immediately before
 
 429  * this node. Once inserted, the sibling is only dependent on this document fragment.
 
 431  * <p>When a sibling is inserted before a variable declarator, it must first
 
 434  * @see IDOMNode#insertSibling(IDOMNode)
 
 436 public void insertSibling(IDOMNode sibling) throws IllegalArgumentException, DOMException {
 
 437         if (isVariableDeclarator()) {
 
 440         super.insertSibling(sibling);
 
 443  * Returns true if this field's type has been altered
 
 444  * from the original document contents.
 
 446 protected boolean isTypeAltered() {
 
 447         return getMask(MASK_FIELD_TYPE_ALTERED);
 
 450  * Returns true if this field is declared as a secondary variable
 
 451  * declarator for a previous field declaration.
 
 453 protected boolean isVariableDeclarator() {
 
 454         return getMask(MASK_FIELD_IS_VARIABLE_DECLARATOR);
 
 459 protected DOMNode newDOMNode() {
 
 460         return new DOMField();
 
 463  * Normalizes this <code>DOMNode</code>'s end position.
 
 465 void normalizeEndPosition(ILineStartFinder finder, DOMNode next) {
 
 467                 // this node's end position includes all of the characters up
 
 468                 // to the end of the enclosing node
 
 469                 DOMNode parent = (DOMNode) getParent();
 
 470                 if (parent == null || parent instanceof DOMCompilationUnit) {
 
 471                         setSourceRangeEnd(fDocument.length - 1);
 
 474                         int temp = ((DOMType)parent).getCloseBodyPosition() - 1;
 
 475                         setSourceRangeEnd(temp);
 
 476                         fInsertionPosition = Math.max(finder.getLineStart(temp + 1), getEndPosition());
 
 479                 // this node's end position is just before the start of the next node
 
 480                 // unless the next node is a field that is declared along with this one
 
 481                 int temp = next.getStartPosition() - 1;
 
 482                 fInsertionPosition = Math.max(finder.getLineStart(temp + 1), getEndPosition());
 
 484                 next.normalizeStartPosition(getEndPosition(), finder);
 
 485                 if (next instanceof DOMField) {
 
 486                         DOMField field = (DOMField) next;
 
 487                         if (field.isVariableDeclarator() && fTypeRange[0] == field.fTypeRange[0])
 
 490                 setSourceRangeEnd(next.getStartPosition() - 1);
 
 494  * Normalizes this <code>DOMNode</code>'s start position.
 
 496 void normalizeStartPosition(int endPosition, ILineStartFinder finder) {
 
 497         if (isVariableDeclarator()) {
 
 498                 // start position is end of last element
 
 499                 setStartPosition(fPreviousNode.getEndPosition() + 1);
 
 501                 super.normalizeStartPosition(endPosition, finder);
 
 505  * Offsets all the source indexes in this node by the given amount.
 
 507 protected void offset(int offset) {
 
 508         super.offset(offset);
 
 509         offsetRange(fInitializerRange, offset);
 
 510         offsetRange(fTypeRange, offset);
 
 513  * Separates this node from its parent and siblings, maintaining any ties that
 
 514  * this node has to the underlying document fragment.
 
 516  * <p>When a field with multiple declarators is removed, its declaration
 
 517  * must first be expanded.
 
 519  * @see IDOMNode#remove()
 
 521 public void remove() {
 
 526  * @see IDOMMember#setComment(String)
 
 528 public void setComment(String comment) {
 
 530         super.setComment(comment);
 
 533  * @see IDOMMember#setFlags(int)
 
 535 public void setFlags(int flags) {
 
 537         super.setFlags(flags);
 
 540  * Sets the state of this field declaration as having
 
 541  * an initializer expression.
 
 543 protected void setHasInitializer(boolean hasInitializer) {
 
 544         setMask(MASK_FIELD_HAS_INITIALIZER, hasInitializer);
 
 547  * @see IDOMField#setInitializer(char[])
 
 549 public void setInitializer(String initializer) {
 
 552         setHasInitializer(initializer != null);
 
 553         fInitializer= initializer;
 
 556  * Sets the initializer range.
 
 558 void setInitializerRange(int start, int end) {
 
 559         fInitializerRange[0] = start;
 
 560         fInitializerRange[1] = end;
 
 563  * Sets the state of this field declaration as being a
 
 564  * secondary variable declarator for a previous field
 
 567 protected void setIsVariableDeclarator(boolean isVariableDeclarator) {
 
 568         setMask(MASK_FIELD_IS_VARIABLE_DECLARATOR, isVariableDeclarator);
 
 571  * @see IDOMField#setName(char[])
 
 573 public void setName(String name) throws IllegalArgumentException {
 
 575                 throw new IllegalArgumentException(Util.bind("element.nullName")); //$NON-NLS-1$
 
 578                 setTypeAltered(true);
 
 582  * @see IDOMField#setType(char[])
 
 584 public void setType(String typeName) throws IllegalArgumentException {
 
 585         if (typeName == null) {
 
 586                 throw new IllegalArgumentException(Util.bind("element.nullType")); //$NON-NLS-1$
 
 591         setTypeAltered(true);
 
 592         setNameAltered(true);
 
 596  * Sets the state of this field declaration as having
 
 597  * the field type altered from the original document.
 
 599 protected void setTypeAltered(boolean typeAltered) {
 
 600         setMask(MASK_FIELD_TYPE_ALTERED, typeAltered);
 
 603  * @see DOMNode#shareContents(DOMNode)
 
 605 protected void shareContents(DOMNode node) {
 
 606         super.shareContents(node);
 
 607         DOMField field= (DOMField)node;
 
 608         fInitializer= field.fInitializer;
 
 609         fInitializerRange= rangeCopy(field.fInitializerRange);
 
 611         fTypeRange= rangeCopy(field.fTypeRange);
 
 614  * @see IDOMNode#toString()
 
 616 public String toString() {
 
 617         return "FIELD: " + getName(); //$NON-NLS-1$