You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by cd...@apache.org on 2016/04/18 15:32:00 UTC
[11/14] git commit: [flex-falcon]
[refs/heads/feature/maven-migration-test] - Merge branches 'develop' and
'feature/maven-migration-test' of
https://git-wip-us.apache.org/repos/asf/flex-falcon into
feature/maven-migration-test
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java
----------------------------------------------------------------------
diff --cc compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java
index 738bdf3,0000000..25f6b6d
mode 100644,000000..100644
--- a/compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java
+++ b/compiler/src/main/java/org/apache/flex/compiler/common/ISourceLocation.java
@@@ -1,75 -1,0 +1,85 @@@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.common;
+
+/**
+ * This interface provides information about where something
+ * (a token, a tag, an attribute, a node, a definition, etc.)
+ * appears in source code.
+ * <p>
+ * Its primary purpose is for problem reporting.
+ * The start and end offsets are used to highlight
+ * a soruce range in the Flash Builder editor.
+ * The line and column numbers are used by command-line
+ * tools to report problems.
+ */
+public interface ISourceLocation
+{
+ /**
+ * Indicates an unknown or implicit starting offset, ending offset,
+ * line number, or column number.
+ */
+ static final int UNKNOWN = -1;
+
+ /**
+ * Gets the normalized source path.
+ */
+ String getSourcePath();
+
+ /**
+ * Gets the local starting offset. It is zero-based.
+ */
+ int getStart();
+
+ /**
+ * Gets the local ending offset. It is zero-based.
+ */
+ int getEnd();
+
+ /**
+ * Gets the local line number. It is zero-based.
+ */
+ int getLine();
+
+ /**
+ * Gets the local column number. It is zero-based.
+ */
+ int getColumn();
+
+ /**
++ * Gets the local line number at the end. It is zero-based.
++ */
++ int getEndLine();
++
++ /**
++ * Gets the local column number at the end. It is zero-based.
++ */
++ int getEndColumn();
++
++ /**
+ * Gets the absolute starting offset. It is zero-based.
+ */
+ int getAbsoluteStart();
+
+ /**
+ * Gets the absolute starting offset. It is zero-based.
+ */
+ int getAbsoluteEnd();
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java
----------------------------------------------------------------------
diff --cc compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java
index 88998b7,0000000..0d0e1b0
mode 100644,000000..100644
--- a/compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java
+++ b/compiler/src/main/java/org/apache/flex/compiler/common/SourceLocation.java
@@@ -1,361 -1,0 +1,415 @@@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.common;
+
+import org.apache.flex.utils.FilenameNormalization;
+
+/**
+ * Common class to store file/location information across all source types
+ * such as AS, CSS etc
+ */
+public class SourceLocation implements ISourceLocation
+{
+ /**
+ * Constructor for a known source location.
+ */
+ public SourceLocation(String sourcePath, int start, int end, int line, int column)
+ {
+ this.sourcePath = sourcePath;
+ this.start = start;
+ this.end = end;
+ this.line = line;
+ this.column = column;
++ this.endLine = UNKNOWN;
++ this.endColumn = UNKNOWN;
+ }
+
+ /**
+ * Copy Constructor for a known source location.
+ */
+ public SourceLocation(ISourceLocation location)
+ {
+ this(location.getSourcePath(),
+ location.getStart(),
+ location.getEnd(),
+ location.getLine(),
+ location.getColumn());
+ }
+
+ /**
+ * Constructor for an unknown source location.
+ */
+ public SourceLocation()
+ {
+ this(null, UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN);
+ }
+
+ /**
+ * Source path.
+ */
+ private String sourcePath;
+
+ /**
+ * Zero-based starting offset.
+ * <p>
+ * This class does not distinguish between local and absolute offsets,
+ * but subclasses may. If so, they should store the absolute starting
+ * offset in this field and use it to compute the local starting offset.
+ */
+ private int start;
+
+ /**
+ * Zero-based ending offset.
+ * <p>
+ * This class does not distinguish between local and absolute offsets,
+ * but subclasses may. If so, they should store the absolute ending
+ * offset in this field and use it to compute the local ending offset.
+ */
+ private int end;
+
+ /**
+ * Zero-based line number.
+ * Corresponds to start, not end.
+ */
+ private int line;
+
+ /**
+ * Zero-based column number.
+ * Corresponds to start, not end.
+ */
+ private int column;
++
++ /**
++ * Zero-based line number that corresponds to end.
++ */
++ private int endLine;
++
++ /**
++ * Zero-based column number that corresponds to end.
++ */
++ private int endColumn;
+
+ /**
+ * Copies source location information from another instance
+ * into this instance.
+ */
+ public final void setSourceLocation(ISourceLocation src)
+ {
+ assert src != null : "source location can't be null";
+
+ this.start = src.getStart();
+ this.end = src.getEnd();
+ this.line = src.getLine();
+ this.column = src.getColumn();
++ this.endLine = src.getEndLine();
++ this.endColumn = src.getEndColumn();
+ this.sourcePath = src.getSourcePath();
+ }
+
+ /**
+ * @return The local start offset
+ */
+ @Override
+ public int getStart()
+ {
+ assert start >= 0 || start == UNKNOWN : "Invalid value for start: " + start;
+ return start;
+ }
+
+ /**
+ * @return The absolute start offset.
+ */
+ @Override
+ public int getAbsoluteStart()
+ {
+ assert start >= 0 || start == UNKNOWN : "Invalid value for start: " + start;
+ return start;
+ }
+
+ /**
+ * @return The absolute end offset.
+ */
+ @Override
+ public int getAbsoluteEnd()
+ {
+ assert end >= 0 || end == UNKNOWN : "Invalid value for end: " + end;
+ return end;
+ }
+
+ /**
+ * Set the absolute offset where this node starts.
+ */
+ public void setStart(int start)
+ {
+ if (start != UNKNOWN)
+ this.start = start;
+ }
+
+ /**
+ * @return The local end offset.
+ */
+ @Override
+ public int getEnd()
+ {
+ assert end >= 0 || end == UNKNOWN : "Invalid value for end: " + end;
+ return end;
+ }
+
+ /**
+ * Set the absolute offset where this node ends.
+ */
+ public void setEnd(int end)
+ {
+ if (end != UNKNOWN)
+ this.end = end;
+ }
+
+ /**
+ * Get the line number where this node starts.
+ * Line numbers start at 0, not 1.
+ * @return The line number
+ */
+ @Override
+ public int getLine()
+ {
+ assert line >= 0 || line == UNKNOWN : "Invalid value for line: " + line;
+ return line;
+ }
+
+ /**
+ * Set the line number where this node starts.
+ * Column numbers start at 0, not 1.
+ * @param line The line number
+ */
+ public void setLine(int line)
+ {
+ if (line != UNKNOWN)
+ this.line = line;
+ }
+
+ /**
+ * Get the column number where this node starts.
+ * @return The column number
+ */
+ @Override
+ public int getColumn()
+ {
+ assert column >= 0 || column == UNKNOWN : "Invalid value for column: " + column;
+ return column;
+ }
+
+ /**
+ * Set the column number where this node starts.
+ * @param column The column number
+ */
+ public void setColumn(int column)
+ {
+ if (column != UNKNOWN)
+ this.column = column;
+ }
+
+ /**
++ * Get the line number where this node ends.
++ * Line numbers start at 0, not 1.
++ * @return The line number
++ */
++ public int getEndLine()
++ {
++ return endLine;
++ }
++
++ /**
++ * Set the line number where this node ends.
++ * Column numbers start at 0, not 1.
++ * @param line The line number
++ */
++ public void setEndLine(int line)
++ {
++ this.endLine = line;
++ }
++
++ /**
++ * Get the column number where this node ends.
++ * @return The column number
++ */
++ public int getEndColumn()
++ {
++ return endColumn;
++ }
++
++ /**
++ * Set the column number where this node ends.
++ * @param column The column number
++ */
++ public void setEndColumn(int column)
++ {
++ this.endColumn = column;
++ }
++
++ /**
+ * Get the source path for this node.
+ * @return The source path for this node
+ */
+ @Override
+ public final String getSourcePath()
+ {
+ // null means the source is unknown.
+ // "" means the source is a buffer that hasn't yet been saved to a file.
+ // Something like "framework.swc:defaults.css" means the source is a file inside a SWC.
+ // TODO Shouldn't the part before the colon be normalized?
+ // Anything else should be a normalized path to a source file.
+ assert sourcePath == null ||
+ sourcePath.isEmpty() ||
+ sourcePath.contains(".swc:") ||
+ FilenameNormalization.isNormalized(sourcePath) :
+ "Invalid value for sourcePath: " + sourcePath;
+ return sourcePath;
+ }
+
+ /**
+ * Set the source path of the node.
+ * @param sourcePath The source path
+ */
+ public final void setSourcePath(String sourcePath)
+ {
+ this.sourcePath = sourcePath;
+ }
+
+ /**
+ * Displays line, column, start, end, and sourcepath in a format such as
+ * <pre>
+ * "17:5 160-188 C:\test.as"
+ * </pre>
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getLineColumnString());
+ sb.append(getOffsetsString());
+ sb.append(getSourcePathString());
+ return sb.toString();
+ }
+
+ /**
+ * Displays Line and Column numbers
+ */
+ protected String getLineColumnString()
+ {
+ StringBuilder sb = new StringBuilder();
+ int line = getLine();
+ if (line != UNKNOWN)
+ sb.append(line);
+ else
+ sb.append('?');
+ sb.append(':');
+ int column = getColumn();
+ if (column != UNKNOWN)
+ sb.append(column);
+ else
+ sb.append('?');
+
+ sb.append(' ');
+ return sb.toString();
+ }
+
+ /**
+ * Displays sourcepath
+ *
+ */
+ protected String getSourcePathString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(' ');
+
+ String sourcePath = getSourcePath();
+ if (sourcePath != null)
+ sb.append(sourcePath);
+ else
+ sb.append('?');
+ return sb.toString();
+ }
+
+ /**
+ * Displays line, column, start, end
+ *
+ */
+ protected String getOffsetsString()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("loc: ");
+ int start = getStart();
+ if (start != UNKNOWN)
+ sb.append(start);
+ else
+ sb.append('?');
+ sb.append('-');
+ int end = getEnd();
+ if (end != UNKNOWN)
+ sb.append(end);
+ else
+ sb.append('?');
+
+ sb.append(' ');
+
+ sb.append("abs: ");
+ int absoluteStart = getAbsoluteStart();
+ if (absoluteStart != UNKNOWN)
+ sb.append(absoluteStart);
+ else
+ sb.append('?');
+ sb.append('-');
+ int absoluteEnd = getAbsoluteEnd();
+ if (absoluteEnd != UNKNOWN)
+ sb.append(absoluteEnd);
+ else
+ sb.append('?');
+ return sb.toString();
+ }
+
+
+ /**
+ * Span the location range from {@code start} to {@code end}.
+ *
+ * @param start Start location.
+ * @param end End location.
+ */
+ public final void span(ISourceLocation start, ISourceLocation end)
+ {
+ setSourcePath(start.getSourcePath());
+ setStart(start.getStart());
+ setEnd(end.getEnd());
+ setLine(start.getLine());
+ setColumn(start.getColumn());
++ setEndLine(end.getEndLine());
++ setEndColumn(end.getEndColumn());
+ }
+
+ /**
+ * Span the location range from {@code start} to {@code end}.
+ *
+ * @param location The location
+ */
+ public final void span(ISourceLocation location)
+ {
+ if (location != null)
+ setSourceLocation(location);
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java
----------------------------------------------------------------------
diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java
index 92b9350,0000000..5b0b18e
mode 100644,000000..100644
--- a/compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java
+++ b/compiler/src/main/java/org/apache/flex/compiler/internal/definitions/metadata/MetaTag.java
@@@ -1,365 -1,0 +1,377 @@@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.internal.definitions.metadata;
+
+import java.util.Arrays;
+
+import org.apache.flex.compiler.common.IMetaInfo;
+import org.apache.flex.compiler.common.NodeReference;
+import org.apache.flex.compiler.constants.IMetaAttributeConstants;
+import org.apache.flex.compiler.definitions.IDefinition;
+import org.apache.flex.compiler.definitions.metadata.IMetaTag;
+import org.apache.flex.compiler.definitions.metadata.IMetaTagAttribute;
+import org.apache.flex.compiler.filespecs.IFileSpecification;
+import org.apache.flex.compiler.internal.definitions.DefinitionBase;
+import org.apache.flex.compiler.internal.parsing.as.OffsetLookup;
+import org.apache.flex.compiler.internal.scopes.ASFileScope;
+import org.apache.flex.compiler.internal.scopes.ASScope;
+import org.apache.flex.compiler.internal.tree.as.ClassNode;
+import org.apache.flex.compiler.tree.as.IASNode;
+import org.apache.flex.compiler.tree.metadata.IMetaTagNode;
+import org.apache.flex.compiler.tree.metadata.IMetaTagsNode;
+import org.apache.flex.compiler.workspaces.IWorkspace;
+
+public class MetaTag implements IMetaTag
+{
+ /**
+ * Append a {@link IMetaTag} to an array of {@link IMetaTag}.
+ *
+ * @param metaTags An existing array of meta tags. May be empty but
+ * may not be null.
+ * @param metaTag The new meta tag to append to the metaTags array.
+ * If null, the metaTags parameter is returned unmodified.
+ * @return The new array of meta tags.
+ */
+ public static IMetaInfo[] addMetaTag(IMetaInfo[] metaTags, IMetaInfo metaTag)
+ {
+ assert metaTags != null;
+
+ if (metaTag != null)
+ {
+ IMetaInfo[] newMetaTags = Arrays.copyOf(metaTags, metaTags.length + 1, IMetaInfo[].class);
+ newMetaTags[metaTags.length] = metaTag;
+ metaTags = newMetaTags;
+ }
+
+ return metaTags;
+ }
+
+ /**
+ * Create a new meta tag for either "__go_to_ctor_definition_help" or
+ * "__go_to_definition_help".
+ *
+ * @param definition The definition to add the meta data for.
+ * @param file The absolute path of the source file the definition is found
+ * in. May be null.
+ * @param pos The position of the definition in the source file. If "-1"
+ * no MetaTag is created.
+ * @param ctor True if the definition is for a constructor, false otherwise.
+ * @return A new MetaTag. If the pos paramater is "-1", null is returned.
+ */
+ public static MetaTag createGotoDefinitionHelp(IDefinition definition,
+ String file, String pos, boolean ctor)
+ {
+ assert pos != null;
+
+ if (pos.equals("-1"))
+ return null;
+
+ IMetaTagAttribute[] attributes = new MetaTagAttribute[file != null ? 2 : 1];
+ if (file != null)
+ {
+ attributes[0] = new MetaTagAttribute(IMetaAttributeConstants.NAME_GOTODEFINITIONHELP_FILE,
+ file);
+ }
+
+ attributes[file != null ? 1 : 0] = new MetaTagAttribute(IMetaAttributeConstants.NAME_GOTODEFINITIONHELP_POS,
+ pos);
+
+ return new MetaTag(definition,
+ ctor ? IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITION_CTOR_HELP :
+ IMetaAttributeConstants.ATTRIBUTE_GOTODEFINITIONHELP,
+ attributes);
+ }
+
+ public MetaTag(IDefinition decoratedDefinition, String tagName, IMetaTagAttribute[] attributes)
+ {
+ this.decoratedDefinition = decoratedDefinition;
+ this.tagName = tagName;
+ if (attributes == null)
+ {
+ // this is a low cost way to make sure that clients never get null attributes
+ attributes = emptyAttributes;
+ }
+ this.attributes = attributes;
+ }
+
+ private IDefinition decoratedDefinition;
+
+ private String tagName;
+
+ private IMetaTagAttribute[] attributes;
+
+ // Singleton empty array to be shared by all instances that don't have attributes
+ private static final IMetaTagAttribute[] emptyAttributes = new IMetaTagAttribute[0];
+
+ private String sourcePath;
+
+ private int absoluteStart = UNKNOWN;
+
+ private int absoluteEnd = UNKNOWN;
+
+ private int line = UNKNOWN;
+
+ private int column = UNKNOWN;
+
+ // Hold a reference to the node this definition came from
+ // (NodeReference only holds onto the node weakly, so we don't have to worry about leaks).
+ private NodeReference nodeRef = NodeReference.noReference;
+
+ @Override
+ public String getTagName()
+ {
+ return tagName;
+ }
+
+ @Override
+ public IMetaTagAttribute[] getAllAttributes()
+ {
+ return attributes;
+ }
+
+ @Override
+ public IMetaTagAttribute getAttribute(String key)
+ {
+ for (IMetaTagAttribute attribute : attributes)
+ {
+ String attrKey = attribute.getKey();
+ if (attrKey != null && attrKey.equals(key))
+ return attribute;
+ }
+ return null;
+ }
+
+ @Override
+ public String getAttributeValue(String key)
+ {
+ for (IMetaTagAttribute attribute : attributes)
+ {
+ // For metadata such as [Foo("abc", "def")],
+ // the attribute keys are null, so a null
+ // check on the key is necessary.
+ // BTW, keyless values like "abc" and "def"
+ // cannot be retrieved by this API;
+ // you have to use getAttributes()[i].getValue().
+ String attrKey = attribute.getKey();
+ if (attrKey != null && attrKey.equals(key))
+ return attribute.getValue();
+ }
+ return null;
+ }
+
+ @Override
+ public String getSourcePath()
+ {
+ return sourcePath;
+ }
+
+ private OffsetLookup getOffsetLookup()
+ {
+ DefinitionBase definition = (DefinitionBase)getDecoratedDefinition();
+ if (definition == null)
+ return null;
+
+ final ASFileScope fileScope = definition.getFileScope();
+ if (fileScope == null)
+ return null;
+
+ return fileScope.getOffsetLookup();
+ }
+
+ @Override
+ public int getStart()
+ {
+ OffsetLookup offsetLookup = getOffsetLookup();
+
+ if (offsetLookup == null)
+ return absoluteStart;
+
+ return offsetLookup.getLocalOffset(absoluteStart);
+ }
+
+ @Override
+ public int getEnd()
+ {
+ OffsetLookup offsetLookup = getOffsetLookup();
+
+ if (offsetLookup == null)
+ return absoluteEnd;
+
+ return offsetLookup.getLocalOffset(absoluteEnd);
+ }
+
+ @Override
+ public int getLine()
+ {
+ return line;
+ }
+
+ @Override
+ public int getColumn()
+ {
+ return column;
+ }
+
+ @Override
++ public int getEndLine()
++ {
++ return line;
++ }
++
++ @Override
++ public int getEndColumn()
++ {
++ return column;
++ }
++
++ @Override
+ public int getAbsoluteStart()
+ {
+ return absoluteStart;
+ }
+
+ @Override
+ public int getAbsoluteEnd()
+ {
+ return absoluteEnd;
+ }
+
+ @Override
+ public IDefinition getDecoratedDefinition()
+ {
+ return decoratedDefinition;
+ }
+
+ @Override
+ public String getValue()
+ {
+ return attributes.length == 1 && !attributes[0].hasKey() ?
+ attributes[0].getValue() :
+ null;
+ }
+
+ @Override
+ public IMetaTagNode getTagNode()
+ {
+ // If this definition didn't come from source, return null.
+ if (nodeRef == NodeReference.noReference)
+ return null;
+
+ // Get the scope for the definition this metadata is attached to.
+ ASScope containingScope = (ASScope)getDecoratedDefinition().getContainingScope();
+ if (containingScope == null)
+ return null;
+
+ // Get the file scope for that scope.
+ ASFileScope fileScope = containingScope.getFileScope();
+ if (fileScope == null)
+ return null;
+
+ // Get the workspace.
+ IWorkspace workspace = fileScope.getWorkspace();
+ assert workspace != null;
+
+ // Use the stored NodeReference to get the original node.
+ IASNode node = nodeRef.getNode(workspace, containingScope);
+ if (!(node instanceof IMetaTagNode))
+ {
+ // CMP-2168: The NodeReference resolver assumes that all definitions
+ // have a unique start offset. This true in every case except when
+ // there are metadata definitions decorating a class. In this case, the
+ // start of the metadata and the start of the class are the same, and the
+ // resolver will return the ClassNode, not the MetaTagNode. Catch this
+ // case by when we get a ClassNode, walking any metatags on the class
+ // looking for an offset that matches this MetaTag offset.
+ if (node instanceof ClassNode)
+ {
+ IMetaTagsNode metaTags = ((ClassNode)node).getMetaTags();
+ if (metaTags != null)
+ {
+ for (IMetaTagNode metaTagNode : metaTags.getAllTags())
+ {
+ if (metaTagNode.getAbsoluteStart() == getAbsoluteStart())
+ return metaTagNode;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ return (IMetaTagNode)node;
+ }
+
+ /**
+ * Updates the location information of this tag.
+ */
+ public void setLocation(IFileSpecification containingFileSpec, int absoluteStart, int absoluteEnd, int line, int column)
+ {
+ this.nodeRef = new NodeReference(containingFileSpec, absoluteStart);
+ this.sourcePath = containingFileSpec.getPath();
+ this.absoluteStart = absoluteStart;
+ this.absoluteEnd = absoluteEnd;
+ this.line = line;
+ this.column = column;
+ }
+
+ /**
+ * For debugging only.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append('[');
+ sb.append(tagName);
+
+ IMetaTagAttribute[] attrs = getAllAttributes();
+ if (attrs != null && attrs.length > 0)
+ {
+ sb.append('(');
+
+ int i = 0;
+ for (IMetaTagAttribute attr : getAllAttributes())
+ {
+ if (i != 0)
+ {
+ sb.append(',');
+ sb.append(' ');
+ }
+
+ String key = attr.getKey();
+ String value = attr.getValue();
+
+ sb.append(key);
+ sb.append('=');
+ sb.append('"');
+ sb.append(value);
+ sb.append('"');
+
+ i++;
+ }
+ sb.append(')');
+ }
+
+ sb.append(']');
+
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java
----------------------------------------------------------------------
diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java
index 68a76eb,0000000..ff726da
mode 100644,000000..100644
--- a/compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java
+++ b/compiler/src/main/java/org/apache/flex/compiler/internal/parsing/TokenBase.java
@@@ -1,515 -1,0 +1,559 @@@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.internal.parsing;
+
+import antlr.Token;
+
+import org.apache.flex.compiler.common.ISourceLocation;
+import org.apache.flex.compiler.internal.common.Counter;
+import org.apache.flex.compiler.internal.parsing.as.ASTokenTypes;
+import org.apache.flex.compiler.internal.parsing.as.IncludeHandler;
+import org.apache.flex.compiler.parsing.ICMToken;
+
+/**
+ * Base class of ASToken, MXMLToken, CSSToken
+ */
+public abstract class TokenBase extends Token implements ICMToken, ISourceLocation
+{
+ /**
+ * Constructor
+ *
+ * @param tokenType type of token
+ * @param start location location information
+ * @param end location location information
+ * @param line location location information
+ * @param column location location information
+ * @param text actual text represented by token
+ */
+ public TokenBase(int tokenType, int start, int end, int line, int column, CharSequence text)
+ {
+ type = tokenType;
+ localStart = this.start = start;
+ localEnd = this.end = end;
+ this.line = line;
+ this.column = column;
+ this.text = text;
++ this.endLine = line;
++ this.endColumn = column + end - start;
+
+ if (Counter.COUNT_TOKENS)
+ countTokens();
+ }
+
+ /**
+ * Copy constructor
+ *
+ * @param o token to copy
+ */
+ public TokenBase(TokenBase o)
+ {
+ type = o.type;
+ start = o.start;
+ end = o.end;
+ line = o.line;
+ column = o.column;
++ endLine = o.endLine;
++ endColumn = o.endColumn;
+ text = o.text;
+
+ localStart = o.localStart;
+ localEnd = o.localEnd;
+ sourcePath = o.sourcePath;
+
+ if (Counter.COUNT_TOKENS)
+ countTokens();
+ }
+
+ /**
+ * Text represented by this token
+ */
+ private CharSequence text;
+
+ /**
+ * Start of this token
+ */
+ private int start;
+
+ /**
+ * End offset of this token
+ */
+ private int end;
+
+ /**
+ * Line of this token
+ */
+ private int line;
+
+ /**
+ * Column of this token
+ */
+ private int column;
+
+ /**
++ * End line of this token
++ */
++ private int endLine;
++
++ /**
++ * End column of this token
++ */
++ private int endColumn;
++
++ /**
+ * Flag to determine if this token is locked
+ */
+ private boolean locked;
+
+ /**
+ * Local start offset.
+ */
+ private int localStart;
+
+ /**
+ * Local end offset.
+ */
+ private int localEnd;
+
+ protected abstract String getTypeString();
+
+ /**
+ * @return Local start offset.
+ */
+ public final int getLocalStart()
+ {
+ return localStart;
+ }
+
+ /**
+ * @return Local end offset.
+ */
+ public final int getLocalEnd()
+ {
+ return localEnd;
+ }
+
+ public final void reuse(final int tokenType, final int start, final int end,
+ final int line, final int column, final CharSequence text)
+ {
+ type = tokenType;
+ this.start = start;
+ this.end = end;
+ this.line = line;
+ this.column = column;
++ this.endLine = line;
++ this.endColumn = column + end - start;
+ this.text = text;
+ }
+
+ /**
+ * Locks this token. When locked, if this token is in a token pool, it will
+ * not be overwritten
+ */
+ public void lock()
+ {
+ locked = true;
+ }
+
+ /**
+ * Returns whether this token can be overwritten when it is a member of a
+ * token pool
+ *
+ * @return true if we are locked
+ */
+ public boolean isLocked()
+ {
+ return locked;
+ }
+
+ /**
+ * Get the text represented by this token
+ *
+ * @return text represented by this token
+ * @see antlr.Token#getText()
+ */
+ @Override
+ public String getText()
+ {
+ //we're either going to be a String or a StringBuilder
+ //String.toString returns itself
+ //StringBuilder toString returns a new String
+ if (text != null)
+ return text.toString();
+ return "";
+ }
+
+ /**
+ * Returns the underlying CharSequence that represents the contents of this
+ * token
+ *
+ * @return a {@link CharSequence} or null
+ */
+ public CharSequence getCharSequence()
+ {
+ return text;
+ }
+
+ /**
+ * Set the text represented by this token
+ *
+ * @param text text represented by this token
+ * @see antlr.Token#setText(java.lang.String)
+ */
+ @Override
+ public void setText(String text)
+ {
+ this.text = text;
+ }
+
+ /**
+ * Set the CharSequence represented by this token
+ *
+ * @param text text represented by this token
+ */
+ public void setText(CharSequence text)
+ {
+ this.text = text;
+ }
+
+ public void setLocation(int start, int end, int line, int column)
+ {
+ this.start = start;
+ this.end = end;
+ this.line = line;
+ this.column = column;
++ this.endLine = line;
++ this.endColumn = column + end - start;
+ }
+
+ @Override
+ public int getStart()
+ {
+ return start;
+ }
+
+ public void setStart(int start)
+ {
+ this.start = start;
+ }
+
+ @Override
+ public int getEnd()
+ {
+ return end;
+ }
+
+ public void setEnd(int end)
+ {
+ this.end = end;
+ }
+
+ @Override
+ public int getLine()
+ {
+ return line;
+ }
+
+ @Override
+ public void setLine(int line)
+ {
+ this.line = line;
+ }
+
+ public final boolean matchesLine(final TokenBase other)
+ {
+ return other != null && other.line == line;
+ }
+
+ @Override
+ public int getColumn()
+ {
+ return column;
+ }
+
+ @Override
+ public void setColumn(int column)
+ {
+ this.column = column;
+ }
+
++ @Override
++ public int getEndLine()
++ {
++ return endLine;
++ }
++
++ public void setEndLine(int line)
++ {
++ endLine = line;
++ }
++
++ @Override
++ public int getEndColumn()
++ {
++ return endColumn;
++ }
++
++ public void setEndColumn(int column)
++ {
++ endColumn = column;
++ }
++
+ /**
+ * Determine whether or not this token is bogus (i.e. the start and end
+ * offsets are the same, which implies that it was inserted from an included
+ * file or during token fixup)
+ *
+ * @return true iff the token is bogus
+ */
+ @Override
+ public boolean isImplicit()
+ {
+ return start == end;
+ }
+
+ /**
+ * For debugging only.
+ */
+ private String getEscapedText()
+ {
+ String text = getText();
+
+ text = text.replaceAll("\n", "\\\\n");
+ text = text.replaceAll("\r", "\\\\r");
+ text = text.replaceAll("\t", "\\\\t");
+
+ return text;
+ }
+
+ /**
+ * For debugging only. This format is nice in the Eclipse debugger.
+ */
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append('|');
+ sb.append(getEscapedText());
+ sb.append('|');
+
+ sb.append(' ');
+
+ sb.append(getTypeString());
+
+ sb.append(' ');
+
+ if (locked)
+ sb.append("locked ");
+
+ int line = getLine();
+ if (line != UNKNOWN)
+ sb.append(line + 1);
+ else
+ sb.append('?');
+ sb.append(':');
+ int column = getColumn();
+ if (column != UNKNOWN)
+ sb.append(column + 1);
+ else
+ sb.append('?');
+
+ sb.append(' ');
+
+ int start = getStart();
+ if (start != UNKNOWN)
+ sb.append(start);
+ else
+ sb.append('?');
+ sb.append('-');
+ int end = getEnd();
+ if (end != UNKNOWN)
+ sb.append(end);
+ else
+ sb.append('?');
+
+ sb.append(' ');
+ String sourcePath = getSourcePath();
+ if (sourcePath != null)
+ {
+ sb.append('"');
+ sb.append(sourcePath);
+ sb.append('"');
+ }
+ else
+ {
+ sb.append('?');
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * For debugging only. This format is nice in a text file.
+ */
+ public String toDumpString()
+ {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(getLine() + 1);
+ sb.append('\t');
+ sb.append(getColumn() + 1);
+ sb.append('\t');
+ sb.append(getStart());
+ sb.append('\t');
+ sb.append(getEnd());
+ sb.append('\t');
+
+ String typeString = getTypeString();
+ sb.append(typeString);
+ int n = 28 - typeString.length();
+ for (int i = 0; i < n; i++)
+ sb.append(' ');
+ sb.append('\t');
+
+ sb.append('|');
+ sb.append(getEscapedText());
+ sb.append('|');
+
+ return sb.toString();
+ }
+
+ /**
+ * Reduce the span of the token by removing characters from the beginning
+ * and end. This is used to remove quote characters and such from tokens.
+ *
+ * @param trimLeft number of characters to remove from the left of the token
+ * @param trimRight number of characters to remove from the right of the
+ * token
+ */
+ public void truncate(int trimLeft, int trimRight)
+ {
+ String text = getText();
+ if (trimLeft + trimRight <= text.length())
+ {
+ text = text.substring(trimLeft, text.length() - trimRight);
+ setText(text);
+ start += trimLeft;
+ end -= trimRight;
+ }
+ }
+
+ /**
+ * Adjust all associated offsets by the adjustment amount
+ *
+ * @param offsetAdjustment amount to add to offsets
+ */
+ public void adjustOffsets(int offsetAdjustment)
+ {
+ start += offsetAdjustment;
+ end += offsetAdjustment;
+ }
+
+ /**
+ * Adjust all associated offsets by the adjustment amount
+ *
+ * @param offsetAdjustment amount to add to offsets
+ * @param lineAdjustment amount to add to the line number
+ * @param columnAdjustment amount to add to the column number
+ */
+ public void adjustLocation(int offsetAdjustment, int lineAdjustment, int columnAdjustment)
+ {
+ start += offsetAdjustment;
+ end += offsetAdjustment;
+ line += lineAdjustment;
+ column += columnAdjustment;
++ endLine += lineAdjustment;
++ endColumn += columnAdjustment;
+ }
+
+ /**
+ * Capture the current start/end offsets as this token's local offsets. This
+ * method is called in {@code StreamingASTokenizer#nextTokenFromReader()}
+ * after the token is initialized, and before being updated by
+ * {@link IncludeHandler#onNextToken}.
+ */
+ public final void storeLocalOffset()
+ {
+ this.localStart = start;
+ this.localEnd = end;
+ }
+
+ private String sourcePath;
+
+ @Override
+ public final String getSourcePath()
+ {
+ return sourcePath;
+ }
+
+ public final void setSourcePath(String path)
+ {
+ this.sourcePath = path;
+ }
+
+ /**
+ * Verifies that this token has its type and location information set.
+ * <p>
+ * This is used only in asserts.
+ */
+ public boolean verify()
+ {
+ // Verify the token type.
+ int type = getType();
+ assert type != 0 : "Token has no type: " + toString();
+
+ // Verify the source location (except for EOF tokens,
+ // which are special and don't have a source location).
+ if (type != ASTokenTypes.EOF)
+ {
+ assert getStart() != UNKNOWN : "Token has unknown start: " + toString();
+ assert getEnd() != UNKNOWN : "Token has unknown end: " + toString();
+ assert getLine() != UNKNOWN : "Token has an unknown line: " + toString();
+ assert getColumn() != UNKNOWN : "Token has an unknown column: " + toString();
++ assert getEndLine() != UNKNOWN : "Token has an unknown end line: " + toString();
++ assert getEndColumn() != UNKNOWN : "Token has an unknown end column: " + toString();
+ }
+
+ return true;
+ }
+
+ /**
+ * Counts various types of tokens that are created, as well as the total
+ * number of tokens.
+ */
+ private void countTokens()
+ {
+ Counter counter = Counter.getInstance();
+ counter.incrementCount(getClass().getSimpleName());
+ counter.incrementCount("tokens");
+ }
+
+ @Override
+ public int getAbsoluteEnd()
+ {
+ return getEnd();
+ }
+
+ @Override
+ public int getAbsoluteStart()
+ {
+ return getStart();
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java
----------------------------------------------------------------------
diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java
index be0510d,0000000..8dd4fb6
mode 100644,000000..100644
--- a/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java
+++ b/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/NodeBase.java
@@@ -1,1038 -1,0 +1,1049 @@@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.internal.tree.as;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+
+import antlr.Token;
+
+import org.apache.flex.compiler.common.ASModifier;
+import org.apache.flex.compiler.common.ISourceLocation;
+import org.apache.flex.compiler.common.SourceLocation;
+import org.apache.flex.compiler.definitions.IDefinition;
+import org.apache.flex.compiler.filespecs.IFileSpecification;
+import org.apache.flex.compiler.internal.common.Counter;
+import org.apache.flex.compiler.internal.definitions.DefinitionBase;
+import org.apache.flex.compiler.internal.parsing.as.OffsetLookup;
+import org.apache.flex.compiler.internal.scopes.ASFileScope;
+import org.apache.flex.compiler.internal.scopes.ASScope;
+import org.apache.flex.compiler.internal.scopes.TypeScope;
+import org.apache.flex.compiler.internal.semantics.PostProcessStep;
+import org.apache.flex.compiler.problems.ICompilerProblem;
+import org.apache.flex.compiler.scopes.IASScope;
+import org.apache.flex.compiler.tree.ASTNodeID;
+import org.apache.flex.compiler.tree.as.IASNode;
+import org.apache.flex.compiler.tree.as.IFileNode;
+import org.apache.flex.compiler.tree.as.IImportNode;
+import org.apache.flex.compiler.tree.as.IPackageNode;
+import org.apache.flex.compiler.tree.as.IScopedNode;
+import org.apache.flex.compiler.tree.mxml.IMXMLClassDefinitionNode;
+import org.apache.flex.compiler.workspaces.IWorkspace;
+
+/**
+ * Base class for ActionScript parse tree nodes
+ */
+public abstract class NodeBase extends SourceLocation implements IASNode
+{
+ /**
+ * Used in calls to CheapArray functions. It represents the type of objects
+ * that appear in fSymbols.
+ */
+ protected static final IASNode[] emptyNodeArray = new IASNode[0];
+
+
+ /**
+ * Combine the attributes from the base class with the attributes specific
+ * to this class
+ *
+ * @param superAttributes The attributes of the base class.
+ * @param myAttributes The attributes of this class.
+ * @return attribute value
+ */
+ public static String[] combineAttributes(String[] superAttributes, String[] myAttributes)
+ {
+ String[] combinedAttributes = new String[superAttributes.length + myAttributes.length];
+ for (int i = 0; i < superAttributes.length; i++)
+ {
+ combinedAttributes[i] = superAttributes[i];
+ }
+ for (int i = 0; i < myAttributes.length; i++)
+ {
+ combinedAttributes[superAttributes.length + i] = myAttributes[i];
+ }
+ return combinedAttributes;
+ }
+
+ /**
+ * Constructor.
+ */
+ public NodeBase()
+ {
+ super();
+ parent = null;
+
+ if (Counter.COUNT_NODES)
+ countNodes();
+ }
+
+ /**
+ * Parent node.
+ * <p>
+ * Methods (even in NodeBase itself) should use getParent() instead of
+ * accessing this member directly. getParent() is overridden in FileNode to
+ * do something special when the FileNode represents a shared file.
+ */
+ protected IASNode parent;
+
+ @Override
+ public IASNode getParent()
+ {
+ return parent;
+ }
+
+ @Override
+ public int getChildCount()
+ {
+ return 0;
+ }
+
+ @Override
+ public IASNode getChild(int i)
+ {
+ return null;
+ }
+
+ @Override
+ public IFileSpecification getFileSpecification()
+ {
+ // TODO Make sure this works with include processing!!!
+ ASFileScope fileScope = getFileScope();
+ IWorkspace w = fileScope.getWorkspace();
+ return w.getFileSpecification(fileScope.getContainingPath());
+ }
+
+ @Override
+ public int getSpanningStart()
+ {
+ return getStart();
+ }
+
+ /**
+ * Get the node type as a string. For example, this will return
+ * "IdentifierNode" for an IdentifierNode.
+ *
+ * @return the node type
+ */
+ public String getNodeKind()
+ {
+ return getClass().getSimpleName();
+ }
+
+ @Override
+ public boolean contains(int offset)
+ {
+ return getAbsoluteStart() < offset && getAbsoluteEnd() >= offset;
+ }
+
+ /**
+ * Determine whether the offset "loosely" fits within this node. With normal
+ * nodes, this will return true if the offset matches fLocation.fStart or
+ * fLocation.fEnd or anything in between. With bogus nodes (which have
+ * fStart == fEnd), this will return true if the offset comes after fStart
+ * and before any other nodes in the parse tree
+ *
+ * @param offset the offset to test
+ * @return true if the offset is "loosely" contained within this node
+ */
+ public boolean looselyContains(int offset)
+ {
+ if (getAbsoluteStart() == getAbsoluteEnd())
+ {
+ // This is a bogus placeholder node (generally an identifier that's been inserted to complete an expression).
+ // We'll pretend that it extends all the way to the start offset of the next node. Now to find that next node...
+ if (getAbsoluteStart() <= offset)
+ {
+ NodeBase containingNode = (NodeBase)getParent();
+ // Step 1: Find a node that extends beyond the target offset
+ while (containingNode != null && containingNode.getAbsoluteEnd() <= getAbsoluteEnd())
+ containingNode = (NodeBase)containingNode.getParent();
+ // If no such node exists, we know that there aren't any tokens in the parse
+ // tree that end after this bogus token. So we can go ahead and pretend this node
+ // extends all the way out.
+ if (containingNode == null)
+ return true;
+ // Step 2: Find a node that starts after the target offset
+ IASNode succeedingNode = containingNode.getSucceedingNode(getAbsoluteEnd());
+ // If no such node exists, we know that there aren't any tokens in the parse
+ // tree that start after this bogus token. So we can go ahead and pretend this
+ // node extends all the way out.
+ if (succeedingNode == null)
+ return true;
+ // Otherwise, pretend this node extends all the way to the next token.
+ if (succeedingNode.getAbsoluteStart() >= offset)
+ return true;
+ }
+ }
+ else
+ {
+ // This is a real token. See if the test offset fits within (including the boundaries, since we're looking
+ // at "loose" containment.
+ return getAbsoluteStart() <= offset && getAbsoluteEnd() >= offset;
+ }
+ return false;
+ }
+
+ @Override
+ public IASNode getSucceedingNode(int offset)
+ {
+ // This node ends before the offset is even reached. This is hopeless.
+ if (getAbsoluteEnd() <= offset)
+ return null;
+
+ // See if one of our children starts after the offset
+ for (int i = 0; i < getChildCount(); i++)
+ {
+ IASNode child = getChild(i);
+
+ if (child.getAbsoluteStart() > offset)
+ return child;
+ else if (child.getAbsoluteEnd() > offset)
+ return child.getSucceedingNode(offset);
+ }
+
+ return null;
+ }
+
+ /**
+ * Determine whether this node is transparent. If a node is transparent, it
+ * can never be returned from getContainingNode... the offset will be
+ * considered part of the parent node instead.
+ *
+ * @return true if the node is transparent
+ */
+ public boolean isTransparent()
+ {
+ return false;
+ }
+
+ @Override
+ public IASNode getContainingNode(int offset)
+ {
+ // This node doesn't even contain the offset. This is hopeless.
+ if (!contains(offset))
+ {
+ return null;
+ }
+ IASNode containingNode = this;
+ int childCount = getChildCount();
+ // See if the offset is contained within one of our children.
+ for (int i = 0; i < childCount; i++)
+ {
+ IASNode child = getChild(i);
+ if (child.getAbsoluteStart() <= offset)
+ {
+ if (child.contains(offset))
+ {
+ containingNode = child.getContainingNode(offset);
+ if (child instanceof NodeBase && ((NodeBase)child).canContinueContainmentSearch(containingNode, this, i, true))
+ continue;
+ break;
+ }
+ }
+ else
+ {
+ if (child instanceof NodeBase && ((NodeBase)child).canContinueContainmentSearch(containingNode, this, i, false))
+ continue;
+ // We've passed this offset without finding a child that contains it. This is
+ // the nearest enclosing node.
+ break;
+ }
+ }
+ // Make sure we don't return a transparent node
+ while (containingNode != null && containingNode instanceof NodeBase && ((NodeBase)containingNode).isTransparent())
+ {
+ containingNode = containingNode.getParent();
+ }
+ return containingNode;
+ }
+
+ @Override
+ public boolean isTerminal()
+ {
+ return false;
+ }
+
+ @Override
+ public IScopedNode getContainingScope()
+ {
+ return (IScopedNode)getAncestorOfType(IScopedNode.class);
+ }
+
+ // TODO Can probably eliminate this.
+ protected boolean canContinueContainmentSearch(IASNode containingNode, IASNode currentNode, int childOffset, boolean offsetsStillValid)
+ {
+ return false;
+ }
+
+ @Override
+ public IASNode getAncestorOfType(Class<? extends IASNode> nodeType)
+ {
+ IASNode current = getParent();
+ while (current != null && !(nodeType.isInstance(current)))
+ current = current.getParent();
+ if (current != null)
+ return current;
+ return null;
+ }
+
+ @Override
+ public String getPackageName()
+ {
+ // Starting with this node's parent, walk up the parent chain
+ // looking for a package node or an MXML class definition node.
+ // These two types of nodes understand what their package is.
+ IASNode current = getParent();
+ while (current != null &&
+ !(current instanceof IPackageNode ||
+ current instanceof IMXMLClassDefinitionNode))
+ {
+ current = current.getParent();
+ }
+
+ if (current instanceof IPackageNode)
+ return ((IPackageNode)current).getPackageName();
+ else if (current instanceof IMXMLClassDefinitionNode)
+ return ((IMXMLClassDefinitionNode)current).getPackageName();
+
+ return null;
+ }
+
+ /**
+ * Set the start offset of the node to fall just after the token. Used
+ * during parsing.
+ *
+ * @param token start this node after this token
+ */
+ public void startAfter(Token token)
+ {
+ if (token instanceof ISourceLocation)
+ {
+ startAfter((ISourceLocation) token);
- setColumn(getColumn() + token.getText().length());
+ }
+ }
+
+ /**
+ * Set the start offset of the node to fall just after the token. Used
+ * during parsing.
+ *
+ * @param location start this node after this token
+ */
+ public final void startAfter(ISourceLocation location)
+ {
+ final int end = location.getEnd();
+ if (end != UNKNOWN)
+ {
+ setSourcePath(location.getSourcePath());
+ setStart(end);
- setLine(location.getLine());
- setColumn(location.getColumn());
++ setLine(location.getEndLine());
++ setColumn(location.getEndColumn());
+ }
+ }
+
+ /**
+ * Set the start offset of the node to fall just before the token. Used
+ * during parsing.
+ *
+ * @param token start this node before this token
+ */
+ public final void startBefore(Token token)
+ {
+ if (token instanceof ISourceLocation)
+ startBefore((ISourceLocation)token);
+ }
+
+ /**
+ * Set the start offset of the node to fall just before the token. Used
+ * during parsing.
+ *
+ * @param location start this node before this token
+ */
+ public final void startBefore(ISourceLocation location)
+ {
+ final int start = location.getStart();
+ if (start != UNKNOWN)
+ {
+ setSourcePath(location.getSourcePath());
+ setStart(start);
+ setLine(location.getLine());
+ setColumn(location.getColumn());
+ }
+ }
+
+ /**
+ * Set the end offset of the node to fall just after the token. Used during
+ * parsing.
+ *
+ * @param token end this node after this token
+ */
+ public final void endAfter(Token token)
+ {
+ if (token instanceof ISourceLocation)
+ endAfter((ISourceLocation)token);
+ }
+
+ /**
+ * Set the end offset of the node to fall just after the token. Used during
+ * parsing.
+ *
+ * @param location end this node after this token
+ */
+ public final void endAfter(ISourceLocation location)
+ {
+ final int end = location.getEnd();
+ if (end != UNKNOWN)
++ {
+ setEnd(end);
++ setEndLine(location.getEndLine());
++ setEndColumn(location.getEndColumn());
++ }
+ }
+
+ /**
+ * Set the end offset of the node to fall just before the token. Used during
+ * parsing.
+ *
+ * @param token start this node before this token
+ */
+ public final void endBefore(Token token)
+ {
+ if (token instanceof ISourceLocation)
+ endBefore((ISourceLocation)token);
+ }
+
+ /**
+ * Set the end offset of the node to fall just before the token. Used during
+ * parsing.
+ *
+ * @param location start this node before this token
+ */
+ public final void endBefore(ISourceLocation location)
+ {
+ final int start = location.getStart();
+ if (start != UNKNOWN)
++ {
+ setEnd(start);
++ setEndLine(location.getLine());
++ setEndColumn(location.getColumn());
++ }
+ }
+
+ /**
+ * Set the start and end offsets of the node to coincide with the token's
+ * offsets. Used during parsing.
+ *
+ * @param token the token to take the offsets from
+ */
+ public final void span(Token token)
+ {
+ if (token instanceof ISourceLocation)
+ span((ISourceLocation)token);
+ }
+
+ /**
+ * Set the start and end offsets of the node to coincide with the tokens'
+ * offsets. Used during parsing.
+ *
+ * @param firstToken the token to take the start offset and line number from
+ * @param lastToken the token to take the end offset from
+ */
+ public final void span(Token firstToken, Token lastToken)
+ {
+ if (firstToken instanceof ISourceLocation && lastToken instanceof ISourceLocation)
+ span((ISourceLocation)firstToken, (ISourceLocation)lastToken);
+ }
+
+ /**
+ * Set the start and end offsets of the node. Used during parsing.
+ *
+ * @param start start offset for the node
+ * @param end end offset for the node
+ * @param line line number for the node
+ */
+ public final void span(int start, int end, int line, int column)
+ {
+ setStart(start);
+ setEnd(end);
+ setLine(line);
+ setColumn(column);
+ }
+
+ public Collection<ICompilerProblem> runPostProcess(EnumSet<PostProcessStep> set, ASScope containingScope)
+ {
+ ArrayList<ICompilerProblem> problems = new ArrayList<ICompilerProblem>(10);
+ normalize(set.contains(PostProcessStep.CALCULATE_OFFSETS));
+ if (set.contains(PostProcessStep.POPULATE_SCOPE) || set.contains(PostProcessStep.RECONNECT_DEFINITIONS))
+ analyze(set, containingScope, problems);
+ return problems;
+ }
+
+ protected void analyze(EnumSet<PostProcessStep> set, ASScope scope, Collection<ICompilerProblem> problems)
+ {
+ int childrenSize = getChildCount();
+ // Populate this scope with definitions found among the children
+ for (int i = 0; i < childrenSize; i++)
+ {
+ IASNode child = getChild(i);
+ if (child instanceof NodeBase)
+ {
+ if (child.getParent() == null)
+ ((NodeBase)child).setParent(this);
+ ((NodeBase)child).analyze(set, scope, problems);
+ }
+ }
+ }
+
+ /**
+ * Changes the position of two children in our tree. Neither child can be
+ * null and both must have the same parent
+ *
+ * @param child the child to replace target
+ * @param target the child to replace child
+ */
+ protected void swapChildren(NodeBase child, NodeBase target)
+ {
+ //no op in this impl
+ }
+
+ /**
+ * Replaces the child with the given target. The child will be removed from
+ * the tree, and its parentage will be updated
+ *
+ * @param child the {@link NodeBase} to replace
+ * @param target the {@link NodeBase} to replace the replaced
+ */
+ protected void replaceChild(NodeBase child, NodeBase target)
+ {
+ //no op
+ }
+
+ /**
+ * Normalize the tree. Move custom children into the real child list and
+ * fill in missing offsets so that offset lookup will work. Used during
+ * parsing.
+ */
+ public void normalize(boolean fillInOffsets)
+ {
+ // The list of children doesn't change, so the child count should be constant throughout the loop
+ int childrenSize = getChildCount();
+ // Normalize the regular children
+ for (int i = 0; i < childrenSize; i++)
+ {
+ IASNode child = getChild(i);
+ if (child instanceof NodeBase)
+ {
+ ((NodeBase)child).setParent(this);
+ ((NodeBase)child).normalize(fillInOffsets);
+ }
+ }
+ // Add the special children (which get normalized as they're added)
+ if (childrenSize == 0)
+ setChildren(fillInOffsets);
+ // Fill in offsets based on the child nodes
+ if (fillInOffsets)
+ fillInOffsets();
+ // fill in any dangling ends
+ //fillEndOffsets();
+ // get rid of unused child space
+ }
+
+ /**
+ * Allow various subclasses to do special kludgy things like identify
+ * mx.core.Application.application
+ */
+ protected void connectedToProjectScope()
+ {
+ // The list of children doesn't change, so the child count should be constant throughout the loop
+ int childrenSize = getChildCount();
+ for (int i = 0; i < childrenSize; i++)
+ {
+ IASNode child = getChild(i);
+ if (child instanceof NodeBase)
+ ((NodeBase)child).connectedToProjectScope();
+ }
+ }
+
+ /**
+ * If this node has custom children (names, arguments, etc), shove them into
+ * the list of children. Used during parsing.
+ */
+ protected void setChildren(boolean fillInOffsets)
+ {
+ //nothing in this class
+ }
+
+ /**
+ * If the start and end offsets haven't been set explicitly, fill them in
+ * based on the offsets of the children. Used during parsing. If the end
+ * offset is less than any of the kids' offsets, change fLocation.fEnd to
+ * encompass kids.
+ */
+ protected void fillInOffsets()
+ {
+ int numChildren = getChildCount();
+ if (numChildren > 0)
+ {
+ int start = getAbsoluteStart();
+ int end = getAbsoluteEnd();
+ if (start == -1)
+ {
+ for (int i = 0; i < numChildren; i++)
+ {
+ IASNode child = getChild(i);
+ int childStart = child.getAbsoluteStart();
+ if (childStart != -1)
+ {
+ if (getSourcePath() == null)
+ setSourcePath(child.getSourcePath());
+ setStart(childStart);
+ setLine(child.getLine());
+ setColumn(child.getColumn());
+ break;
+ }
+ }
+ }
+ for (int i = numChildren - 1; i >= 0; i--)
+ {
+ IASNode child = getChild(i);
+ int childEnd = child.getAbsoluteEnd();
+ if (childEnd != -1)
+ {
+ if (end < childEnd)
++ {
+ setEnd(childEnd);
++ setEndLine(child.getEndLine());
++ setEndColumn(child.getEndColumn());
++ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the parent node. Used during parsing.
+ *
+ * @param parent parent node
+ */
+ public void setParent(NodeBase parent)
+ {
+ this.parent = parent;
+ }
+
+ /**
+ * Get the nearest containing scope for this node. Used during type
+ * decoration.
+ *
+ * @return nearest containing scope for this node
+ */
+ // TODO Make this more efficient using overrides on BlockNode and FileNode.
+ public IScopedNode getScopeNode()
+ {
+ if (this instanceof IScopedNode && ((IScopedNode)this).getScope() != null)
+ return (IScopedNode)this;
+ IASNode parent = getParent();
+ if (parent != null && parent instanceof NodeBase)
+ return ((NodeBase)parent).getScopeNode();
+ return null;
+ }
+
+ /**
+ * Get the path of the file in which this parse tree node resides.
+ * <p>
+ * The tree builder can set a node's origin source file using
+ * {@link #setSourcePath(String)}. If the source path was set, return the
+ * source path; Otherwise, use root {@link FileNode}'s path.
+ *
+ * @return file path that contains this node
+ */
+ public String getContainingFilePath()
+ {
+ String path = getSourcePath();
+ if (path != null)
+ return path;
+
+ final FileNode fileNode = (FileNode)getAncestorOfType(FileNode.class);
+ if (fileNode != null)
+ return fileNode.getSourcePath();
+ return null;
+ }
+
+ /**
+ * For debugging only. Displays this node and its children in a form like
+ * this:
+ *
+ * <pre>
+ * FileNode "D:\tests\UIComponent.as" 0:0 0-467464 D:\tests\UIComponent.as
+ * PackageNode "mx.core" 12:1 436-465667 D:\tests\UIComponent.as
+ * FullNameNode "mx.core" 0:0 444-451 null
+ * IdentifierNode "mx" 12:9 444-446 D:\tests\UIComponent.as
+ * IdentifierNode "core" 12:12 447-451 D:\tests\UIComponent.as
+ * ScopedBlockNode 13:1 454-465667 D:\tests\UIComponent.as
+ * ImportNode "flash.accessibility.Accessibility" 14:1 457-497 D:\tests\UIComponent.as
+ * FullNameNode "flash.accessibility.Accessibility" 0:0 464-497 null
+ * FullNameNode "flash.accessibility" 0:0 464-483 null
+ * IdentifierNode "flash" 14:8 464-469 D:\tests\UIComponent.as
+ * IdentifierNode "accessibility" 14:14 470-483 D:\tests\UIComponent.as
+ * IdentifierNode "Accessibility" 14:28 484-497 D:\tests\UIComponent.as
+ * ...
+ * </pre>
+ * <p>
+ * Subclasses may not override this, because we want to maintain regularity
+ * for how all nodes display.
+ */
+ @Override
+ public final String toString()
+ {
+ StringBuilder sb = new StringBuilder();
+ buildStringRecursive(sb, 0, false);
+ return sb.toString();
+ }
+
+ /**
+ * For debugging only. Called by {@code toString()}, and recursively by
+ * itself, to display this node on one line and each descendant node on
+ * subsequent indented lines.
+ *
+ * * */
+ public void buildStringRecursive(StringBuilder sb, int level, boolean skipSrcPath)
+ {
+ // Indent two spaces for each nesting level.
+ for (int i = 0; i < level; i++)
+ {
+ sb.append(" ");
+ }
+
+ // Build the string that represents this node.
+ buildOuterString(sb, skipSrcPath);
+
+ sb.append('\n');
+
+ //To test scopes in ParserSuite
+ if(skipSrcPath && (this instanceof IScopedNode)) {
+ for (int i = 0; i < level+1; i++)
+ sb.append(" ");
+ sb.append("[Scope]");
+ sb.append("\n");
+ IScopedNode scopedNode = (IScopedNode)this;
+ IASScope scope = scopedNode.getScope();
+ Collection<IDefinition> definitions = scope.getAllLocalDefinitions();
+ for(IDefinition def : definitions) {
+ for (int i = 0; i < level+2; i++)
+ sb.append(" ");
+ ((DefinitionBase)def).buildString(sb, false);
+ sb.append('\n');
+ }
+ }
+
+ // Recurse over the child nodes.
+ int n = getChildCount();
+ for (int i = 0; i < n; i++)
+ {
+ NodeBase child = (NodeBase)getChild(i);
+ // The child can be null if we're toString()'ing
+ // in the debugger during tree building.
+ if (child != null)
+ child.buildStringRecursive(sb, level + 1, skipSrcPath);
+ }
+ }
+
+ /**
+ * For debugging only. Called by {@code buildStringRecursive()} to display
+ * information for this node only.
+ * <p>
+ * An example is
+ *
+ * <pre>
+ * PackageNode "mx.core" 12:1 436-465667 D:\tests\UIComponent.as
+ * </pre>.
+ * <p>
+ * The type of node (PackageNode) is displayed first, followed by
+ * node-specific information ("mx.core") followed by location information in
+ * the form
+ *
+ * <pre>
+ * line:column start-end sourcepath
+ * </pre>
+ *
+ */
+ private void buildOuterString(StringBuilder sb, boolean skipSrcPath)
+ {
+ // First display the type of node, as represented by
+ // the short name of the node class.
+ sb.append(getNodeKind());
+
+ sb.append("(").append(getNodeID().name()).append(")");
+
+ sb.append(' ');
+
+ // Display optional node-specific information in the middle.
+ if (buildInnerString(sb))
+ sb.append(' ');
+
+ // The SourceLocation superclass handles producing a string such as
+ // "17:5 160-188" C:/test.as".
+ if(skipSrcPath)
+ sb.append(getOffsetsString());
+ else
+ sb.append(super.toString());
+ }
+
+ /**
+ * For debugging only. This method is called by {@code buildOuterString()}.
+ * It is overridden by subclasses to display optional node-specific
+ * information in the middle of the string, between the node type and the
+ * location information.
+ */
+ protected boolean buildInnerString(StringBuilder sb)
+ {
+ return false;
+ }
+
+ /**
+ * For debugging only.
+ */
+ public String getInnerString()
+ {
+ StringBuilder sb = new StringBuilder();
+ buildInnerString(sb);
+ return sb.toString();
+ }
+
+ public ASFileScope getFileScope()
+ {
+ ASScope scope = getASScope();
+ assert scope != null;
+ while (!(scope instanceof ASFileScope))
+ {
+ scope = scope.getContainingScope();
+ assert scope != null;
+ }
+ return (ASFileScope)scope;
+ }
+
+ /**
+ * @return {@link OffsetLookup} object for the current tree or null.
+ */
+ protected final OffsetLookup tryGetOffsetLookup()
+ {
+ // Try FileNode.getOffsetLookup()
+ final IASNode fileNode = getAncestorOfType(IFileNode.class);
+ if (fileNode != null)
+ return ((IFileNode)fileNode).getOffsetLookup();
+ else
+ return null;
+ }
+
+ /**
+ * Get's the {@link IWorkspace} in which this {@link NodeBase} lives.
+ *
+ * @return The {@link IWorkspace} in which this {@link NodeBase} lives.
+ */
+ public IWorkspace getWorkspace()
+ {
+ return getFileScope().getWorkspace();
+ }
+
+ /**
+ * Get the scope this Node uses for name resolution as an ASScope.
+ *
+ * @return the ASScope for this node, or null if there isn't one.
+ */
+ public ASScope getASScope()
+ {
+ IScopedNode scopeNode = getContainingScope();
+ IASScope scope = scopeNode != null ? scopeNode.getScope() : null;
+
+ // If the ScopedNode had a null scope, keep looking up the tree until we
+ // find one with a non-null scope.
+ // TODO: Is it a bug that an IScopedNode returns null for it's scope?
+ // TODO: this seems like a leftover from block scoping - for example, a
+ // TODO: ForLoopNode is an IScopedNode, but it doesn't really have a scope
+ while (scope == null && scopeNode != null)
+ {
+ scopeNode = scopeNode.getContainingScope();
+ scope = scopeNode != null ? scopeNode.getScope() : null;
+ }
+
+ if (scope instanceof TypeScope)
+ {
+ TypeScope typeScope = (TypeScope)scope;
+ if (this instanceof BaseDefinitionNode)
+ {
+ if (((BaseDefinitionNode)this).hasModifier(ASModifier.STATIC))
+ scope = typeScope.getStaticScope();
+ else
+ scope = typeScope.getInstanceScope();
+ }
+ else
+ {
+ // Do we need the class or instance scope?
+ BaseDefinitionNode bdn = (BaseDefinitionNode)this.getAncestorOfType(BaseDefinitionNode.class);
+ if (bdn instanceof ClassNode)
+ {
+ // We must be loose code in a class
+ scope = typeScope.getStaticScope();
+ }
+ else
+ {
+ if (bdn != null && bdn.hasModifier(ASModifier.STATIC)
+ // Namespaces are always static
+ || bdn instanceof NamespaceNode)
+ scope = typeScope.getStaticScope();
+ else
+ scope = typeScope.getInstanceScope();
+ }
+ }
+ }
+ ASScope asScope = scope instanceof ASScope ? (ASScope)scope : null;
+ return asScope;
+ }
+
+ /**
+ * Get the node's local start offset.
+ */
+ @Override
+ public int getStart()
+ {
+ final OffsetLookup offsetLookup = tryGetOffsetLookup();
+ if (offsetLookup != null)
+ {
+ // to handle start offset in an included file
+ int absoluteOffset = getAbsoluteStart();
+ final String pathBeforeCaret = offsetLookup.getFilename(absoluteOffset - 1);
+ // if the offset is the first offset in the included file return without adjustment
+ if (pathBeforeCaret != null && pathBeforeCaret.equals(getSourcePath()))
+ return offsetLookup.getLocalOffset(absoluteOffset-1)+1;
+
+ return offsetLookup.getLocalOffset(absoluteOffset);
+ }
+
+ return super.getStart();
+ }
+
+ /**
+ * Get the node's local end offset.
+ */
+ @Override
+ public int getEnd()
+ {
+ final OffsetLookup offsetLookup = tryGetOffsetLookup();
+ return offsetLookup != null ?
+ // to handle end offset in an included file
+ offsetLookup.getLocalOffset(super.getEnd() - 1) + 1 :
+ super.getEnd();
+ }
+
+ /**
+ * @return The node's absolute start offset.
+ */
+ @Override
+ public int getAbsoluteStart()
+ {
+ return super.getStart();
+ }
+
+ /**
+ * @return The node's absolute end offset.
+ */
+ @Override
+ public int getAbsoluteEnd()
+ {
+ return super.getEnd();
+ }
+
+ /**
+ * Collects the import nodes that are descendants of this node
+ * but which are not contained within a scoped node.
+ * <p>
+ * This is a helper method for {@link IScopedNode#getAllImportNodes}().
+ * Since that method walks up the chain of scoped nodes, we don't want
+ * to look inside scoped nodes that were already processed.
+ *
+ * @param importNodes The collection of import nodes being built.
+ */
+ public void collectImportNodes(Collection<IImportNode> importNodes)
+ {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i)
+ {
+ IASNode child = getChild(i);
+
+ if (child instanceof IImportNode)
+ importNodes.add((IImportNode)child);
+
+ else if (!(child instanceof IScopedNode))
+ ((NodeBase)child).collectImportNodes(importNodes);
+ }
+ }
+
+ /**
+ * Used only in asserts.
+ */
+ public boolean verify()
+ {
+ // Verify the id.
+ ASTNodeID id = getNodeID();
+ assert id != null &&
+ id != ASTNodeID.InvalidNodeID &&
+ id != ASTNodeID.UnknownID : "Definition has bad id";
+
+ // TODO: Verify the source location eventually.
+ // assert getSourcePath() != null : "Node has null source path:\n" + toString();
+ // assert getStart() != UNKNOWN : "Node has unknown start:\n" + toString();
+ // assert getEnd() != UNKNOWN : "Node has unknown end:\n" + toString();
+ // assert getLine() != UNKNOWN : "Node has unknown line:\n" + toString();
+ // assert getColumn() != UNKNOWN : "Node has unknown column:\n" + toString();
+
+ // Verify the children.
+ int n = getChildCount();
+ for (int i = 0; i < n; i++)
+ {
+ // Any null children?
+ NodeBase child = (NodeBase)getChild(i);
+ assert child != null : "Node has null child";
+
+ // Does each child have this node as its parent?
+ // (Note: Two node classes override getParent() to not return the parent,
+ // so exclude these for now.)
+ if (!(child instanceof NamespaceIdentifierNode || child instanceof QualifiedNamespaceExpressionNode))
+ {
+ assert child.getParent() == this : "Child node has bad parent";
+ }
+
+ // Recurse on each child.
+ child.verify();
+ }
+
+ return true;
+ }
+
+ /**
+ * Counts various types of nodes that are created,
+ * as well as the total number of nodes.
+ */
+ private void countNodes()
+ {
+ Counter counter = Counter.getInstance();
+ counter.incrementCount(getClass().getSimpleName());
+ counter.incrementCount("nodes");
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/beaf5b63/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java
----------------------------------------------------------------------
diff --cc compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java
index 5de6521,0000000..51eb0fc
mode 100644,000000..100644
--- a/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java
+++ b/compiler/src/main/java/org/apache/flex/compiler/internal/tree/as/OperatorNodeBase.java
@@@ -1,130 -1,0 +1,132 @@@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.flex.compiler.internal.tree.as;
+
+import org.apache.flex.compiler.internal.parsing.as.OffsetLookup;
+import org.apache.flex.compiler.parsing.IASToken;
+import org.apache.flex.compiler.tree.as.IOperatorNode;
+
+/**
+ * ActionScript parse tree node representing a binary operator expression (e.g.
+ * x + 2 or var1 == var2)
+ */
+public abstract class OperatorNodeBase extends ExpressionNodeBase implements IOperatorNode
+{
+ /**
+ * Constructor.
+ *
+ * @param operator ASToken holding the operator itself
+ */
+ public OperatorNodeBase(IASToken operator)
+ {
+ if (operator != null)
+ {
+ operatorStart = operator.getStart();
+ setLine(operator.getLine());
+ setColumn(operator.getColumn());
++ setEndLine(operator.getEndLine());
++ setEndColumn(operator.getEndColumn());
+ setSourcePath(operator.getSourcePath());
+ }
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param other The node to copy.
+ */
+ protected OperatorNodeBase(OperatorNodeBase other)
+ {
+ super(other);
+
+ this.operatorStart = other.operatorStart;
+ }
+
+ /**
+ * Offset where the operator starts
+ */
+ protected int operatorStart = UNKNOWN;
+
+ //
+ // NodeBase overrides
+ //
+
+ @Override
+ public boolean isTerminal()
+ {
+ return false;
+ }
+
+ /*
+ * For debugging only. Builds a string such as <code>"+"</code> from the
+ * operator.
+ */
+ @Override
+ protected boolean buildInnerString(StringBuilder sb)
+ {
+ sb.append('"');
+ sb.append(getOperatorText());
+ sb.append('"');
+
+ return true;
+ }
+
+ //
+ // IOperatorNode implementations
+ //
+
+ @Override
+ public int getOperatorStart()
+ {
+ final OffsetLookup offsetLookup = tryGetOffsetLookup();
+ return offsetLookup != null ?
+ offsetLookup.getLocalOffset(operatorStart) :
+ operatorStart;
+ }
+
+ @Override
+ public int getOperatorEnd()
+ {
+ int operatorStart = getOperatorStart();
+ return operatorStart != -1 ? operatorStart + getOperatorText().length() : operatorStart;
+ }
+
+ @Override
+ public int getOperatorAbsoluteStart()
+ {
+ return operatorStart;
+ }
+
+ @Override
+ public int getOperatorAbsoluteEnd()
+ {
+ return operatorStart != -1 ? operatorStart + getOperatorText().length() : operatorStart;
+ }
+
+ //
+ // Other methods
+ //
+
+ public String getOperatorText()
+ {
+ OperatorType operator = getOperator();
+ return operator != null ? operator.getOperatorText() : "";
+ }
+}