| /******************************************************************************* |
| * Copyright (c) 2003, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| /* |
| * Created on Mar 9, 2004 |
| * |
| * To change the template for this generated file go to |
| * Window - Preferences - Java - Code Generation - Code and Comments |
| */ |
| package org.eclipse.jst.common.internal.annotations.ui; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.MissingResourceException; |
| import java.util.Set; |
| import java.util.TreeSet; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IField; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.ui.JavaUI; |
| import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal; |
| import org.eclipse.jdt.ui.text.java.IJavadocCompletionProcessor; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.contentassist.IContextInformation; |
| import org.eclipse.jst.common.internal.annotations.core.AnnotationTagParser; |
| import org.eclipse.jst.common.internal.annotations.core.TagParseEventHandler; |
| import org.eclipse.jst.common.internal.annotations.core.Token; |
| import org.eclipse.jst.common.internal.annotations.registry.AnnotationTagRegistry; |
| import org.eclipse.jst.common.internal.annotations.registry.AttributeValueProposalHelper; |
| import org.eclipse.jst.common.internal.annotations.registry.AttributeValuesHelper; |
| import org.eclipse.jst.common.internal.annotations.registry.TagAttribSpec; |
| import org.eclipse.jst.common.internal.annotations.registry.TagSpec; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.part.FileEditorInput; |
| |
| |
| /** |
| * @author Pat Kelley |
| * |
| * To change the template for this generated type comment go to Window - Preferences - Java - Code |
| * Generation - Code and Comments |
| */ |
| public class AnnotationTagCompletionProc implements IJavadocCompletionProcessor, TagParseEventHandler { |
| private static final String[] BOOLEAN_VALID_VALUES = new String[]{"false", "true"}; //$NON-NLS-1$ //$NON-NLS-2$ |
| ICompilationUnit m_icu; |
| |
| IDocument m_doc; |
| |
| List m_tags; |
| |
| // Instance variables active when maybeCompleteAttribute is live. |
| Token m_tagName; |
| |
| /** |
| * Set of all attributes names encountered. Only live when maybeCompleteAttribute is live. |
| */ |
| Set m_attSet = new TreeSet(); |
| |
| /** |
| * List of Attribute. Only live when maybeCompleAttribute is live. |
| */ |
| List m_attributes = new ArrayList(); |
| |
| AnnotationTagParser m_parser = new AnnotationTagParser(this); |
| |
| /** |
| * Scope of the tag. TagSpec.TYPE | TagSpec.METHOD | TagSpec.FIELD. Not valid until |
| * getAnnotationArea has been called for a completions request, and only then if |
| * getAnnotationArea() did not return null. |
| */ |
| int m_tagScope; |
| |
| public AnnotationTagCompletionProc() { |
| initTagInfo(); |
| } |
| |
| private void initTagInfo() { |
| if (m_tags == null) |
| m_tags = AnnotationTagRegistry.getAllTagSpecs(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.ui.text.java.IJavadocCompletionProcessor#computeContextInformation(org.eclipse.jdt.core.ICompilationUnit, |
| * int) |
| */ |
| public IContextInformation[] computeContextInformation(ICompilationUnit cu, int offset) { |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.ui.text.java.IJavadocCompletionProcessor#computeCompletionProposals(org.eclipse.jdt.core.ICompilationUnit, |
| * int, int, int) |
| */ |
| public IJavaCompletionProposal[] computeCompletionProposals(ICompilationUnit cu, int offset, int length, int flags) { |
| if (cu == null) //bug 262362 |
| return null; |
| IEditorInput editorInput = new FileEditorInput((IFile) cu.getResource()); |
| |
| // Set up completion processor state. |
| m_doc = JavaUI.getDocumentProvider().getDocument(editorInput); |
| m_icu = cu; |
| |
| try { |
| AnnotationArea area = getAnnotationArea(offset); |
| |
| if (area == null) { |
| return null; |
| } |
| |
| // Check for tag completion first. ( the easier case ) |
| String tsf = getTagSoFarIfNotCompleted(area.beginOffset, offset); |
| |
| if (tsf != null) { |
| return getTagCompletionsFor(tsf, area, length); |
| } |
| |
| // Ach, have to try the harder case now, where we parse the |
| // annotation |
| return maybeCompleteAttribute(area, offset); |
| |
| } catch (JavaModelException e) { |
| // Silently fail. |
| return null; |
| } catch (BadLocationException ex) { |
| return null; |
| } |
| } |
| |
| private IJavaCompletionProposal[] maybeCompleteAttribute(AnnotationArea area, int cursorPos) throws BadLocationException { |
| m_attSet.clear(); |
| m_attributes.clear(); |
| |
| m_parser.setParserInput(m_doc.get(area.beginOffset, area.length())); |
| m_parser.parse(); |
| |
| TagSpec ts = null; |
| if (m_tagName!=null) |
| ts = getTagSpecForTagName(m_tagName.getText()); |
| |
| // Do we even recognize this tag? |
| if (ts == null) { |
| return null; |
| } |
| |
| // Loop through and determine whether the cursor is within a attribute |
| // assignment, or between assignements. |
| Attribute target = null; |
| Attribute last = null; |
| Attribute before = null; |
| Attribute a = null; |
| boolean between = false; |
| int rCurPos = area.relativeCursorPos(cursorPos); |
| Iterator i = m_attributes.iterator(); |
| while (i.hasNext()) { |
| a = (Attribute) i.next(); |
| |
| if (a.contains(rCurPos)) { |
| target = a; |
| break; |
| } else if (last != null) { |
| // See if the cursor is between, but not directly adjacent to |
| // the last two attributes. |
| if (rCurPos > last.maxExtent() + 1 && rCurPos < a.minExtent() - 1) { |
| between = true; |
| break; |
| } else if (a.immediatelyPrecedes(rCurPos)) { |
| before = a; |
| break; |
| } |
| } |
| last = a; |
| } |
| |
| if (target == null) { |
| if (between) { |
| // If we're between attributes, suggest all possible attributes. |
| return attributeCompletionsFor(ts, cursorPos, 0, "", true); //$NON-NLS-1$ |
| } else if (before != null) { |
| // We're right after the attribute named in 'before', so set the |
| // target to it, and fall |
| // through to the target handling code. |
| target = before; |
| } else { |
| // not between and not immediately after an attribute. We are |
| // past the end of the parsed annotation. |
| // Only offer suggestions if it looks like the last annotation |
| // attribute is valid. |
| if (a == null) { |
| // No annotations attributes, suggest everything. |
| return attributeCompletionsFor(ts, cursorPos, 0, "", true); //$NON-NLS-1$ |
| } else if (rCurPos > a.maxExtent()) { |
| if (a.hasAssignment() && a.hasValue()) { |
| // Last annotation was good, and we're past it, so do |
| // completions for anything |
| return attributeCompletionsFor(ts, cursorPos, 0, "", true); //$NON-NLS-1$ |
| } else if (a.hasAssignment()) |
| return attributeValidValuesFor(ts, a, area, cursorPos); |
| else |
| return attributeCompletionsFor(ts, cursorPos - a.name.length(), 0, a.name.getText(), true); |
| } else { |
| // Didn't match anything, not past the end - we're probably |
| // the first attribute |
| // being added to the tag. |
| return attributeCompletionsFor(ts, cursorPos, 0, "", true); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| // Completion for a partial attribute name? |
| if (target.name.immediatelyPrecedes(rCurPos)) { |
| return attributeCompletionsFor(ts, area.relativeToAbs(target.name.getBeginning()), target.name.length(), target.name.getText(), !target.hasAssignment()); |
| } |
| |
| // Are we in the middle of a name? |
| if (target.name.contains(rCurPos)) { |
| // We've opted to replace the entire name for this case, which seems |
| // to make the most sense. |
| return attributeCompletionsFor(ts, area.relativeToAbs(target.name.getBeginning()), target.name.length(), target.name.getText().substring(0, rCurPos - target.name.getBeginning()), !target.hasAssignment()); |
| } |
| |
| // If we got this far, we're either in a value, or really confused. |
| // try and return valid values or bail? |
| if (a!= null && a.value != null && (a.value.contains(rCurPos) || (target.hasAssignment() && area.relativeCursorPos(cursorPos) > a.name.getBeginning()))) |
| return attributeValidValuesFor(ts, a, area, cursorPos); |
| return attributeCompletionsFor(ts, cursorPos, 0, "", true); //$NON-NLS-1$ |
| } |
| |
| /** |
| * @return valid values for the attribute |
| */ |
| private IJavaCompletionProposal[] attributeValidValuesFor(TagSpec ts, Attribute a, AnnotationArea area, int cursorPos) { |
| TagAttribSpec tas = ts.attributeNamed(a.name.getText()); |
| if (tas == null) |
| return null; |
| String[] validValues = getValidValues(tas, a, area); |
| String partialValue = calculatePartialValue(a, area, cursorPos); |
| int valueOffset = calculateValueOffset(a, area, cursorPos); |
| if (validValues == null || validValues.length == 0) |
| return createCustomAttributeCompletionProposals(ts, tas, partialValue, valueOffset, a.value.getText(), area.javaElement); |
| return createAttributeCompletionProposals(partialValue, valueOffset, validValues); |
| } |
| |
| /** |
| * @param ts |
| * @param tas |
| * @param partialValue |
| * @param valueOffset |
| * @param value |
| * @param javaElement |
| * @return |
| */ |
| private IJavaCompletionProposal[] createCustomAttributeCompletionProposals(TagSpec ts, TagAttribSpec tas, String partialValue, int valueOffset, String value, IJavaElement javaElement) { |
| AttributeValuesHelper helper = ts.getValidValuesHelper(); |
| if (helper == null) |
| return null; |
| AttributeValueProposalHelper[] proposalHelpers = helper.getAttributeValueProposalHelpers(tas, partialValue, valueOffset, javaElement); |
| if (proposalHelpers == null || proposalHelpers.length == 0) |
| return null; |
| IJavaCompletionProposal[] proposals = new IJavaCompletionProposal[proposalHelpers.length]; |
| AnnotationTagProposal proposal; |
| for (int i = 0; i < proposalHelpers.length; i++) { |
| proposal = new AnnotationTagProposal(proposalHelpers[i]); |
| //proposal.setPartialValueString(partialValue); |
| proposals[i] = proposal; |
| } |
| return proposals; |
| } |
| |
| private IJavaCompletionProposal[] createAttributeCompletionProposals(String partialValue, int valueOffset, String[] validValues) { |
| List resultingValues = new ArrayList(); |
| for (int i = 0; i < validValues.length; i++) { |
| String rplString = validValues[i]; |
| if (partialValue != null && !rplString.startsWith(partialValue)) |
| continue; |
| AnnotationTagProposal prop = new AnnotationTagProposal(rplString, valueOffset, 0, null, rplString, 90); |
| prop.setEnsureQuoted(true); |
| //prop.setPartialValueString(partialValue); |
| resultingValues.add(prop); |
| } |
| if (resultingValues.isEmpty()) |
| return null; |
| return (IJavaCompletionProposal[]) resultingValues.toArray(new IJavaCompletionProposal[resultingValues.size()]); |
| } |
| |
| private String[] getValidValues(TagAttribSpec tas, Attribute a, AnnotationArea area) { |
| String[] validValues = tas.getValidValues(); |
| if (validValues == null || validValues.length == 0) { |
| AttributeValuesHelper helper = tas.getTagSpec().getValidValuesHelper(); |
| if (helper == null) |
| return null; |
| validValues = helper.getValidValues(tas, area.javaElement); |
| if ((validValues == null || validValues.length == 0) && tas.valueIsBool()) |
| validValues = BOOLEAN_VALID_VALUES; |
| } |
| return validValues; |
| } |
| |
| /** |
| * @param a |
| * @param area |
| * @param cursorPos |
| * @return |
| */ |
| private int calculateValueOffset(Attribute a, AnnotationArea area, int cursorPos) { |
| if (a.value == null) |
| return cursorPos; |
| int nameEnd = a.name.getEnd(); |
| int valBeg = a.value.getBeginning(); |
| if (valBeg > nameEnd + 2) |
| return area.relativeToAbs(nameEnd + 2); //Value too far away to be correct. |
| return area.relativeToAbs(valBeg); |
| } |
| |
| /** |
| * @param a |
| * @param area |
| * @param cursorPos |
| * @return |
| */ |
| private String calculatePartialValue(Attribute a, AnnotationArea area, int cursorPos) { |
| if (a.value == null) |
| return null; |
| int nameEnd = a.name.getEnd(); |
| int valueBeg = a.value.getBeginning(); |
| if (valueBeg > nameEnd + 2) |
| return null; //Value is too far away so it must not be part of this attribute. |
| int relativePos = area.relativeCursorPos(cursorPos); |
| if (a.value.contains(relativePos)) { |
| boolean hasBeginQuote = valueBeg - nameEnd == 2; |
| String value = a.value.getText(); |
| int end = relativePos - valueBeg; |
| if (hasBeginQuote) |
| end--; |
| if (end > -1) { |
| int length = value.length(); |
| if (end < length) |
| return value.substring(0, end); |
| else if (end == length) |
| return value; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * @param tagName |
| * @return |
| */ |
| private TagSpec getTagSpecForTagName(String tagName) { |
| String simpleName = tagName; |
| if (tagName != null && tagName.length() > 0 && tagName.charAt(0) == '@') |
| simpleName = tagName.length() == 2 ? "" : tagName.substring(1); //$NON-NLS-1$ |
| switch (m_tagScope) { |
| case TagSpec.TYPE : |
| return AnnotationTagRegistry.getTypeTag(simpleName); |
| case TagSpec.METHOD : |
| return AnnotationTagRegistry.getMethodTag(simpleName); |
| case TagSpec.FIELD : |
| return AnnotationTagRegistry.getFieldTag(simpleName); |
| } |
| return null; |
| } |
| |
| private IJavaCompletionProposal[] attributeCompletionsFor(TagSpec ts, int replaceOffset, int replaceLength, String partialAttributeName, boolean appendEquals) { |
| Iterator i = ts.getAttributes().iterator(); |
| List props = new ArrayList(); |
| while (i.hasNext()) { |
| TagAttribSpec tas = (TagAttribSpec) i.next(); |
| String aname = tas.getAttribName(); |
| |
| // Don't suggest attributes that have already been specified. |
| if (!m_attSet.contains(aname)) { |
| if (aname.startsWith(partialAttributeName)) { |
| String rtxt = appendEquals ? aname + '=' : aname; |
| AnnotationTagProposal prop = new AnnotationTagProposal(rtxt, replaceOffset, replaceLength, null, aname, 90); |
| prop.setHelpText(lookupAttHelp(tas)); |
| props.add(prop); |
| } |
| } |
| } |
| if (props.isEmpty()) { |
| return null; |
| } |
| return (IJavaCompletionProposal[]) props.toArray(new IJavaCompletionProposal[props.size()]); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see com.ibm.ws.rd.annotations.TagParseEventHandler#annotationTag(com.ibm.ws.rd.annotations.Token) |
| */ |
| public void annotationTag(Token tag) { |
| m_tagName = tag; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see com.ibm.ws.rd.annotations.TagParseEventHandler#endOfTag(int) |
| */ |
| public void endOfTag(int pos) { |
| // Do nothing |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see com.ibm.ws.rd.annotations.TagParseEventHandler#attribute(com.ibm.ws.rd.annotations.Token, |
| * int, com.ibm.ws.rd.annotations.Token) |
| */ |
| public void attribute(Token name, int equalsPosition, Token value) { |
| m_attributes.add(new Attribute(name, equalsPosition, value)); |
| m_attSet.add(name.getText()); |
| } |
| |
| private String getReplacementForTag(TagSpec ts, int beginIndex) { |
| StringBuffer bud = new StringBuffer(32); |
| |
| bud.append('@'); |
| bud.append(ts.getTagName()); |
| |
| String prefix = getArrayPrefixForMultipleAttribs(beginIndex); |
| List attributes = ts.getAttributes(); |
| |
| for (int i = 0; i < attributes.size(); i++) { |
| TagAttribSpec tas = (TagAttribSpec) attributes.get(i); |
| |
| if (tas.isRequired()) { |
| bud.append(prefix); |
| bud.append(tas.getAttribName()); |
| bud.append('='); |
| } |
| } |
| return bud.toString(); |
| } |
| |
| private String getArrayPrefixForMultipleAttribs(int beginIndex) { |
| String result = null; |
| String source = null; |
| // Get source from compilation unit |
| try { |
| source = m_icu.getSource(); |
| if (source == null || beginIndex < 0) |
| return result; |
| // trim off everything after our begin index |
| source = source.substring(0, beginIndex + 1); |
| int newLineIndex = source.lastIndexOf('\n'); |
| //if we are on first line... |
| if (newLineIndex == -1) |
| newLineIndex = 0; |
| // Get the current line |
| String currentLine = source.substring(newLineIndex, beginIndex + 1); |
| // Currently we have to have the '@' sign to show our menu |
| int annotationIndex = currentLine.lastIndexOf('@'); |
| result = currentLine.substring(0, annotationIndex); |
| result = result + " "; //$NON-NLS-1$ |
| } catch (Exception e) { |
| // Do nothing |
| } |
| |
| return result; |
| } |
| |
| private IJavaCompletionProposal[] getTagCompletionsFor(String partialTagName, AnnotationArea area, int selectLength) { |
| List found = new ArrayList(); |
| |
| for (int i = 0; i < m_tags.size(); i++) { |
| TagSpec ts = (TagSpec) m_tags.get(i); |
| String tname = ts.getTagName(); |
| |
| if (ts.getScope() == m_tagScope && tname.startsWith(partialTagName)) { |
| String rtxt = getReplacementForTag(ts, area.beginOffset); |
| String labl = '@' + tname; |
| AnnotationTagProposal prop = new AnnotationTagProposal(rtxt, area.beginOffset, Math.max(selectLength, rtxt.length()), null, labl, 90); |
| prop.setHelpText(lookupTagHelp(ts)); |
| found.add(prop); |
| } |
| } |
| |
| if (!found.isEmpty()) { |
| return (IJavaCompletionProposal[]) found.toArray(new IJavaCompletionProposal[found.size()]); |
| } |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see org.eclipse.jdt.ui.text.java.IJavadocCompletionProcessor#getErrorMessage() |
| */ |
| public String getErrorMessage() { |
| return null; |
| } |
| |
| private static boolean isWS1(char c) { |
| return c == ' ' || c == '\t' || c == '*' || c == '\r' || c == '\n'; |
| } |
| |
| private String getTagSoFarIfNotCompleted(int startingAt, int cursorAt) throws BadLocationException { |
| if (m_doc.getChar(startingAt) != '@') { |
| return null; |
| } |
| |
| int firstChar = startingAt + 1; |
| |
| if (firstChar == cursorAt) { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| for (int i = firstChar; i < cursorAt; i++) { |
| char c = m_doc.getChar(i); |
| |
| if (isWS1(c)) { |
| return null; |
| } |
| } |
| |
| return m_doc.get(firstChar, cursorAt - firstChar); |
| } |
| |
| /** |
| * Calculates the the area of the annotation we're trying to complete. Also initializes |
| * m_tagScope. |
| * |
| * @param fromOffset |
| * @return |
| * @throws JavaModelException |
| */ |
| private AnnotationArea getAnnotationArea(int fromOffset) throws JavaModelException { |
| // First, roughly calculate the end of the comment. |
| IJavaElement el = m_icu.getElementAt(fromOffset); |
| int absmax, absmin; |
| if (el == null) |
| return null; |
| int ty = el.getElementType(); |
| |
| switch (ty) { |
| case IJavaElement.FIELD : |
| IField f = (IField) el; |
| absmax = f.getNameRange().getOffset(); |
| absmin = f.getSourceRange().getOffset(); |
| m_tagScope = TagSpec.FIELD; |
| break; |
| |
| case IJavaElement.TYPE : |
| IType t = (IType) el; |
| absmax = t.getNameRange().getOffset(); |
| absmin = t.getSourceRange().getOffset(); |
| m_tagScope = TagSpec.TYPE; |
| break; |
| |
| case IJavaElement.METHOD : |
| IMethod m = (IMethod) el; |
| absmax = m.getNameRange().getOffset(); |
| absmin = m.getSourceRange().getOffset(); |
| m_tagScope = TagSpec.METHOD; |
| break; |
| |
| default : |
| m_tagScope = -1; |
| return null; |
| } |
| |
| // Make sure we're not after the name for the member. |
| if (absmax < fromOffset) { |
| return null; |
| } |
| |
| int min = 0, max = 0; |
| try { |
| // Search backwards for the starting '@'. |
| boolean found = false; |
| for (min = fromOffset; min >= absmin; min--) { |
| if (m_doc.getChar(min) == '@') { |
| found = true; |
| break; |
| } |
| } |
| if (!found) { |
| return null; |
| } |
| |
| // Search forwards for the next '@', or the end of the comment. |
| for (max = fromOffset + 1; max < absmax; max++) { |
| if (m_doc.getChar(max) == '@') { |
| break; |
| } |
| } |
| } catch (BadLocationException e) { |
| return null; |
| } |
| |
| return new AnnotationArea(el, min, Math.min(absmax, max)); |
| } |
| |
| private String lookupTagHelp(TagSpec ts) { |
| if (ts != null) |
| try { |
| return ts.lookupTagHelp(); |
| } catch (MissingResourceException e) { |
| // Do nothing, return null |
| } |
| return null; |
| } |
| |
| private String lookupAttHelp(TagAttribSpec tas) { |
| if (tas != null) |
| try { |
| return tas.lookupTagHelp(); |
| } catch (MissingResourceException e) { |
| // Do nothing, return null |
| } |
| return null; |
| } |
| |
| /** |
| * A range that goes from the beginning position up to, but not including, the end position. |
| */ |
| private static class AnnotationArea { |
| /** |
| * Document offset of the beginning of the javadoc annotation. |
| */ |
| int beginOffset; |
| |
| /** |
| * Document offset of the end of the area that could contain an annotation. |
| */ |
| int endOffset; |
| /** |
| * The Java element that this annotation is assigned. |
| * |
| * @param beg |
| * @param end |
| */ |
| IJavaElement javaElement; |
| |
| public AnnotationArea(IJavaElement javaElement, int beg, int end) { |
| this.javaElement = javaElement; |
| beginOffset = beg; |
| endOffset = end; |
| } |
| |
| public int length() { |
| return endOffset - beginOffset; |
| } |
| |
| /** |
| * Returns the cursor position relative to the area. Only valid if |
| * <code>this.contains( absCursorPos )</code> |
| * |
| * @param absCursorPos |
| * @return |
| */ |
| public int relativeCursorPos(int absCursorPos) { |
| return absCursorPos - beginOffset; |
| } |
| |
| public int relativeToAbs(int relPos) { |
| return beginOffset + relPos; |
| } |
| } |
| |
| private static class Attribute { |
| Token name; |
| |
| Token value; |
| |
| int equalsPos; |
| |
| Attribute(Token n, int ep, Token v) { |
| name = n; |
| value = v; |
| equalsPos = ep; |
| } |
| |
| public boolean hasAssignment() { |
| return equalsPos != -1; |
| } |
| |
| public boolean hasValue() { |
| return value.length() != 0; |
| } |
| |
| public boolean contains(int srcPos) { |
| return srcPos >= minExtent() && srcPos <= maxExtent(); |
| } |
| |
| public int minExtent() { |
| return name.getBeginning(); |
| } |
| |
| public int maxExtent() { |
| if (hasAssignment()) { |
| if (hasValue()) |
| return value.getEnd(); |
| return equalsPos; |
| } |
| return name.getEnd(); |
| } |
| |
| public boolean immediatelyPrecedes(int pos) { |
| return maxExtent() + 1 == pos; |
| } |
| } |
| |
| } |