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() : "";
 +    }
 +}